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