import importlib.util import unittest import unittest.mock as mock from unittest.mock import patch from ansible.errors import AnsibleError from . import PROJECT_ROOT 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 DatabaseLookupTests(unittest.TestCase): @classmethod def setUpClass(cls): cls.db_lookup_mod = _load_module( "plugins/lookup/database.py", "lookup_database" ) def _make_lookup(self, available_vars: dict): lm = self.db_lookup_mod.LookupModule() lm._templar = _DummyTemplar(available_vars) lm._loader = mock.MagicMock() patcher = mock.patch.object(self.db_lookup_mod, "lookup_loader") loader_mock = patcher.start() self.addCleanup(patcher.stop) loader_mock.get.return_value = mock.MagicMock( run=lambda _terms, variables=None, **_k: [ (variables or {}).get("applications", {}) ] ) return lm @staticmethod def _fake_get_entity_name(role_name: str) -> str: """ Make entity resolution deterministic for unit tests (no filesystem access). Mirrors the typical behavior for your role naming. """ role_name = role_name.strip() for prefix in ("web-app-", "web-svc-", "svc-db-", "svc-", "persona-"): if role_name.startswith(prefix): return role_name[len(prefix) :] return role_name def test_invalid_terms_raises(self): vars_ = {"applications": {}, "ports": {}, "DIR_COMPOSITIONS": "/opt/compose/"} lookup = self._make_lookup(vars_) with self.assertRaises(AnsibleError): lookup.run([], variables=vars_) with self.assertRaises(AnsibleError): lookup.run(["a", "b", "c"], variables=vars_) def test_kwarg_want_is_not_supported_raises(self): vars_ = {"applications": {}, "ports": {}, "DIR_COMPOSITIONS": "/opt/compose/"} lookup = self._make_lookup(vars_) with self.assertRaises(AnsibleError): lookup.run(["web-app-foo"], variables=vars_, want="url_full") def test_no_dbtype_configured_returns_empty_like_vars_logic(self): applications = { "web-app-foo": { "services": {}, "secrets": {"credentials": {"database_password": "pw"}}, } } ports = {"localhost": {"database": {"svc-db-postgres": "5432"}}} vars_ = { "applications": applications, "ports": ports, "DIR_COMPOSITIONS": "/opt/compose/", } lookup = self._make_lookup(vars_) with patch.object( self.db_lookup_mod, "get_entity_name", side_effect=self._fake_get_entity_name, ): out = lookup.run(["web-app-foo"], variables=vars_)[0] self.assertFalse(out["enabled"]) self.assertFalse(out["shared"]) self.assertFalse(out["local"]) self.assertEqual(out.get("id", ""), "") self.assertEqual(out["type"], "") self.assertEqual(out["name"], "foo") self.assertEqual(out["username"], "foo") self.assertEqual(out["host"], "") self.assertEqual(out["network"], "") self.assertEqual(out["port"], "") self.assertEqual(out["env"], "") self.assertEqual(out["url_jdbc"], "") self.assertEqual(out["url_full"], "") self.assertEqual(out["volume"], "") self.assertEqual(out["image"], "") self.assertEqual(out["version"], "") self.assertEqual(out["reach_host"], "127.0.0.1") with patch.object( self.db_lookup_mod, "get_entity_name", side_effect=self._fake_get_entity_name, ): self.assertEqual( lookup.run(["web-app-foo", "url_full"], variables=vars_)[0], "" ) def test_postgres_dedicated_matches_helper_variables_definition(self): applications = { "web-app-foo": { "services": {"postgres": {"enabled": True, "shared": False}}, "secrets": {"credentials": {"database_password": "pw"}}, }, "svc-db-postgres": { "services": { "postgres": { "name": "postgres-central", "image": "postgis/postgis", "version": "16", "ports": {"local": {"postgres": "5432"}}, } } }, } ports = {"localhost": {"database": {"svc-db-postgres": "5432"}}} vars_ = { "applications": applications, "ports": ports, "DIR_COMPOSITIONS": "/opt/compose/", } lookup = self._make_lookup(vars_) with patch.object( self.db_lookup_mod, "get_entity_name", side_effect=self._fake_get_entity_name, ): out = lookup.run(["web-app-foo"], variables=vars_)[0] self.assertTrue(out["enabled"]) self.assertFalse(out["shared"]) self.assertTrue(out["local"]) self.assertTrue(out["managed"]) self.assertEqual(out["id"], "svc-db-postgres") self.assertEqual(out["type"], "postgres") self.assertEqual(out["name"], "foo") self.assertEqual(out["username"], "foo") self.assertEqual(out["host"], "database") self.assertEqual(out["container"], "foo-database") self.assertEqual(out["network"], "foo") self.assertEqual(out["password"], "pw") self.assertEqual(out["port"], "5432") self.assertEqual(out["env"], "/opt/compose/foo/.env/postgres.env") self.assertEqual(out["initdb_dir"], "/opt/compose/foo/.initdb.d/") self.assertEqual(out["build_dir"], "/opt/compose/foo/.postgres-build/") self.assertEqual(out["url_jdbc"], "jdbc:postgresql://database:5432/foo") self.assertEqual(out["url_full"], "postgres://foo:pw@database:5432/foo") self.assertEqual(out["volume"], "foo_database") self.assertEqual(out["image"], "postgis/postgis") self.assertEqual(out["version"], "16") self.assertEqual(out["reach_host"], "127.0.0.1") self.assertEqual(out["instance"], "foo") with patch.object( self.db_lookup_mod, "get_entity_name", side_effect=self._fake_get_entity_name, ): self.assertEqual( lookup.run(["web-app-foo", "url_full"], variables=vars_)[0], "postgres://foo:pw@database:5432/foo", ) self.assertEqual( lookup.run(["web-app-foo", "port"], variables=vars_)[0], "5432", ) def test_dedicated_with_name_override_is_not_managed(self): applications = { "web-app-foo": { "services": { "postgres": { "enabled": True, "shared": False, "name": "foo-postgres-1", } }, "credentials": {"database_password": "pw"}, }, "svc-db-postgres": { "services": { "postgres": { "name": "postgres-central", "image": "postgis/postgis", "version": "16", "ports": {"local": {"postgres": "5432"}}, } } }, } ports = {"localhost": {"database": {"svc-db-postgres": "5432"}}} vars_ = { "applications": applications, "ports": ports, "DIR_COMPOSITIONS": "/opt/compose/", } lookup = self._make_lookup(vars_) with patch.object( self.db_lookup_mod, "get_entity_name", side_effect=self._fake_get_entity_name, ): out = lookup.run(["web-app-foo"], variables=vars_)[0] self.assertTrue(out["enabled"]) self.assertFalse(out["shared"]) self.assertTrue(out["local"]) self.assertFalse(out["managed"]) def test_postgres_shared_uses_central_name_for_host_instance_container_volume(self): applications = { "web-app-foo": { "services": {"postgres": {"enabled": True, "shared": True}}, "secrets": {"credentials": {"database_password": "pw"}}, }, "svc-db-postgres": { "services": { "postgres": { "name": "postgres-central", "version": "16", "ports": {"local": {"postgres": "5432"}}, } } }, } ports = {"localhost": {"database": {"svc-db-postgres": "5432"}}} vars_ = { "applications": applications, "ports": ports, "DIR_COMPOSITIONS": "/opt/compose/", } lookup = self._make_lookup(vars_) with patch.object( self.db_lookup_mod, "get_entity_name", side_effect=self._fake_get_entity_name, ): out = lookup.run(["web-app-foo"], variables=vars_)[0] self.assertTrue(out["enabled"]) self.assertTrue(out["shared"]) self.assertFalse(out["local"]) self.assertFalse(out["managed"]) self.assertEqual(out["id"], "svc-db-postgres") self.assertEqual(out["host"], "postgres-central") self.assertEqual(out["instance"], "postgres-central") self.assertEqual(out["container"], "postgres") self.assertEqual(out["network"], "postgres") self.assertEqual(out["volume"], "postgres-central") self.assertEqual(out["url_jdbc"], "jdbc:postgresql://postgres-central:5432/foo") self.assertEqual(out["url_full"], "postgres://foo:pw@postgres-central:5432/foo") def test_mariadb_jdbc_scheme_stays_mariadb(self): applications = { "web-app-foo": { "services": {"mariadb": {"enabled": True, "shared": False}}, "secrets": {"credentials": {"database_password": "pw"}}, }, "svc-db-mariadb": { "services": { "mariadb": { "name": "mariadb-central", "version": "11.4", "ports": {"local": {"mariadb": "3306"}}, } } }, } ports = {"localhost": {"database": {"svc-db-mariadb": "3306"}}} vars_ = { "applications": applications, "ports": ports, "DIR_COMPOSITIONS": "/opt/compose/", } lookup = self._make_lookup(vars_) with patch.object( self.db_lookup_mod, "get_entity_name", side_effect=self._fake_get_entity_name, ): out = lookup.run(["web-app-foo"], variables=vars_)[0] self.assertTrue(out["enabled"]) self.assertFalse(out["shared"]) self.assertTrue(out["local"]) self.assertEqual(out["id"], "svc-db-mariadb") self.assertEqual(out["type"], "mariadb") self.assertEqual(out["host"], "database") self.assertEqual(out["port"], "3306") self.assertEqual(out["env"], "/opt/compose/foo/.env/mariadb.env") self.assertEqual(out["url_jdbc"], "jdbc:mariadb://database:3306/foo") self.assertEqual(out["url_full"], "mariadb://foo:pw@database:3306/foo") def test_version_override_on_consumer_wins_over_default(self): applications = { "web-app-foo": { "services": { "postgres": { "enabled": True, "shared": False, "version": "15", } }, "secrets": {"credentials": {"database_password": "pw"}}, }, "svc-db-postgres": { "services": {"postgres": {"name": "postgres-central", "version": "16"}} }, } ports = {"localhost": {"database": {"svc-db-postgres": "5432"}}} vars_ = { "applications": applications, "ports": ports, "DIR_COMPOSITIONS": "/opt/compose/", } lookup = self._make_lookup(vars_) with patch.object( self.db_lookup_mod, "get_entity_name", side_effect=self._fake_get_entity_name, ): out = lookup.run(["web-app-foo"], variables=vars_)[0] self.assertTrue(out["enabled"]) self.assertFalse(out["shared"]) self.assertEqual(out["id"], "svc-db-postgres") self.assertEqual(out["version"], "15") def test_image_override_on_consumer_wins_over_default(self): applications = { "web-app-foo": { "services": { "postgres": { "enabled": True, "shared": False, "image": "postgres", "version": "17", } }, "credentials": {"database_password": "pw"}, }, "svc-db-postgres": { "services": { "postgres": { "name": "postgres-central", "image": "postgis/postgis", "version": "16", } } }, } ports = {"localhost": {"database": {"svc-db-postgres": "5432"}}} vars_ = { "applications": applications, "ports": ports, "DIR_COMPOSITIONS": "/opt/compose/", } lookup = self._make_lookup(vars_) with patch.object( self.db_lookup_mod, "get_entity_name", side_effect=self._fake_get_entity_name, ): out = lookup.run(["web-app-foo"], variables=vars_)[0] self.assertEqual(out["image"], "postgres") self.assertEqual(out["version"], "17") def test_local_flag_for_variant_disabling_dedicated_db(self): applications = { "web-app-foo": { "services": {"postgres": {"enabled": False, "shared": False}}, "secrets": {"credentials": {"database_password": "pw"}}, } } ports = {"localhost": {"database": {"svc-db-postgres": "5432"}}} vars_ = { "applications": applications, "ports": ports, "DIR_COMPOSITIONS": "/opt/compose/", } lookup = self._make_lookup(vars_) with patch.object( self.db_lookup_mod, "get_entity_name", side_effect=self._fake_get_entity_name, ): out = lookup.run(["web-app-foo"], variables=vars_)[0] self.assertFalse(out["enabled"]) self.assertFalse(out["shared"]) self.assertFalse(out["local"]) self.assertEqual(out["host"], "") with patch.object( self.db_lookup_mod, "get_entity_name", side_effect=self._fake_get_entity_name, ): self.assertFalse(lookup.run(["web-app-foo", "local"], variables=vars_)[0]) def test_multiple_database_services_raise(self): applications = { "web-app-foo": { "services": { "mariadb": {"enabled": True, "shared": False}, "postgres": {"enabled": True, "shared": False}, } } } vars_ = { "applications": applications, "ports": {"localhost": {"database": {}}}, "DIR_COMPOSITIONS": "/opt/compose/", } lookup = self._make_lookup(vars_) with self.assertRaises(AnsibleError): lookup.run(["web-app-foo"], variables=vars_) def test_a_dedicated_engine_keeps_the_name_its_role_declares(self): applications = { "web-app-foo": { "services": { "postgres": { "enabled": True, "shared": False, "name": "foo-postgres-1", } }, "secrets": {"credentials": {"database_password": "pw"}}, }, "svc-db-postgres": { "services": { "postgres": {"version": "16", "ports": {"local": {"postgres": ""}}} } }, } vars_ = { "applications": applications, "ports": {"localhost": {"database": {"svc-db-postgres": "5432"}}}, "DIR_COMPOSITIONS": "/opt/compose/", } self.assertEqual(self._resolve(vars_)["container"], "foo-postgres-1") def _mode_vars(self, shared, **extra): applications = { "web-app-foo": { "services": {"postgres": {"enabled": True, "shared": shared}}, "secrets": {"credentials": {"database_password": "pw"}}, }, "svc-db-postgres": { "services": { "postgres": { "name": "postgres", "version": "16", "ports": {"local": {"postgres": ""}}, } } }, } return { "applications": applications, "ports": {"localhost": {"database": {"svc-db-postgres": "5432"}}}, "DIR_COMPOSITIONS": "/opt/compose/", **extra, } def _dedicated_vars(self, **extra): return self._mode_vars(False, **extra) def _resolve(self, vars_): lookup = self._make_lookup(vars_) with patch.object( self.db_lookup_mod, "get_entity_name", side_effect=self._fake_get_entity_name, ): return lookup.run(["web-app-foo"], variables=vars_)[0] def test_swarm_names_the_service_after_the_stack(self): out = self._resolve(self._dedicated_vars(DEPLOYMENT_MODE="swarm")) self.assertEqual(out["service_name"], "foo_database") self.assertIn("resolve-container-id", out["address"]) def test_compose_mode_force_beats_the_cluster_mode(self): out = self._resolve( self._dedicated_vars(DEPLOYMENT_MODE="swarm", compose_mode_force="compose") ) self.assertEqual(out["service_name"], "database") self.assertEqual(out["address"], out["container"]) def test_an_empty_force_falls_back_to_the_cluster_mode(self): out = self._resolve( self._dedicated_vars(DEPLOYMENT_MODE="swarm", compose_mode_force="") ) self.assertEqual(out["service_name"], "foo_database") def test_a_consumers_force_does_not_reach_the_central_engine(self): out = self._resolve( self._mode_vars(True, DEPLOYMENT_MODE="swarm", compose_mode_force="compose") ) self.assertEqual(out["service_name"], "postgres_postgres") if __name__ == "__main__": unittest.main()