mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/jxxghp/MoviePilot.git
synced 2026-09-21 00:23:39 +08:00
144 lines
5.4 KiB
Python
144 lines
5.4 KiB
Python
"""首次初始化 API 的业务契约测试。"""
|
|
|
|
import asyncio
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
from fastapi import HTTPException
|
|
|
|
from app.api.endpoints import login as login_endpoint
|
|
from app.schemas.initialization import InitializationRequest
|
|
|
|
|
|
class _FakeRuntimeSettings:
|
|
"""记录首次初始化对部署设置的更新与回滚。"""
|
|
|
|
def __init__(self, superuser: str = "") -> None:
|
|
"""初始化空设置快照。"""
|
|
self.values = {"SUPERUSER": superuser, "API_TOKEN": None}
|
|
self.updates: list[tuple[str, object]] = []
|
|
|
|
def get(self, key: str, default=None):
|
|
"""读取测试设置。"""
|
|
return self.values.get(key, default)
|
|
|
|
def update(self, key: str, value: object):
|
|
"""记录并应用测试设置。"""
|
|
self.updates.append((key, value))
|
|
self.values[key] = value
|
|
return True, ""
|
|
|
|
|
|
class _FakeUserService:
|
|
"""提供初始化接口所需的最小异步用户端口。"""
|
|
|
|
def __init__(self, initialized: bool = False) -> None:
|
|
"""保存初始用户状态。"""
|
|
self.initialized = initialized
|
|
self.created: list[dict] = []
|
|
|
|
async def is_initialized(self) -> bool:
|
|
"""返回测试用户状态。"""
|
|
return self.initialized
|
|
|
|
async def create(self, payload: dict):
|
|
"""记录创建的管理员。"""
|
|
self.created.append(payload)
|
|
self.initialized = True
|
|
return SimpleNamespace(id=1)
|
|
|
|
|
|
def _payload(username: str = "admin") -> InitializationRequest:
|
|
"""构造有效初始化请求。"""
|
|
return InitializationRequest(
|
|
username=username,
|
|
password="Admin123!",
|
|
confirm_password="Admin123!",
|
|
api_key="a" * 32,
|
|
)
|
|
|
|
|
|
def test_initialization_status_reports_existing_users():
|
|
"""已有用户时状态接口不返回配置用户名或 API Key。"""
|
|
service = _FakeUserService(initialized=True)
|
|
|
|
response = asyncio.run(login_endpoint.get_initialization_status(service))
|
|
|
|
assert response.success is True
|
|
assert response.data.initialized is True
|
|
assert response.data.configured_username is None
|
|
assert not hasattr(response.data, "api_key")
|
|
|
|
|
|
def test_initialization_status_reports_configured_username_before_setup(monkeypatch):
|
|
"""未初始化且已配置 SUPERUSER 时,状态接口应返回固定用户名供页面锁定。"""
|
|
service = _FakeUserService()
|
|
settings = _FakeRuntimeSettings(superuser="configured-admin")
|
|
monkeypatch.setattr(login_endpoint, "get_runtime_settings", lambda: settings)
|
|
|
|
response = asyncio.run(login_endpoint.get_initialization_status(service))
|
|
|
|
assert response.data.initialized is False
|
|
assert response.data.configured_username == "configured-admin"
|
|
|
|
|
|
def test_initialize_instance_updates_settings_and_creates_superuser(monkeypatch):
|
|
"""首次初始化应先保存部署凭据,再创建唯一超级管理员。"""
|
|
service = _FakeUserService()
|
|
settings = _FakeRuntimeSettings()
|
|
monkeypatch.setattr(login_endpoint, "get_runtime_settings", lambda: settings)
|
|
monkeypatch.setattr(login_endpoint, "get_password_hash", lambda password: "hashed:" + password)
|
|
|
|
response = asyncio.run(login_endpoint.initialize_instance(_payload(), service))
|
|
|
|
assert response.success is True
|
|
assert settings.updates == [("SUPERUSER", "admin"), ("API_TOKEN", "a" * 32)]
|
|
assert service.created[0]["hashed_password"] == "hashed:Admin123!"
|
|
assert service.created[0]["is_superuser"] is True
|
|
|
|
|
|
def test_initialize_instance_uses_preconfigured_superuser(monkeypatch):
|
|
"""部署已配置 SUPERUSER 时,初始化应使用该用户名且不尝试覆盖配置。"""
|
|
service = _FakeUserService()
|
|
settings = _FakeRuntimeSettings(superuser="configured-admin")
|
|
monkeypatch.setattr(login_endpoint, "get_runtime_settings", lambda: settings)
|
|
monkeypatch.setattr(login_endpoint, "get_password_hash", lambda password: "hashed:" + password)
|
|
|
|
response = asyncio.run(
|
|
login_endpoint.initialize_instance(
|
|
_payload("configured-admin"),
|
|
service,
|
|
)
|
|
)
|
|
|
|
assert response.success is True
|
|
assert settings.updates == [("API_TOKEN", "a" * 32)]
|
|
assert service.created[0]["name"] == "configured-admin"
|
|
|
|
|
|
def test_initialize_instance_rejects_preconfigured_superuser_mismatch(monkeypatch):
|
|
"""初始化请求用户名与部署固定值不一致时,必须在创建用户前拒绝。"""
|
|
service = _FakeUserService()
|
|
settings = _FakeRuntimeSettings(superuser="configured-admin")
|
|
monkeypatch.setattr(login_endpoint, "get_runtime_settings", lambda: settings)
|
|
|
|
with pytest.raises(HTTPException) as error:
|
|
asyncio.run(login_endpoint.initialize_instance(_payload(), service))
|
|
|
|
assert error.value.status_code == 400
|
|
assert error.value.detail == "SUPERUSER 已由配置文件或环境变量固定,初始化用户名不能修改"
|
|
assert settings.updates == []
|
|
assert service.created == []
|
|
|
|
|
|
def test_initialize_instance_rejects_second_claim(monkeypatch):
|
|
"""已经存在用户时,第二次初始化必须返回冲突而不是覆盖账号。"""
|
|
service = _FakeUserService(initialized=True)
|
|
monkeypatch.setattr(login_endpoint, "get_runtime_settings", lambda: _FakeRuntimeSettings())
|
|
|
|
with pytest.raises(HTTPException) as error:
|
|
asyncio.run(login_endpoint.initialize_instance(_payload(), service))
|
|
|
|
assert error.value.status_code == 409
|
|
assert service.created == []
|