From aab2f3d898fe4caed6b519b759644982f21e1ec8 Mon Sep 17 00:00:00 2001 From: Agent Zero Date: Thu, 6 Aug 2026 14:30:15 +0200 Subject: [PATCH] fix(tests): remove deleted test_sample plugin, inline SamplePlugin definition - Remove import from app.plugins.builtins.test_sample (deleted in Phase 4) - Define SamplePlugin inline in test_plugins.py with same lifecycle behavior - Replace all test_sample/TestSamplePlugin references with sample_plugin/SamplePlugin - Create migration SQL files: 0001_sample_plugin.sql, 0001_bad_migration.sql - Update discover_builtins test to check for tags plugin instead --- .../migrations/0001_bad_migration.sql | 6 + .../migrations/0001_sample_plugin.sql | 9 + tests/test_plugins.py | 218 +++++++++++------- 3 files changed, 147 insertions(+), 86 deletions(-) create mode 100644 app/plugins/builtins/migrations/0001_bad_migration.sql create mode 100644 app/plugins/builtins/migrations/0001_sample_plugin.sql diff --git a/app/plugins/builtins/migrations/0001_bad_migration.sql b/app/plugins/builtins/migrations/0001_bad_migration.sql new file mode 100644 index 0000000..8eebb32 --- /dev/null +++ b/app/plugins/builtins/migrations/0001_bad_migration.sql @@ -0,0 +1,6 @@ +-- Bad migration: creates table WITHOUT tenant_id (for validator testing) +CREATE TABLE IF NOT EXISTS plugin_bad_data ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + name VARCHAR(255) NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); diff --git a/app/plugins/builtins/migrations/0001_sample_plugin.sql b/app/plugins/builtins/migrations/0001_sample_plugin.sql new file mode 100644 index 0000000..fb5327d --- /dev/null +++ b/app/plugins/builtins/migrations/0001_sample_plugin.sql @@ -0,0 +1,9 @@ +-- Sample plugin migration: creates plugin_sample_data table with tenant_id +CREATE TABLE IF NOT EXISTS plugin_sample_data ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL, + name VARCHAR(255) NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS ix_plugin_sample_data_tenant ON plugin_sample_data(tenant_id); diff --git a/tests/test_plugins.py b/tests/test_plugins.py index 35e9581..a64003e 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -18,8 +18,54 @@ from app.main import create_app from app.models.plugin import Plugin as PluginModel from app.models.plugin import PluginMigration from app.plugins.base import BasePlugin -from app.plugins.builtins.test_sample import TestSamplePlugin from app.plugins.manifest import PluginManifest + + +# ─── Sample Plugin for lifecycle tests ─── + + +class SamplePlugin(BasePlugin): + """A sample plugin for testing the plugin lifecycle.""" + + manifest = PluginManifest( + name="sample_plugin", + version="1.0.0", + display_name="Sample Plugin", + description="A sample plugin for testing install/activate/deactivate/uninstall lifecycle.", + dependencies=[], + routes=[], + events=["contact.created", "company.created"], + migrations=["0001_sample_plugin.sql"], + permissions=[], + ) + + def __init__(self) -> None: + super().__init__() + self.install_called = False + self.activate_called = False + self.deactivate_called = False + self.uninstall_called = False + self.event_log: list[dict[str, Any]] = [] + + async def on_install(self, db, service_container) -> None: + self.install_called = True + + async def on_activate(self, db, service_container, event_bus) -> None: + self.activate_called = True + await super().on_activate(db, service_container, event_bus) + + async def on_deactivate(self, db, service_container, event_bus) -> None: + self.deactivate_called = True + await super().on_deactivate(db, service_container, event_bus) + + async def on_uninstall(self, db, service_container) -> None: + self.uninstall_called = True + + async def on_contact_created(self, payload: dict[str, Any]) -> None: + self.event_log.append({"event": "contact.created", "payload": payload}) + + async def on_company_created(self, payload: dict[str, Any]) -> None: + self.event_log.append({"event": "company.created", "payload": payload}) from app.plugins.migration_runner import MigrationRunner, MigrationValidationError from app.plugins.registry import get_registry, reset_registry_for_testing from app.services.plugin_service import reset_plugin_service_for_testing @@ -62,7 +108,7 @@ async def plugin_app(engine: AsyncEngine, redis_client): await container.initialize() # Register test plugins manually (don't auto-discover to avoid side effects) - registry.register_plugin(TestSamplePlugin()) + registry.register_plugin(SamplePlugin()) registry.register_plugin(BadMigrationPlugin()) # Reset plugin service to use the new registry @@ -110,7 +156,7 @@ async def test_ac01_list_plugins(authed_plugin_client: AsyncClient): assert "total" in data assert data["total"] >= 2 plugin_names = [p["name"] for p in data["plugins"]] - assert "test_sample" in plugin_names + assert "sample_plugin" in plugin_names assert "bad_migration_plugin" in plugin_names # Check status field exists for p in data["plugins"]: @@ -128,11 +174,11 @@ async def test_ac02_install_plugin( ): """AC2: Install plugin runs migrations and sets status=installed.""" resp = await authed_plugin_client.post( - "/api/v1/plugins/test_sample/install", headers=ORIGIN_HEADER + "/api/v1/plugins/sample_plugin/install", headers=ORIGIN_HEADER ) assert resp.status_code == 200 data = resp.json() - assert data["name"] == "test_sample" + assert data["name"] == "sample_plugin" assert data["status"] == "installed" assert data["installed"] is True assert data["active"] is False @@ -140,10 +186,10 @@ async def test_ac02_install_plugin( # Verify migration table was created (tenant_id column exists) result = await db_session_for_plugins.execute( text( - "SELECT column_name FROM information_schema.columns WHERE table_name = 'plugin_test_data' AND column_name = 'tenant_id'" + "SELECT column_name FROM information_schema.columns WHERE table_name = 'plugin_sample_data' AND column_name = 'tenant_id'" ) ) - assert result.fetchone() is not None, "plugin_test_data table should have tenant_id column" + assert result.fetchone() is not None, "plugin_sample_data table should have tenant_id column" # ─── AC3: POST /api/v1/plugins/{name}/activate → 200, status=active, routes registered ─── @@ -154,17 +200,17 @@ async def test_ac03_activate_plugin(authed_plugin_client: AsyncClient): """AC3: Activate plugin sets status=active and registers event listeners.""" # Install first resp = await authed_plugin_client.post( - "/api/v1/plugins/test_sample/install", headers=ORIGIN_HEADER + "/api/v1/plugins/sample_plugin/install", headers=ORIGIN_HEADER ) assert resp.status_code == 200 # Activate resp = await authed_plugin_client.post( - "/api/v1/plugins/test_sample/activate", headers=ORIGIN_HEADER + "/api/v1/plugins/sample_plugin/activate", headers=ORIGIN_HEADER ) assert resp.status_code == 200 data = resp.json() - assert data["name"] == "test_sample" + assert data["name"] == "sample_plugin" assert data["status"] == "active" assert data["active"] is True @@ -176,16 +222,16 @@ async def test_ac03_activate_plugin(authed_plugin_client: AsyncClient): async def test_ac04_deactivate_plugin(authed_plugin_client: AsyncClient): """AC4: Deactivate plugin sets status=inactive and unregisters event listeners.""" # Install and activate first - await authed_plugin_client.post("/api/v1/plugins/test_sample/install", headers=ORIGIN_HEADER) - await authed_plugin_client.post("/api/v1/plugins/test_sample/activate", headers=ORIGIN_HEADER) + await authed_plugin_client.post("/api/v1/plugins/sample_plugin/install", headers=ORIGIN_HEADER) + await authed_plugin_client.post("/api/v1/plugins/sample_plugin/activate", headers=ORIGIN_HEADER) # Deactivate resp = await authed_plugin_client.post( - "/api/v1/plugins/test_sample/deactivate", headers=ORIGIN_HEADER + "/api/v1/plugins/sample_plugin/deactivate", headers=ORIGIN_HEADER ) assert resp.status_code == 200 data = resp.json() - assert data["name"] == "test_sample" + assert data["name"] == "sample_plugin" assert data["status"] == "inactive" assert data["active"] is False @@ -199,10 +245,10 @@ async def test_ac05_uninstall_plugin( ): """AC5: Uninstall plugin removes DB record.""" # Install first - await authed_plugin_client.post("/api/v1/plugins/test_sample/install", headers=ORIGIN_HEADER) + await authed_plugin_client.post("/api/v1/plugins/sample_plugin/install", headers=ORIGIN_HEADER) # Uninstall - resp = await authed_plugin_client.delete("/api/v1/plugins/test_sample", headers=ORIGIN_HEADER) + resp = await authed_plugin_client.delete("/api/v1/plugins/sample_plugin", headers=ORIGIN_HEADER) assert resp.status_code == 200 data = resp.json() assert data["status"] == "uninstalled" @@ -210,7 +256,7 @@ async def test_ac05_uninstall_plugin( # Verify DB record is gone result = await db_session_for_plugins.execute( - select(PluginModel).where(PluginModel.name == "test_sample") + select(PluginModel).where(PluginModel.name == "sample_plugin") ) assert result.scalar_one_or_none() is None @@ -221,33 +267,33 @@ async def test_ac05_uninstall_plugin( @pytest.mark.asyncio async def test_ac06_uninstall_remove_data(authed_plugin_client: AsyncClient, engine: AsyncEngine): """AC6: Uninstall with remove_data=true drops plugin tables.""" - # Install first (creates plugin_test_data table) - await authed_plugin_client.post("/api/v1/plugins/test_sample/install", headers=ORIGIN_HEADER) + # Install first (creates plugin_sample_data table) + await authed_plugin_client.post("/api/v1/plugins/sample_plugin/install", headers=ORIGIN_HEADER) # Verify table exists def _check_table(sync_conn): insp = inspect(sync_conn) - return "plugin_test_data" in insp.get_table_names() + return "plugin_sample_data" in insp.get_table_names() async with engine.connect() as conn: table_exists_before = await conn.run_sync(_check_table) - assert table_exists_before, "plugin_test_data table should exist after install" + assert table_exists_before, "plugin_sample_data table should exist after install" # Uninstall with remove_data=true resp = await authed_plugin_client.delete( - "/api/v1/plugins/test_sample?remove_data=true", headers=ORIGIN_HEADER + "/api/v1/plugins/sample_plugin?remove_data=true", headers=ORIGIN_HEADER ) assert resp.status_code == 200 data = resp.json() assert data["status"] == "uninstalled" - assert "plugin_test_data" in data.get("dropped_tables", []) + assert "plugin_sample_data" in data.get("dropped_tables", []) # Verify table is gone async with engine.connect() as conn: table_exists_after = await conn.run_sync(_check_table) assert ( not table_exists_after - ), "plugin_test_data table should be dropped after uninstall with remove_data=true" + ), "plugin_sample_data table should be dropped after uninstall with remove_data=true" # ─── AC7: GET /api/v1/plugins/manifest → 200 + manifest schema documentation ─── @@ -282,12 +328,12 @@ async def test_ac08_activation_registers_event_listeners( ): """AC8: Activating a plugin registers its event listeners on the event bus.""" registry = get_registry() - plugin = registry.get_plugin("test_sample") + plugin = registry.get_plugin("sample_plugin") assert plugin is not None # Install and activate - await authed_plugin_client.post("/api/v1/plugins/test_sample/install", headers=ORIGIN_HEADER) - await authed_plugin_client.post("/api/v1/plugins/test_sample/activate", headers=ORIGIN_HEADER) + await authed_plugin_client.post("/api/v1/plugins/sample_plugin/install", headers=ORIGIN_HEADER) + await authed_plugin_client.post("/api/v1/plugins/sample_plugin/activate", headers=ORIGIN_HEADER) event_bus = get_event_bus() @@ -312,13 +358,13 @@ async def test_ac08_activation_registers_event_listeners( async def test_ac09_deactivation_unregisters_event_listeners(authed_plugin_client: AsyncClient): """AC9: Deactivating a plugin unregisters its event listeners from the event bus.""" registry = get_registry() - plugin = registry.get_plugin("test_sample") + plugin = registry.get_plugin("sample_plugin") assert plugin is not None # Install, activate, then deactivate - await authed_plugin_client.post("/api/v1/plugins/test_sample/install", headers=ORIGIN_HEADER) - await authed_plugin_client.post("/api/v1/plugins/test_sample/activate", headers=ORIGIN_HEADER) - await authed_plugin_client.post("/api/v1/plugins/test_sample/deactivate", headers=ORIGIN_HEADER) + await authed_plugin_client.post("/api/v1/plugins/sample_plugin/install", headers=ORIGIN_HEADER) + await authed_plugin_client.post("/api/v1/plugins/sample_plugin/activate", headers=ORIGIN_HEADER) + await authed_plugin_client.post("/api/v1/plugins/sample_plugin/deactivate", headers=ORIGIN_HEADER) event_bus = get_event_bus() @@ -349,24 +395,24 @@ async def test_ac10_migration_creates_tenant_id( authed_plugin_client: AsyncClient, engine: AsyncEngine ): """AC10: Plugin migration creates tables that have tenant_id column.""" - # Install the test_sample plugin which has a migration creating plugin_test_data + # Install the sample_plugin plugin which has a migration creating plugin_sample_data resp = await authed_plugin_client.post( - "/api/v1/plugins/test_sample/install", headers=ORIGIN_HEADER + "/api/v1/plugins/sample_plugin/install", headers=ORIGIN_HEADER ) assert resp.status_code == 200 # Verify the table has tenant_id column def _get_columns(sync_conn): insp = inspect(sync_conn) - if "plugin_test_data" not in insp.get_table_names(): + if "plugin_sample_data" not in insp.get_table_names(): return None - return [col["name"] for col in insp.get_columns("plugin_test_data")] + return [col["name"] for col in insp.get_columns("plugin_sample_data")] async with engine.connect() as conn: columns = await conn.run_sync(_get_columns) - assert columns is not None, "plugin_test_data table should exist" - assert "tenant_id" in columns, "plugin_test_data table must have tenant_id column" + assert columns is not None, "plugin_sample_data table should exist" + assert "tenant_id" in columns, "plugin_sample_data table must have tenant_id column" # ─── AC11: Plugin migration validator rejects tables without tenant_id ─── @@ -400,19 +446,19 @@ async def test_ac12_migrations_tracked( authed_plugin_client: AsyncClient, db_session_for_plugins: AsyncSession ): """AC12: Plugin migrations are tracked in plugin_migrations table.""" - # Install the test_sample plugin + # Install the sample_plugin plugin resp = await authed_plugin_client.post( - "/api/v1/plugins/test_sample/install", headers=ORIGIN_HEADER + "/api/v1/plugins/sample_plugin/install", headers=ORIGIN_HEADER ) assert resp.status_code == 200 # Check plugin_migrations table has a record result = await db_session_for_plugins.execute( - select(PluginMigration).where(PluginMigration.plugin_name == "test_sample") + select(PluginMigration).where(PluginMigration.plugin_name == "sample_plugin") ) migrations = result.scalars().all() - assert len(migrations) > 0, "plugin_migrations table should have a record for test_sample" - assert migrations[0].migration_file == "0001_test_plugin.sql" + assert len(migrations) > 0, "plugin_migrations table should have a record for sample_plugin" + assert migrations[0].migration_file == "0001_sample_plugin.sql" assert migrations[0].status == "applied" @@ -423,16 +469,16 @@ async def test_ac12_migrations_tracked( async def test_ac13_activate_already_active(authed_plugin_client: AsyncClient): """AC13: Activating an already-active plugin is idempotent (returns 200, no error).""" # Install and activate - await authed_plugin_client.post("/api/v1/plugins/test_sample/install", headers=ORIGIN_HEADER) + await authed_plugin_client.post("/api/v1/plugins/sample_plugin/install", headers=ORIGIN_HEADER) resp = await authed_plugin_client.post( - "/api/v1/plugins/test_sample/activate", headers=ORIGIN_HEADER + "/api/v1/plugins/sample_plugin/activate", headers=ORIGIN_HEADER ) assert resp.status_code == 200 assert resp.json()["status"] == "active" # Activate again — should be idempotent resp = await authed_plugin_client.post( - "/api/v1/plugins/test_sample/activate", headers=ORIGIN_HEADER + "/api/v1/plugins/sample_plugin/activate", headers=ORIGIN_HEADER ) assert resp.status_code == 200 data = resp.json() @@ -448,19 +494,19 @@ async def test_ac13_activate_already_active(authed_plugin_client: AsyncClient): async def test_ac14_deactivate_already_inactive(authed_plugin_client: AsyncClient): """AC14: Deactivating an already-inactive plugin is idempotent (returns 200, no error).""" # Install and activate - await authed_plugin_client.post("/api/v1/plugins/test_sample/install", headers=ORIGIN_HEADER) - await authed_plugin_client.post("/api/v1/plugins/test_sample/activate", headers=ORIGIN_HEADER) + await authed_plugin_client.post("/api/v1/plugins/sample_plugin/install", headers=ORIGIN_HEADER) + await authed_plugin_client.post("/api/v1/plugins/sample_plugin/activate", headers=ORIGIN_HEADER) # Deactivate resp = await authed_plugin_client.post( - "/api/v1/plugins/test_sample/deactivate", headers=ORIGIN_HEADER + "/api/v1/plugins/sample_plugin/deactivate", headers=ORIGIN_HEADER ) assert resp.status_code == 200 assert resp.json()["status"] == "inactive" # Deactivate again — should be idempotent resp = await authed_plugin_client.post( - "/api/v1/plugins/test_sample/deactivate", headers=ORIGIN_HEADER + "/api/v1/plugins/sample_plugin/deactivate", headers=ORIGIN_HEADER ) assert resp.status_code == 200 data = resp.json() @@ -478,9 +524,9 @@ async def test_registry_discover_builtins(engine: AsyncEngine): registry = reset_registry_for_testing() registry.initialize(engine, None) discovered = registry.discover_builtins() - # test_sample should be discovered from builtins package - assert "test_sample" in discovered - assert registry.get_plugin("test_sample") is not None + # tags should be discovered from builtins package + assert "tags" in discovered + assert registry.get_plugin("tags") is not None @pytest.mark.asyncio @@ -490,7 +536,7 @@ async def test_registry_list_plugins_mixed_states( """Test listing plugins in various states (discovered, installed, active, inactive).""" registry = reset_registry_for_testing() registry.initialize(engine, None) - registry.register_plugin(TestSamplePlugin()) + registry.register_plugin(SamplePlugin()) # List before install — all discovered plugins = await registry.list_plugins(db_session_for_plugins) @@ -499,7 +545,7 @@ async def test_registry_list_plugins_mixed_states( assert plugins[0]["installed"] is False # Install - await registry.install(db_session_for_plugins, "test_sample") + await registry.install(db_session_for_plugins, "sample_plugin") await db_session_for_plugins.commit() # List after install — installed @@ -509,7 +555,7 @@ async def test_registry_list_plugins_mixed_states( assert plugins[0]["installed"] is True # Activate - await registry.activate(db_session_for_plugins, "test_sample") + await registry.activate(db_session_for_plugins, "sample_plugin") await db_session_for_plugins.commit() # List after activate — active @@ -525,12 +571,12 @@ async def test_registry_install_idempotent( """Test that installing an already-installed plugin is idempotent.""" registry = reset_registry_for_testing() registry.initialize(engine, None) - registry.register_plugin(TestSamplePlugin()) + registry.register_plugin(SamplePlugin()) - record1 = await registry.install(db_session_for_plugins, "test_sample") + record1 = await registry.install(db_session_for_plugins, "sample_plugin") await db_session_for_plugins.commit() - record2 = await registry.install(db_session_for_plugins, "test_sample") + record2 = await registry.install(db_session_for_plugins, "sample_plugin") await db_session_for_plugins.commit() # Same record returned @@ -563,10 +609,10 @@ async def test_registry_activate_without_install( """Test that activating without install raises error.""" registry = reset_registry_for_testing() registry.initialize(engine, None) - registry.register_plugin(TestSamplePlugin()) + registry.register_plugin(SamplePlugin()) with pytest.raises(ValueError, match="not installed"): - await registry.activate(db_session_for_plugins, "test_sample") + await registry.activate(db_session_for_plugins, "sample_plugin") @pytest.mark.asyncio @@ -577,12 +623,12 @@ async def test_migration_runner_drop_tables( runner = MigrationRunner(engine) # Run migration first - await runner.run_migration(db_session_for_plugins, "drop_test", "0001_test_plugin.sql") + await runner.run_migration(db_session_for_plugins, "drop_test", "0001_sample_plugin.sql") await db_session_for_plugins.commit() # Verify table exists result = await db_session_for_plugins.execute( - text("SELECT tablename FROM pg_tables WHERE tablename = 'plugin_test_data'") + text("SELECT tablename FROM pg_tables WHERE tablename = 'plugin_sample_data'") ) assert result.fetchone() is not None @@ -590,11 +636,11 @@ async def test_migration_runner_drop_tables( dropped = await runner.drop_plugin_tables(db_session_for_plugins, "drop_test") await db_session_for_plugins.commit() - assert "plugin_test_data" in dropped + assert "plugin_sample_data" in dropped # Verify table is gone result = await db_session_for_plugins.execute( - text("SELECT tablename FROM pg_tables WHERE tablename = 'plugin_test_data'") + text("SELECT tablename FROM pg_tables WHERE tablename = 'plugin_sample_data'") ) assert result.fetchone() is None @@ -661,9 +707,9 @@ def test_plugin_manifest_validation(): def test_base_plugin_repr(): """Test BasePlugin __repr__ method.""" - plugin = TestSamplePlugin() + plugin = SamplePlugin() repr_str = repr(plugin) - assert "test_sample" in repr_str + assert "sample_plugin" in repr_str assert "1.0.0" in repr_str @@ -679,8 +725,8 @@ def test_base_plugin_no_manifest_error(): def test_base_plugin_name_version_properties(): """Test BasePlugin name and version properties.""" - plugin = TestSamplePlugin() - assert plugin.name == "test_sample" + plugin = SamplePlugin() + assert plugin.name == "sample_plugin" assert plugin.version == "1.0.0" @@ -718,7 +764,7 @@ async def test_base_plugin_on_uninstall_default(): def test_base_plugin_get_routes_empty(): """Test get_routes returns empty list when manifest has no routes.""" - plugin = TestSamplePlugin() + plugin = SamplePlugin() routes = plugin.get_routes() assert routes == [] @@ -758,14 +804,14 @@ async def test_uninstall_inactive_plugin(engine: AsyncEngine, db_session_for_plu """Test uninstalling a plugin that is installed but not active.""" registry = reset_registry_for_testing() registry.initialize(engine, None) - registry.register_plugin(TestSamplePlugin()) + registry.register_plugin(SamplePlugin()) # Install only (not activated) - await registry.install(db_session_for_plugins, "test_sample") + await registry.install(db_session_for_plugins, "sample_plugin") await db_session_for_plugins.commit() # Uninstall should work without deactivation - record = await registry.uninstall(db_session_for_plugins, "test_sample") + record = await registry.uninstall(db_session_for_plugins, "sample_plugin") await db_session_for_plugins.commit() assert record.status == "uninstalled" @@ -826,15 +872,15 @@ async def test_registry_uninstall_with_remove_data( """Test registry uninstall with remove_data drops tables.""" registry = reset_registry_for_testing() registry.initialize(engine, None) - registry.register_plugin(TestSamplePlugin()) + registry.register_plugin(SamplePlugin()) - await registry.install(db_session_for_plugins, "test_sample") + await registry.install(db_session_for_plugins, "sample_plugin") await db_session_for_plugins.commit() - record = await registry.uninstall(db_session_for_plugins, "test_sample", remove_data=True) + record = await registry.uninstall(db_session_for_plugins, "sample_plugin", remove_data=True) await db_session_for_plugins.commit() assert record.status == "uninstalled" - assert "plugin_test_data" in getattr(record, "dropped_tables", []) + assert "plugin_sample_data" in getattr(record, "dropped_tables", []) @pytest.mark.asyncio @@ -854,19 +900,19 @@ async def test_registry_deactivate_already_inactive_direct( """Test registry.deactivate is idempotent when already inactive.""" registry = reset_registry_for_testing() registry.initialize(engine, None) - registry.register_plugin(TestSamplePlugin()) + registry.register_plugin(SamplePlugin()) - await registry.install(db_session_for_plugins, "test_sample") + await registry.install(db_session_for_plugins, "sample_plugin") await db_session_for_plugins.commit() # Deactivate without activating first — status is 'installed', not 'inactive' # But calling deactivate should still work (sets to inactive) - record = await registry.deactivate(db_session_for_plugins, "test_sample") + record = await registry.deactivate(db_session_for_plugins, "sample_plugin") await db_session_for_plugins.commit() assert record.status == "inactive" # Deactivate again — idempotent - record2 = await registry.deactivate(db_session_for_plugins, "test_sample") + record2 = await registry.deactivate(db_session_for_plugins, "sample_plugin") await db_session_for_plugins.commit() assert record2.status == "inactive" @@ -895,11 +941,11 @@ async def test_plugin_service_activate_error_handling( registry = reset_registry_for_testing() registry.initialize(engine, None) - registry.register_plugin(TestSamplePlugin()) + registry.register_plugin(SamplePlugin()) service = PluginService(registry=registry) with pytest.raises(ValueError, match="not installed"): - await service.activate_plugin(db_session_for_plugins, "test_sample") + await service.activate_plugin(db_session_for_plugins, "sample_plugin") @pytest.mark.asyncio @@ -997,11 +1043,11 @@ async def test_migration_runner_valid_migration( record = await runner.run_migration( db_session_for_plugins, "test_plugin", - "0001_test_plugin.sql", + "0001_sample_plugin.sql", ) await db_session_for_plugins.commit() assert record.plugin_name == "test_plugin" - assert record.migration_file == "0001_test_plugin.sql" + assert record.migration_file == "0001_sample_plugin.sql" assert record.status == "applied" @@ -1027,11 +1073,11 @@ async def test_migration_runner_idempotent( """Test MigrationRunner skips already-applied migrations.""" runner = MigrationRunner(engine) # Run migration - await runner.run_migration(db_session_for_plugins, "test_idempotent", "0001_test_plugin.sql") + await runner.run_migration(db_session_for_plugins, "test_idempotent", "0001_sample_plugin.sql") await db_session_for_plugins.commit() # Run all migrations again — should skip records = await runner.run_all_migrations( - db_session_for_plugins, "test_idempotent", ["0001_test_plugin.sql"] + db_session_for_plugins, "test_idempotent", ["0001_sample_plugin.sql"] ) assert len(records) == 0, "Already-applied migration should be skipped"