현재 작업 전체 반영

This commit is contained in:
Yun Chan 2026-06-27 16:08:41 +09:00
parent 5560638e54
commit c0dddab594
85 changed files with 11322 additions and 539 deletions

View file

@ -3,7 +3,9 @@
from __future__ import annotations
import unittest
import time
from contextlib import contextmanager
from types import SimpleNamespace
from unittest.mock import AsyncMock, patch
from fastapi import HTTPException
@ -215,6 +217,95 @@ class RuntimeFallbackPolicyTest(unittest.IsolatedAsyncioTestCase):
self.assertEqual(db.metric, "비영구 런타임 기록")
self.assertIn("비영구 개발 런타임 기록", db.detail)
async def test_dev_admin_usage_falls_back_to_runtime_store(self) -> None:
principal = Principal(
user_id="00000000-0000-0000-0000-000000000007",
role=Role.ADMIN,
cohort_ids=[],
email="admin@twentyoz.kr",
display_name="Admin",
)
now = time.time()
fake_session = SimpleNamespace(
turns=[
SimpleNamespace(
speaker="client",
created_at=now,
llm_provider="claude_cli",
model="gateway-default",
tokens_in=11,
tokens_out=13,
cost_usd=0.0042,
),
SimpleNamespace(
speaker="client",
created_at=now,
llm_provider=None,
model=None,
tokens_in=None,
tokens_out=None,
cost_usd=None,
),
SimpleNamespace(
speaker="counselor",
created_at=now,
llm_provider="ignored",
model="ignored",
tokens_in=100,
tokens_out=100,
cost_usd=9.0,
),
]
)
with (
environment("dev"),
patch.object(
admin_routes,
"_usage_from_database",
AsyncMock(side_effect=RuntimeError("db unavailable")),
),
patch.object(admin_routes.store, "list", return_value=[fake_session]),
patch.object(admin_routes.settings, "admin_usage_budget_usd", 0.005),
):
usage = await admin_routes.admin_usage(principal, window_days=7)
self.assertEqual(usage.source, "server_session_registry")
self.assertFalse(usage.durable)
self.assertEqual(usage.total_turns, 2)
self.assertEqual(usage.metered_turns, 1)
self.assertEqual(usage.tokens_in, 11)
self.assertEqual(usage.tokens_out, 13)
self.assertAlmostEqual(usage.cost_usd, 0.0042)
self.assertEqual(usage.budget.status, "warn")
self.assertAlmostEqual(usage.budget.limit_usd, 0.005)
self.assertAlmostEqual(usage.budget.used_ratio, 0.84)
self.assertEqual(usage.by_provider[0].provider, "claude_cli")
self.assertEqual(usage.by_provider[0].turns, 1)
async def test_prod_admin_usage_rejects_runtime_store_fallback(self) -> None:
principal = Principal(
user_id="00000000-0000-0000-0000-000000000008",
role=Role.ADMIN,
cohort_ids=[],
email="admin@twentyoz.kr",
display_name="Admin",
)
with (
environment("prod"),
patch.object(
admin_routes,
"_usage_from_database",
AsyncMock(side_effect=RuntimeError("db unavailable")),
),
):
with self.assertRaises(HTTPException) as caught:
await admin_routes.admin_usage(principal, window_days=7)
self.assertEqual(caught.exception.status_code, 503)
self.assertIn("runtime fallback is disabled in prod", caught.exception.detail)
def test_non_dev_rejects_fixture_runtime_flags(self) -> None:
with self.assertRaises(ValueError) as caught:
Settings(
@ -261,6 +352,7 @@ class RuntimeFallbackPolicyTest(unittest.IsolatedAsyncioTestCase):
oauth_google_client_secret="google-client-secret",
frontend_base_url="https://vignette.chanpaca.net",
cors_origins=["https://vignette.chanpaca.net"],
voice_poc_sample_tts_enabled=False,
)
self.assertEqual(cfg.environment, "staging")
@ -281,6 +373,7 @@ class RuntimeFallbackPolicyTest(unittest.IsolatedAsyncioTestCase):
"api-vnet.18ka.net": "https://vnet.18ka.net",
},
cors_origins=["https://vignette.chanpaca.net", "https://vnet.18ka.net"],
voice_poc_sample_tts_enabled=False,
)
self.assertEqual(cfg.frontend_origin_map["api-vnet.18ka.net"], "https://vnet.18ka.net")
@ -318,6 +411,7 @@ class RuntimeFallbackPolicyTest(unittest.IsolatedAsyncioTestCase):
"http://localhost:5170",
"http://127.0.0.1:5180",
],
voice_poc_sample_tts_enabled=False,
)
self.assertIn("http://localhost:5170", cfg.cors_origins)