feat(system):优化后端
1.新增后端测试 2.增加了后端的加密 3.增加了i18n(国际化)
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
"""``api/resource.py`` Custom toolset 路由:归属鉴权。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import types
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import pytest
|
||||
from fastapi import FastAPI
|
||||
from httpx import AsyncClient, ASGITransport
|
||||
|
||||
from kilostar.api.resource import resource_router
|
||||
from kilostar.core.postgres_database.model import UserAuthority
|
||||
from kilostar.utils.access import Accessor, TokenData
|
||||
|
||||
|
||||
def _fake_user(user_id: str = "alice"):
|
||||
return TokenData(user_id=user_id, username=user_id)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app_with_user(monkeypatch):
|
||||
"""挂上 resource_router;用 dependency_overrides 跳过 JWT,并把 get_authority 默认放成 USER。"""
|
||||
app = FastAPI()
|
||||
app.include_router(resource_router)
|
||||
app.dependency_overrides[Accessor.get_current_user] = lambda: _fake_user("alice")
|
||||
|
||||
# 默认把权限置为 USER;具体 case 内部可再 monkeypatch 覆盖
|
||||
async def _default_authority(uid):
|
||||
return UserAuthority.USER
|
||||
|
||||
monkeypatch.setattr(
|
||||
"kilostar.utils.check_user.role_check.get_authority", _default_authority
|
||||
)
|
||||
return app
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_custom_toolset_forbidden_for_non_owner(
|
||||
app_with_user, fake_actors
|
||||
):
|
||||
gsm = types.SimpleNamespace()
|
||||
gsm.get_custom_toolset = types.SimpleNamespace(
|
||||
remote=AsyncMock(
|
||||
return_value={"toolset_id": "t1", "owner_id": "bob", "tools": []}
|
||||
)
|
||||
)
|
||||
fake_actors.register("global_state_machine", gsm)
|
||||
|
||||
transport = ASGITransport(app=app_with_user)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as client:
|
||||
resp = await client.get("/api/v1/resource/custom-toolset/t1")
|
||||
|
||||
assert resp.status_code == 403
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_custom_toolset_allowed_for_owner(app_with_user, fake_actors):
|
||||
gsm = types.SimpleNamespace()
|
||||
gsm.get_custom_toolset = types.SimpleNamespace(
|
||||
remote=AsyncMock(
|
||||
return_value={"toolset_id": "t1", "owner_id": "alice", "tools": []}
|
||||
)
|
||||
)
|
||||
fake_actors.register("global_state_machine", gsm)
|
||||
|
||||
transport = ASGITransport(app=app_with_user)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as client:
|
||||
resp = await client.get("/api/v1/resource/custom-toolset/t1")
|
||||
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["owner_id"] == "alice"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_custom_toolset_allowed_for_admin(
|
||||
app_with_user, fake_actors, monkeypatch
|
||||
):
|
||||
gsm = types.SimpleNamespace()
|
||||
gsm.get_custom_toolset = types.SimpleNamespace(
|
||||
remote=AsyncMock(
|
||||
return_value={"toolset_id": "t1", "owner_id": "bob", "tools": []}
|
||||
)
|
||||
)
|
||||
fake_actors.register("global_state_machine", gsm)
|
||||
|
||||
async def _admin(uid):
|
||||
return UserAuthority.SUPER_ADMINISTRATOR
|
||||
|
||||
monkeypatch.setattr(
|
||||
"kilostar.utils.check_user.role_check.get_authority", _admin
|
||||
)
|
||||
|
||||
transport = ASGITransport(app=app_with_user)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as client:
|
||||
resp = await client.get("/api/v1/resource/custom-toolset/t1")
|
||||
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_custom_toolsets_filters_by_owner(app_with_user, fake_actors):
|
||||
all_sets = [
|
||||
{"toolset_id": "t1", "owner_id": "alice"},
|
||||
{"toolset_id": "t2", "owner_id": "bob"},
|
||||
]
|
||||
gsm = types.SimpleNamespace()
|
||||
gsm.list_custom_toolsets = types.SimpleNamespace(
|
||||
remote=AsyncMock(return_value=all_sets)
|
||||
)
|
||||
fake_actors.register("global_state_machine", gsm)
|
||||
|
||||
transport = ASGITransport(app=app_with_user)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as client:
|
||||
resp = await client.get("/api/v1/resource/custom-toolset")
|
||||
|
||||
assert resp.status_code == 200
|
||||
body = resp.json()
|
||||
assert len(body["toolsets"]) == 1
|
||||
assert body["toolsets"][0]["toolset_id"] == "t1"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_custom_toolset_forbidden_for_non_owner(
|
||||
app_with_user, fake_actors
|
||||
):
|
||||
gsm = types.SimpleNamespace()
|
||||
gsm.get_custom_toolset = types.SimpleNamespace(
|
||||
remote=AsyncMock(
|
||||
return_value={"toolset_id": "t1", "owner_id": "bob"}
|
||||
)
|
||||
)
|
||||
delete_mock = AsyncMock(return_value=True)
|
||||
gsm.delete_custom_toolset = types.SimpleNamespace(remote=delete_mock)
|
||||
fake_actors.register("global_state_machine", gsm)
|
||||
|
||||
transport = ASGITransport(app=app_with_user)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as client:
|
||||
resp = await client.delete("/api/v1/resource/custom-toolset/t1")
|
||||
|
||||
assert resp.status_code == 403
|
||||
delete_mock.assert_not_called()
|
||||
Reference in New Issue
Block a user