From 42199f7282711bf40dcc7a4028665cf0de900207 Mon Sep 17 00:00:00 2001 From: Anton Kukushkin Date: Wed, 14 Feb 2024 18:14:37 +0000 Subject: [PATCH] add basic unit tests Signed-off-by: Anton Kukushkin --- modules/mlflow/mlflow-fargate/coverage.ini | 3 + .../mlflow-fargate/requirements-dev.txt | 254 ++++++++++++++++++ .../mlflow/mlflow-fargate/tests/__init__.py | 0 .../mlflow/mlflow-fargate/tests/test_app.py | 46 ++++ .../mlflow/mlflow-fargate/tests/test_stack.py | 56 ++++ .../mlflow/mlflow-image/requirements-dev.txt | 2 +- modules/mlflow/mlflow-image/tests/__init__.py | 0 modules/mlflow/mlflow-image/tests/test_app.py | 30 +++ .../mlflow/mlflow-image/tests/test_stack.py | 43 +++ 9 files changed, 433 insertions(+), 1 deletion(-) create mode 100644 modules/mlflow/mlflow-fargate/coverage.ini create mode 100644 modules/mlflow/mlflow-fargate/requirements-dev.txt create mode 100644 modules/mlflow/mlflow-fargate/tests/__init__.py create mode 100644 modules/mlflow/mlflow-fargate/tests/test_app.py create mode 100644 modules/mlflow/mlflow-fargate/tests/test_stack.py create mode 100644 modules/mlflow/mlflow-image/tests/__init__.py create mode 100644 modules/mlflow/mlflow-image/tests/test_app.py create mode 100644 modules/mlflow/mlflow-image/tests/test_stack.py diff --git a/modules/mlflow/mlflow-fargate/coverage.ini b/modules/mlflow/mlflow-fargate/coverage.ini new file mode 100644 index 00000000..c3878739 --- /dev/null +++ b/modules/mlflow/mlflow-fargate/coverage.ini @@ -0,0 +1,3 @@ +[run] +omit = + tests/* \ No newline at end of file diff --git a/modules/mlflow/mlflow-fargate/requirements-dev.txt b/modules/mlflow/mlflow-fargate/requirements-dev.txt new file mode 100644 index 00000000..1864c0bb --- /dev/null +++ b/modules/mlflow/mlflow-fargate/requirements-dev.txt @@ -0,0 +1,254 @@ +# +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: +# +# pip-compile --output-file=requirements-dev.txt requirements-dev.in +# +annotated-types==0.6.0 + # via pydantic +attrs==23.2.0 + # via + # cattrs + # jschema-to-python + # jsii + # jsonschema + # referencing + # sarif-om +aws-cdk-asset-awscli-v1==2.2.202 + # via aws-cdk-lib +aws-cdk-asset-kubectl-v20==2.1.2 + # via aws-cdk-lib +aws-cdk-asset-node-proxy-agent-v6==2.0.1 + # via aws-cdk-lib +aws-cdk-lib==2.127.0 + # via cdk-nag +aws-sam-translator==1.84.0 + # via cfn-lint +awscli==1.32.41 + # via -r requirements-dev.in +black==24.2.0 + # via -r requirements-dev.in +boto3==1.34.41 + # via aws-sam-translator +botocore==1.34.41 + # via + # awscli + # boto3 + # s3transfer +build==1.0.3 + # via + # check-manifest + # pip-tools + # pyroma +cattrs==23.2.3 + # via jsii +cdk-nag==2.28.34 + # via -r requirements-dev.in +certifi==2024.2.2 + # via requests +cfn-lint==0.85.1 + # via -r requirements-dev.in +charset-normalizer==3.3.2 + # via requests +check-manifest==0.49 + # via -r requirements-dev.in +click==8.1.7 + # via + # black + # pip-tools +colorama==0.4.4 + # via awscli +constructs==10.3.0 + # via + # aws-cdk-lib + # cdk-nag +docutils==0.16 + # via + # awscli + # pyroma +exceptiongroup==1.2.0 + # via + # cattrs + # pytest +flake8==7.0.0 + # via -r requirements-dev.in +idna==3.6 + # via requests +importlib-metadata==7.0.1 + # via build +importlib-resources==6.1.1 + # via jsii +iniconfig==2.0.0 + # via pytest +isort==5.13.2 + # via -r requirements-dev.in +jmespath==1.0.1 + # via + # boto3 + # botocore +jschema-to-python==1.2.3 + # via cfn-lint +jsii==1.94.0 + # via + # aws-cdk-asset-awscli-v1 + # aws-cdk-asset-kubectl-v20 + # aws-cdk-asset-node-proxy-agent-v6 + # aws-cdk-lib + # cdk-nag + # constructs +jsonpatch==1.33 + # via cfn-lint +jsonpickle==3.0.2 + # via jschema-to-python +jsonpointer==2.4 + # via jsonpatch +jsonschema==4.21.1 + # via + # aws-sam-translator + # cfn-lint +jsonschema-specifications==2023.12.1 + # via jsonschema +junit-xml==1.9 + # via cfn-lint +mccabe==0.7.0 + # via flake8 +mpmath==1.3.0 + # via sympy +mypy==1.8.0 + # via -r requirements-dev.in +mypy-extensions==1.0.0 + # via + # black + # mypy +networkx==3.2.1 + # via cfn-lint +packaging==23.2 + # via + # black + # build + # pyroma + # pytest +pathspec==0.12.1 + # via black +pbr==6.0.0 + # via + # jschema-to-python + # sarif-om +pip-tools==7.3.0 + # via -r requirements-dev.in +platformdirs==4.2.0 + # via black +pluggy==1.4.0 + # via pytest +publication==0.0.3 + # via + # aws-cdk-asset-awscli-v1 + # aws-cdk-asset-kubectl-v20 + # aws-cdk-asset-node-proxy-agent-v6 + # aws-cdk-lib + # cdk-nag + # constructs + # jsii +pyasn1==0.5.1 + # via rsa +pycodestyle==2.11.1 + # via flake8 +pydantic==2.6.1 + # via aws-sam-translator +pydantic-core==2.16.2 + # via pydantic +pydot==2.0.0 + # via -r requirements-dev.in +pyflakes==3.2.0 + # via flake8 +pygments==2.17.2 + # via pyroma +pyparsing==3.1.1 + # via pydot +pyproject-hooks==1.0.0 + # via build +pyroma==4.2 + # via -r requirements-dev.in +pytest==8.0.0 + # via -r requirements-dev.in +python-dateutil==2.8.2 + # via + # botocore + # jsii +pyyaml==6.0.1 + # via + # awscli + # cfn-lint +referencing==0.33.0 + # via + # jsonschema + # jsonschema-specifications +regex==2023.12.25 + # via cfn-lint +requests==2.31.0 + # via pyroma +rpds-py==0.18.0 + # via + # jsonschema + # referencing +rsa==4.7.2 + # via awscli +s3transfer==0.10.0 + # via + # awscli + # boto3 +sarif-om==1.0.4 + # via cfn-lint +six==1.16.0 + # via + # junit-xml + # python-dateutil +sympy==1.12 + # via cfn-lint +tomli==2.0.1 + # via + # black + # build + # check-manifest + # mypy + # pip-tools + # pyproject-hooks + # pytest +trove-classifiers==2024.1.31 + # via pyroma +typeguard==2.13.3 + # via + # aws-cdk-asset-awscli-v1 + # aws-cdk-asset-kubectl-v20 + # aws-cdk-asset-node-proxy-agent-v6 + # aws-cdk-lib + # cdk-nag + # constructs + # jsii +types-pyyaml==6.0.12.12 + # via -r requirements-dev.in +types-setuptools==69.0.0.20240125 + # via -r requirements-dev.in +typing-extensions==4.9.0 + # via + # aws-sam-translator + # black + # cattrs + # jsii + # mypy + # pydantic + # pydantic-core +urllib3==1.26.18 + # via + # botocore + # requests +wheel==0.42.0 + # via pip-tools +zipp==3.17.0 + # via + # importlib-metadata + # importlib-resources + +# The following packages are considered to be unsafe in a requirements file: +# pip +# setuptools diff --git a/modules/mlflow/mlflow-fargate/tests/__init__.py b/modules/mlflow/mlflow-fargate/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/modules/mlflow/mlflow-fargate/tests/test_app.py b/modules/mlflow/mlflow-fargate/tests/test_app.py new file mode 100644 index 00000000..d0615f84 --- /dev/null +++ b/modules/mlflow/mlflow-fargate/tests/test_app.py @@ -0,0 +1,46 @@ +import os +import sys + +import pytest + + +@pytest.fixture(scope="function") +def stack_defaults(): + os.environ["SEEDFARMER_PROJECT_NAME"] = "test-project" + os.environ["SEEDFARMER_DEPLOYMENT_NAME"] = "test-deployment" + os.environ["SEEDFARMER_MODULE_NAME"] = "test-module" + os.environ["CDK_DEFAULT_ACCOUNT"] = "111111111111" + os.environ["CDK_DEFAULT_REGION"] = "us-east-1" + + os.environ["SEEDFARMER_PARAMETER_VPC_ID"] = "vpc-12345" + os.environ["SEEDFARMER_PARAMETER_ECR_REPOSITORY_NAME"] = "repo5" + os.environ["SEEDFARMER_PARAMETER_ARTIFACTS_BUCKET_NAME"] = "bucket" + + # Unload the app import so that subsequent tests don't reuse + if "app" in sys.modules: + del sys.modules["app"] + + +def test_app(stack_defaults): + import app # noqa: F401 + + +def test_vpc_id(stack_defaults): + del os.environ["SEEDFARMER_PARAMETER_VPC_ID"] + + with pytest.raises(Exception, match="Missing input parameter vpc-id"): + import app # noqa: F401 + + +def test_ecr_repository_name(stack_defaults): + del os.environ["SEEDFARMER_PARAMETER_ECR_REPOSITORY_NAME"] + + with pytest.raises(Exception, match="Missing input parameter ecr-repository-name"): + import app # noqa: F401 + + +def test_artifacts_bucket_name(stack_defaults): + del os.environ["SEEDFARMER_PARAMETER_ARTIFACTS_BUCKET_NAME"] + + with pytest.raises(Exception, match="Missing input parameter artifacts-bucket-name"): + import app # noqa: F401 diff --git a/modules/mlflow/mlflow-fargate/tests/test_stack.py b/modules/mlflow/mlflow-fargate/tests/test_stack.py new file mode 100644 index 00000000..ec05b2f1 --- /dev/null +++ b/modules/mlflow/mlflow-fargate/tests/test_stack.py @@ -0,0 +1,56 @@ +import os +import sys + +import aws_cdk as cdk +import pytest +from aws_cdk.assertions import Template + + +@pytest.fixture(scope="function") +def stack_defaults() -> None: + os.environ["CDK_DEFAULT_ACCOUNT"] = "111111111111" + os.environ["CDK_DEFAULT_REGION"] = "us-east-1" + + # Unload the app import so that subsequent tests don't reuse + if "stack" in sys.modules: + del sys.modules["stack"] + + +def test_synthesize_stack() -> None: + import stack + + app = cdk.App() + + project_name = "test-project" + dep_name = "test-deployment" + mod_name = "test-module" + app_prefix = f"{project_name}-{dep_name}-{mod_name}" + + vpc_id = "vpc-123" + subnet_ids = [] + ecr_repo_name = "repo" + task_cpu_units = 4 * 1024 + task_memory_limit_mb = 8 * 1024 + artifacts_bucket_name = "bucket" + + stack = stack.MlflowFargateStack( + scope=app, + id=app_prefix, + app_prefix=app_prefix, + vpc_id=vpc_id, + subnet_ids=subnet_ids, + ecs_cluster_name=None, + service_name=None, + ecr_repo_name=ecr_repo_name, + task_cpu_units=task_cpu_units, + task_memory_limit_mb=task_memory_limit_mb, + artifacts_bucket_name=artifacts_bucket_name, + env=cdk.Environment( + account=os.environ["CDK_DEFAULT_ACCOUNT"], + region=os.environ["CDK_DEFAULT_REGION"], + ), + ) + + template = Template.from_stack(stack) + template.resource_count_is("AWS::ECS::Cluster", 1) + template.resource_count_is("AWS::ECS::TaskDefinition", 1) diff --git a/modules/mlflow/mlflow-image/requirements-dev.txt b/modules/mlflow/mlflow-image/requirements-dev.txt index 59d38e3f..96ce3195 100644 --- a/modules/mlflow/mlflow-image/requirements-dev.txt +++ b/modules/mlflow/mlflow-image/requirements-dev.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile with Python 3.9 # by the following command: # -# pip-compile requirements-dev.in +# pip-compile --output-file=requirements-dev.txt requirements-dev.in # aiohttp==3.9.2 # via black diff --git a/modules/mlflow/mlflow-image/tests/__init__.py b/modules/mlflow/mlflow-image/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/modules/mlflow/mlflow-image/tests/test_app.py b/modules/mlflow/mlflow-image/tests/test_app.py new file mode 100644 index 00000000..4a2d61c2 --- /dev/null +++ b/modules/mlflow/mlflow-image/tests/test_app.py @@ -0,0 +1,30 @@ +import os +import sys + +import pytest + + +@pytest.fixture(scope="function") +def stack_defaults(): + os.environ["SEEDFARMER_PROJECT_NAME"] = "test-project" + os.environ["SEEDFARMER_DEPLOYMENT_NAME"] = "test-deployment" + os.environ["SEEDFARMER_MODULE_NAME"] = "test-module" + os.environ["CDK_DEFAULT_ACCOUNT"] = "111111111111" + os.environ["CDK_DEFAULT_REGION"] = "us-east-1" + + os.environ["SEEDFARMER_PARAMETER_ECR_REPOSITORY_NAME"] = "repo" + + # Unload the app import so that subsequent tests don't reuse + if "app" in sys.modules: + del sys.modules["app"] + + +def test_app(stack_defaults): + import app # noqa: F401 + + +def test_vpc_id(stack_defaults): + del os.environ["SEEDFARMER_PARAMETER_ECR_REPOSITORY_NAME"] + + with pytest.raises(Exception, match="Missing input parameter ecr-repository-name"): + import app # noqa: F401 diff --git a/modules/mlflow/mlflow-image/tests/test_stack.py b/modules/mlflow/mlflow-image/tests/test_stack.py new file mode 100644 index 00000000..d60ac344 --- /dev/null +++ b/modules/mlflow/mlflow-image/tests/test_stack.py @@ -0,0 +1,43 @@ +import os +import sys + +import aws_cdk as cdk +import pytest +from aws_cdk.assertions import Template + + +@pytest.fixture(scope="function") +def stack_defaults() -> None: + os.environ["CDK_DEFAULT_ACCOUNT"] = "111111111111" + os.environ["CDK_DEFAULT_REGION"] = "us-east-1" + + # Unload the app import so that subsequent tests don't reuse + if "stack" in sys.modules: + del sys.modules["stack"] + + +def test_synthesize_stack() -> None: + import stack + + app = cdk.App() + + project_name = "test-project" + dep_name = "test-deployment" + mod_name = "test-module" + app_prefix = f"{project_name}-{dep_name}-{mod_name}" + + ecr_repo_name = "repo" + + stack = stack.MlflowImagePublishingStack( + scope=app, + id=app_prefix, + app_prefix=app_prefix, + ecr_repo_name=ecr_repo_name, + env=cdk.Environment( + account=os.environ["CDK_DEFAULT_ACCOUNT"], + region=os.environ["CDK_DEFAULT_REGION"], + ), + ) + + template = Template.from_stack(stack) + template.resource_count_is("Custom::CDKBucketDeployment", 1)