mirror of
https://github.com/insistence/RuoYi-Vue3-FastAPI.git
synced 2026-10-14 21:07:36 +00:00
* feat: 支持 v2 插件、原生扩展与签名发布 * fix: 修复测试 * feat: 增强宿主 SDK、运行观测与制品维护能力 * feat: 增强文件传输、配置观测与本地开发能力 * feat: 支持 SSE 实时事件流并完善开发脚手架 * feat: 完善长连接鉴权复核与关闭回收 * feat: 增强生命周期与任务关闭可靠性 * feat: 收紧健康检查契约并补齐前端与真实网络验收 * feat: 增加SDK溯源与离线更新工具并验证跨版本兼容 * feat: 增加持久化业务示例与受控 Python 交付 * feat: 增加统一运行诊断与多worker状态观测 * feat: 增加固定目标发布等待与自动验收 * fix: 修复多环境插件部署前缀与代理配置不一致 * fix: 修复测试
75 lines
3.0 KiB
Python
75 lines
3.0 KiB
Python
import json
|
|
from types import SimpleNamespace
|
|
from unittest.mock import AsyncMock
|
|
|
|
import pytest
|
|
|
|
from common.aspect.interface_auth import CheckUserInterfaceAuth
|
|
from module_plugin.controller import plugin_release_controller as controller
|
|
|
|
DIGEST = 'a' * 64
|
|
|
|
|
|
def test_artifact_http_endpoints_are_get_only_and_require_query_permission() -> None:
|
|
"""验证制品 HTTP 接口只接受查询且要求插件查询权限。"""
|
|
routes = controller.plugin_release_controller.routes
|
|
assert {route.path for route in routes} == {
|
|
'/system/plugin/artifacts/list',
|
|
'/system/plugin/release/status',
|
|
'/system/plugin/release/plan',
|
|
}
|
|
for route in routes:
|
|
assert route.methods == {'GET'}
|
|
assert any(
|
|
isinstance(dependency.dependency, CheckUserInterfaceAuth)
|
|
and dependency.dependency.perm == 'system:plugin:query'
|
|
for dependency in route.dependencies
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_readonly_endpoints_forward_filters_and_plan_target(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
"""验证只读接口正确传递过滤条件与目标制品信息。"""
|
|
service = SimpleNamespace(
|
|
catalog=SimpleNamespace(list_artifacts=AsyncMock(return_value={'ok': True, 'artifacts': []})),
|
|
status=AsyncMock(return_value={'ok': True, 'releases': []}),
|
|
plan=AsyncMock(return_value={'ok': True, 'pluginId': 'demo', 'digest': DIGEST}),
|
|
)
|
|
monkeypatch.setattr(controller, 'get_plugin_deployment_service', lambda: service)
|
|
responses = [
|
|
await controller.list_plugin_artifacts('demo'),
|
|
await controller.get_plugin_release_status('demo'),
|
|
await controller.plan_plugin_release('demo', DIGEST),
|
|
]
|
|
assert all(json.loads(response.body)['success'] for response in responses)
|
|
service.catalog.list_artifacts.assert_awaited_once_with('demo')
|
|
service.status.assert_awaited_once_with('demo')
|
|
service.plan.assert_awaited_once_with('demo', DIGEST)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_disabled_feature_returns_clear_blocked_response(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
"""验证功能关闭时返回明确的受限响应。"""
|
|
|
|
def unavailable() -> None:
|
|
raise ValueError('签名制品发布功能未启用')
|
|
|
|
monkeypatch.setattr(controller, 'get_plugin_deployment_service', unavailable)
|
|
body = json.loads((await controller.get_plugin_release_status()).body)
|
|
assert body['success'] is False
|
|
assert body['data']['status'] == 'blocked'
|
|
assert '未启用' in body['msg']
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_unexpected_database_error_does_not_expose_connection_details(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
"""验证意外数据库异常不会将连接细节返回客户端。"""
|
|
|
|
def unavailable() -> None:
|
|
raise RuntimeError('mysql://secret-user:secret-password@host')
|
|
|
|
monkeypatch.setattr(controller, 'get_plugin_deployment_service', unavailable)
|
|
response = await controller.list_plugin_artifacts()
|
|
assert json.loads(response.body)['success'] is False
|
|
assert b'secret-password' not in response.body
|