음성 재생과 운영 배포 정리

This commit is contained in:
Yun Chan 2026-06-28 12:18:20 +09:00
parent 8ed185ce6c
commit ac7db95542
1020 changed files with 46863 additions and 2175 deletions

View file

@ -0,0 +1,124 @@
"""Session review public share regression tests."""
from __future__ import annotations
import unittest
from starlette.requests import Request
from . import session_persistence
from .deps import Principal, Role
from .routes import sessions
from .routes import share as share_routes
from .services import persona as persona_service, state_machine
from .store import InProcSession, TurnRecord, store
def _principal() -> Principal:
return Principal(
user_id="00000000-0000-0000-0000-000000000201",
role=Role.LEARNER,
cohort_ids=[],
email="share-test@hs.ac.kr",
display_name="Share Test",
consent_at=1.0,
)
def _request(path: str = "/sessions/share-test-session/share") -> Request:
return Request(
{
"type": "http",
"method": "POST",
"path": path,
"scheme": "https",
"server": ("api.test", 443),
"headers": [(b"host", b"api.test")],
}
)
def _ended_session(principal: Principal) -> InProcSession:
card = persona_service.P1
sess = InProcSession(
session_id="share-test-session",
case_id="share-test-case",
learner_id=principal.user_id,
persona_code=card.code,
theory_mode="humanistic",
persona=card,
state=state_machine.SessionState(
resistance=card.base_resistance(),
ideation_stage=card.ideation_baseline(),
),
ended=True,
ended_at=1_800_000_100,
)
sess.turns.extend(
[
TurnRecord(
turn_seq=1,
speaker="counselor",
stage="라포",
text="원문 학습자 민감 발화",
text_masked="원문 학습자 민감 발화",
created_at=1_800_000_000,
),
TurnRecord(
turn_seq=2,
speaker="client",
stage="라포",
text="내담자 응답",
text_masked="내담자 응답",
created_at=1_800_000_030,
),
]
)
store.put(sess)
return sess
class SessionShareTest(unittest.IsolatedAsyncioTestCase):
async def asyncSetUp(self) -> None:
store._sessions.clear()
session_persistence._SESSION_SHARE_CACHE.clear()
session_persistence._SESSION_SHARE_TOKEN_INDEX.clear()
async def asyncTearDown(self) -> None:
store._sessions.clear()
session_persistence._SESSION_SHARE_CACHE.clear()
session_persistence._SESSION_SHARE_TOKEN_INDEX.clear()
async def test_create_share_returns_public_summary_without_transcript(self) -> None:
principal = _principal()
_ended_session(principal)
response = await sessions.create_session_share(
"share-test-session",
_request(),
principal,
)
self.assertTrue(response.shareUrl.startswith("https://api.test/share/session/"))
token = response.shareUrl.rsplit("/", 1)[-1]
summary = await share_routes._load_share_or_404(token)
self.assertIn("Vignette 회기 리뷰", summary.title)
self.assertNotIn("원문 학습자 민감 발화", summary.model_dump_json())
self.assertIn("저장된 실제 축어록 2개", summary.summary)
async def test_revoke_share_blocks_public_lookup(self) -> None:
principal = _principal()
_ended_session(principal)
response = await sessions.create_session_share(
"share-test-session",
_request(),
principal,
)
token = response.shareUrl.rsplit("/", 1)[-1]
revoked = await sessions.revoke_session_share("share-test-session", principal)
self.assertTrue(revoked.revoked)
with self.assertRaises(share_routes.HTTPException) as caught:
await share_routes._load_share_or_404(token)
self.assertEqual(caught.exception.status_code, 404)