From 154d42d5c832f75246080b87febc45a8c98dbfe6 Mon Sep 17 00:00:00 2001 From: saville Date: Thu, 1 Feb 2024 16:36:22 -0700 Subject: [PATCH] Prepare to use pydantic config Refactor most validation tests to be parameterized Remove redundant tests --- buildrunner/__init__.py | 3 +- buildrunner/config.py | 4 +- buildrunner/validation/config.py | 19 +- tests/test_config_validation/conftest.py | 32 ++ .../test_global_config.py | 53 ++- .../test_multi_platform.py | 79 ++-- .../test_config_validation/test_retagging.py | 307 +++++++------- .../test_validation_artifacts.py | 151 +++---- .../test_validation_config.py | 392 +++--------------- .../test_validation_run.py | 52 --- .../test_validation_step.py | 293 ++++++------- 11 files changed, 491 insertions(+), 894 deletions(-) create mode 100644 tests/test_config_validation/conftest.py delete mode 100644 tests/test_config_validation/test_validation_run.py diff --git a/buildrunner/__init__.py b/buildrunner/__init__.py index 2b1463d4..41f5e4f9 100644 --- a/buildrunner/__init__.py +++ b/buildrunner/__init__.py @@ -129,7 +129,7 @@ def __init__( push: bool, cleanup_images: bool, cleanup_cache: bool, - steps_to_run: List[str], + steps_to_run: Optional[List[str]], publish_ports: bool, log_generated_files: bool, docker_timeout: int, @@ -409,7 +409,6 @@ def get_filename(caches_root, cache_name): cache_name = f"{project_name}-{cache_name}" caches_root = self.global_config.get("caches-root", DEFAULT_CACHES_ROOT) - local_cache_archive_file = None try: local_cache_archive_file = get_filename(caches_root, cache_name) except Exception as exc: # pylint: disable=broad-except diff --git a/buildrunner/config.py b/buildrunner/config.py index fa0e0f9a..95e2290b 100644 --- a/buildrunner/config.py +++ b/buildrunner/config.py @@ -37,7 +37,7 @@ load_config, ) -from buildrunner.validation.config import validate_config +from buildrunner.validation.config import generate_and_validate_config from . import fetch @@ -461,7 +461,7 @@ def load_config(self, cfg_file, ctx=None, log_file=True, default_tag=None) -> di # Always add default tag if not set config = self._set_default_tag(config, default_tag) - errors = validate_config(**config) + _, errors = generate_and_validate_config(**config) if errors: raise BuildRunnerConfigurationError( f"Invalid configuration, {errors.count()} error(s) found:" diff --git a/buildrunner/validation/config.py b/buildrunner/validation/config.py index fa244424..402f4b98 100644 --- a/buildrunner/validation/config.py +++ b/buildrunner/validation/config.py @@ -7,7 +7,7 @@ """ import os -from typing import Any, Dict, List, Optional, Set, Union +from typing import Any, Dict, List, Optional, Set, Tuple, Union # pylint: disable=no-name-in-module from pydantic import BaseModel, Field, field_validator, ValidationError @@ -327,7 +327,7 @@ def validate_multi_platform_build(mp_push_tags: Set[str]): ) if step.run: - raise ValueError(f"{RUN_MP_ERROR_MESSAGE} {step_name}") + raise ValueError(f"{RUN_MP_ERROR_MESSAGE} step {step_name}") # Check for valid push section, duplicate mp tags are not allowed validate_push(step.push, mp_push_tags, step_name) @@ -359,16 +359,15 @@ def validate_multi_platform_build(mp_push_tags: Set[str]): return vals -def validate_config(**kwargs) -> Errors: +def generate_and_validate_config(**kwargs) -> Tuple[Optional[Config], Optional[Errors]]: """ - Check if the config file is valid + Check if the config file is valid and return the config instance or validation errors. - Raises: - ValueError | pydantic.ValidationError : If the config file is invalid + Returns: + errors.Errors : If the config file is invalid + Config : If the config file is valid """ - errors = None try: - Config(**kwargs) + return Config(**kwargs), None except ValidationError as exc: - errors = get_validation_errors(exc) - return errors + return None, get_validation_errors(exc) diff --git a/tests/test_config_validation/conftest.py b/tests/test_config_validation/conftest.py new file mode 100644 index 00000000..dd13ec4e --- /dev/null +++ b/tests/test_config_validation/conftest.py @@ -0,0 +1,32 @@ +import os +from typing import List, Union + +import pytest +import yaml + +from buildrunner.validation.config import generate_and_validate_config, Errors + + +@pytest.fixture(autouse=True) +def set_cwd(): + # Some of the validation tests rely on loading the Dockerfiles in the correct directories, so set the path to the + # top-level project folder (i.e. the root of the repository) + os.chdir(os.path.realpath(os.path.join(os.path.dirname(__file__), "../.."))) + + +@pytest.fixture() +def assert_generate_and_validate_config_errors(): + def _func(config_data: Union[str, dict], error_matches: List[str]): + if isinstance(config_data, str): + config_data = yaml.load(config_data, Loader=yaml.Loader) + config, errors = generate_and_validate_config(**config_data) + if error_matches: + assert not config + assert isinstance(errors, Errors) + for index, error_match in enumerate(error_matches): + assert error_match in errors.errors[index].message + else: + assert config + assert not errors + + return _func diff --git a/tests/test_config_validation/test_global_config.py b/tests/test_config_validation/test_global_config.py index ecccd07f..56fbfe18 100644 --- a/tests/test_config_validation/test_global_config.py +++ b/tests/test_config_validation/test_global_config.py @@ -1,11 +1,11 @@ -import yaml +import pytest -from buildrunner.validation.config import validate_config -from buildrunner.validation.errors import Errors - -def test_global_config(): - config_yaml = """ +@pytest.mark.parametrize( + "config_yaml, error_matches", + [ + ( + """ # The 'env' global configuration may be used to set environment variables # available to all buildrunner runs that load this config file. Env vars do # not need to begin with a prefix to be included in this list (i.e. @@ -70,24 +70,18 @@ def test_global_config(): # Setting the TMP, TMPDIR, or TEMP env vars should do the same thing, # but on some systems it may be necessary to use this instead. temp-dir: /my/tmp/dir - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - -def test_global_config_ssh_key_file(): - config_yaml = """ + """, + [], + ), + ( + """ ssh-keys: - file: /path/to/ssh/private/key.pem - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - -def test_global_config_ssh_invalid(): - config_yaml = """ + """, + [], + ), + ( + """ ssh-keys: key: | -----INLINE KEY----- @@ -98,9 +92,12 @@ def test_global_config_ssh_invalid(): aliases: - 'my-github-key' bogus-attribute: 'bogus' - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - print(errors) - assert isinstance(errors, Errors) - assert errors.count() > 0 + """, + ["Extra inputs are not permitted", "Input should be a valid list"], + ), + ], +) +def test_config_data( + config_yaml, error_matches, assert_generate_and_validate_config_errors +): + assert_generate_and_validate_config_errors(config_yaml, error_matches) diff --git a/tests/test_config_validation/test_multi_platform.py b/tests/test_config_validation/test_multi_platform.py index b27a5517..86104591 100644 --- a/tests/test_config_validation/test_multi_platform.py +++ b/tests/test_config_validation/test_multi_platform.py @@ -1,10 +1,16 @@ -import yaml -from buildrunner.validation.config import validate_config, Errors, RUN_MP_ERROR_MESSAGE +import pytest +from buildrunner.validation.config import ( + RUN_MP_ERROR_MESSAGE, +) -def test_no_run_with_multiplatform_build(): - # Run in multi platform build is not supported - config_yaml = """ + +@pytest.mark.parametrize( + "config_yaml, error_matches", + [ + # Run in multiplatform build is not supported + ( + """ steps: build-container-multi-platform: build: @@ -19,16 +25,14 @@ def test_no_run_with_multiplatform_build(): run: image: user1/buildrunner-test-multi-platform cmd: echo "Hello World" - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert isinstance(errors, Errors) - assert errors.count() == 1 - - -def test_no_run_with_single_build(): - # Run in single platform build is supported - config_yaml = """ + """, + [ + "run is not allowed in the same step as a multi-platform build step build-container-multi-platform" + ], + ), + # Run in single platform build is supported + ( + """ steps: build-container: build: @@ -40,15 +44,12 @@ def test_no_run_with_single_build(): run: image: user1/buildrunner-test-multi-platform cmd: echo "Hello World" - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - -def test_invalid_post_build(): - # Post build is not supported for multi platform builds - config_yaml = """ + """, + [], + ), + # Post build is not supported for multiplatform builds + ( + """ steps: build-container-multi-platform: build: @@ -59,17 +60,12 @@ def test_invalid_post_build(): - linux/arm64/v8 run: post-build: path/to/build/context - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert isinstance(errors, Errors) - assert errors.count() == 1 - assert RUN_MP_ERROR_MESSAGE in errors.errors[0].message - - -def test_valid_post_build(): - # Post build is supported for single platform builds - config_yaml = """ + """, + [RUN_MP_ERROR_MESSAGE], + ), + # Post build is supported for single platform builds + ( + """ steps: build-container-single-platform: build: @@ -77,7 +73,12 @@ def test_valid_post_build(): FROM busybox:latest run: post-build: path/to/build/context - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None + """, + [], + ), + ], +) +def test_config_data( + config_yaml, error_matches, assert_generate_and_validate_config_errors +): + assert_generate_and_validate_config_errors(config_yaml, error_matches) diff --git a/tests/test_config_validation/test_retagging.py b/tests/test_config_validation/test_retagging.py index ce301e38..740bab5b 100644 --- a/tests/test_config_validation/test_retagging.py +++ b/tests/test_config_validation/test_retagging.py @@ -1,20 +1,23 @@ import os -import tempfile from unittest import mock import pytest -import yaml from buildrunner import BuildRunner +from buildrunner.validation.config import ( + RETAG_ERROR_MESSAGE, +) from buildrunner.errors import BuildRunnerConfigurationError -from buildrunner.validation.config import validate_config, Errors, RETAG_ERROR_MESSAGE TEST_DIR = os.path.dirname(os.path.abspath(__file__)) BLANK_GLOBAL_CONFIG = os.path.join(TEST_DIR, "files/blank_global_config.yaml") -def test_invalid_multiplatform_retagging_with_push(): - # Retagging a multiplatform image is not supported - config_yaml = """ +@pytest.mark.parametrize( + "config_yaml, error_matches", + [ + # Retagging a multiplatform image is not supported + ( + """ steps: build-container-multi-platform: build: @@ -29,17 +32,15 @@ def test_invalid_multiplatform_retagging_with_push(): image: user1/buildrunner-multi-platform-image:latest cmd: echo "Hello World" push: user1/buildrunner-multi-platform-image2:latest - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert isinstance(errors, Errors) - assert errors.count() == 1 - - -def test_invalid_multiplatform_retagging_latest_tag(): - # Retagging a multiplatform image is not supported - # Tests adding 'latest' tag when left out - config_yaml = """ + """, + [ + "The following images are re-tagged: ['user1/buildrunner-multi-platform-image:latest']" + ], + ), + # Retagging a multiplatform image is not supported + # Tests adding 'latest' tag when left out + ( + """ steps: build-container-multi-platform: build: @@ -54,15 +55,12 @@ def test_invalid_multiplatform_retagging_latest_tag(): image: user1/buildrunner-multi-platform-image:latest cmd: echo "Hello World" push: user1/buildrunner-multi-platform-image2 - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - -def test_invalid_multiplatform_retagging_with_push_empty_tags(): - # Retagging a multiplatform image is not supported - config_yaml = """ + """, + [], + ), + # Retagging a multiplatform image is not supported + ( + """ steps: build-container-multi-platform: build: @@ -82,17 +80,15 @@ def test_invalid_multiplatform_retagging_with_push_empty_tags(): push: repository: user1/buildrunner-test-multi-platform2 tags: [] - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert isinstance(errors, Errors) - assert errors.count() == 1 - - -def test_invalid_multiplatform_retagging_with_commit(): - # Retagging a multiplatform image is not supported - # Tests with commit in build step - config_yaml = """ + """, + [ + "The following images are re-tagged: ['user1/buildrunner-test-multi-platform']" + ], + ), + # Retagging a multiplatform image is not supported + # Tests with commit in build step + ( + """ steps: build-container-multi-platform: build: @@ -105,19 +101,17 @@ def test_invalid_multiplatform_retagging_with_commit(): retag-multi-platform-image: run: image: user1/buildrunner-multi-platform-image - command: echo "Hello World" + cmd: echo "Hello World" push: user1/buildrunner-multi-platform-image2 - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert isinstance(errors, Errors) - assert errors.count() == 1 - - -def test_invalid_multiplatform_retagging_with_commit2(): - # Retagging a multiplatform image is not supported - # Tests with commit after build step - config_yaml = """ + """, + [ + "The following images are re-tagged: ['user1/buildrunner-multi-platform-image']" + ], + ), + # Retagging a multiplatform image is not supported + # Tests with commit after build step + ( + """ steps: build-container-multi-platform: build: @@ -133,16 +127,14 @@ def test_invalid_multiplatform_retagging_with_commit2(): image: user1/buildrunner-test-multi-platform cmd: echo "Hello World" commit: user1/buildrunner-test-multi-platform2 - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert isinstance(errors, Errors) - assert errors.count() == 1 - - -def test_valid_single_platform_retagging(): - # Retagging a single platform image is supported - config_yaml = """ + """, + [ + "The following images are re-tagged: ['user1/buildrunner-test-multi-platform']" + ], + ), + # Retagging a single platform image is supported + ( + """ steps: build-container-single-platform: build: @@ -155,16 +147,13 @@ def test_valid_single_platform_retagging(): image: user1/buildrunner-test-single-platform:latest cmd: echo "Hello World" push: user1/buildrunner-test-single-platform2 - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - -def test_invalid_multiplatform_rebuild_and_push(): - # Retagging a multiplatform image is not supported - # Tests reading from dockerfile for the 2nd dockerfile - config_yaml = """ + """, + [], + ), + # Retagging a multiplatform image is not supported + # Tests reading from dockerfile for the 2nd dockerfile + ( + """ steps: build-container-multi-platform: build: @@ -179,17 +168,15 @@ def test_invalid_multiplatform_rebuild_and_push(): dockerfile: | FROM user1/buildrunner-multi-platform-image push: user1/buildrunner-multi-platform-image2 - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert isinstance(errors, Errors) - assert errors.count() == 1 - - -def test_invalid_multiplatform_from_dockerfile_in_filesystem(): - # Retagging a multiplatform image is not supported - # Tests reading from dockerfile for the 2nd dockerfile - config_yaml = """ + """, + [ + "The following images are re-tagged: ['user1/buildrunner-multi-platform-image:latest']" + ], + ), + # Retagging a multiplatform image is not supported + # Tests reading from dockerfile for the 2nd dockerfile + ( + """ steps: build-container-multi-platform: build: @@ -203,17 +190,12 @@ def test_invalid_multiplatform_from_dockerfile_in_filesystem(): dockerfile: | FROM user1/buildrunner-multi-platform-image push: user1/buildrunner-multi-platform-image2 - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert isinstance(errors, Errors) - assert errors.count() == 1 - assert RETAG_ERROR_MESSAGE in errors.errors[0].message - - -def test_reusing_multi_platform_images(): - # Reuse multi-platform images is valid if the image isn't committed or pushed - config_yaml = """ + """, + [RETAG_ERROR_MESSAGE], + ), + # Reuse multi-platform images is valid if the image isn't committed or pushed + ( + """ steps: build-container-multi-platform: build: @@ -237,10 +219,15 @@ def test_reusing_multi_platform_images(): run: image: user2/buildrunner-test-multi-platform:0.0.1 cmd: echo "Hello World" - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None + """, + [], + ), + ], +) +def test_config_data( + config_yaml, error_matches, assert_generate_and_validate_config_errors +): + assert_generate_and_validate_config_errors(config_yaml, error_matches) @pytest.mark.parametrize( @@ -265,6 +252,7 @@ def test_reusing_multi_platform_images(): image: user1/buildrunner-test-multi-platform cmd: echo "Hello World" """, + # Re-pushing images where both steps are multi-platform is valid """ steps: build-container: @@ -314,51 +302,46 @@ def test_reusing_multi_platform_images(): ) @mock.patch("buildrunner.config.DEFAULT_GLOBAL_CONFIG_FILES", []) @mock.patch("buildrunner.detect_vcs") -def test_valid_config_with_buildrunner_build_tag(detect_vcs_mock, config_yaml): +def test_valid_config_with_buildrunner_build_tag( + detect_vcs_mock, config_yaml, tmp_path +): id_string = "main-921.ie02ed8.m1705616822" build_number = 342 type(detect_vcs_mock.return_value).id_string = mock.PropertyMock( return_value=id_string ) - with tempfile.TemporaryDirectory() as tmpdirname: - with tempfile.NamedTemporaryFile( - dir=tmpdirname, mode="w", delete=False - ) as tmpfile: - tmpfile.write(config_yaml) - tmpfile.close() - try: - runner = BuildRunner( - build_dir=tmpdirname, - build_results_dir=tmpdirname, - global_config_file=None, - run_config_file=tmpfile.name, - build_time=0, - build_number=build_number, - push=False, - cleanup_images=False, - cleanup_cache=False, - steps_to_run=None, - publish_ports=False, - log_generated_files=False, - docker_timeout=30, - local_images=False, - platform=None, - disable_multi_platform=False, - ) - config = runner.run_config - assert isinstance(config, dict) - push_info = config.get("steps").get("build-container").get("push") - if isinstance(push_info, list): - assert f"{id_string}-{build_number}" in push_info[0].get("tags") - else: - assert f"{id_string}-{build_number}" in push_info.get("tags") - except Exception as e: - assert False, f"Unexpected exception raised: {e}" + buildrunner_path = tmp_path / "buildrunner.yaml" + buildrunner_path.write_text(config_yaml) + runner = BuildRunner( + build_dir=str(tmp_path), + build_results_dir=str(tmp_path / "buildrunner.results"), + global_config_file=None, + run_config_file=str(buildrunner_path), + build_time=0, + build_number=build_number, + push=False, + cleanup_images=False, + cleanup_cache=False, + steps_to_run=None, + publish_ports=False, + log_generated_files=False, + docker_timeout=30, + local_images=False, + platform=None, + disable_multi_platform=False, + ) + config = runner.run_config + assert isinstance(config, dict) + push_info = config.get("steps").get("build-container").get("push") + if isinstance(push_info, list): + assert f"{id_string}-{build_number}" in push_info[0].get("tags") + else: + assert f"{id_string}-{build_number}" in push_info.get("tags") @pytest.mark.parametrize( - "config_json", + "config_yaml", [ """ steps: @@ -401,11 +384,13 @@ def test_valid_config_with_buildrunner_build_tag(detect_vcs_mock, config_yaml): push: user1/buildrunner-test-multi-platform2 """, ], - ids=["buildrunnder_build_tag_explict", "buildrunnder_build_tag_implied"], + ids=["buildrunner_build_tag_explict", "buildrunner_build_tag_implied"], ) @mock.patch("buildrunner.config.DEFAULT_GLOBAL_CONFIG_FILES", []) @mock.patch("buildrunner.detect_vcs") -def test_invalid_retagging_with_buildrunner_build_tag(detect_vcs_mock, config_json): +def test_invalid_retagging_with_buildrunner_build_tag( + detect_vcs_mock, config_yaml, tmp_path +): # Tests that BUILDRUNNER_BUILD_DOCKER_TAG is added to push tags and fails for re-tagging id_string = "main-921.ie02ed8.m1705616822" build_number = 342 @@ -413,33 +398,29 @@ def test_invalid_retagging_with_buildrunner_build_tag(detect_vcs_mock, config_js return_value=id_string ) - with tempfile.TemporaryDirectory() as tmpdirname: - with tempfile.NamedTemporaryFile( - dir=tmpdirname, mode="w", delete=False - ) as tmpfile: - tmpfile.write(config_json) - tmpfile.close() - with pytest.raises(BuildRunnerConfigurationError) as excinfo: - BuildRunner( - build_dir=tmpdirname, - build_results_dir=tmpdirname, - global_config_file=None, - run_config_file=tmpfile.name, - build_time=0, - build_number=build_number, - push=False, - cleanup_images=False, - cleanup_cache=False, - steps_to_run=None, - publish_ports=False, - log_generated_files=False, - docker_timeout=30, - local_images=False, - platform=None, - disable_multi_platform=False, - ) - assert RETAG_ERROR_MESSAGE in excinfo.value.args[0] - assert ( - f"user1/buildrunner-test-multi-platform:{id_string}-{build_number}" - in excinfo.value.args[0] - ) + buildrunner_path = tmp_path / "buildrunner.yaml" + buildrunner_path.write_text(config_yaml) + with pytest.raises(BuildRunnerConfigurationError) as excinfo: + BuildRunner( + build_dir=str(tmp_path), + build_results_dir=str(tmp_path / "buildrunner.results"), + global_config_file=None, + run_config_file=str(buildrunner_path), + build_time=0, + build_number=build_number, + push=False, + cleanup_images=False, + cleanup_cache=False, + steps_to_run=None, + publish_ports=False, + log_generated_files=False, + docker_timeout=30, + local_images=False, + platform=None, + disable_multi_platform=False, + ) + assert RETAG_ERROR_MESSAGE in excinfo.value.args[0] + assert ( + f"user1/buildrunner-test-multi-platform:{id_string}-{build_number}" + in excinfo.value.args[0] + ) diff --git a/tests/test_config_validation/test_validation_artifacts.py b/tests/test_config_validation/test_validation_artifacts.py index 40feebd4..13841383 100644 --- a/tests/test_config_validation/test_validation_artifacts.py +++ b/tests/test_config_validation/test_validation_artifacts.py @@ -1,9 +1,11 @@ -import yaml -from buildrunner.validation.config import validate_config, Errors +import pytest -def test_step_remote_artifacts_valid(): - config_yaml = """ +@pytest.mark.parametrize( + "config_yaml, error_matches", + [ + ( + """ steps: build-remote: remote: @@ -13,14 +15,11 @@ def test_step_remote_artifacts_valid(): bogus/path/to/artifacts/*: type: tar compression: lzma - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - -def test_step_run_artifacts_valid(): - config_yaml = """ + """, + [], + ), + ( + """ steps: build-run: run: @@ -28,15 +27,12 @@ def test_step_run_artifacts_valid(): bogus/path/to/artifacts/*: type: zip compression: lzma - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - print(errors) - assert errors is None - - -def test_step_artifacts_valid_compression(): - config_yaml = """ + """, + [], + ), + # Valid compression + ( + """ steps: build-remote: remote: @@ -46,100 +42,84 @@ def test_step_artifacts_valid_compression(): bogus/path/to/artifacts/*: type: tar compression: gz - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - -def test_step_run_format_valid(): - config_yaml = """ + """, + [], + ), + # Valid run format + ( + """ steps: build-run: run: artifacts: bogus/path/to/artifacts/*: format: uncompressed - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - -def test_step_type_valid(): - # Checks zip type - config_yaml = """ + """, + [], + ), + # Checks zip type + ( + """ steps: build-run: run: artifacts: bogus/path/to/artifacts/*: type: zip - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - # Checks tar type - config_yaml = """ + """, + [], + ), + # Checks tar type + ( + """ steps: build-run: run: artifacts: bogus/path/to/artifacts/*: type: tar - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - -def test_push_invalid(): - # Push must be a boolean - config_yaml = """ + """, + [], + ), + # Push must be a boolean + ( + """ steps: build-run: run: artifacts: bogus/path/to/artifacts/*: push: bogus - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert isinstance(errors, Errors) - assert errors.count() == 1 - - -def test_valid_artifacts_blank_string(): - config_yaml = """ + """, + ["Input should be a valid boolean"], + ), + # Artifact may be a blank string + ( + """ steps: build-run: run: artifacts: bogus/path/to/artifacts/*: '' bogus/path/to/this_thing: '' - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - -def test_push_valid(): - config_yaml = """ + """, + [], + ), + # Valid push + ( + """ steps: build-run: run: artifacts: bogus/path/to/artifacts/*: push: True - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - -def test_valid_extra_properties(): - config_yaml = """ + """, + [], + ), + # Valid extra properties + ( + """ steps: build-run: run: @@ -149,7 +129,12 @@ def test_valid_extra_properties(): something_else: awesome data something_else2: True something_else3: 123 - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None + """, + [], + ), + ], +) +def test_config_data( + config_yaml, error_matches, assert_generate_and_validate_config_errors +): + assert_generate_and_validate_config_errors(config_yaml, error_matches) diff --git a/tests/test_config_validation/test_validation_config.py b/tests/test_config_validation/test_validation_config.py index a4c516ef..4e0e3f89 100644 --- a/tests/test_config_validation/test_validation_config.py +++ b/tests/test_config_validation/test_validation_config.py @@ -1,28 +1,18 @@ -import yaml -from buildrunner.validation.config import validate_config, Errors - - -def test_valid_version_config(): - # Invalid version - config = {"version": "string"} - errors = validate_config(**config) - assert isinstance(errors, Errors) - assert errors.count() == 1 - - # Valid version - config = {"version": 2.0, "steps": {}} - errors = validate_config(**config) - assert errors is None - - # Optional version - config = {"steps": {}} - errors = validate_config(**config) - assert errors is None - - -def test_valid_config(): - # Sample valid config, but not exhaustive - config_yaml = """ +import pytest + + +@pytest.mark.parametrize( + "config_data, error_matches", + [ + # Invalid version + ({"version": "string"}, ["Input should be a valid number"]), + # Valid version + ({"version": 2.0, "steps": {}}, []), + # Optional version + ({"steps": {}}, []), + # Sample valid config, but not exhaustive + ( + """ version: 2.0 steps: build-container-single-platform1: @@ -60,17 +50,14 @@ def test_valid_config(): - repository: myimages/image2 tags: - latest - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - -def test_multiple_errors(): - # Multiple errors - # Invalid to have version as a string - # Invalid to have platforms and platform - config_yaml = """ + """, + [], + ), + # Multiple errors + # Invalid to have version as a string + # Invalid to have platforms and platform + ( + """ version: string steps: build-container-multi-platform: @@ -86,291 +73,12 @@ def test_multiple_errors(): repository: mytest-reg/buildrunner-test-multi-platform tags: - latest - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert isinstance(errors, Errors) - assert errors.count() == 2 - - -def test_doc_config(): - # Tests the documentation example with minimal changes to make valid yaml - config_yaml = """ - version: 2.0 - steps: - my-build-step: - # Optional step dependency definition to specify which steps need to be processed before this step. - # The `version` must be present and set to `2.0` or higher for buildrunner to utilize the step dependencies list. - # An buildrunner error will occur if `depends` is present but `version` is missing or value is lower than `2.0`. - depends: - - test-step - - validation-step - - run: - # xfail indicates whether the run operation is expected to fail. The - # default is false - the operation is expected to succeed. If xfail - # is true and the operation succeeds then it will result in a failure. - xfail: True - - # A map of additional containers that should be created and linked to - # the primary run container. These can be used to bring up services - # (such as databases) that are required to run the step. More details - # on services below. - services: - service-name-1: - image: - service-name-2: - cmd: - - # The Docker image to run. If empty the image created with the 'build' - # attribute will be used. - image: - - # The command(s) to run. If omitted Buildrunner runs the command - # configured in the Docker image without modification. If provided - # Buildrunner always sets the container command to a shell, running the - # given command here within the shell. If both 'cmd' and 'cmds' are - # present the command in 'cmd' is run before the commands in the 'cmds' - # list are run. - cmd: - cmds: - - - - - - # A collection of provisioners to run. Provisioners work similar to the - # way Packer provisioners do and are always run within a shell. - # When a provisioner is specified Buildrunner always sets the container - # command to a shell, running the provisioners within the shell. - # Currently Buildrunner supports shell and salt provisioners. - provisioners: - shell: path/to/script.sh | [path/to/script.sh, ARG1, ...] - salt: - - # The shell to use when specifying the cmd or provisioners attributes. - # Defaults to /bin/sh. If the cmd and provisioners attributes are not - # specified this setting has no effect. - shell: /bin/sh - - # The directory to run commands within. Defaults to /source. - cwd: /source - - # The user to run commands as. Defaults to the user specified in the - # Docker image. - user: - - # The hostname assigned to the run container. - hostname: - - # Custom dns servers to use in the run container. - dns: - - 8.8.8.8 - - 8.8.4.4 - - # A custom dns search path to use in the run container. - dns_search: mydomain.com - - # Add entries to the hosts file - # The keys are the hostnames. The values can be either - # ip addresses or references to service containers. - extra_hosts: - "www1.test.com": "192.168.0.1" - "www2.test.com": "192.168.0.2" - - # A map specifying additional environment variables to be injected into - # the container. Keys are the variable names and values are variable - # values. - env: - ENV_VARIABLE_ONE: value1 - ENV_VARIABLE_TWO: value2 - - # A map specifying files that should be injected into the container. - # The map key is the alias referencing a given file (as configured in - # the "local-files" section of the global configuration file) or a - # relative path to a file/directory in the build directory. The value - # is the path the given file should be mounted at within the container. - files: - namespaced.file.alias1: "/path/to/readonly/file/or/dir" - namespaced.file.alias2: "/path/to/readwrite/file/or/dir:rw" - build/dir/file: "/path/to/build/dir/file" - - # A map specifying cache directories that are stored as archive files on the - # host system as `local cache key` and extracted as a directory in - # the container named `docker path`. The cache directories are maintained - # between builds and can be used to store files, such as downloaded - # dependencies, to speed up builds. - # Caches can be shared between any builds or projects on the system - # as the names are not prefixed with any project-specific information. - # Caches should be treated as ephemeral and should only store items - # that can be obtained/generated by subsequent builds. - # - # Two formats are supported when defining caches. - # 1) RECOMMENDED - # : - # - - # - - # - # Restore Cache: - # This format allows for prefix matching. The order of the list dictates the - # order which should be searched in the local system cache location. - # When an item isn't found it will search for archive files which prefix matches - # the item in the list. If more than one archive file is matched for a prefix - # the archive file most recently modified will be used. If there is no - # matching archive file then nothing will be restored in the docker container. - # - # Save Cache: - # The first local cache key in the list is used for the name of the local - # cache archive file. - # - # 2) : (backwards compatible with older caching method, but more limited) - # - caches: - # Recommended format. - : - - - - - - "/root/.m2/repository": - # Buildrunner will look for a cache that matches this cache key/prefix, - # typically the first key should be the most specific as it is the closest match - # Note that this first key will also be used to save the cache for use across builds or projects - - m2repo-{{ checksum("pom.xml", "subproj/pom.xml") }} - # If the first cache key is not found in the caches, use this prefix to look for a cache that may not - # be an exact match, but may still be close and not require as much downloading of dependencies, etc - # Note that this may match across any cache done by any build on the same system, so it may be wise to - # use a unique prefix for any number of builds that have a similar dependency tree, etc - - m2repo- - # If no cache is found, nothing will be extracted and the application will need to rebuild the cache - - # Backwards compatible format. Not recommended for future or updated configurations. - : - maven: "/root/.m2/repository" - - # A map specifying ports to expose, this is only used when the - # --publish-ports parameter is passed to buildrunner - ports: - 5458: 8080 - - # A list specifying service containers (see below) whose exposed - # volumes should be mapped into the run container's file system. - # An exposed volume is one created by the volume Dockerfile command. - # See https://docs.docker.com/engine/reference/builder/#volume for more - # details regarding the volume Dockerfile command. - volumes_from: - - my-service-container - - # A list specifying ssh keys that should be injected into the container - # via an ssh agent. The list should specify the ssh key aliases (as - # configured in the "ssh-keys" section of the global configuration - # file) that buildrunner should inject into the container. Buildrunner - # injects the keys by mounting a ssh-agent socket and setting the - # appropriate environment variable, meaning that the private key itself - # is never available inside the container. - ssh-keys: - - my_ssh_key_alias - - # A map specifying the artifacts that should be archived for the step. - # The keys in the map specify glob patterns of files to archive. If a - # value is present it should be a map of additional properties that - # should be added to the build artifacts.json file. The artifacts.json - # file can be used to publish artifacts to another system (such as - # Gauntlet) with the accompanying metadata. By default artifacts will be - # listed in the artifacts.json file; this can be disabled by adding the - # ``push`` property and set it to false. - # - # When archiving *directories* special properties can be set to change - # the behavior of the archiver. Directories by default are archived as - # gzip'ed TARs. The compression can be changed by setting the - # ``compression`` property to one of the below-listed values. The - # archive type can be changed by setting the property ``type:zip``. - # When a zip archive is requested then the ``compression`` property is - # ignored. If the directory tree should be gathered verbatim without - # archiving then the property ``format:uncompressed`` can be used. - # - # NOTE: Artifacts can only be archived from the /source directory using - # a relative path or a full path. Files outside of this directory will - # fail to be archived. - artifacts: - artifacts/to/archive/*: - format: uncompressed - type: tar - compression: gz - push: true - property1: value1 - property2: value2 - - # Whether or not to pull the image from upstream prior to running - # the step. This is almost always desirable, as it ensures the - # most up to date source image. - # NOTE: If the image was created from a 'push' or 'commit' earlier in - # this ``buildrunner.yaml`` then this will default to false - pull: true - - # Specify a different platform architecture when pulling and running images. - # This is useful if you are running an image that was built for a different architecture - # than what buildrunner is running on, such as using a linux/arm64/v8 Apple M1 architecture - # development machine to run or test an image built for linux/amd64 architecture. - platform: linux/amd64 - # - # platform: linux/arm64/v8 # an apple m1 architecture - - # systemd does not play well with docker typically, but you can - # use this setting to tell buildrunner to set the necessary docker - # flags to get systemd to work properly: - # - /usr/sbin/init needs to run as pid 1 - # - /sys/fs/cgroup needs to be mounted as readonly - # (-v /sys/fs/cgroup:/sys/fs/cgroup:ro) - # - The security setting seccomp=unconfined must be set - # (--security-opt seccomp=unconfined) - # If this is ommitted, the image will be inspected for the label - # 'BUILDRUNNER_SYSTEMD'. - # If found, systemd=true will be assumed. - # systemd: true/false - systemd: true - - # Docker supports certain kernel capabilities, like 'SYS_ADMIN'. - # see https://goo.gl/gTQrqW for more infromation on setting these. - cap_add: 'SYS_ADMIN' - # - # cap_add: - # - 'SYS_ADMIN' - # - 'SYS_RAWIO' - - # Docker can run in a privileged mode. This allows access to all devices - # on the host. Using privileged is rare, but there are good use cases - # for this feature. see https://goo.gl/gTQrqW for more infromation on - # setting these. - # Default: false - # privileged: true/false - privileged: true - - # The post-build attribute commits the resulting run container as an - # image and allows additional Docker build processing to occur. This is - # useful for adding Docker configuration, such as EXPOSE and CMD - # instructions, when building an image via the run task that cannot be - # done without running a Docker build. The post-build attribute - # functions the same way as the 'build' step attribute does, except - # that it prepends the committed run container image to the provided - post-build: path/to/build/context - # - # post-build: - # dockerfile: | - # EXPOSE 80 - # CMD /runserver.sh - - # A list of container names or labels created within any run container - # that buildrunner should clean up. (Use if you call - containers: - - container1 - - container2 - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - -def test_full_confg(): - # Minus template areas - config_yaml = """ + """, + ["Input should be a valid number", "Cannot specify both platform"], + ), + # Tests the documentation example with minimal changes to make valid yaml + ( + """ steps: generate_files: run: @@ -476,28 +184,24 @@ def test_full_confg(): cmd: "sbt clean generateDocs ${BUILDRUNNER_DO_PUSH+publishGHPages}" artifacts: 'target/docs/*': - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - -def test_github_config(): - # Valid github config - config_yaml = """ + """, + [], + ), + # Valid github config + ( + """ github: company_github: endpoint: 'https://git.company.com/api' version: 'v3' username: 'USERNAME' app_token: 'APP_TOKEN' - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - # Invalid github config - config_yaml = """ + """, + [], + ), + # Invalid github config + ( + """ github: company_github: endpoint: 'https://git.company.com/api' @@ -505,8 +209,12 @@ def test_github_config(): username: 'USERNAME' app_token: 'APP_TOKEN' bogus: 'bogus' - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert isinstance(errors, Errors) - assert errors.count() == 1 + """, + ["Extra inputs are not permitted"], + ), + ], +) +def test_config_data( + config_data, error_matches, assert_generate_and_validate_config_errors +): + assert_generate_and_validate_config_errors(config_data, error_matches) diff --git a/tests/test_config_validation/test_validation_run.py b/tests/test_config_validation/test_validation_run.py deleted file mode 100644 index 472f8e9c..00000000 --- a/tests/test_config_validation/test_validation_run.py +++ /dev/null @@ -1,52 +0,0 @@ -from buildrunner.validation.config import validate_config, Errors -import yaml - - -def test_step_run_artifacts_valid(): - config_yaml = """ - steps: - build-run: - run: - image: mytest-reg/buildrunner-test - artifacts: - bogus/path/to/artifacts/*: - type: zip - compression: lzma - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - -def test_push_valid(): - config_yaml = """ - steps: - build-run: - run: - artifacts: - bogus/path/to/artifacts/*: - type: zip - compression: lzma - push: True - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - -def test_push_invalid(): - # Push must be a boolean - config_yaml = """ - steps: - build-run: - run: - artifacts: - bogus/path/to/artifacts/*: - type: zip - compression: lzma - push: 1212 - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert isinstance(errors, Errors) - assert errors.count() == 1 diff --git a/tests/test_config_validation/test_validation_step.py b/tests/test_config_validation/test_validation_step.py index efad9b94..c01b84ad 100644 --- a/tests/test_config_validation/test_validation_step.py +++ b/tests/test_config_validation/test_validation_step.py @@ -1,10 +1,12 @@ -import yaml -from buildrunner.validation.config import validate_config, Errors +import pytest -def test_platform_and_platforms_invalid(): - # Invalid to have platform and platforms - config_yaml = """ +@pytest.mark.parametrize( + "config_yaml, error_matches", + [ + # Invalid to have platform and platforms + ( + """ steps: build-container-multi-platform: build: @@ -19,16 +21,12 @@ def test_platform_and_platforms_invalid(): repository: mytest-reg/buildrunner-test-multi-platform tags: - latest - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert isinstance(errors, Errors) - assert errors.count() == 1 - - -def test_platforms_invalid(): - # Invalid to have platforms as a string, it should be a list - config_yaml = """ + """, + ["Cannot specify both platform"], + ), + # Invalid to have platforms as a string, it should be a list + ( + """ steps: build-container-multi-platform: build: @@ -40,16 +38,12 @@ def test_platforms_invalid(): repository: mytest-reg/buildrunner-test-multi-platform tags: - latest - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert isinstance(errors, Errors) - assert errors.count() == 2 - - -def test_cache_from_invalid(): - # Invalid to have cache_from specified with platforms - config_yaml = """ + """, + ["Input should be a valid list", "Input should be a valid string"], + ), + # Invalid to have cache_from specified with platforms + ( + """ steps: build-container-multi-platform: build: @@ -64,27 +58,21 @@ def test_cache_from_invalid(): repository: mytest-reg/buildrunner-test-multi-platform tags: - latest - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert isinstance(errors, Errors) - assert errors.count() == 1 - assert "cache_from" in str(errors) - - -def test_build_is_path(): - config_yaml = """ + """, + ["cache_from"], + ), + # Build is a path + ( + """ steps: build-is-path: build: . - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - -def test_valid_platforms(): - config_yaml = """ + """, + [], + ), + # Valid platforms + ( + """ steps: build-container-multi-platform: build: @@ -98,14 +86,12 @@ def test_valid_platforms(): repository: mytest-reg/buildrunner-test-multi-platform tags: - latest - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - -def test_valid_platforms_no_cache(): - config_yaml = """ + """, + [], + ), + # Platforms with no-cache + ( + """ steps: build-container-multi-platform: build: @@ -120,15 +106,12 @@ def test_valid_platforms_no_cache(): repository: mytest-reg/buildrunner-test-multi-platform tags: - latest - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - -def test_duplicate_mp_tags_dictionary_invalid(): - # Invalid to have duplicate multi-platform tag - config_yaml = """ + """, + [], + ), + # Invalid to have duplicate multi-platform tag + ( + """ steps: build-container-multi-platform1: build: @@ -148,38 +131,14 @@ def test_duplicate_mp_tags_dictionary_invalid(): repository: mytest-reg/buildrunner-test-multi-platform tags: - latest - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert isinstance(errors, Errors) - assert errors.count() == 1 - - -def test_duplicate_mp_tags_strings_invalid(): - # Invalid to have duplicate multi-platform tag - # Testing with both string format, one inferred 'latest' the other explicit 'latest' - config_yaml = """ - steps: - build-container-multi-platform1: - build: - platforms: - - linux/amd64 - - linux/arm64 - push: mytest-reg/buildrunner-test-multi-platform - build-container-multi-platform2: - build: - platforms: - - linux/amd64 - - linux/arm64 - push: mytest-reg/buildrunner-test-multi-platform:latest - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert isinstance(errors, Errors) - assert errors.count() == 1 - - # Indentical tags in same string format - config_yaml = """ + """, + [ + "Cannot specify duplicate tag mytest-reg/buildrunner-test-multi-platform:latest in build step" + ], + ), + # Identical tags in same string format + ( + """ steps: build-container-multi-platform1: build: @@ -193,15 +152,14 @@ def test_duplicate_mp_tags_strings_invalid(): - linux/amd64 - linux/arm64 push: mytest-reg/buildrunner-test-multi-platform:latest - """ - errors = validate_config(**config) - assert isinstance(errors, Errors) - assert errors.count() == 1 - - -def test_duplicate_mp_tags_strings_valid(): - # Same string format but different MP tags - config_yaml = """ + """, + [ + "Cannot specify duplicate tag mytest-reg/buildrunner-test-multi-platform:latest in build step" + ], + ), + # Same string format but different MP tags + ( + """ steps: build-container-multi-platform1: build: @@ -219,35 +177,33 @@ def test_duplicate_mp_tags_strings_valid(): - linux/amd64 - linux/arm64 push: mytest-reg/buildrunner-test-multi-platform:not-latest - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - -def test_duplicate_mp_tags_platform_platforms_invalid(): - # Invalid to have duplicate multi-platform tag and single platform tag - config_yaml = """ + """, + [], + ), + # Invalid to have duplicate multi-platform tag and single platform tag + ( + """ steps: build-container-multi-platform1: build: + path: . platforms: - linux/amd64 - linux/arm64 push: mytest-reg/buildrunner-test-multi-platform:latest build-container-single-platform: build: + path: . platform: linux/arm64 push: mytest-reg/buildrunner-test-multi-platform:latest - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert isinstance(errors, Errors) - assert errors.count() == 1 - - -def test_step_remote_valid(): - config_yaml = """ + """, + [ + "Cannot specify duplicate tag mytest-reg/buildrunner-test-multi-platform:latest in build step" + ], + ), + # Valid remote step + ( + """ steps: build-remote: remote: @@ -257,27 +213,22 @@ def test_step_remote_valid(): bogus/path/to/artifacts/*: type: tar compression: lzma - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - -def test_step_remote_missing_cmd(): - config_yaml = """ + """, + [], + ), + # Remote missing command + ( + """ steps: build-remote: remote: host: myserver.ut1 - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert isinstance(errors, Errors) - assert errors.count() == 1 - - -def test_commit(): - config_yaml = """ + """, + ["Field required"], + ), + # Valid commit + ( + """ steps: step1: build: @@ -294,14 +245,12 @@ def test_commit(): dockerfile: Dockerfile pull: false commit: mytest-reg/image1 - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - -def test_pypi_push(): - config_yaml = """ + """, + [], + ), + # Valid pypi push + ( + """ steps: pypi1: run: @@ -323,14 +272,12 @@ def test_pypi_push(): repository: https://artifactory.example.com/artifactory/api/pypi/pypi-myownrepo username: myuser password: mypass - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - -def test_invalid_mp_import(): - config_yaml = """ + """, + [], + ), + # Invalid multiplatform import + ( + """ steps: build-container-multi-platform: build: @@ -340,29 +287,24 @@ def test_invalid_mp_import(): - linux/amd64 - linux/arm64 import: mytest-reg/buildrunner-test-multi-platform:latest - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert isinstance(errors, Errors) - assert errors.count() == 1 - - -def test_valid_import(): - config_yaml = """ + """, + ["import is not allowed in multi-platform build step"], + ), + # Valid import + ( + """ steps: build-container-multi-platform: build: path: . dockerfile: Dockerfile import: mytest-reg/buildrunner-test-multi-platform:latest - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None - - -def test_services(): - config_yaml = """ + """, + [], + ), + # Valid services + ( + """ steps: my-build-step: run: @@ -491,7 +433,12 @@ def test_services(): # also. This isn't enabled by default as there is the theoretical # (though unlikely) possibility that a this access could be exploited. inject-ssh-agent: true - """ - config = yaml.load(config_yaml, Loader=yaml.Loader) - errors = validate_config(**config) - assert errors is None + """, + [], + ), + ], +) +def test_config_data( + config_yaml, error_matches, assert_generate_and_validate_config_errors +): + assert_generate_and_validate_config_errors(config_yaml, error_matches)