diff --git a/tests/tests_unit/cli_validate/aws.error.kubernetes-version.yaml b/tests/tests_unit/cli_validate/aws.error.kubernetes-version.yaml new file mode 100644 index 0000000000..ec07330960 --- /dev/null +++ b/tests/tests_unit/cli_validate/aws.error.kubernetes-version.yaml @@ -0,0 +1,3 @@ +project_name: test +amazon_web_services: + kubernetes_version: '1.0' diff --git a/tests/tests_unit/cli_validate/aws.happy.yaml b/tests/tests_unit/cli_validate/aws.happy.yaml new file mode 100644 index 0000000000..7c91a7adfc --- /dev/null +++ b/tests/tests_unit/cli_validate/aws.happy.yaml @@ -0,0 +1,27 @@ +provider: aws +namespace: dev +nebari_version: 2023.7.2.dev23+g53d17964.d20230824 +project_name: test +domain: test.example.com +ci_cd: + type: none +terraform_state: + type: local +security: + keycloak: + initial_root_password: i8mawmz1ek6s9n9ms6ifgwborgryve8q + authentication: + type: password +theme: + jupyterhub: + hub_title: Nebari - test + welcome: Welcome! Learn about Nebari's features and configurations in the + documentation. If you have any questions or feedback, reach the team on + Nebari's support + forums. + hub_subtitle: Your open source data science platform, hosted on Amazon Web Services +certificate: + type: lets-encrypt + acme_email: test@example.com +amazon_web_services: + kubernetes_version: '1.20' diff --git a/tests/tests_unit/cli_validate/azure.happy.yaml b/tests/tests_unit/cli_validate/azure.happy.yaml new file mode 100644 index 0000000000..bf938b37c3 --- /dev/null +++ b/tests/tests_unit/cli_validate/azure.happy.yaml @@ -0,0 +1,27 @@ +provider: azure +namespace: dev +nebari_version: 2023.7.2.dev23+g53d17964.d20230824 +project_name: test +domain: test.example.com +ci_cd: + type: none +terraform_state: + type: local +security: + keycloak: + initial_root_password: m1s25vc4k43dxbk5jaxubxcq39n4vmjq + authentication: + type: password +theme: + jupyterhub: + hub_title: Nebari - test + welcome: Welcome! Learn about Nebari's features and configurations in the + documentation. If you have any questions or feedback, reach the team on + Nebari's support + forums. + hub_subtitle: Your open source data science platform, hosted on Azure +certificate: + type: lets-encrypt + acme_email: test@example.com +azure: + kubernetes_version: '1.20' diff --git a/tests/tests_unit/cli_validate/do.happy.yaml b/tests/tests_unit/cli_validate/do.happy.yaml new file mode 100644 index 0000000000..0a7b64007d --- /dev/null +++ b/tests/tests_unit/cli_validate/do.happy.yaml @@ -0,0 +1,27 @@ +provider: do +namespace: dev +nebari_version: 2023.7.2.dev23+g53d17964.d20230824 +project_name: test +domain: test.example.com +ci_cd: + type: none +terraform_state: + type: local +security: + keycloak: + initial_root_password: m1s25vc4k43dxbk5jaxubxcq39n4vmjq + authentication: + type: password +theme: + jupyterhub: + hub_title: Nebari - test + welcome: Welcome! Learn about Nebari's features and configurations in the + documentation. If you have any questions or feedback, reach the team on + Nebari's support + forums. + hub_subtitle: Your open source data science platform, hosted on Azure +certificate: + type: lets-encrypt + acme_email: test@example.com +digital_ocean: + kubernetes_version: '1.20.2-do.0' diff --git a/tests/tests_unit/cli_validate/gcp.happy.yaml b/tests/tests_unit/cli_validate/gcp.happy.yaml new file mode 100644 index 0000000000..cbea1e61e2 --- /dev/null +++ b/tests/tests_unit/cli_validate/gcp.happy.yaml @@ -0,0 +1,27 @@ +provider: gcp +namespace: dev +nebari_version: 2023.7.2.dev23+g53d17964.d20230824 +project_name: test +domain: test.example.com +ci_cd: + type: none +terraform_state: + type: local +security: + keycloak: + initial_root_password: m1s25vc4k43dxbk5jaxubxcq39n4vmjq + authentication: + type: password +theme: + jupyterhub: + hub_title: Nebari - test + welcome: Welcome! Learn about Nebari's features and configurations in the + documentation. If you have any questions or feedback, reach the team on + Nebari's support + forums. + hub_subtitle: Your open source data science platform, hosted on Azure +certificate: + type: lets-encrypt + acme_email: test@example.com +google_cloud_platform: + kubernetes_version: '1.20' diff --git a/tests/tests_unit/cli_validate/local.error.extra-fields.yaml b/tests/tests_unit/cli_validate/local.error.extra-fields.yaml new file mode 100644 index 0000000000..b664558368 --- /dev/null +++ b/tests/tests_unit/cli_validate/local.error.extra-fields.yaml @@ -0,0 +1,2 @@ +project_name: test +this_is_an_error: true diff --git a/tests/tests_unit/cli_validate/local.error.project_name.yaml b/tests/tests_unit/cli_validate/local.error.project_name.yaml new file mode 100644 index 0000000000..5b04f1bb12 --- /dev/null +++ b/tests/tests_unit/cli_validate/local.error.project_name.yaml @@ -0,0 +1 @@ +project_name: 123invalidproject diff --git a/tests/tests_unit/cli_validate/local.happy.yaml b/tests/tests_unit/cli_validate/local.happy.yaml new file mode 100644 index 0000000000..e6ec771e09 --- /dev/null +++ b/tests/tests_unit/cli_validate/local.happy.yaml @@ -0,0 +1,25 @@ +provider: local +namespace: dev +nebari_version: 2023.7.2.dev23+g53d17964.d20230824 +project_name: test +domain: test.example.com +ci_cd: + type: none +terraform_state: + type: local +security: + keycloak: + initial_root_password: muwti3n4d7m81c1svcgaahwhfi869yhg + authentication: + type: password +theme: + jupyterhub: + hub_title: Nebari - test + welcome: Welcome! Learn about Nebari's features and configurations in the + documentation. If you have any questions or feedback, reach the team on + Nebari's support + forums. + hub_subtitle: Your open source data science platform, hosted +certificate: + type: lets-encrypt + acme_email: test@example.com diff --git a/tests/tests_unit/cli_validate/min.happy.yaml b/tests/tests_unit/cli_validate/min.happy.yaml new file mode 100644 index 0000000000..2194cd0d1c --- /dev/null +++ b/tests/tests_unit/cli_validate/min.happy.yaml @@ -0,0 +1 @@ +project_name: test diff --git a/tests/tests_unit/test_cli_init.py b/tests/tests_unit/test_cli_init.py index b7e831bf89..ce4695efff 100644 --- a/tests/tests_unit/test_cli_init.py +++ b/tests/tests_unit/test_cli_init.py @@ -9,16 +9,11 @@ from typer.testing import CliRunner from _nebari.cli import create_cli -from _nebari.provider.cloud import ( - amazon_web_services, - azure_cloud, - digital_ocean, - google_cloud, -) runner = CliRunner() -MOCK_KUBERNETES_VERSIONS = ["1.24"] +TEST_KUBERNETES_VERSION = {"default": "1.20", "do": "1.20.2-do.0"} + MOCK_ENV = { k: "test" for k in [ @@ -95,9 +90,11 @@ def generate_test_data_test_all_init_happy_path(): for ci_provider in ["none", "github-actions", "gitlab-ci"]: for terraform_state in ["local", "remote", "existing"]: for email in ["noreply@example.com"]: - for ( - kubernetes_version - ) in MOCK_KUBERNETES_VERSIONS + ["latest"]: + for kubernetes_version in [ + TEST_KUBERNETES_VERSION[provider] + if provider in TEST_KUBERNETES_VERSION + else TEST_KUBERNETES_VERSION["default"] + ] + ["latest"]: test_data.append( ( provider, @@ -129,7 +126,6 @@ def generate_test_data_test_all_init_happy_path(): def test_all_init_happy_path( - monkeypatch, provider: str, project_name: str, domain_name: str, @@ -141,20 +137,6 @@ def test_all_init_happy_path( email: str, kubernetes_version: str, ): - # the kubernetes-version parameter can trigger calls out to AWS, Azure, etc... to validate, mocking - monkeypatch.setattr( - amazon_web_services, "kubernetes_versions", lambda: MOCK_KUBERNETES_VERSIONS - ) - monkeypatch.setattr( - azure_cloud, "kubernetes_versions", lambda: MOCK_KUBERNETES_VERSIONS - ) - monkeypatch.setattr( - digital_ocean, "kubernetes_versions", lambda _: MOCK_KUBERNETES_VERSIONS - ) - monkeypatch.setattr( - google_cloud, "kubernetes_versions", lambda _: MOCK_KUBERNETES_VERSIONS - ) - app = create_cli() args = [ "init", diff --git a/tests/tests_unit/test_cli_validate.py b/tests/tests_unit/test_cli_validate.py new file mode 100644 index 0000000000..1afc5cd431 --- /dev/null +++ b/tests/tests_unit/test_cli_validate.py @@ -0,0 +1,140 @@ +import re +from pathlib import Path +from typing import List + +import pytest +from typer.testing import CliRunner + +from _nebari.cli import create_cli + +TEST_DATA_DIR = Path(__file__).resolve().parent / "cli_validate" + +MOCK_ENV = { + k: "test" + for k in [ + "AWS_ACCESS_KEY_ID", + "AWS_SECRET_ACCESS_KEY", # aws + "GOOGLE_CREDENTIALS", + "PROJECT_ID", # gcp + "ARM_SUBSCRIPTION_ID", + "ARM_TENANT_ID", + "ARM_CLIENT_ID", + "ARM_CLIENT_SECRET", # azure + "DIGITALOCEAN_TOKEN", + "SPACES_ACCESS_KEY_ID", + "SPACES_SECRET_ACCESS_KEY", # digital ocean + ] +} + +runner = CliRunner() + + +@pytest.mark.parametrize( + "args, exit_code, content", + [ + # --help + (["--help"], 0, ["Usage:"]), + (["-h"], 0, ["Usage:"]), + # error, missing args + ([], 2, ["Missing option"]), + (["--config"], 2, ["requires an argument"]), + (["-c"], 2, ["requires an argument"]), + ( + ["--enable-commenting"], + 2, + ["Missing option"], + ), # /~https://github.com/nebari-dev/nebari/issues/1937 + ], +) +def test_validate_stdout(args: List[str], exit_code: int, content: List[str]): + app = create_cli() + result = runner.invoke(app, ["validate"] + args) + assert result.exit_code == exit_code + for c in content: + assert c in result.stdout + + +def generate_test_data_test_validate_local_happy_path(): + """ + Search the cli_validate folder for happy path test cases + and add them to the parameterized list of inputs for + test_validate_local_happy_path + """ + + test_data = [] + for f in TEST_DATA_DIR.iterdir(): + if f.is_file() and re.match(r"^\w*\.happy\.yaml$", f.name): # sample.happy.yaml + test_data.append((f.name)) + keys = [ + "config_yaml", + ] + return {"keys": keys, "test_data": test_data} + + +def test_validate_local_happy_path(config_yaml: str): + test_file = TEST_DATA_DIR / config_yaml + assert test_file.exists() is True + + app = create_cli() + result = runner.invoke(app, ["validate", "--config", test_file], env=MOCK_ENV) + assert not result.exception + assert 0 == result.exit_code + assert "Successfully validated configuration" in result.stdout + + +def generate_test_data_test_validate_error(): + """ + Search the cli_validate folder for unhappy path test cases + and add them to the parameterized list of inputs for + test_validate_error. Optionally parse an expected + error message from the file name to assert is present + in the validate output + """ + + test_data = [] + for f in TEST_DATA_DIR.iterdir(): + if f.is_file(): + m = re.match( + r"^\w*\.error\.([\w-]*)\.yaml$", f.name + ) # sample.error.message.yaml + if m: + test_data.append((f.name, m.groups()[0])) + elif re.match(r"^\w*\.error\.yaml$", f.name): # sample.error.yaml + test_data.append((f.name, None)) + keys = [ + "config_yaml", + "expected_message", + ] + return {"keys": keys, "test_data": test_data} + + +def test_validate_error(config_yaml: str, expected_message: str): + test_file = TEST_DATA_DIR / config_yaml + assert test_file.exists() is True + + app = create_cli() + result = runner.invoke(app, ["validate", "--config", test_file], env=MOCK_ENV) + print(result.stdout) + assert result.exception + assert 1 == result.exit_code + assert "ERROR validating configuration" in result.stdout + if expected_message: + # since this will usually come from a parsed filename, assume spacing/hyphenation/case is optional + assert (expected_message in result.stdout.lower()) or ( + expected_message.replace("-", " ").replace("_", " ") + in result.stdout.lower() + ) + + +def pytest_generate_tests(metafunc): + """ + Dynamically generate test data parameters for test functions by looking for + and executing an associated generate_test_data_{function_name} if one exists. + """ + + try: + td = eval(f"generate_test_data_{metafunc.function.__name__}")() + metafunc.parametrize(",".join(td["keys"]), td["test_data"]) + except Exception: + # expected when a generate_test_data_ function doesn't exist + pass