|
| 1 | +# -*- coding: utf-8 -*- |
| 2 | +"""Integration tests for the plugin SDK (PluginApi) and PluginRegistry. |
| 3 | +
|
| 4 | +Third coverage-sprint batch, targeted at uncovered lines in |
| 5 | +src/qwenpaw/plugins/api.py (hook registration, router/middleware |
| 6 | +registration, ownership helpers). |
| 7 | +
|
| 8 | +These are module-level integration tests exercising the plugin SDK's |
| 9 | +public API against a real PluginRegistry instance. |
| 10 | +
|
| 11 | +Tests cover: |
| 12 | +- PluginApi construction and registry binding |
| 13 | +- startup/shutdown/uninstall hook registration into the registry |
| 14 | +- control command and middleware registration |
| 15 | +- tool ownership claim/release helpers |
| 16 | +""" |
| 17 | + |
| 18 | +from __future__ import annotations |
| 19 | + |
| 20 | +import pytest |
| 21 | + |
| 22 | + |
| 23 | +def _make_api(plugin_id="integ_test_plugin"): |
| 24 | + from qwenpaw.plugins.api import PluginApi |
| 25 | + from qwenpaw.plugins.registry import PluginRegistry |
| 26 | + |
| 27 | + registry = PluginRegistry() |
| 28 | + api = PluginApi(plugin_id=plugin_id, config={"k": "v"}) |
| 29 | + api.set_registry(registry) |
| 30 | + return api, registry |
| 31 | + |
| 32 | + |
| 33 | +@pytest.mark.integration |
| 34 | +@pytest.mark.p1 |
| 35 | +def test_plugin_api_construction() -> None: |
| 36 | + """Test purpose: |
| 37 | + - Verify PluginApi stores plugin_id/config/manifest defaults. |
| 38 | +
|
| 39 | + Test flow: |
| 40 | + 1. Construct PluginApi with and without manifest. |
| 41 | + 2. Verify fields. |
| 42 | + """ |
| 43 | + from qwenpaw.plugins.api import PluginApi |
| 44 | + |
| 45 | + api = PluginApi(plugin_id="p1", config={"a": 1}) |
| 46 | + assert api.plugin_id == "p1" |
| 47 | + assert api.config == {"a": 1} |
| 48 | + assert api.manifest == {} |
| 49 | + |
| 50 | + api2 = PluginApi(plugin_id="p2", config={}, manifest={"name": "x"}) |
| 51 | + assert api2.manifest == {"name": "x"} |
| 52 | + |
| 53 | + |
| 54 | +@pytest.mark.integration |
| 55 | +@pytest.mark.p1 |
| 56 | +def test_plugin_api_startup_hook_registration() -> None: |
| 57 | + """Test purpose: |
| 58 | + - Verify register_startup_hook records the hook in the registry. |
| 59 | +
|
| 60 | + Test flow: |
| 61 | + 1. Bind PluginApi to a fresh registry. |
| 62 | + 2. Register a startup hook. |
| 63 | + 3. Verify the registry holds it. |
| 64 | + """ |
| 65 | + api, registry = _make_api() |
| 66 | + |
| 67 | + async def _hook(): |
| 68 | + return None |
| 69 | + |
| 70 | + api.register_startup_hook( |
| 71 | + hook_name="integ_hook", |
| 72 | + callback=_hook, |
| 73 | + priority=5, |
| 74 | + ) |
| 75 | + hooks = getattr(registry, "startup_hooks", None) or getattr( |
| 76 | + registry, |
| 77 | + "_startup_hooks", |
| 78 | + {}, |
| 79 | + ) |
| 80 | + assert hooks, "startup hook not recorded in registry" |
| 81 | + |
| 82 | + |
| 83 | +@pytest.mark.integration |
| 84 | +@pytest.mark.p1 |
| 85 | +def test_plugin_api_shutdown_hook_registration() -> None: |
| 86 | + """Test purpose: |
| 87 | + - Verify register_shutdown_hook records the hook in the registry. |
| 88 | +
|
| 89 | + API surface: |
| 90 | + - PluginApi.register_shutdown_hook |
| 91 | + """ |
| 92 | + api, registry = _make_api() |
| 93 | + |
| 94 | + async def _hook(): |
| 95 | + return None |
| 96 | + |
| 97 | + api.register_shutdown_hook(hook_name="integ_shutdown", callback=_hook) |
| 98 | + hooks = getattr(registry, "shutdown_hooks", None) or getattr( |
| 99 | + registry, |
| 100 | + "_shutdown_hooks", |
| 101 | + {}, |
| 102 | + ) |
| 103 | + assert hooks, "shutdown hook not recorded in registry" |
| 104 | + |
| 105 | + |
| 106 | +@pytest.mark.integration |
| 107 | +@pytest.mark.p1 |
| 108 | +def test_plugin_api_uninstall_hook_registration() -> None: |
| 109 | + """Test purpose: |
| 110 | + - Verify register_uninstall_hook records the hook in the registry. |
| 111 | +
|
| 112 | + API surface: |
| 113 | + - PluginApi.register_uninstall_hook |
| 114 | + """ |
| 115 | + api, registry = _make_api() |
| 116 | + |
| 117 | + async def _hook(): |
| 118 | + return None |
| 119 | + |
| 120 | + api.register_uninstall_hook(hook_name="integ_uninstall", callback=_hook) |
| 121 | + hooks = getattr(registry, "uninstall_hooks", None) or getattr( |
| 122 | + registry, |
| 123 | + "_uninstall_hooks", |
| 124 | + {}, |
| 125 | + ) |
| 126 | + assert hooks, "uninstall hook not recorded in registry" |
| 127 | + |
| 128 | + |
| 129 | +@pytest.mark.integration |
| 130 | +@pytest.mark.p1 |
| 131 | +def test_plugin_api_middleware_registration() -> None: |
| 132 | + """Test purpose: |
| 133 | + - Verify register_middleware records a middleware factory. |
| 134 | +
|
| 135 | + API surface: |
| 136 | + - PluginApi.register_middleware |
| 137 | + """ |
| 138 | + api, registry = _make_api() |
| 139 | + |
| 140 | + def _factory(ctx, _agent_config): |
| 141 | + return None |
| 142 | + |
| 143 | + api.register_middleware(_factory, priority=50) |
| 144 | + mws = getattr(registry, "_middleware_registrations", []) |
| 145 | + assert mws, "middleware not recorded in registry" |
| 146 | + |
| 147 | + |
| 148 | +@pytest.mark.integration |
| 149 | +@pytest.mark.p1 |
| 150 | +def test_tool_ownership_claim_and_release() -> None: |
| 151 | + """Test purpose: |
| 152 | + - Verify tool ownership claim/release helpers round-trip. |
| 153 | +
|
| 154 | + API surface: |
| 155 | + - qwenpaw.plugins.api._claim_tool_ownership |
| 156 | + - qwenpaw.plugins.api.release_tool_ownership_for_plugin |
| 157 | + """ |
| 158 | + from qwenpaw.plugins.api import ( |
| 159 | + _claim_tool_ownership, |
| 160 | + release_tool_ownership_for_plugin, |
| 161 | + ) |
| 162 | + |
| 163 | + _claim_tool_ownership("integ_owned_tool", "integ_owner_plugin") |
| 164 | + release_tool_ownership_for_plugin("integ_owner_plugin") |
0 commit comments