Files
computer-playbook/tests/unit/python/utils/github/variant/test_instructions.py
T
kevinveenbirkenbachandClaude Opus 5 74a91e7adf refactor(ci): declare the README replay as a service edge instead of a matrix flag
The replay used to be decided by the matrix: utils.roles.guide picked a
variant, instructions.mark wrote a flag, and the workflow appended
web-app-docs to the row's apps. Now each invokable role declares the
guide host in meta/services.yml with bond 0.5 behind the group gate and
switches it on in exactly one variant, and the deploy pulls it in
through that edge. The matrix only reads the declaration back.

web-app-docs was no provider at all: the registry requires shared,
provides or an alias on the role's own entity entry and it had none, so
the 188 declarations would have been decorative. Registering it brings
a structural contract with it -- a dynamically loaded service role must
set its run_once flag as its very first task or load/app.yml includes
it again -- so the role body moves into tasks/00_core.yml and main.yml
becomes the guard.

smallest_variant no longer counts the guide. It picks the variant that
then switches the guide on, which on the next read would make that
variant one service larger and no longer the smallest; without the
exclusion 30 of 188 roles disagreed with the file.

The job title loses the marker. The row is an ordinary deploy of its
variant, so nothing distinguishes it any more -- which is why the unit
test now asserts the label is left alone.

Measured against the previous tree, the sweep marks 68 rows where it
marked 88. Twenty roles declare the guide on a variant this sweep does
not run in the guide's mode, and by decision the declaration is
absolute: the file is the truth and those roles carry no replay in such
a run. The old code instead searched the deployed variants, which hid
the mismatch behind a fallback.

The same measurement found that the predicate first read the group gate
as a declaration -- the gate renders to a non-empty string, so every
variant looked like the declaring one. It now tests for a literal true.
Neither defect showed up in the unit tests, which check the new logic
rather than its difference to the old.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-23 02:30:56 +02:00

97 lines
3.5 KiB
Python

"""The README replay rides on the variant that declares the guide host.
Which row carries it is read out of ``meta/variants.yml``, not computed, so a
role moves the replay by moving the ``docs`` service between its variants.
Two traps this guards. The row still has to run in the guide's own mode: the
replay is a second deploy on the same runner, and on a swarm row that runner
would have to bring a compose stack up beside a swarm one. And the job title
carries no marker any more, so a wrongly marked row is invisible in the run
list; only these assertions catch it.
"""
from __future__ import annotations
import unittest
from unittest.mock import patch
from utils.github.variant import instructions
def _entry(app: str, variant: str, mode: str, **overrides: str) -> dict[str, str]:
entry = {
"apps": app,
"variant": variant,
"mode": mode,
"priority": "false",
"clone": "false",
"covered": "0",
"instructions": "",
"label": f"{app}#{variant}",
}
entry.update(overrides)
return entry
_VARIANTS = {
"web-app-x": [
{"services": {"a": {"enabled": True}}},
{"services": {"a": {"enabled": True}, "docs": {"enabled": True}}},
]
}
def _mark(entries, mode="compose", variants=None):
with patch.object(instructions, "guide_deployable", return_value=mode):
return instructions.mark(
entries, variants if variants is not None else _VARIANTS
)
class TestInstructionsMode(unittest.TestCase):
def test_the_variant_that_declares_the_guide_carries_it(self) -> None:
marked = _mark([_entry("web-app-x", "1", "compose")])
self.assertEqual("compose", marked[0]["instructions"])
def test_a_variant_without_the_guide_carries_nothing(self) -> None:
marked = _mark([_entry("web-app-x", "0", "compose")])
self.assertEqual("", marked[0]["instructions"])
def test_a_swarm_row_never_carries_a_compose_replay(self) -> None:
marked = _mark([_entry("web-app-x", "1", "swarm")])
self.assertEqual("", marked[0]["instructions"])
def test_the_swarm_row_is_skipped_and_the_compose_row_takes_it(self) -> None:
marked = _mark(
[_entry("web-app-x", "1", "swarm"), _entry("web-app-x", "1", "compose")]
)
self.assertEqual(["", "compose"], [e["instructions"] for e in marked])
def test_a_host_role_carries_it_on_its_host_row(self) -> None:
marked = _mark([_entry("web-app-x", "1", "host")], mode="host")
self.assertEqual("host", marked[0]["instructions"])
def test_a_redundant_row_carries_nothing(self) -> None:
marked = _mark([_entry("web-app-x", "1", "compose", covered="7")])
self.assertEqual("", marked[0]["instructions"])
def test_only_one_row_per_role_is_marked(self) -> None:
marked = _mark(
[_entry("web-app-x", "1", "compose"), _entry("web-app-x", "1", "compose")]
)
self.assertEqual(["compose", ""], [e["instructions"] for e in marked])
def test_an_unknown_variant_index_carries_nothing(self) -> None:
marked = _mark([_entry("web-app-x", "9", "compose")])
self.assertEqual("", marked[0]["instructions"])
def test_the_label_is_left_alone(self) -> None:
marked = _mark(
[_entry("web-app-x", "1", "swarm"), _entry("web-app-x", "1", "compose")]
)
self.assertEqual(["web-app-x#1", "web-app-x#1"], [e["label"] for e in marked])
if __name__ == "__main__":
unittest.main()