reflex/tests/test_reflex.py

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