From d1e1aa615005397086fa65c7434d6ce42eca6fee Mon Sep 17 00:00:00 2001 From: Kairo Araujo Date: Sat, 4 Apr 2026 10:05:23 +0200 Subject: [PATCH 1/4] tests: add tests for workflow settings schema Signed-off-by: Kairo Araujo --- .../schemas/test_workflow_settings_schema.py | 223 ++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 tests/schemas/test_workflow_settings_schema.py diff --git a/tests/schemas/test_workflow_settings_schema.py b/tests/schemas/test_workflow_settings_schema.py new file mode 100644 index 00000000..1955e0e1 --- /dev/null +++ b/tests/schemas/test_workflow_settings_schema.py @@ -0,0 +1,223 @@ +# ******************************************************************************* +# Copyright (c) 2025 Eclipse Foundation and others. +# This program and the accompanying materials are made available +# under the terms of the Eclipse Public License 2.0 +# which is available at http://www.eclipse.org/legal/epl-v20.html +# SPDX-License-Identifier: EPL-2.0 +# ******************************************************************************* + +"""Test schema composition for workflow-settings.json and its extensions.""" + +import json +from importlib.resources import as_file, files + +import pytest +from jsonschema import Draft202012Validator, ValidationError +from referencing import Registry, Resource +from referencing.exceptions import NoSuchResource + +import otterdog.resources as resources + + +class TestWorkflowSettingsSchemaComposition: + """Test that workflow-settings.json properties are properly inherited.""" + + @pytest.fixture + def schemas_registry(self): + """Create a registry for resolving schema references.""" + with as_file(files(resources).joinpath("schemas")) as resource_dir: + + def retrieve_from_filesystem(uri: str): + path = resource_dir.joinpath(uri) + if not path.exists(): + raise NoSuchResource(ref=uri) + + contents = json.loads(path.read_text()) + return Resource.from_contents(contents) + + yield Registry(retrieve=retrieve_from_filesystem) + + @pytest.fixture + def org_workflow_schema(self): + """Load the org-workflow-settings.json schema.""" + schema_text = files(resources).joinpath("schemas/org-workflow-settings.json").read_text() + return json.loads(schema_text) + + @pytest.fixture + def repo_workflow_schema(self): + """Load the repo-workflow-settings.json schema.""" + schema_text = files(resources).joinpath("schemas/repo-workflow-settings.json").read_text() + return json.loads(schema_text) + + def test_org_workflow_settings_accepts_fork_pr_approval_policy(self, org_workflow_schema, schemas_registry): + """Test that org-workflow-settings accepts fork_pr_approval_policy from base schema.""" + validator = Draft202012Validator(org_workflow_schema, registry=schemas_registry) + + data = { + "actions_can_approve_pull_request_reviews": False, + "allow_action_patterns": [], + "allow_github_owned_actions": True, + "allow_verified_creator_actions": True, + "allowed_actions": "all", + "default_workflow_permissions": "write", + "enabled_repositories": "all", + "fork_pr_approval_policy": "first_time_contributors_new_to_github", + "selected_repositories": [], + } + + validator.validate(data) + + def test_org_workflow_settings_accepts_all_base_properties(self, org_workflow_schema, schemas_registry): + """Test that all base workflow-settings properties are accepted.""" + validator = Draft202012Validator(org_workflow_schema, registry=schemas_registry) + + data = { + # Required org-specific field + "enabled_repositories": "selected", + "selected_repositories": ["repo1", "repo2"], + # Base workflow-settings fields + "allowed_actions": "selected", + "allow_github_owned_actions": False, + "allow_verified_creator_actions": False, + "allow_action_patterns": ["org/*", "action@v*"], + "default_workflow_permissions": "read", + "actions_can_approve_pull_request_reviews": True, + "fork_pr_approval_policy": "first_time_contributors", + } + + validator.validate(data) + + def test_org_workflow_settings_rejects_unknown_properties(self, org_workflow_schema, schemas_registry): + """Test that unknown properties are rejected due to unevaluatedProperties: false.""" + validator = Draft202012Validator(org_workflow_schema, registry=schemas_registry) + + data = { + "enabled_repositories": "all", + "unknown_property": "should_fail", + } + + with pytest.raises(ValidationError) as exc_info: + validator.validate(data) + + assert "unevaluatedProperties" in str(exc_info.value) + assert "unknown_property" in str(exc_info.value) + + def test_repo_workflow_settings_accepts_fork_pr_approval_policy(self, repo_workflow_schema, schemas_registry): + """Test that repo-workflow-settings accepts fork_pr_approval_policy from base schema.""" + validator = Draft202012Validator(repo_workflow_schema, registry=schemas_registry) + + data = { + # Required repo-specific field + "enabled": True, + # Base workflow-settings fields + "allowed_actions": "all", + "allow_github_owned_actions": True, + "allow_verified_creator_actions": True, + "allow_action_patterns": [], + "default_workflow_permissions": "write", + "actions_can_approve_pull_request_reviews": False, + "fork_pr_approval_policy": "all_external_contributors", + } + + validator.validate(data) + + def test_repo_workflow_settings_accepts_all_base_properties(self, repo_workflow_schema, schemas_registry): + """Test that all base workflow-settings properties are accepted.""" + validator = Draft202012Validator(repo_workflow_schema, registry=schemas_registry) + + data = { + # Required repo-specific field + "enabled": False, + # Base workflow-settings fields + "allowed_actions": "local_only", + "allow_github_owned_actions": True, + "allow_verified_creator_actions": False, + "allow_action_patterns": ["my-org/*"], + "default_workflow_permissions": "read", + "actions_can_approve_pull_request_reviews": False, + "fork_pr_approval_policy": "first_time_contributors_new_to_github", + } + + validator.validate(data) + + def test_repo_workflow_settings_rejects_unknown_properties(self, repo_workflow_schema, schemas_registry): + """Test that unknown properties are rejected.""" + validator = Draft202012Validator(repo_workflow_schema, registry=schemas_registry) + + data = { + "enabled": True, + "this_does_not_exist": "invalid", + } + + with pytest.raises(ValidationError) as exc_info: + validator.validate(data) + + assert "unevaluatedProperties" in str(exc_info.value) + assert "this_does_not_exist" in str(exc_info.value) + + @pytest.mark.parametrize( + "field_name,field_value", + [ + ("allowed_actions", "all"), + ("allow_github_owned_actions", True), + ("allow_verified_creator_actions", False), + ("allow_action_patterns", ["pattern1", "pattern2"]), + ("default_workflow_permissions", "write"), + ("actions_can_approve_pull_request_reviews", True), + ("fork_pr_approval_policy", "first_time_contributors"), + ], + ) + def test_org_workflow_inherits_individual_base_fields( + self, org_workflow_schema, schemas_registry, field_name, field_value + ): + """Test that each base field is properly inherited in org-workflow-settings.""" + validator = Draft202012Validator(org_workflow_schema, registry=schemas_registry) + + data = { + "enabled_repositories": "all", # Required field + field_name: field_value, + } + + validator.validate(data) + + @pytest.mark.parametrize( + "field_name,field_value", + [ + ("allowed_actions", "selected"), + ("allow_github_owned_actions", False), + ("allow_verified_creator_actions", True), + ("allow_action_patterns", []), + ("default_workflow_permissions", "read"), + ("actions_can_approve_pull_request_reviews", False), + ("fork_pr_approval_policy", "all_external_contributors"), + ], + ) + def test_repo_workflow_inherits_individual_base_fields( + self, repo_workflow_schema, schemas_registry, field_name, field_value + ): + """Test that each base field is properly inherited in repo-workflow-settings.""" + validator = Draft202012Validator(repo_workflow_schema, registry=schemas_registry) + + data = { + "enabled": True, # Required field + field_name: field_value, + } + + validator.validate(data) + + def test_schema_composition_with_allof(self, org_workflow_schema): + """Test that the schema uses allOf for proper composition.""" + # Verify that allOf is used instead of direct $ref + assert "allOf" in org_workflow_schema + assert isinstance(org_workflow_schema["allOf"], list) + assert len(org_workflow_schema["allOf"]) > 0 + assert "$ref" in org_workflow_schema["allOf"][0] + assert org_workflow_schema["allOf"][0]["$ref"] == "workflow-settings.json" + + def test_repo_schema_composition_with_allof(self, repo_workflow_schema): + """Test that the repo schema also uses allOf for proper composition.""" + assert "allOf" in repo_workflow_schema + assert isinstance(repo_workflow_schema["allOf"], list) + assert len(repo_workflow_schema["allOf"]) > 0 + assert "$ref" in repo_workflow_schema["allOf"][0] + assert repo_workflow_schema["allOf"][0]["$ref"] == "workflow-settings.json" From e789bceaca2b7cc68c444cf5e2b08dfc6daa4856 Mon Sep 17 00:00:00 2001 From: Kairo Araujo Date: Sat, 4 Apr 2026 10:03:30 +0200 Subject: [PATCH 2/4] fix: validationworkflow settings schema Signed-off-by: Kairo Araujo --- CHANGELOG.md | 6 ++++++ otterdog/resources/schemas/org-workflow-settings.json | 4 +++- otterdog/resources/schemas/repo-workflow-settings.json | 4 +++- tests/schemas/test_workflow_settings_schema.py | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d198e002..d6dbc0ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## [1.3.1] - unreleased + +### Fixed + + - test workflow validation settings ([#633](https://github.com/eclipse-csi/otterdog/pull/633)) + ## [1.3.0] - 30/03/2026 ### Added diff --git a/otterdog/resources/schemas/org-workflow-settings.json b/otterdog/resources/schemas/org-workflow-settings.json index e60f7f05..f19c5f84 100644 --- a/otterdog/resources/schemas/org-workflow-settings.json +++ b/otterdog/resources/schemas/org-workflow-settings.json @@ -1,7 +1,9 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "workflow-settings.json", + "allOf": [ + { "$ref": "workflow-settings.json" } + ], "type": "object", "properties": { "enabled_repositories": { "type": "string" }, diff --git a/otterdog/resources/schemas/repo-workflow-settings.json b/otterdog/resources/schemas/repo-workflow-settings.json index c2af64de..541146fc 100644 --- a/otterdog/resources/schemas/repo-workflow-settings.json +++ b/otterdog/resources/schemas/repo-workflow-settings.json @@ -1,7 +1,9 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "workflow-settings.json", + "allOf": [ + { "$ref": "workflow-settings.json" } + ], "type": "object", "properties": { "enabled": { "type": "boolean" } diff --git a/tests/schemas/test_workflow_settings_schema.py b/tests/schemas/test_workflow_settings_schema.py index 1955e0e1..806e5532 100644 --- a/tests/schemas/test_workflow_settings_schema.py +++ b/tests/schemas/test_workflow_settings_schema.py @@ -1,5 +1,5 @@ # ******************************************************************************* -# Copyright (c) 2025 Eclipse Foundation and others. +# Copyright (c) 2026 Eclipse Foundation and others. # This program and the accompanying materials are made available # under the terms of the Eclipse Public License 2.0 # which is available at http://www.eclipse.org/legal/epl-v20.html From 28bbc112bfd89b6c9ff99188f154f2ffd35f6b79 Mon Sep 17 00:00:00 2001 From: Thomas Neidhart Date: Wed, 22 Apr 2026 09:13:36 +0200 Subject: [PATCH 3/4] revert unnecessary schema change, add missing test dependency --- .../schemas/org-workflow-settings.json | 4 +--- .../schemas/repo-workflow-settings.json | 4 +--- poetry.lock | 15 ++++----------- pyproject.toml | 1 + tests/schemas/test_workflow_settings_schema.py | 17 ----------------- 5 files changed, 7 insertions(+), 34 deletions(-) diff --git a/otterdog/resources/schemas/org-workflow-settings.json b/otterdog/resources/schemas/org-workflow-settings.json index f19c5f84..e60f7f05 100644 --- a/otterdog/resources/schemas/org-workflow-settings.json +++ b/otterdog/resources/schemas/org-workflow-settings.json @@ -1,9 +1,7 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "allOf": [ - { "$ref": "workflow-settings.json" } - ], + "$ref": "workflow-settings.json", "type": "object", "properties": { "enabled_repositories": { "type": "string" }, diff --git a/otterdog/resources/schemas/repo-workflow-settings.json b/otterdog/resources/schemas/repo-workflow-settings.json index 541146fc..c2af64de 100644 --- a/otterdog/resources/schemas/repo-workflow-settings.json +++ b/otterdog/resources/schemas/repo-workflow-settings.json @@ -1,9 +1,7 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "allOf": [ - { "$ref": "workflow-settings.json" } - ], + "$ref": "workflow-settings.json", "type": "object", "properties": { "enabled": { "type": "boolean" } diff --git a/poetry.lock b/poetry.lock index f6a01409..e5ac7d20 100644 --- a/poetry.lock +++ b/poetry.lock @@ -343,7 +343,7 @@ version = "25.4.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.9" -groups = ["main", "app", "typing"] +groups = ["main", "app", "test", "typing"] files = [ {file = "attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373"}, {file = "attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11"}, @@ -3086,13 +3086,6 @@ optional = false python-versions = ">=3.8" groups = ["app", "docs"] files = [ - {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, - {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, - {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, - {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, - {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, - {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, - {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, @@ -3276,7 +3269,7 @@ version = "0.37.0" description = "JSON Referencing + Python" optional = false python-versions = ">=3.10" -groups = ["main", "typing"] +groups = ["main", "test", "typing"] files = [ {file = "referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231"}, {file = "referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8"}, @@ -3360,7 +3353,7 @@ version = "0.30.0" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.10" -groups = ["main", "typing"] +groups = ["main", "test", "typing"] files = [ {file = "rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288"}, {file = "rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00"}, @@ -4140,4 +4133,4 @@ propcache = ">=0.2.1" [metadata] lock-version = "2.1" python-versions = ">=3.11,<4.0" -content-hash = "1c419f8c8e9cbb69c76384b2382c31e3598f4ea89f095b202c497590f1aa065e" +content-hash = "b64bb56ec0b8b313913075243daf4dc79a258a434d27d1c7fea20f695f015c02" diff --git a/pyproject.toml b/pyproject.toml index bee993db..e3c5583b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -114,6 +114,7 @@ pytest-asyncio = ">=0.24" parameterized = ">=0.9" pytest-cov = ">=6.1.1" pretend = "^1.0.9" +referencing = "^0.37.0" [tool.poetry.group.typing.dependencies] mypy = ">=1.19.1" diff --git a/tests/schemas/test_workflow_settings_schema.py b/tests/schemas/test_workflow_settings_schema.py index 806e5532..e85a325d 100644 --- a/tests/schemas/test_workflow_settings_schema.py +++ b/tests/schemas/test_workflow_settings_schema.py @@ -204,20 +204,3 @@ def test_repo_workflow_inherits_individual_base_fields( } validator.validate(data) - - def test_schema_composition_with_allof(self, org_workflow_schema): - """Test that the schema uses allOf for proper composition.""" - # Verify that allOf is used instead of direct $ref - assert "allOf" in org_workflow_schema - assert isinstance(org_workflow_schema["allOf"], list) - assert len(org_workflow_schema["allOf"]) > 0 - assert "$ref" in org_workflow_schema["allOf"][0] - assert org_workflow_schema["allOf"][0]["$ref"] == "workflow-settings.json" - - def test_repo_schema_composition_with_allof(self, repo_workflow_schema): - """Test that the repo schema also uses allOf for proper composition.""" - assert "allOf" in repo_workflow_schema - assert isinstance(repo_workflow_schema["allOf"], list) - assert len(repo_workflow_schema["allOf"]) > 0 - assert "$ref" in repo_workflow_schema["allOf"][0] - assert repo_workflow_schema["allOf"][0]["$ref"] == "workflow-settings.json" From 125de60bbb56ecffc1e075256b4ce95faea2eaac Mon Sep 17 00:00:00 2001 From: Thomas Neidhart Date: Wed, 22 Apr 2026 09:14:18 +0200 Subject: [PATCH 4/4] update changelog entry --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6dbc0ac..0d34162b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,9 @@ ## [1.3.1] - unreleased -### Fixed +### Added - - test workflow validation settings ([#633](https://github.com/eclipse-csi/otterdog/pull/633)) + - add tests for workflow validation settings ([#633](https://github.com/eclipse-csi/otterdog/pull/633)) ## [1.3.0] - 30/03/2026