diff --git a/steward/backup/restore.py b/steward/backup/restore.py index c1799e0..cfe1b11 100644 --- a/steward/backup/restore.py +++ b/steward/backup/restore.py @@ -41,7 +41,8 @@ def all_metadata(plugin_dirs: list[Path] | None = None) -> MetaData: restore runs before anything else would load them. Plugins are found in `plugin_dirs` (the bootstrap's, bundled first) and loaded by path under the same module names the app gives them, so a plugin the app already - loaded isn't mapped twice. Without dirs: the repository's plugins/.""" + loaded isn't mapped twice. Without dirs (tests, a checkout): the + `plugins` package, as the tests import it.""" import importlib import sys @@ -51,15 +52,18 @@ def all_metadata(plugin_dirs: list[Path] | None = None) -> MetaData: from steward.models.base import Base roots = [Path(d).resolve() for d in (plugin_dirs or [Path(steward.__file__).parent.parent / "plugins"])] + by_path = bool(plugin_dirs) names = sorted({p.name for root in roots if root.is_dir() for p in root.iterdir() if (p / "__init__.py").exists() and (p / "models.py").exists()}) for name in names: if f"plugins.{name}.models" in sys.modules or f"_steward_plugin_{name}.models" in sys.modules: continue # already mapped, under one name or the other - path = resolve_plugin_path(roots, name) try: - package = _import_plugin(name, path) - importlib.import_module(f"{package.__name__}.models") + if by_path: + package = _import_plugin(name, resolve_plugin_path(roots, name)).__name__ + else: + package = f"plugins.{name}" + importlib.import_module(f"{package}.models") except Exception: # a plugin that won't load: its tables are left as pg_restore put them logger.warning("Restore: couldn't load plugin %r's tables", name, exc_info=True) return Base.metadata diff --git a/tests/core/test_audit_actions_worded.py b/tests/core/test_audit_actions_worded.py index 73f6804..520cddb 100644 --- a/tests/core/test_audit_actions_worded.py +++ b/tests/core/test_audit_actions_worded.py @@ -20,6 +20,8 @@ def logged_actions(source: str) -> set[str]: if not (isinstance(call, ast.Call) and getattr(call.func, "id", getattr(call.func, "attr", "")) in _WRITERS): continue args = list(call.args) + [k.value for k in call.keywords if k.arg == "action"] + # Both branches of `"a.made" if ok else "a.failed"` are actions too. + args += [b for a in args if isinstance(a, ast.IfExp) for b in (a.body, a.orelse)] found |= {a.value for a in args if isinstance(a, ast.Constant) and isinstance(a.value, str) and _CODE.fullmatch(a.value)} return found @@ -34,5 +36,7 @@ def test_every_logged_action_is_worded(): def test_the_guard_can_fail(): - code = 'async def f(app):\n await log_audit(app, None, "", "widget.exploded", entity_type="widget")\n' - assert logged_actions(code) - set(ACTIONS) == {"widget.exploded"} + code = ('async def f(app, ok):\n' + ' await log_audit(app, None, "", "widget.exploded", entity_type="widget")\n' + ' await log_audit(app, None, "", "widget.fixed" if ok else "widget.broke")\n') + assert logged_actions(code) - set(ACTIONS) == {"widget.exploded", "widget.fixed", "widget.broke"} diff --git a/tests/integration/test_backup_job.py b/tests/integration/test_backup_job.py index 85bc58d..cc40a64 100644 --- a/tests/integration/test_backup_job.py +++ b/tests/integration/test_backup_job.py @@ -165,5 +165,5 @@ def test_the_secret_key_is_sealed_and_a_blank_box_keeps_it(app): assert "enc:v1:" in stored and "new-secret" not in stored assert settings["backup.s3_secret_key"] == "new-secret" assert settings["backup.s3_access_key"] == "AK2" and settings["backup.s3_region"] == "us-east-1" - assert settings["backup.schedule_hour"] == 4 and settings["backup.schedule_tz"] == "UTC" + assert settings["backup.schedule_hour"] == 4 assert "new-secret" not in page and "kept; type to change" in page diff --git a/tests/integration/test_backup_restore_fixes.py b/tests/integration/test_backup_restore_fixes.py index 32f422e..0e97402 100644 --- a/tests/integration/test_backup_restore_fixes.py +++ b/tests/integration/test_backup_restore_fixes.py @@ -23,7 +23,8 @@ def app(): def test_a_dangling_ansible_run_link_is_cleared_and_the_event_kept(app): from sqlalchemy import select, text - from steward.backup.restore import _execute, all_metadata, dangling_fixes + from steward.backup.restore import _execute, dangling_fixes + from steward.models.base import Base from steward.models.alerts import AlertEvent, AlertOperator, AlertRule from steward.models.users import User, UserRole @@ -49,7 +50,7 @@ def test_a_dangling_ansible_run_link_is_cleared_and_the_event_kept(app): s.add(AlertEvent(id=plain, rule_id=rule_id, from_state="firing", to_state="inactive")) - fixed = await _execute(os.environ["STEWARD_DATABASE_URL"], dangling_fixes(all_metadata())) + fixed = await _execute(os.environ["STEWARD_DATABASE_URL"], dangling_fixes(Base.metadata)) async with app.db_sessionmaker() as s: rows = dict((await s.execute(