feat: OTA 版本下发端点(检查更新)
- app_config 加 get/set_app_version(独立 key,复用表不进 CONFIG_DEFS) - GET /api/v1/platform/app-version 公开读 + POST /internal/app-version 内部写(X-Internal-Secret) - tests/test_app_version.py 5 端到端测试 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
"""OTA 检查更新端点测试。
|
||||
|
||||
覆盖:公开读端点未配置时返回默认(latest_version_code=0=无更新)、内部写端点鉴权
|
||||
(503 未配 / 401 头错 / 200)、写后能读到同一组值、写入参数校验(422)。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.core.config import settings
|
||||
from app.db.session import SessionLocal
|
||||
from app.models.app_config import AppConfig
|
||||
from app.repositories.app_config import APP_VERSION_KEY
|
||||
|
||||
_SECRET = "test-internal-secret-only-for-pytest"
|
||||
|
||||
# 合法 body(够过 pydantic 校验,用于鉴权用例)
|
||||
_VALID = {
|
||||
"latest_version_code": 31,
|
||||
"latest_version_name": "0.1.6",
|
||||
"apk_url": "https://app-api.shaguabijia.com/media/releases/shaguabijia-v0.1.6-build31.apk",
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def clean_version():
|
||||
"""每个用例前后清掉 latest_app_version 行(表由 conftest 建好,session 级共享)。"""
|
||||
def _clear():
|
||||
s = SessionLocal()
|
||||
s.query(AppConfig).filter(AppConfig.key == APP_VERSION_KEY).delete()
|
||||
s.commit()
|
||||
s.close()
|
||||
_clear()
|
||||
yield
|
||||
_clear()
|
||||
|
||||
|
||||
def test_read_default_when_unset(client: TestClient, clean_version) -> None:
|
||||
"""未配置任何版本时,公开读端点返回 latest_version_code=0(客户端视为已是最新);不鉴权。"""
|
||||
r = client.get("/api/v1/platform/app-version")
|
||||
assert r.status_code == 200, r.text
|
||||
assert r.json()["latest_version_code"] == 0
|
||||
|
||||
|
||||
def test_write_secret_unset_503(client: TestClient, clean_version, monkeypatch) -> None:
|
||||
monkeypatch.setattr(settings, "INTERNAL_API_SECRET", "") # 未配 = 写端点关闭
|
||||
r = client.post("/internal/app-version", json=_VALID)
|
||||
assert r.status_code == 503
|
||||
|
||||
|
||||
def test_write_auth(client: TestClient, clean_version, monkeypatch) -> None:
|
||||
monkeypatch.setattr(settings, "INTERNAL_API_SECRET", _SECRET)
|
||||
assert client.post("/internal/app-version", json=_VALID).status_code == 401
|
||||
assert client.post(
|
||||
"/internal/app-version", json=_VALID, headers={"X-Internal-Secret": "wrong"}
|
||||
).status_code == 401
|
||||
r = client.post("/internal/app-version", json=_VALID, headers={"X-Internal-Secret": _SECRET})
|
||||
assert r.status_code == 200, r.text
|
||||
|
||||
|
||||
def test_write_then_read(client: TestClient, clean_version, monkeypatch) -> None:
|
||||
monkeypatch.setattr(settings, "INTERNAL_API_SECRET", _SECRET)
|
||||
payload = {
|
||||
"latest_version_code": 99,
|
||||
"latest_version_name": "9.9.9",
|
||||
"apk_url": "https://app-api.shaguabijia.com/media/releases/shaguabijia-v9.9.9-build99.apk",
|
||||
"update_note": "测试更新说明",
|
||||
"min_supported_version_code": 50,
|
||||
"apk_size_bytes": 12345678,
|
||||
"apk_sha256": "abc123",
|
||||
}
|
||||
w = client.post("/internal/app-version", json=payload, headers={"X-Internal-Secret": _SECRET})
|
||||
assert w.status_code == 200, w.text
|
||||
assert w.json()["latest_version_code"] == 99
|
||||
|
||||
body = client.get("/api/v1/platform/app-version").json()
|
||||
assert body["latest_version_code"] == 99
|
||||
assert body["latest_version_name"] == "9.9.9"
|
||||
assert body["apk_url"] == payload["apk_url"]
|
||||
assert body["update_note"] == "测试更新说明"
|
||||
assert body["min_supported_version_code"] == 50
|
||||
assert body["apk_size_bytes"] == 12345678
|
||||
assert body["apk_sha256"] == "abc123"
|
||||
|
||||
|
||||
def test_write_rejects_invalid(client: TestClient, clean_version, monkeypatch) -> None:
|
||||
monkeypatch.setattr(settings, "INTERNAL_API_SECRET", _SECRET)
|
||||
h = {"X-Internal-Secret": _SECRET}
|
||||
# latest_version_code 必须 > 0
|
||||
bad_code = {"latest_version_code": 0, "latest_version_name": "x", "apk_url": "u"}
|
||||
assert client.post("/internal/app-version", json=bad_code, headers=h).status_code == 422
|
||||
# apk_url 不能空
|
||||
bad_url = {"latest_version_code": 5, "latest_version_name": "x", "apk_url": ""}
|
||||
assert client.post("/internal/app-version", json=bad_url, headers=h).status_code == 422
|
||||
Reference in New Issue
Block a user