mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
15a6251d7 introduced the merge but tripped over three of the repository's own rules and over the lookup's existing contract test.
_templated called templar.template(value, fail_on_undefined=False). A templar whose signature is template(self, value) raises TypeError on that kwarg, the except swallowed it, and the value came back unrendered: the lookup returned ['{{ vault_cf }}'] where ['secret-cf'] was due. It now falls back to the positional call, the way utils/cache/base.py has always done it.
The SPOT read goes through load_yaml_any instead of yaml.safe_load, which tests/lint/repository/yaml/test_no_direct_calls.py requires so the file is parsed once per process. The module docstring drops the inventories/development/default.yml literal, which tests/unit/python/cli/administration/deploy/development/test_inventory_vars_file_spot.py whitelists by path.
test_missing_api_global_raises_unknown_key asserted exactly the behaviour that broke CI: a scope without API had to raise. It now asserts the fallback, and two new cases pin the edges. A provider the scope omits still resolves; a provider the declaration does not know still raises.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
124 lines
4.4 KiB
Python
124 lines
4.4 KiB
Python
import unittest
|
|
|
|
from ansible.errors import AnsibleError
|
|
|
|
from plugins.lookup.api import LookupModule
|
|
|
|
|
|
class _FakeTemplar:
|
|
"""Minimal templar: deep-substitutes string leaves via ``mapping``."""
|
|
|
|
def __init__(self, mapping=None, available=None):
|
|
self._mapping = mapping or {}
|
|
self.available_variables = available or {}
|
|
|
|
def template(self, value):
|
|
if isinstance(value, dict):
|
|
return {k: self.template(v) for k, v in value.items()}
|
|
if isinstance(value, list):
|
|
return [self.template(v) for v in value]
|
|
return self._mapping.get(value, value)
|
|
|
|
|
|
class TestApiLookup(unittest.TestCase):
|
|
def setUp(self):
|
|
self.lookup = LookupModule()
|
|
self.api = {
|
|
"cloudflare": {"api_token": "cf-token"},
|
|
"telegram": {"bot_token": "", "api_id": "12345", "api_hash": "h"},
|
|
"github": {"client_id": "gh-id", "client_secret": "gh-secret"},
|
|
}
|
|
self.vars = {"API": self.api}
|
|
|
|
def test_resolves_nested_scalar(self):
|
|
out = self.lookup.run(["cloudflare.api_token"], variables=self.vars)
|
|
self.assertEqual(out, ["cf-token"])
|
|
|
|
def test_resolves_deeper_provider_key(self):
|
|
self.assertEqual(
|
|
self.lookup.run(["github.client_secret"], variables=self.vars)[0],
|
|
"gh-secret",
|
|
)
|
|
|
|
def test_resolves_empty_string_value(self):
|
|
self.assertEqual(
|
|
self.lookup.run(["telegram.bot_token"], variables=self.vars)[0], ""
|
|
)
|
|
|
|
def test_returns_subtree_when_path_is_provider_only(self):
|
|
self.assertEqual(
|
|
self.lookup.run(["cloudflare"], variables=self.vars)[0],
|
|
{"api_token": "cf-token"},
|
|
)
|
|
|
|
def test_path_is_stripped(self):
|
|
self.assertEqual(
|
|
self.lookup.run([" cloudflare.api_token "], variables=self.vars)[0],
|
|
"cf-token",
|
|
)
|
|
|
|
def test_unknown_provider_raises(self):
|
|
with self.assertRaises(AnsibleError) as ctx:
|
|
self.lookup.run(["nope.client_id"], variables=self.vars)
|
|
self.assertIn("nope", str(ctx.exception))
|
|
|
|
def test_unknown_leaf_key_raises_at_failing_part(self):
|
|
with self.assertRaises(AnsibleError) as ctx:
|
|
self.lookup.run(["cloudflare.missing"], variables=self.vars)
|
|
msg = str(ctx.exception)
|
|
self.assertIn("cloudflare.missing", msg)
|
|
self.assertIn("missing", msg)
|
|
|
|
def test_descend_into_scalar_raises(self):
|
|
with self.assertRaises(AnsibleError):
|
|
self.lookup.run(["cloudflare.api_token.extra"], variables=self.vars)
|
|
|
|
def test_no_terms_raises(self):
|
|
with self.assertRaises(AnsibleError):
|
|
self.lookup.run([], variables=self.vars)
|
|
|
|
def test_too_many_terms_raises(self):
|
|
with self.assertRaises(AnsibleError):
|
|
self.lookup.run(["a.b", "c.d"], variables=self.vars)
|
|
|
|
def test_empty_path_raises(self):
|
|
with self.assertRaises(AnsibleError):
|
|
self.lookup.run([" "], variables=self.vars)
|
|
|
|
def test_api_not_a_mapping_raises(self):
|
|
with self.assertRaises(AnsibleError) as ctx:
|
|
self.lookup.run(["cloudflare.api_token"], variables={"API": "nope"})
|
|
self.assertIn("mapping", str(ctx.exception))
|
|
|
|
def test_missing_api_global_falls_back_to_the_declaration(self):
|
|
self.assertEqual(self.lookup.run(["cloudflare.api_token"], variables={})[0], "")
|
|
|
|
def test_a_provider_the_scope_omits_still_resolves(self):
|
|
out = self.lookup.run(
|
|
["github.client_id"],
|
|
variables={"API": {"cloudflare": {"api_token": "cf"}}},
|
|
)
|
|
self.assertEqual(out, [""])
|
|
|
|
def test_an_undeclared_provider_still_raises(self):
|
|
with self.assertRaises(AnsibleError):
|
|
self.lookup.run(["nope.api_key"], variables=self.vars)
|
|
|
|
def test_templar_resolves_jinja_values(self):
|
|
self.lookup._templar = _FakeTemplar(mapping={"{{ vault_cf }}": "secret-cf"})
|
|
out = self.lookup.run(
|
|
["cloudflare.api_token"],
|
|
variables={"API": {"cloudflare": {"api_token": "{{ vault_cf }}"}}},
|
|
)
|
|
self.assertEqual(out, ["secret-cf"])
|
|
|
|
def test_falls_back_to_templar_available_variables(self):
|
|
self.lookup._templar = _FakeTemplar(available={"API": self.api})
|
|
self.assertEqual(
|
|
self.lookup.run(["telegram.api_id"], variables=None)[0], "12345"
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|