All checks were successful
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-browser (push) Successful in 36s
Deploy Trade-In / build-frontend (push) Successful in 2m23s
Deploy Trade-In / test (push) Successful in 3m14s
Deploy Trade-In / build-backend (push) Successful in 4m19s
Deploy Trade-In / deploy (push) Successful in 1m44s
99 lines
3.7 KiB
Python
99 lines
3.7 KiB
Python
"""Tests for GET /api/v1/trade-in/version (build metadata) — app/core/version.py +
|
||
app/api/v1/version.py.
|
||
|
||
Isolated FastAPI app (no full app.main import, no DB) — same pattern as
|
||
tests/test_geocode_reverse_api.py: mount only the router under test.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import importlib
|
||
import os
|
||
|
||
os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost:5432/test")
|
||
|
||
import pytest
|
||
from fastapi import FastAPI
|
||
from fastapi.testclient import TestClient
|
||
|
||
from app.api.v1 import version as version_module
|
||
from app.core import version as version_core
|
||
|
||
|
||
@pytest.fixture
|
||
def app() -> FastAPI:
|
||
application = FastAPI()
|
||
application.include_router(version_module.router, prefix="/api/v1/trade-in")
|
||
return application
|
||
|
||
|
||
# ── GET /api/v1/trade-in/version ─────────────────────────────────────────────
|
||
|
||
|
||
def test_version_endpoint_shape(app: FastAPI) -> None:
|
||
client = TestClient(app)
|
||
r = client.get("/api/v1/trade-in/version")
|
||
assert r.status_code == 200
|
||
body = r.json()
|
||
assert set(body.keys()) == {"version", "sha", "built_at"}
|
||
assert isinstance(body["version"], str) and body["version"]
|
||
assert isinstance(body["sha"], str) and body["sha"]
|
||
assert isinstance(body["built_at"], str) and body["built_at"]
|
||
|
||
|
||
def test_version_endpoint_matches_core_constants(app: FastAPI) -> None:
|
||
client = TestClient(app)
|
||
body = client.get("/api/v1/trade-in/version").json()
|
||
assert body["version"] == version_core.APP_VERSION
|
||
assert body["sha"] == version_core.BUILD_SHA
|
||
assert body["built_at"] == version_core.BUILD_DATE
|
||
|
||
|
||
def test_version_path_is_public_no_auth_required() -> None:
|
||
"""rbac_guard must let this path through without X-Authenticated-User /
|
||
session — see app/core/rbac.py::_PUBLIC_PATHS. Not a secret, no DB call."""
|
||
from app.core.rbac import _PUBLIC_PATHS
|
||
|
||
assert "/api/v1/trade-in/version" in _PUBLIC_PATHS
|
||
|
||
|
||
# ── app/core/version.py — product_version_line / format_build_date_human ────
|
||
|
||
|
||
def test_product_version_line_format() -> None:
|
||
line = version_core.product_version_line("Мера")
|
||
assert line.startswith("Мера v")
|
||
parts = line.split(" · ")
|
||
assert len(parts) == 3, f"expected 'name vX.Y.Z · sha · date', got {line!r}"
|
||
|
||
|
||
def test_format_build_date_human_parses_iso_utc() -> None:
|
||
assert version_core.format_build_date_human("2026-08-10T12:00:00Z") == "10.08.2026"
|
||
|
||
|
||
def test_format_build_date_human_falls_back_on_garbage_without_raising() -> None:
|
||
assert version_core.format_build_date_human("not-a-date") == "not-a-date"
|
||
|
||
|
||
# ── Fallback when APP_VERSION/BUILD_SHA/BUILD_DATE env vars are absent ──────
|
||
# (local `uvicorn` run without a Docker build — see module docstring in
|
||
# app/core/version.py). Reloading the module re-executes its module-level
|
||
# env reads; nothing here may raise.
|
||
|
||
|
||
def test_module_import_falls_back_without_build_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
||
monkeypatch.delenv("APP_VERSION", raising=False)
|
||
monkeypatch.delenv("BUILD_SHA", raising=False)
|
||
monkeypatch.delenv("BUILD_DATE", raising=False)
|
||
|
||
reloaded = importlib.reload(version_core)
|
||
|
||
assert reloaded.BUILD_SHA == "dev"
|
||
assert reloaded.APP_VERSION # non-empty: VERSION file content or "0.0.0" default
|
||
assert reloaded.BUILD_DATE.endswith("Z")
|
||
# format/product helpers must still work off the fallback values (no crash).
|
||
assert reloaded.product_version_line("Мера").startswith("Мера v")
|
||
|
||
# Reload once more so any test running later in this process sees a module
|
||
# state consistent with whatever env pytest was actually invoked under.
|
||
importlib.reload(version_core)
|