387 lines
12 KiB
Python
387 lines
12 KiB
Python
from functools import reduce
|
|
from unittest.mock import Mock
|
|
|
|
import pytest
|
|
from typer.testing import CliRunner
|
|
|
|
from reflex.reflex import cli
|
|
from reflex.utils.hosting import DeploymentPrepInfo
|
|
|
|
runner = CliRunner()
|
|
|
|
|
|
def test_login_success_existing_token(mocker):
|
|
mocker.patch(
|
|
"reflex.utils.hosting.authenticated_token",
|
|
return_value=("fake-token", "fake-code"),
|
|
)
|
|
result = runner.invoke(cli, ["login"])
|
|
assert result.exit_code == 0
|
|
|
|
|
|
def test_login_success_on_browser(mocker):
|
|
mocker.patch(
|
|
"reflex.utils.hosting.authenticated_token",
|
|
return_value=("", "fake-code"),
|
|
)
|
|
mock_authenticate_on_browser = mocker.patch(
|
|
"reflex.utils.hosting.authenticate_on_browser", return_value="fake-token"
|
|
)
|
|
result = runner.invoke(cli, ["login"])
|
|
assert result.exit_code == 0
|
|
mock_authenticate_on_browser.assert_called_once_with("fake-code")
|
|
|
|
|
|
def test_login_fail(mocker):
|
|
# Access token does not exist, but user authenticates successfully on browser.
|
|
mocker.patch(
|
|
"reflex.utils.hosting.get_existing_access_token", return_value=("", "")
|
|
)
|
|
mocker.patch("reflex.utils.hosting.authenticate_on_browser", return_value="")
|
|
result = runner.invoke(cli, ["login"])
|
|
assert result.exit_code == 1
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"args",
|
|
[
|
|
["--no-interactive", "-k", "chatroom"],
|
|
["--no-interactive", "--deployment-key", "chatroom"],
|
|
["--no-interactive", "-r", "sjc"],
|
|
["--no-interactive", "--region", "sjc"],
|
|
["--no-interactive", "-r", "sjc", "-r", "lax"],
|
|
["--no-interactive", "-r", "sjc", "--region", "lax"],
|
|
],
|
|
)
|
|
def test_deploy_required_args_missing(args):
|
|
result = runner.invoke(cli, ["deploy", *args])
|
|
assert result.exit_code == 1
|
|
|
|
|
|
@pytest.fixture
|
|
def setup_env_authentication(mocker):
|
|
mocker.patch("reflex.utils.prerequisites.check_initialized")
|
|
mocker.patch("reflex.utils.hosting.authenticated_token", return_value="fake-token")
|
|
mocker.patch("time.sleep")
|
|
|
|
|
|
def test_deploy_non_interactive_prepare_failed(
|
|
mocker,
|
|
setup_env_authentication,
|
|
):
|
|
mocker.patch(
|
|
"reflex.utils.hosting.prepare_deploy",
|
|
side_effect=Exception("server did not like params in prepare"),
|
|
)
|
|
result = runner.invoke(
|
|
cli, ["deploy", "--no-interactive", "-k", "chatroom", "-r", "sjc"]
|
|
)
|
|
assert result.exit_code == 1
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"optional_args,values",
|
|
[
|
|
([], None),
|
|
(["--env", "k1=v1"], {"envs": {"k1": "v1"}}),
|
|
(["--cpus", 2], {"cpus": 2}),
|
|
(["--memory-mb", 2048], {"memory_mb": 2048}),
|
|
(["--no-auto-start"], {"auto_start": False}),
|
|
(["--no-auto-stop"], {"auto_stop": False}),
|
|
(
|
|
["--frontend-hostname", "myfrontend.com"],
|
|
{"frontend_hostname": "myfrontend.com"},
|
|
),
|
|
],
|
|
)
|
|
def test_deploy_non_interactive_success(
|
|
mocker, setup_env_authentication, optional_args, values
|
|
):
|
|
mocker.patch("reflex.utils.console.ask")
|
|
app_prefix = "fake-prefix"
|
|
mocker.patch(
|
|
"reflex.utils.hosting.prepare_deploy",
|
|
return_value=Mock(
|
|
app_prefix=app_prefix,
|
|
reply=Mock(
|
|
api_url="fake-api-url", deploy_url="fake-deploy-url", key="fake-key"
|
|
),
|
|
),
|
|
)
|
|
fake_export_dir = "fake-export-dir"
|
|
mocker.patch("tempfile.mkdtemp", return_value=fake_export_dir)
|
|
mocker.patch("reflex.reflex.export")
|
|
mock_deploy = mocker.patch(
|
|
"reflex.utils.hosting.deploy",
|
|
return_value=Mock(
|
|
frontend_url="fake-frontend-url", backend_url="fake-backend-url"
|
|
),
|
|
)
|
|
mocker.patch("reflex.utils.hosting.wait_for_server_to_pick_up_request")
|
|
mocker.patch("reflex.utils.hosting.poll_deploy_milestones")
|
|
mocker.patch("reflex.utils.hosting.poll_backend", return_value=True)
|
|
mocker.patch("reflex.utils.hosting.poll_frontend", return_value=True)
|
|
# TODO: typer option default not working in test for app name
|
|
deployment_key = "chatroom-0"
|
|
app_name = "chatroom"
|
|
regions = ["sjc"]
|
|
result = runner.invoke(
|
|
cli,
|
|
[
|
|
"deploy",
|
|
"--no-interactive",
|
|
"-k",
|
|
deployment_key,
|
|
*reduce(lambda x, y: x + ["-r", y], regions, []),
|
|
"--app-name",
|
|
app_name,
|
|
*optional_args,
|
|
],
|
|
)
|
|
assert result.exit_code == 0
|
|
|
|
expected_call_args = dict(
|
|
frontend_file_name="frontend.zip",
|
|
backend_file_name="backend.zip",
|
|
export_dir=fake_export_dir,
|
|
key=deployment_key,
|
|
app_name=app_name,
|
|
regions=regions,
|
|
app_prefix=app_prefix,
|
|
cpus=None,
|
|
memory_mb=None,
|
|
auto_start=None,
|
|
auto_stop=None,
|
|
frontend_hostname=None,
|
|
envs=None,
|
|
with_metrics=None,
|
|
with_tracing=None,
|
|
)
|
|
expected_call_args.update(values or {})
|
|
assert mock_deploy.call_args.kwargs == expected_call_args
|
|
|
|
|
|
def get_app_prefix():
|
|
return "fake-prefix"
|
|
|
|
|
|
def get_deployment_key():
|
|
return "i-want-this-site"
|
|
|
|
|
|
def get_suggested_key():
|
|
return "suggested-key"
|
|
|
|
|
|
def test_deploy_interactive_prepare_failed(
|
|
mocker,
|
|
setup_env_authentication,
|
|
):
|
|
mocker.patch(
|
|
"reflex.utils.hosting.prepare_deploy",
|
|
side_effect=Exception("server did not like params in prepare"),
|
|
)
|
|
result = runner.invoke(cli, ["deploy"])
|
|
assert result.exit_code == 1
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"app_prefix,deployment_key,prepare_responses,user_input_region,user_input_envs,expected_key,args_patch",
|
|
[
|
|
# CLI provides suggestion and but user enters a different key
|
|
(
|
|
get_app_prefix(),
|
|
get_deployment_key(),
|
|
Mock(
|
|
app_prefix=get_app_prefix(),
|
|
reply=None,
|
|
suggestion=Mock(
|
|
api_url="fake-api-url",
|
|
deploy_url="fake-deploy-url",
|
|
key=get_suggested_key(),
|
|
),
|
|
existing=None,
|
|
enabled_regions=["sjc"],
|
|
),
|
|
["sjc"],
|
|
[],
|
|
get_deployment_key(),
|
|
None,
|
|
),
|
|
# CLI provides suggestion and but user enters a different key and enters envs
|
|
(
|
|
get_app_prefix(),
|
|
get_deployment_key(),
|
|
Mock(
|
|
app_prefix=get_app_prefix(),
|
|
reply=None,
|
|
suggestion=Mock(
|
|
api_url="fake-api-url",
|
|
deploy_url="fake-deploy-url",
|
|
key=get_suggested_key(),
|
|
),
|
|
existing=None,
|
|
enabled_regions=["sjc"],
|
|
),
|
|
["sjc"],
|
|
["k1=v1", "k2=v2"],
|
|
get_deployment_key(),
|
|
{"envs": {"k1": "v1", "k2": "v2"}},
|
|
),
|
|
# CLI provides suggestion and but user takes it
|
|
(
|
|
get_app_prefix(),
|
|
get_deployment_key(),
|
|
Mock(
|
|
app_prefix=get_app_prefix(),
|
|
reply=None,
|
|
suggestion=Mock(
|
|
api_url="fake-api-url",
|
|
deploy_url="fake-deploy-url",
|
|
key=get_suggested_key(),
|
|
),
|
|
existing=None,
|
|
enabled_regions=["sjc"],
|
|
),
|
|
["sjc"],
|
|
[],
|
|
get_suggested_key(),
|
|
None,
|
|
),
|
|
# CLI provides suggestion and but user takes it and enters envs
|
|
(
|
|
get_app_prefix(),
|
|
get_deployment_key(),
|
|
Mock(
|
|
app_prefix=get_app_prefix(),
|
|
reply=None,
|
|
suggestion=Mock(
|
|
api_url="fake-api-url",
|
|
deploy_url="fake-deploy-url",
|
|
key=get_suggested_key(),
|
|
),
|
|
existing=None,
|
|
enabled_regions=["sjc"],
|
|
),
|
|
["sjc"],
|
|
["k1=v1", "k3=v3"],
|
|
get_suggested_key(),
|
|
{"envs": {"k1": "v1", "k3": "v3"}},
|
|
),
|
|
# User has an existing deployment
|
|
(
|
|
get_app_prefix(),
|
|
get_deployment_key(),
|
|
Mock(
|
|
app_prefix=get_app_prefix(),
|
|
reply=None,
|
|
existing=Mock(
|
|
__getitem__=lambda _, __: DeploymentPrepInfo(
|
|
api_url="fake-api-url",
|
|
deploy_url="fake-deploy-url",
|
|
key=get_deployment_key(),
|
|
)
|
|
),
|
|
suggestion=None,
|
|
enabled_regions=["sjc"],
|
|
),
|
|
["sjc"],
|
|
[],
|
|
get_deployment_key(),
|
|
None,
|
|
),
|
|
# User has an existing deployment then updates the envs
|
|
(
|
|
get_app_prefix(),
|
|
get_deployment_key(),
|
|
Mock(
|
|
app_prefix=get_app_prefix(),
|
|
reply=None,
|
|
existing=Mock(
|
|
__getitem__=lambda _, __: DeploymentPrepInfo(
|
|
api_url="fake-api-url",
|
|
deploy_url="fake-deploy-url",
|
|
key=get_deployment_key(),
|
|
)
|
|
),
|
|
suggestion=None,
|
|
enabled_regions=["sjc"],
|
|
),
|
|
["sjc"],
|
|
["k4=v4"],
|
|
get_deployment_key(),
|
|
{"envs": {"k4": "v4"}},
|
|
),
|
|
],
|
|
)
|
|
def test_deploy_interactive(
|
|
mocker,
|
|
setup_env_authentication,
|
|
app_prefix,
|
|
deployment_key,
|
|
prepare_responses,
|
|
user_input_region,
|
|
user_input_envs,
|
|
expected_key,
|
|
args_patch,
|
|
):
|
|
mocker.patch(
|
|
"reflex.utils.hosting.check_requirements_for_non_reflex_packages",
|
|
return_value=True,
|
|
)
|
|
mocker.patch(
|
|
"reflex.utils.hosting.prepare_deploy",
|
|
return_value=prepare_responses,
|
|
)
|
|
mocker.patch(
|
|
"reflex.utils.hosting.interactive_get_deployment_key_from_user_input",
|
|
return_value=(expected_key, "fake-api-url", "fake-deploy-url"),
|
|
)
|
|
mocker.patch("reflex.utils.console.ask", side_effect=user_input_region)
|
|
mocker.patch(
|
|
"reflex.utils.hosting.interactive_prompt_for_envs", return_value=user_input_envs
|
|
)
|
|
fake_export_dir = "fake-export-dir"
|
|
mocker.patch("tempfile.mkdtemp", return_value=fake_export_dir)
|
|
mocker.patch("reflex.reflex.export")
|
|
mock_deploy = mocker.patch(
|
|
"reflex.utils.hosting.deploy",
|
|
return_value=Mock(
|
|
frontend_url="fake-frontend-url", backend_url="fake-backend-url"
|
|
),
|
|
)
|
|
mocker.patch("reflex.utils.hosting.wait_for_server_to_pick_up_request")
|
|
mocker.patch("reflex.utils.hosting.poll_deploy_milestones")
|
|
mocker.patch("reflex.utils.hosting.poll_backend", return_value=True)
|
|
mocker.patch("reflex.utils.hosting.poll_frontend", return_value=True)
|
|
|
|
# TODO: typer option default not working in test for app name
|
|
app_name = "fake-app-workaround"
|
|
regions = ["sjc"]
|
|
result = runner.invoke(
|
|
cli,
|
|
["deploy", "--app-name", app_name],
|
|
)
|
|
assert result.exit_code == 0
|
|
|
|
expected_call_args = dict(
|
|
frontend_file_name="frontend.zip",
|
|
backend_file_name="backend.zip",
|
|
export_dir=fake_export_dir,
|
|
key=expected_key,
|
|
app_name=app_name,
|
|
regions=regions,
|
|
app_prefix=app_prefix,
|
|
cpus=None,
|
|
memory_mb=None,
|
|
auto_start=None,
|
|
auto_stop=None,
|
|
frontend_hostname=None,
|
|
envs=None,
|
|
with_metrics=None,
|
|
with_tracing=None,
|
|
)
|
|
expected_call_args.update(args_patch or {})
|
|
|
|
assert mock_deploy.call_args.kwargs == expected_call_args
|