mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-08 11:46:35 +00:00
16 conflicts, resolved per hunk: - sys-stk-front-proxy: tasks/main.yml keeps the network-sibling loop; the single stack_host_contents write of http.conf plus vhost and the front_proxy_vhost_changed gate move into tasks/vhost.yml, with the http.conf decided per sibling domain so an onion vhost gets no HTTPS redirect. - sys-front-tls: 01_http.yml is gone as the updater intends; the cert-renew include in main.yml gates on the vhost domain's TLS. - sys-svc-compose: the updater's resolve_host_cid without the remote echo and the prepared pending flag in up.yml, written with IS_SWARM_MODE and the repeated-when block grouping. - web-app-nextcloud: the updater's post-upgrade gate and pending-only plugin configuration, which already carry the pinned-plugin handling. - web-app-xwiki: one XWIKI_CORE_VERSION for the readiness probe and style.css.j2; OIDC/LDAP follow xwiki_extensions_ready at bootstrap, the S3 blob store stays off there as TestBlobstoreRenderOrder demands. - roles_with_service: onion swap and dashboard card key combined, the canonical domain is the canonical URL's host in both branches. - sys-ctl-hlth-csp: --clearnet-timeout and --concurrency together; run_checker forwards its options to build_docker_cmd, keeping the script under the 500-line limit. - web-app-matrix: Element branding and web_client_location side by side. - web-app-stalwart/README.md: the generated Cosmos section is dropped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
760 lines
28 KiB
Python
760 lines
28 KiB
Python
"""Unit tests for ``plugins/lookup/roles_with_service.py``.
|
|
|
|
The lookup enumerates consumer roles for a given service by walking the
|
|
merged applications mapping. The applications loader is mocked so the
|
|
tests stay hermetic — no filesystem access into ``roles/``.
|
|
"""
|
|
|
|
import importlib.util
|
|
import unittest
|
|
import unittest.mock as mock
|
|
from unittest.mock import patch
|
|
|
|
from ansible.errors import AnsibleError
|
|
|
|
from utils.networks.reachability import network_of
|
|
|
|
from . import PROJECT_ROOT
|
|
|
|
ONION = "abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrstuvw.onion"
|
|
NETWORK_APPLICATIONS = {
|
|
role: {
|
|
"services": {"design": {"enabled": True, "shared": True}},
|
|
"domains": {"canonical": [f"{name}.example.com"]},
|
|
}
|
|
for role, name in (
|
|
("web-app-keycloak", "auth"),
|
|
("web-app-moodle", "academy"),
|
|
("web-app-nextcloud", "cloud"),
|
|
)
|
|
}
|
|
ONION_URL_HOSTS = {
|
|
"web-app-moodle": f"academy.{ONION}",
|
|
"web-app-nextcloud": f"cloud.{ONION}",
|
|
}
|
|
|
|
|
|
def _load_module(rel_path: str, name: str):
|
|
path = PROJECT_ROOT / rel_path
|
|
spec = importlib.util.spec_from_file_location(name, str(path))
|
|
mod = importlib.util.module_from_spec(spec)
|
|
assert spec and spec.loader
|
|
spec.loader.exec_module(mod)
|
|
return mod
|
|
|
|
|
|
class _DummyTemplar:
|
|
def __init__(self, available_variables):
|
|
self.available_variables = available_variables
|
|
|
|
|
|
class RolesWithServiceLookupTests(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
cls.mod = _load_module(
|
|
"plugins/lookup/roles_with_service.py",
|
|
"lookup_roles_with_service",
|
|
)
|
|
|
|
def _make_lookup(self, available_vars: dict | None = None):
|
|
lm = self.mod.LookupModule()
|
|
lm._templar = _DummyTemplar(available_vars or {})
|
|
lm._loader = mock.MagicMock()
|
|
return lm
|
|
|
|
def _run(
|
|
self,
|
|
terms,
|
|
applications: dict,
|
|
vars_: dict | None = None,
|
|
url_host: str | None = None,
|
|
url_hosts: dict | None = None,
|
|
handover: frozenset = frozenset(),
|
|
**kwargs,
|
|
):
|
|
"""Run the lookup with the ``applications``, ``tls``,
|
|
``canonical_url`` and ``network_handover`` sub-lookups patched to stay
|
|
hermetic; any other sub-lookup raises ``KeyError``. The stubbed tls
|
|
answers ``[role_id, "url.base"]`` and the stubbed canonical_url
|
|
``[role_id, key]`` with the role's own configured domain, which is what
|
|
a clearnet deployment resolves; pass ``url_host`` (every role) or
|
|
``url_hosts`` (per role) to model a deployment whose URL host differs,
|
|
such as an onion one. A role in ``handover`` hands every sibling outside
|
|
its canonical network over."""
|
|
lookup = self._make_lookup(vars_ or {})
|
|
resolve = self.mod._resolve_canonical_domain
|
|
|
|
class _StubTls:
|
|
def run(self, terms_, variables=None, **kwargs):
|
|
role = str(terms_[0])
|
|
host = (
|
|
(url_hosts or {}).get(role)
|
|
or url_host
|
|
or resolve(role, applications.get(role, {}))
|
|
)
|
|
return [f"https://{host or role + '.example.com'}"]
|
|
|
|
class _StubHandover:
|
|
def run(self, terms_, variables=None, **kwargs):
|
|
role, domain = str(terms_[0]), str(terms_[1])
|
|
canonical = resolve(role, applications.get(role, {}))
|
|
return [
|
|
role in handover and network_of(domain) != network_of(canonical)
|
|
]
|
|
|
|
named = self.mod._resolve_named_canonical_domain
|
|
|
|
class _StubCanonicalUrl:
|
|
def run(self, terms_, variables=None, **kwargs):
|
|
role, key = str(terms_[0]), str(terms_[1])
|
|
host = (
|
|
(url_hosts or {}).get(role)
|
|
or url_host
|
|
or named(role, applications.get(role, {}), key)
|
|
)
|
|
return [f"https://{host}"]
|
|
|
|
class _StubApplications:
|
|
def run(self, terms_, variables=None, **kwargs):
|
|
return [applications]
|
|
|
|
stubs = {
|
|
"applications": _StubApplications(),
|
|
"tls": _StubTls(),
|
|
"canonical_url": _StubCanonicalUrl(),
|
|
"network_handover": _StubHandover(),
|
|
}
|
|
|
|
def _dispatch(name, **_kwargs):
|
|
return stubs[name]
|
|
|
|
with patch.object(self.mod.lookup_loader, "get", side_effect=_dispatch):
|
|
return lookup.run(terms, variables=vars_ or {}, **kwargs)
|
|
|
|
def test_zero_terms_raises(self):
|
|
with self.assertRaises(AnsibleError):
|
|
self._run([], {})
|
|
|
|
def test_two_terms_raises(self):
|
|
with self.assertRaises(AnsibleError):
|
|
self._run(["dashboard", "extra"], {})
|
|
|
|
def test_blank_service_name_raises(self):
|
|
with self.assertRaises(AnsibleError):
|
|
self._run([" "], {})
|
|
|
|
def test_consumer_with_truthy_enabled_and_shared_is_included(self):
|
|
applications = {
|
|
"web-app-foo": {
|
|
"services": {
|
|
"dashboard": {"enabled": True, "shared": True},
|
|
},
|
|
"domains": {"canonical": ["foo.example.com"]},
|
|
},
|
|
}
|
|
result = self._run(["dashboard"], applications)
|
|
self.assertEqual(
|
|
result,
|
|
[
|
|
[
|
|
{
|
|
"id": "web-app-foo",
|
|
"canonical_domain": "foo.example.com",
|
|
"canonical_url": "https://foo.example.com",
|
|
"iframe": True,
|
|
}
|
|
]
|
|
],
|
|
)
|
|
|
|
def test_canonical_domain_follows_an_onion_url(self):
|
|
applications = {
|
|
"web-app-foo": {
|
|
"services": {
|
|
"dashboard": {"enabled": True, "shared": True},
|
|
},
|
|
"domains": {"canonical": ["foo.example.com"]},
|
|
},
|
|
}
|
|
|
|
result = self._run(["dashboard"], applications, url_host="foo.abcd1234.onion")
|
|
|
|
self.assertEqual(
|
|
result[0][0]["canonical_domain"],
|
|
"foo.abcd1234.onion",
|
|
"a consumer matching this against a rendered href would miss every "
|
|
"tile on an onion host if it stayed clearnet",
|
|
)
|
|
|
|
def test_iframe_defaults_to_enabled_when_unset(self):
|
|
applications = {
|
|
"web-app-foo": {
|
|
"services": {"dashboard": {"enabled": True, "shared": True}},
|
|
"domains": {"canonical": ["foo.example.com"]},
|
|
},
|
|
}
|
|
result = self._run(["dashboard"], applications)[0]
|
|
self.assertTrue(result[0]["iframe"])
|
|
|
|
def test_iframe_false_is_preserved(self):
|
|
applications = {
|
|
"web-app-foo": {
|
|
"services": {
|
|
"dashboard": {
|
|
"enabled": True,
|
|
"shared": True,
|
|
"iframe": False,
|
|
},
|
|
},
|
|
"domains": {"canonical": ["foo.example.com"]},
|
|
},
|
|
}
|
|
result = self._run(["dashboard"], applications)[0]
|
|
self.assertFalse(result[0]["iframe"])
|
|
|
|
def test_falsy_enabled_excludes_role(self):
|
|
applications = {
|
|
"web-app-foo": {
|
|
"services": {
|
|
"dashboard": {"enabled": False, "shared": True},
|
|
},
|
|
"domains": {"canonical": ["foo.example.com"]},
|
|
},
|
|
}
|
|
self.assertEqual(self._run(["dashboard"], applications), [[]])
|
|
|
|
def test_falsy_shared_excludes_role(self):
|
|
applications = {
|
|
"web-app-foo": {
|
|
"services": {
|
|
"dashboard": {"enabled": True, "shared": False},
|
|
},
|
|
"domains": {"canonical": ["foo.example.com"]},
|
|
},
|
|
}
|
|
self.assertEqual(self._run(["dashboard"], applications), [[]])
|
|
|
|
def test_scrape_false_excludes_role_but_keeps_other_consumers(self):
|
|
applications = {
|
|
"web-svc-asset": {
|
|
"services": {
|
|
"prometheus": {
|
|
"enabled": True,
|
|
"shared": True,
|
|
"scrape": False,
|
|
},
|
|
},
|
|
"domains": {"canonical": ["asset.example.com"]},
|
|
},
|
|
"web-app-foo": {
|
|
"services": {
|
|
"prometheus": {"enabled": True, "shared": True},
|
|
},
|
|
"domains": {"canonical": ["foo.example.com"]},
|
|
},
|
|
}
|
|
result = self._run(["prometheus"], applications)[0]
|
|
self.assertEqual([r["id"] for r in result], ["web-app-foo"])
|
|
|
|
def test_scrape_true_keeps_role(self):
|
|
applications = {
|
|
"web-app-foo": {
|
|
"services": {
|
|
"prometheus": {
|
|
"enabled": True,
|
|
"shared": True,
|
|
"scrape": True,
|
|
},
|
|
},
|
|
"domains": {"canonical": ["foo.example.com"]},
|
|
},
|
|
}
|
|
result = self._run(["prometheus"], applications)[0]
|
|
self.assertEqual([r["id"] for r in result], ["web-app-foo"])
|
|
|
|
def test_track_false_excludes_role_but_keeps_other_consumers(self):
|
|
applications = {
|
|
"web-svc-cdn": {
|
|
"services": {
|
|
"matomo": {
|
|
"enabled": True,
|
|
"shared": True,
|
|
"track": False,
|
|
},
|
|
},
|
|
"domains": {"canonical": ["cdn.example.com"]},
|
|
},
|
|
"web-app-foo": {
|
|
"services": {
|
|
"matomo": {"enabled": True, "shared": True},
|
|
},
|
|
"domains": {"canonical": ["foo.example.com"]},
|
|
},
|
|
}
|
|
result = self._run(["matomo"], applications)[0]
|
|
self.assertEqual([r["id"] for r in result], ["web-app-foo"])
|
|
|
|
def test_track_true_keeps_role(self):
|
|
applications = {
|
|
"web-app-foo": {
|
|
"services": {
|
|
"matomo": {
|
|
"enabled": True,
|
|
"shared": True,
|
|
"track": True,
|
|
},
|
|
},
|
|
"domains": {"canonical": ["foo.example.com"]},
|
|
},
|
|
}
|
|
result = self._run(["matomo"], applications)[0]
|
|
self.assertEqual([r["id"] for r in result], ["web-app-foo"])
|
|
|
|
def test_missing_service_block_excludes_role(self):
|
|
applications = {
|
|
"web-app-foo": {
|
|
"services": {"oidc": {"enabled": True}},
|
|
"domains": {"canonical": ["foo.example.com"]},
|
|
},
|
|
}
|
|
self.assertEqual(self._run(["dashboard"], applications), [[]])
|
|
|
|
def test_role_without_canonical_domain_is_skipped(self):
|
|
applications = {
|
|
"web-app-foo": {
|
|
"services": {
|
|
"dashboard": {"enabled": True, "shared": True},
|
|
},
|
|
"domains": {},
|
|
},
|
|
}
|
|
self.assertEqual(self._run(["dashboard"], applications), [[]])
|
|
|
|
def test_include_headless_keeps_a_role_without_canonical_domain(self):
|
|
applications = {
|
|
"svc-ai-foo": {
|
|
"services": {
|
|
"litellm": {"enabled": True, "shared": True},
|
|
},
|
|
"domains": {},
|
|
},
|
|
}
|
|
self.assertEqual(
|
|
self._run(["litellm"], applications, include_headless=True),
|
|
[
|
|
[
|
|
{
|
|
"id": "svc-ai-foo",
|
|
"canonical_domain": "",
|
|
"canonical_url": "",
|
|
"iframe": True,
|
|
}
|
|
]
|
|
],
|
|
)
|
|
|
|
def test_canonical_as_string_is_accepted(self):
|
|
applications = {
|
|
"web-app-foo": {
|
|
"services": {
|
|
"dashboard": {"enabled": True, "shared": True},
|
|
},
|
|
"domains": {"canonical": "foo.example.com"},
|
|
},
|
|
}
|
|
result = self._run(["dashboard"], applications)
|
|
self.assertEqual(
|
|
result,
|
|
[
|
|
[
|
|
{
|
|
"id": "web-app-foo",
|
|
"canonical_domain": "foo.example.com",
|
|
"canonical_url": "https://foo.example.com",
|
|
"iframe": True,
|
|
}
|
|
]
|
|
],
|
|
)
|
|
|
|
def test_results_are_sorted_by_role_id(self):
|
|
applications = {
|
|
"web-app-zeta": {
|
|
"services": {"prometheus": {"enabled": True, "shared": True}},
|
|
"domains": {"canonical": ["zeta.example.com"]},
|
|
},
|
|
"web-app-alpha": {
|
|
"services": {"prometheus": {"enabled": True, "shared": True}},
|
|
"domains": {"canonical": ["alpha.example.com"]},
|
|
},
|
|
"web-app-mu": {
|
|
"services": {"prometheus": {"enabled": True, "shared": True}},
|
|
"domains": {"canonical": ["mu.example.com"]},
|
|
},
|
|
}
|
|
result = self._run(["prometheus"], applications)[0]
|
|
self.assertEqual(
|
|
[r["id"] for r in result],
|
|
["web-app-alpha", "web-app-mu", "web-app-zeta"],
|
|
)
|
|
|
|
def test_different_services_resolve_independently(self):
|
|
applications = {
|
|
"web-app-foo": {
|
|
"services": {
|
|
"dashboard": {"enabled": True, "shared": True},
|
|
"matomo": {"enabled": False, "shared": True},
|
|
},
|
|
"domains": {"canonical": ["foo.example.com"]},
|
|
},
|
|
"web-app-bar": {
|
|
"services": {
|
|
"dashboard": {"enabled": False, "shared": True},
|
|
"matomo": {"enabled": True, "shared": True},
|
|
},
|
|
"domains": {"canonical": ["bar.example.com"]},
|
|
},
|
|
}
|
|
dashboard_consumers = self._run(["dashboard"], applications)[0]
|
|
matomo_consumers = self._run(["matomo"], applications)[0]
|
|
self.assertEqual(
|
|
[r["id"] for r in dashboard_consumers],
|
|
["web-app-foo"],
|
|
)
|
|
self.assertEqual(
|
|
[r["id"] for r in matomo_consumers],
|
|
["web-app-bar"],
|
|
)
|
|
|
|
def test_non_dict_app_config_skipped(self):
|
|
applications = {
|
|
"web-app-foo": "not-a-dict",
|
|
"web-app-bar": {
|
|
"services": {"dashboard": {"enabled": True, "shared": True}},
|
|
"domains": {"canonical": ["bar.example.com"]},
|
|
},
|
|
}
|
|
result = self._run(["dashboard"], applications)[0]
|
|
self.assertEqual([r["id"] for r in result], ["web-app-bar"])
|
|
|
|
def test_canonical_with_empty_first_entry_raises(self):
|
|
applications = {
|
|
"web-app-foo": {
|
|
"services": {"dashboard": {"enabled": True, "shared": True}},
|
|
"domains": {"canonical": ["", "foo.example.com"]},
|
|
},
|
|
}
|
|
with self.assertRaises(AnsibleError):
|
|
self._run(["dashboard"], applications)
|
|
|
|
def test_canonical_mapping_resolves_to_its_first_domain(self):
|
|
applications = {
|
|
"web-app-foo": {
|
|
"services": {"dashboard": {"enabled": True, "shared": True}},
|
|
"domains": {"canonical": {"foo": "foo.example.com"}},
|
|
},
|
|
}
|
|
result = self._run(["dashboard"], applications)[0]
|
|
self.assertEqual([r["canonical_domain"] for r in result], ["foo.example.com"])
|
|
|
|
def test_unrendered_jinja_strings_are_treated_as_truthy(self):
|
|
"""In a real Ansible play, ``get_merged_applications`` runs the
|
|
merged payload through ``_render_with_templar`` so a value like
|
|
``"{{ 'web-app-dashboard' in group_names }}"`` arrives at this
|
|
lookup as the literal ``True`` / ``False`` it resolved to.
|
|
|
|
Without a templar (CLI smoke tests, scripts that call
|
|
``get_merged_applications(templar=None)``), the raw Jinja string
|
|
passes through unchanged. Python's ``bool()`` on a non-empty
|
|
string is ``True``, so the lookup is over-inclusive on that
|
|
path. This test pins that contract: callers MUST run the
|
|
applications payload through a templar before passing it in,
|
|
otherwise every role with both keys present is returned
|
|
regardless of deployment state.
|
|
"""
|
|
applications = {
|
|
"web-app-foo": {
|
|
"services": {
|
|
"dashboard": {
|
|
"enabled": "{{ 'web-app-dashboard' in group_names }}",
|
|
"shared": "{{ 'web-app-dashboard' in group_names }}",
|
|
},
|
|
},
|
|
"domains": {"canonical": ["foo.example.com"]},
|
|
},
|
|
}
|
|
result = self._run(["dashboard"], applications)[0]
|
|
self.assertEqual(
|
|
[r["id"] for r in result],
|
|
["web-app-foo"],
|
|
"non-empty Jinja string is truthy under bool(); the lookup "
|
|
"relies on a templar-rendered payload for correct filtering",
|
|
)
|
|
|
|
def test_group_names_scopes_to_deployed_consumers(self):
|
|
applications = {
|
|
"web-app-foo": {
|
|
"services": {"logout": {"enabled": True, "shared": True}},
|
|
"domains": {"canonical": ["foo.example.com"]},
|
|
},
|
|
"web-app-bar": {
|
|
"services": {"logout": {"enabled": True, "shared": True}},
|
|
"domains": {"canonical": ["bar.example.com"]},
|
|
},
|
|
}
|
|
result = self._run(
|
|
["logout"], applications, vars_={"group_names": ["web-app-foo"]}
|
|
)[0]
|
|
self.assertEqual([r["id"] for r in result], ["web-app-foo"])
|
|
|
|
def test_scope_all_ignores_group_names(self):
|
|
applications = {
|
|
"web-svc-logout": {
|
|
"services": {"matomo": {"enabled": True, "shared": True}},
|
|
"domains": {"canonical": ["logout.example.com"]},
|
|
},
|
|
}
|
|
gn = {"group_names": ["web-app-matomo"]}
|
|
self.assertEqual(
|
|
[r["id"] for r in self._run(["matomo"], applications, vars_=gn)[0]],
|
|
[],
|
|
"default host scope drops a consumer absent from this host's group_names",
|
|
)
|
|
all_scoped = self._run(["matomo"], applications, vars_=gn, scope="all")[0]
|
|
self.assertEqual(
|
|
[r["id"] for r in all_scoped],
|
|
["web-svc-logout"],
|
|
"scope='all' returns deployment-scoped consumers regardless of group_names",
|
|
)
|
|
|
|
def test_deployment_scope_spans_hosts_but_not_the_whole_repository(self):
|
|
applications = {
|
|
"web-app-foo": {
|
|
"services": {"logout": {"enabled": True, "shared": True}},
|
|
"domains": {"canonical": ["foo.example.com"]},
|
|
},
|
|
"web-app-bar": {
|
|
"services": {"logout": {"enabled": True, "shared": True}},
|
|
"domains": {"canonical": ["bar.example.com"]},
|
|
},
|
|
"web-app-undeployed": {
|
|
"services": {"logout": {"enabled": True, "shared": True}},
|
|
"domains": {"canonical": ["undeployed.example.com"]},
|
|
},
|
|
}
|
|
vars_ = {
|
|
"group_names": ["web-app-foo"],
|
|
"groups": {
|
|
"web-app-foo": ["host-a"],
|
|
"web-app-bar": ["host-b"],
|
|
"web-app-undeployed": [],
|
|
},
|
|
}
|
|
result = self._run(["logout"], applications, vars_=vars_, scope="deployment")[0]
|
|
self.assertEqual(
|
|
sorted(r["id"] for r in result),
|
|
["web-app-bar", "web-app-foo"],
|
|
"a role deployed on another host of the same deployment is "
|
|
"reachable; a role in the repository but in no group is not",
|
|
)
|
|
|
|
def test_self_provider_is_excluded(self):
|
|
applications = {
|
|
"web-svc-logout": {
|
|
"services": {"logout": {"enabled": True, "shared": True}},
|
|
"domains": {"canonical": ["logout.example.com"]},
|
|
},
|
|
"web-app-foo": {
|
|
"services": {"logout": {"enabled": True, "shared": True}},
|
|
"domains": {"canonical": ["foo.example.com"]},
|
|
},
|
|
}
|
|
result = self._run(["logout"], applications)[0]
|
|
self.assertEqual([r["id"] for r in result], ["web-app-foo"])
|
|
|
|
def test_absent_group_names_keeps_all_consumers(self):
|
|
applications = {
|
|
"web-app-foo": {
|
|
"services": {"logout": {"enabled": True, "shared": True}},
|
|
"domains": {"canonical": ["foo.example.com"]},
|
|
},
|
|
"web-app-bar": {
|
|
"services": {"logout": {"enabled": True, "shared": True}},
|
|
"domains": {"canonical": ["bar.example.com"]},
|
|
},
|
|
}
|
|
result = self._run(["logout"], applications)[0]
|
|
self.assertEqual([r["id"] for r in result], ["web-app-bar", "web-app-foo"])
|
|
|
|
def _mcp_role(self, direction: str, **mcp_extra) -> dict:
|
|
mcp = {
|
|
"enabled": True,
|
|
"shared": True,
|
|
"direction": direction,
|
|
"transport": "streamable_http",
|
|
"auth": "bearer_token",
|
|
"auth_subject": "user",
|
|
"endpoint": {
|
|
"service_key": "baserow",
|
|
"path": "/mcp",
|
|
"port_key": "http",
|
|
},
|
|
}
|
|
mcp.update(mcp_extra)
|
|
return {
|
|
"services": {
|
|
"mcp": mcp,
|
|
"baserow": {"ports": {"local": {"http": 8021}}},
|
|
},
|
|
"domains": {"canonical": ["srv.example.com"]},
|
|
}
|
|
|
|
def test_direction_filter_excludes_other_direction(self):
|
|
applications = {
|
|
"web-app-server": self._mcp_role("server"),
|
|
"web-app-client": self._mcp_role("client"),
|
|
}
|
|
result = self._run(["mcp"], applications, direction="server")[0]
|
|
self.assertEqual([r["id"] for r in result], ["web-app-server"])
|
|
|
|
def test_direction_filter_includes_both(self):
|
|
applications = {"web-app-duplex": self._mcp_role("both")}
|
|
result = self._run(["mcp"], applications, direction="server")[0]
|
|
self.assertEqual([r["id"] for r in result], ["web-app-duplex"])
|
|
|
|
def test_direction_entry_carries_endpoint_metadata(self):
|
|
applications = {"web-app-server": self._mcp_role("server")}
|
|
entry = self._run(["mcp"], applications, direction="server")[0][0]
|
|
self.assertEqual(entry["transport"], "streamable_http")
|
|
self.assertEqual(entry["auth"], "bearer_token")
|
|
self.assertEqual(entry["auth_subject"], "user")
|
|
self.assertEqual(
|
|
entry["endpoint"],
|
|
{
|
|
"service_key": "baserow",
|
|
"path": "/mcp",
|
|
"port": 8021,
|
|
},
|
|
)
|
|
|
|
def test_direction_endpoint_port_falls_back_to_internal(self):
|
|
role = self._mcp_role("server")
|
|
role["services"]["baserow"] = {"ports": {"internal": {"http": 80}}}
|
|
applications = {"web-app-server": role}
|
|
entry = self._run(["mcp"], applications, direction="server")[0][0]
|
|
self.assertEqual(entry["endpoint"]["port"], 80)
|
|
|
|
def test_the_internal_port_wins_without_any_exposure_declaration(self):
|
|
role = self._mcp_role("server")
|
|
role["services"]["baserow"]["ports"]["internal"] = {"http": 80}
|
|
applications = {"web-app-server": role}
|
|
entry = self._run(["mcp"], applications, direction="server")[0][0]
|
|
self.assertEqual(entry["endpoint"]["port"], 80)
|
|
|
|
def test_an_exposure_declaration_does_not_change_the_port(self):
|
|
role = self._mcp_role("server")
|
|
role["services"]["mcp"]["exposure"] = "public"
|
|
role["services"]["baserow"]["ports"]["internal"] = {"http": 80}
|
|
applications = {"web-app-server": role}
|
|
entry = self._run(["mcp"], applications, direction="server")[0][0]
|
|
self.assertEqual(entry["endpoint"]["port"], 80)
|
|
|
|
def test_without_direction_kwarg_shape_is_unchanged(self):
|
|
applications = {"web-app-server": self._mcp_role("server")}
|
|
entry = self._run(["mcp"], applications)[0][0]
|
|
self.assertEqual(
|
|
sorted(entry), ["canonical_domain", "canonical_url", "id", "iframe"]
|
|
)
|
|
|
|
def _network_ids(self, url_hosts: dict, **kwargs) -> list[str]:
|
|
result = self._run(
|
|
["design"],
|
|
NETWORK_APPLICATIONS,
|
|
url_hosts=url_hosts,
|
|
handover=frozenset({"web-app-moodle"}),
|
|
**kwargs,
|
|
)[0]
|
|
return [r["id"] for r in result]
|
|
|
|
def test_network_keeps_only_roles_whose_page_renders_there(self):
|
|
self.assertEqual(
|
|
self._network_ids(ONION_URL_HOSTS, network="tor"),
|
|
["web-app-nextcloud"],
|
|
"the issuer keeps its clearnet URL and the handover sibling only "
|
|
"redirects to clearnet, so neither renders its page on tor",
|
|
)
|
|
|
|
def test_the_canonical_network_keeps_the_issuer_and_the_handover_role(self):
|
|
self.assertEqual(
|
|
self._network_ids({}, network="clearnet"),
|
|
["web-app-keycloak", "web-app-moodle", "web-app-nextcloud"],
|
|
)
|
|
|
|
def test_without_network_kwarg_no_role_is_dropped(self):
|
|
self.assertEqual(
|
|
self._network_ids(ONION_URL_HOSTS),
|
|
["web-app-keycloak", "web-app-moodle", "web-app-nextcloud"],
|
|
)
|
|
|
|
def test_an_unknown_network_fails_loudly(self):
|
|
with self.assertRaises(AnsibleError):
|
|
self._network_ids({}, network="onion")
|
|
|
|
def _card_target_role(self, service: str, **block_extra) -> dict:
|
|
block = {"enabled": True, "shared": True}
|
|
block.update(block_extra)
|
|
return {
|
|
"web-app-chat": {
|
|
"services": {service: block},
|
|
"domains": {
|
|
"canonical": {
|
|
"synapse": "matrix.example.com",
|
|
"element": "element.example.com",
|
|
}
|
|
},
|
|
},
|
|
}
|
|
|
|
def test_dashboard_card_key_targets_that_domain(self):
|
|
applications = self._card_target_role("dashboard", card="element")
|
|
entry = self._run(["dashboard"], applications)[0][0]
|
|
self.assertEqual(
|
|
(entry["canonical_domain"], entry["canonical_url"]),
|
|
("element.example.com", "https://element.example.com"),
|
|
)
|
|
|
|
def test_dashboard_card_domain_follows_the_onion_url(self):
|
|
applications = self._card_target_role("dashboard", card="element")
|
|
entry = self._run(
|
|
["dashboard"], applications, url_hosts={"web-app-chat": f"element.{ONION}"}
|
|
)[0][0]
|
|
self.assertEqual(
|
|
(entry["canonical_domain"], entry["canonical_url"]),
|
|
(f"element.{ONION}", f"https://element.{ONION}"),
|
|
)
|
|
|
|
def test_dashboard_without_card_key_targets_the_primary_domain(self):
|
|
applications = self._card_target_role("dashboard")
|
|
entry = self._run(["dashboard"], applications)[0][0]
|
|
self.assertEqual(
|
|
(entry["canonical_domain"], entry["canonical_url"]),
|
|
("matrix.example.com", "https://matrix.example.com"),
|
|
)
|
|
|
|
def test_dashboard_unknown_card_key_fails_loudly(self):
|
|
applications = self._card_target_role("dashboard", card="nope")
|
|
with self.assertRaisesRegex(AnsibleError, "nope"):
|
|
self._run(["dashboard"], applications)
|
|
|
|
def test_other_services_ignore_a_card_key(self):
|
|
applications = self._card_target_role("prometheus", card="element")
|
|
entry = self._run(["prometheus"], applications)[0][0]
|
|
self.assertEqual(
|
|
(entry["canonical_domain"], entry["canonical_url"]),
|
|
("matrix.example.com", "https://matrix.example.com"),
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|