From 378e03078a90f18c48317673344e4f1adb11ec8f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 05:16:47 +0900 Subject: [PATCH 1/3] test(office): align Python support contract with event matrix --- office/tests/test_python_support_contract.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/office/tests/test_python_support_contract.py b/office/tests/test_python_support_contract.py index 7104fd66..a52ddec3 100644 --- a/office/tests/test_python_support_contract.py +++ b/office/tests/test_python_support_contract.py @@ -50,10 +50,19 @@ def test_python_support_range_matches_classifiers_and_ci_matrix() -> None: office_job = _workflow_job_block(workflow, "office") assert "runs-on: ubuntu-24.04" in office_job assert "runs-on: ubuntu-latest" not in office_job - matrix_match = re.search(r'python-version:\s*\[([^\]]+)\]', office_job) + matrix_match = re.search( + r"python-version:\s*\$\{\{\s*github\.event_name\s*==\s*'pull_request'" + r"\s*&&\s*fromJSON\('(\[[^']+\])'\)\s*\|\|\s*" + r"fromJSON\('(\[[^']+\])'\)\s*\}\}", + office_job, + ) assert matrix_match is not None - matrix_versions = tuple(re.findall(r'"(3\.\d+)"', matrix_match.group(1))) - assert matrix_versions == SUPPORTED_PYTHON_VERSIONS + pull_request_versions, push_versions = ( + tuple(re.findall(r'"(3\.\d+)"', versions)) + for versions in matrix_match.groups() + ) + assert pull_request_versions == (SUPPORTED_PYTHON_VERSIONS[-1],) + assert push_versions == SUPPORTED_PYTHON_VERSIONS def test_python_support_documentation_matches_the_fixed_ci_environment() -> None: From 7a769c79999b76da4f1572e8b2b8b98cf7cd6db5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 12:25:29 +0900 Subject: [PATCH 2/3] test(office): reject unsupported CI matrix values --- office/tests/test_python_support_contract.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/office/tests/test_python_support_contract.py b/office/tests/test_python_support_contract.py index a52ddec3..d421420d 100644 --- a/office/tests/test_python_support_contract.py +++ b/office/tests/test_python_support_contract.py @@ -1,5 +1,6 @@ """Cross-file contract for the Python versions advertised by Inkspan Office.""" +import json from pathlib import Path import re import tomllib @@ -58,7 +59,7 @@ def test_python_support_range_matches_classifiers_and_ci_matrix() -> None: ) assert matrix_match is not None pull_request_versions, push_versions = ( - tuple(re.findall(r'"(3\.\d+)"', versions)) + tuple(json.loads(versions)) for versions in matrix_match.groups() ) assert pull_request_versions == (SUPPORTED_PYTHON_VERSIONS[-1],) From 740ffb4f4f7669525ebd6bbc28964e0b59cc8df1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 20:19:46 +0900 Subject: [PATCH 3/3] test(office): resolve the CI matrix instead of pinning its syntax The previous revision replaced one surface-syntax pattern with another. It required the python-version matrix to be written as the exact conditional `${{ github.event_name == 'pull_request' && fromJSON(...) || fromJSON(...) }}` and reported `assert None is not None` whenever that spelling changed. That is the same failure mode this branch exists to repair: the contract test names the absence of a pattern rather than the drift of a value, and any legitimate reformatting of the workflow expression turns the required Office job red on every candidate head at once. The matrix is now resolved rather than matched. `_office_matrix_python_versions` reads the job's python-version value, decodes every `fromJSON` payload it selects between, and falls back to decoding a literal YAML/JSON sequence, so a plain inline list, the current conditional, and a reformatted conditional all resolve to the same version tuples. Each assertion then states a resolved obligation and fails with the observed value: no matrix entry may name an unsupported minor, the exhaustive set must equal the supported minors in order, and the pull-request set must include the newest supported minor. This also closes the review comment on the previous revision. Decoding the whole payload rather than extracting only `3.x`-shaped substrings means a stray `latest` is now a named failure instead of a silently dropped entry. Verification: tests/test_python_support_contract.py passes (4 tests) and docstring coverage remains 100%. A direct exercise of the resolver confirms it accepts main's conditional, a whitespace-reformatted conditional, and the older inline list, and that it rejects an unsupported `latest` entry, a dropped minor in the exhaustive set, a pull-request set missing the newest minor, and a job with no matrix at all, each with the observed value in the message. The rest of the Office suite needs the hash-locked Linux wheels and is left to CI; this file imports none of them. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01RCDAt2v7kz4SFyaFDsSoyD --- office/tests/test_python_support_contract.py | 67 ++++++++++++++++---- 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/office/tests/test_python_support_contract.py b/office/tests/test_python_support_contract.py index d421420d..78cdd1a3 100644 --- a/office/tests/test_python_support_contract.py +++ b/office/tests/test_python_support_contract.py @@ -33,6 +33,44 @@ def _workflow_job_block(workflow: str, job_name: str) -> str: return f" {job_name}:\n{match.group('body')}" +def _office_matrix_python_versions(office_job: str) -> tuple[tuple[str, ...], ...]: + """Resolve every Python minor set the Office job can select, in declared order. + + The workflow may declare the matrix as a literal YAML/JSON sequence or as an + expression that selects between ``fromJSON`` payloads per event. Both forms + are resolved to their values so this contract asserts the supported minors + rather than the syntax that happens to express them. + """ + + value_match = re.search( + r"(?m)^\s*python-version:[ \t]*(?P\S.*?)\s*$", office_job + ) + assert value_match is not None, ( + "the office job declares no python-version matrix entry" + ) + matrix_value = value_match.group("value") + + payloads = re.findall(r"fromJSON\(\s*'(\[.*?\])'\s*\)", matrix_value) or [ + matrix_value + ] + declared: list[tuple[str, ...]] = [] + for payload in payloads: + try: + versions = json.loads(payload) + except json.JSONDecodeError as error: + raise AssertionError( + f"the office python-version matrix is not resolvable to a " + f"version list; observed {matrix_value!r}" + ) from error + assert isinstance(versions, list) and versions, ( + f"the office python-version matrix must resolve to a non-empty " + f"list; observed {payload!r}" + ) + declared.append(tuple(str(version) for version in versions)) + + return tuple(declared) + + def test_python_support_range_matches_classifiers_and_ci_matrix() -> None: """Require package metadata and the Office CI job to cover the same minors.""" @@ -51,19 +89,26 @@ def test_python_support_range_matches_classifiers_and_ci_matrix() -> None: office_job = _workflow_job_block(workflow, "office") assert "runs-on: ubuntu-24.04" in office_job assert "runs-on: ubuntu-latest" not in office_job - matrix_match = re.search( - r"python-version:\s*\$\{\{\s*github\.event_name\s*==\s*'pull_request'" - r"\s*&&\s*fromJSON\('(\[[^']+\])'\)\s*\|\|\s*" - r"fromJSON\('(\[[^']+\])'\)\s*\}\}", - office_job, + declared_matrices = _office_matrix_python_versions(office_job) + for declared in declared_matrices: + unsupported = tuple( + version + for version in declared + if version not in SUPPORTED_PYTHON_VERSIONS + ) + assert not unsupported, ( + f"the office python-version matrix declares unsupported entries " + f"{unsupported!r} in {declared!r}" + ) + + assert declared_matrices[-1] == SUPPORTED_PYTHON_VERSIONS, ( + f"the exhaustive office python-version matrix must cover every " + f"supported minor in order; observed {declared_matrices[-1]!r}" ) - assert matrix_match is not None - pull_request_versions, push_versions = ( - tuple(json.loads(versions)) - for versions in matrix_match.groups() + assert SUPPORTED_PYTHON_VERSIONS[-1] in declared_matrices[0], ( + f"the office python-version matrix used for pull requests must include " + f"the newest supported minor; observed {declared_matrices[0]!r}" ) - assert pull_request_versions == (SUPPORTED_PYTHON_VERSIONS[-1],) - assert push_versions == SUPPORTED_PYTHON_VERSIONS def test_python_support_documentation_matches_the_fixed_ci_environment() -> None: