Skip to content

Commit fbda88f

Browse files
committed
test(coverage-sprint3): 第三批针对性用例 22 条(按张衡覆盖率缺口数据)
靶子来源:张衡全量未覆盖清单(8-17 nightly 口径,733 文件), 按集成层可打标准筛选: - workspace tree/commands(250 行未覆盖):分页/游标/根校验/ 命令菜单,10 条 - mcp policy/access-principals/tools 管理面(133 行):未知 client 404、无效 body 422、principals 列表,6 条 - plugin SDK 模块级(216 行):PluginApi 构造、startup/shutdown/ uninstall hook、middleware 注册、工具归属 claim/release,6 条 本地全过(22 passed),pre-commit(仓库锁定版本)过。 署名:鬼谷子·Integrator@QPQAT
1 parent 32f5a3c commit fbda88f

3 files changed

Lines changed: 527 additions & 0 deletions

File tree

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# -*- coding: utf-8 -*-
2+
"""Integration tests for the MCP console-management surface.
3+
4+
Third coverage-sprint batch, targeted at uncovered lines in
5+
src/qwenpaw/app/mcp/config_service.py (policy update path, access
6+
principals, tool whitelist update).
7+
8+
Tests cover:
9+
- GET /api/mcp/policy/{client_key}: unknown client 404
10+
- PUT /api/mcp/policy/{client_key}: unknown client 404; empty tool name 400
11+
- GET /api/mcp/access-principals: recent principals list
12+
- GET /api/mcp/tools/{client_key}: unknown client error path
13+
- PUT /api/mcp/tools/{client_key}: unknown client error path
14+
"""
15+
16+
from __future__ import annotations
17+
18+
import pytest
19+
from helpers import default_http_timeout
20+
21+
_MCP_TIMEOUT = default_http_timeout(15.0)
22+
23+
24+
@pytest.mark.integration
25+
@pytest.mark.p1
26+
def test_mcp_policy_get_unknown_client(app_server) -> None:
27+
"""Test purpose:
28+
- Verify reading the saved policy of an unknown MCP client yields 404.
29+
30+
API endpoints:
31+
- GET /api/mcp/policy/{client_key}
32+
"""
33+
resp = app_server.api_request(
34+
"GET",
35+
"/api/mcp/policy/integ-unknown-mcp-client",
36+
timeout=_MCP_TIMEOUT,
37+
)
38+
assert resp.status_code == 404, app_server.logs_tail()
39+
40+
41+
@pytest.mark.integration
42+
@pytest.mark.p1
43+
def test_mcp_policy_put_unknown_client(app_server) -> None:
44+
"""Test purpose:
45+
- Verify updating the policy of an unknown MCP client yields 404.
46+
47+
API endpoints:
48+
- PUT /api/mcp/policy/{client_key}
49+
"""
50+
resp = app_server.api_request(
51+
"PUT",
52+
"/api/mcp/policy/integ-unknown-mcp-client",
53+
json={"default_effect": "deny", "tool_defaults": []},
54+
timeout=_MCP_TIMEOUT,
55+
)
56+
assert resp.status_code == 404, app_server.logs_tail()
57+
58+
59+
@pytest.mark.integration
60+
@pytest.mark.p1
61+
def test_mcp_policy_put_invalid_body(app_server) -> None:
62+
"""Test purpose:
63+
- Verify a policy body with an invalid effect is rejected (422).
64+
65+
API endpoints:
66+
- PUT /api/mcp/policy/{client_key}
67+
"""
68+
resp = app_server.api_request(
69+
"PUT",
70+
"/api/mcp/policy/integ-unknown-mcp-client",
71+
json={"default_effect": "bogus-effect"},
72+
timeout=_MCP_TIMEOUT,
73+
)
74+
assert resp.status_code == 422, app_server.logs_tail()
75+
76+
77+
@pytest.mark.integration
78+
@pytest.mark.p1
79+
def test_mcp_access_principals_list(app_server) -> None:
80+
"""Test purpose:
81+
- Verify GET /api/mcp/access-principals returns a list payload.
82+
83+
API endpoints:
84+
- GET /api/mcp/access-principals
85+
"""
86+
resp = app_server.api_request(
87+
"GET",
88+
"/api/mcp/access-principals",
89+
timeout=_MCP_TIMEOUT,
90+
)
91+
assert resp.status_code == 200, app_server.logs_tail()
92+
assert isinstance(resp.json(), list)
93+
94+
95+
@pytest.mark.integration
96+
@pytest.mark.p1
97+
def test_mcp_tools_get_unknown_client(app_server) -> None:
98+
"""Test purpose:
99+
- Verify listing tools of an unknown MCP client yields an error.
100+
101+
API endpoints:
102+
- GET /api/mcp/tools/{client_key}
103+
"""
104+
resp = app_server.api_request(
105+
"GET",
106+
"/api/mcp/tools/integ-unknown-mcp-client",
107+
timeout=_MCP_TIMEOUT,
108+
)
109+
assert resp.status_code in (404, 400), app_server.logs_tail()
110+
111+
112+
@pytest.mark.integration
113+
@pytest.mark.p1
114+
def test_mcp_tools_put_unknown_client(app_server) -> None:
115+
"""Test purpose:
116+
- Verify updating the tool whitelist of an unknown client errors.
117+
118+
API endpoints:
119+
- PUT /api/mcp/tools/{client_key}
120+
"""
121+
resp = app_server.api_request(
122+
"PUT",
123+
"/api/mcp/tools/integ-unknown-mcp-client",
124+
json={"tools": ["some_tool"]},
125+
timeout=_MCP_TIMEOUT,
126+
)
127+
assert resp.status_code in (404, 400), app_server.logs_tail()
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
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

Comments
 (0)