diff --git a/tests/unit/python/utils/cache/test_domains.py b/tests/unit/python/utils/cache/test_domains.py index c884e16db2..a683cae05b 100644 --- a/tests/unit/python/utils/cache/test_domains.py +++ b/tests/unit/python/utils/cache/test_domains.py @@ -403,5 +403,45 @@ class TestOnionFlagsAreRendered(unittest.TestCase): ) +class TestOnionNodeIsRequiredWhenTorIsDeployed(unittest.TestCase): + """A deploy carrying `svc-net-tor` without a provisioned node used to + answer the clearnet map and cache it under its own key, because + `group_names` is part of that key. One scope then served onion while + another froze a clearnet asset URL into a stylesheet, which + `body_filter.lua`'s `onionize` returns before rewriting. + """ + + ONION = TestOnionDomainInjection.ONION + + def setUp(self) -> None: + _reset_cache_for_tests() + + def _merge(self, node, group_names): + with tempfile.TemporaryDirectory() as tmp: + roles = _seed_minimal_role(Path(tmp)) + with patch( + "utils.cache.applications.get_merged_applications", + return_value={"svc-net-tor": {"services": {"tor": {"node": node}}}}, + ): + return cache_domains.get_merged_domains( + variables={ + "DOMAIN_PRIMARY": "infinito.test", + "group_names": group_names, + }, + roles_dir=roles, + templar=None, + ) + + def test_an_empty_node_is_an_error(self): + with self.assertRaisesRegex(ValueError, "services.tor.node"): + self._merge("", ["svc-net-tor", "web-app-x"]) + + def test_a_provisioned_node_is_accepted(self): + self.assertIsInstance(self._merge(self.ONION, ["svc-net-tor"]), dict) + + def test_an_empty_node_is_fine_without_tor_in_the_groups(self): + self.assertIsInstance(self._merge("", ["web-app-x"]), dict) + + if __name__ == "__main__": unittest.main() diff --git a/utils/cache/domains.py b/utils/cache/domains.py index ee4bd569a4..cad30db441 100644 --- a/utils/cache/domains.py +++ b/utils/cache/domains.py @@ -269,7 +269,14 @@ def get_merged_domains( ).get("node") node_onion = str(_render_raw(node_raw or "", "services.tor.node") or "").strip() group_names = variables.get("group_names") or [] - if node_onion and "svc-net-tor" in group_names: + if "svc-net-tor" in group_names: + if not node_onion: + raise ValueError( + "get_merged_domains: 'svc-net-tor' is deployed but " + "applications.svc-net-tor.services.tor.node is empty. " + "Skipping the onion injection would serve every exclusive " + "app on clearnet without saying so." + ) merged = _inject_onion_domains( merged, apps,