Files
computer-playbook/tests/unit/python/plugins/lookup/test_role_tests.py
T
kevinveenbirkenbachandClaude Opus 5.5 89063972a6 perf(test-e2e-playwright): order Playwright passes by meta/tests.yml run_after
A role's playwright.run_after names the roles its pass runs after; the new
playwright_order lookup reads it through role_tests (which now renders
Jinja with the queried role as application_id) and orders the round with
a stable topological sort, so only constrained roles move. web-app-docs
runs after every other invokable role: in CI run 41206003651 (Discourse#2)
its pass waited 3126 s for the docs server's Sphinx build while stalwart,
bigbluebutton, jitsi and prometheus (~400 s) queued behind it. Cycles,
self references, unknown roles and non-list values fail loudly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 21:41:55 +01:00

91 lines
3.1 KiB
Python

"""Unit tests for the `role_tests` lookup plugin.
Pins the contract: two terms (application_id, dotted path), values read
from ``roles/<application_id>/meta/tests.yml`` under ``playbook_dir``,
missing file/key falls back to the ``default`` kwarg.
"""
from __future__ import annotations
import tempfile
import unittest
from pathlib import Path
from ansible.errors import AnsibleError
from ansible.parsing.dataloader import DataLoader
from ansible.template import Templar
from plugins.lookup.role_tests import LookupModule
from utils.cache.yaml import dump_yaml
from utils.roles.mapping import ROLE_FILE_META_TESTS
RUN_AFTER_EXPRESSION = "{{ invokables | difference([application_id]) }}"
class TestRoleTestsLookup(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls._tmp = tempfile.TemporaryDirectory()
cls.addClassCleanup(cls._tmp.cleanup)
cls.root = Path(cls._tmp.name)
tests_yml = cls.root / "roles" / "svc-runner" / ROLE_FILE_META_TESTS
tests_yml.parent.mkdir(parents=True)
dump_yaml(
tests_yml,
{
"cli": {"timeout": 9000},
"playwright": {"run_after": RUN_AFTER_EXPRESSION},
},
)
def test_a_jinja_value_renders_with_the_queried_role_as_application_id(self):
variables = {
"playbook_dir": str(self.root),
"application_id": "web-app-caller",
"invokables": ["svc-runner", "web-app-caller", "web-app-x"],
}
lookup = LookupModule(
loader=DataLoader(),
templar=Templar(loader=DataLoader(), variables=variables),
)
[rendered] = lookup.run(
["svc-runner", "playwright.run_after"], variables=variables
)
self.assertEqual(sorted(rendered), ["web-app-caller", "web-app-x"])
def test_a_jinja_value_stays_raw_without_a_templar(self):
self.assertEqual(
self._run(["svc-runner", "playwright.run_after"]), [RUN_AFTER_EXPRESSION]
)
def _run(self, terms, **kwargs):
return LookupModule().run(
terms, variables={"playbook_dir": str(self.root)}, **kwargs
)
def test_reads_dotted_value(self):
self.assertEqual(self._run(["svc-runner", "cli.timeout"]), [9000])
def test_missing_key_returns_default(self):
self.assertEqual(self._run(["svc-runner", "cli.missing"], default=3600), [3600])
def test_missing_file_returns_default(self):
self.assertEqual(self._run(["web-app-x", "cli.timeout"], default=""), [""])
def test_missing_default_is_none(self):
self.assertEqual(self._run(["web-app-x", "cli.timeout"]), [None])
def test_requires_two_terms(self):
with self.assertRaises(AnsibleError):
self._run(["svc-runner"])
with self.assertRaises(AnsibleError):
self._run(["svc-runner", "cli.timeout", "extra"])
def test_requires_playbook_dir(self):
with self.assertRaises(AnsibleError):
LookupModule().run(["svc-runner", "cli.timeout"], variables={})
if __name__ == "__main__":
unittest.main()