Files
computer-playbook/tests/unit/python/plugins/lookup/test_webserver_resource.py
T
kevinveenbirkenbachandClaude Opus 5 52f1758de5 fix(webserver): resolve an application_id the play still holds unrendered
Moving the effective-cpus expression into a lookup changed where the role id
is read from. Inside a Jinja expression the templar resolves a variable
recursively, so a value that is itself a template arrives fully rendered.
Reading it out of the play's variables does not. sys-stk-full sets
application_id from sys_stk_full_application_id, so the lookup asked the
applications dict for a role literally named

    {{ sys_stk_full_application_id }}

and three jobs of one run died on it: dsk-gnt-claude, svc-ai-agent-broker and
svc-ai-searxng, every one of them a role that goes through that stack.

The lookup now templates application_id and service_name when they still
carry a template, and returns them untouched otherwise, so the ordinary role
never reaches the templar at all.

Measured: with the resolution removed the new test fails with the run's
message character for character, path_trace and config_path included, and
passes with it restored.

The same raw read survives as a fallback in guide_role, unreachable while its
only caller passes the id as a term.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-28 11:20:39 +02:00

92 lines
3.0 KiB
Python

import unittest
from unittest.mock import patch
from ansible.errors import AnsibleError
from plugins.lookup.webserver_resource import PROXY_ROLE, LookupModule
RESOURCE = {"cpus": "0.5", "pids_limit": 512, "host_cpus": 20}
class _Templar:
def __init__(self, resolved):
self._resolved = resolved
def template(self, value):
return self._resolved.get(value, value)
def _run(
key,
proxy,
app,
application_id="web-app-docs",
service_name="",
declared_as=None,
templar=None,
):
applications = {
PROXY_ROLE: {"services": {"openresty": {key: proxy}}},
application_id: {"services": {"docs": {key: app}}},
}
variables = {
"application_id": declared_as or application_id,
"service_name": service_name,
}
with (
patch("plugins.lookup.webserver_resource.ApplicationsLookup") as apps,
patch("plugins.lookup.webserver_resource.ResourceLookup") as resource,
):
apps.return_value.run.return_value = [applications]
resource.return_value.run.side_effect = lambda terms, **_k: [RESOURCE[terms[0]]]
lookup = LookupModule()
if templar is not None:
lookup._templar = templar
return lookup.run([key], variables=variables)
class TestWebserverResource(unittest.TestCase):
def test_the_tighter_of_the_two_wins(self):
self.assertEqual(_run("cpus", proxy="4", app="2"), [2.0])
self.assertEqual(_run("cpus", proxy="1", app="2"), [1.0])
def test_a_percentage_is_resolved_before_the_comparison(self):
"""95% of 20 is 19, so a literal 4 is the tighter of the pair."""
self.assertEqual(_run("cpus", proxy="4", app="95%"), [4.0])
def test_a_percentage_can_be_the_tighter_one(self):
self.assertEqual(_run("cpus", proxy="50%", app="16"), [10.0])
def test_pids_limit_stays_an_integer(self):
result = _run("pids_limit", proxy=1024, app=512)
self.assertEqual(result, [512])
self.assertIsInstance(result[0], int)
def test_an_application_id_that_is_still_a_template_is_resolved(self):
"""sys-stk-full sets application_id from a variable, so the play holds
the unrendered string and the lookup must template it itself."""
result = _run(
"cpus",
proxy="4",
app="2",
declared_as="{{ sys_stk_full_application_id }}",
templar=_Templar({"{{ sys_stk_full_application_id }}": "web-app-docs"}),
)
self.assertEqual(result, [2.0])
def test_a_plain_application_id_is_not_sent_through_the_templar(self):
result = _run("cpus", proxy="4", app="2", templar=_Templar({}))
self.assertEqual(result, [2.0])
def test_an_unknown_key_is_refused(self):
with self.assertRaises(AnsibleError):
LookupModule().run(["mem_limit"], variables={})
def test_more_than_one_term_is_refused(self):
with self.assertRaises(AnsibleError):
LookupModule().run(["cpus", "pids_limit"], variables={})
if __name__ == "__main__":
unittest.main()