Files
MoviePilot/tests/test_initialization.py

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 == []