세션 평가와 교수자 분석 보강

This commit is contained in:
Yun Chan 2026-07-01 12:10:52 +09:00
parent 5c4ac04e06
commit fe2796f05a
51 changed files with 4928 additions and 240 deletions

View file

@ -160,6 +160,152 @@ class LearnerSessionIdorTest(unittest.IsolatedAsyncioTestCase):
self.assertIn("does not belong", caught.exception.detail)
review_ready.assert_not_awaited()
async def test_super_admin_primary_learner_can_review_other_learner_session(self) -> None:
owner = _principal(
user_id="00000000-0000-0000-0000-000000000103",
)
super_admin = Principal(
user_id="00000000-0000-0000-0000-000000000303",
role=Role.LEARNER,
admin_access=True,
super_admin=True,
email="yunchan@twentyoz.kr",
display_name="Yun Chan",
)
sess = _session(
session_id="00000000-0000-0000-0000-00000000a103",
learner_id=owner.user_id,
)
store.put(sess)
with (
patch.object(
sessions.session_persistence,
"load_session",
AsyncMock(return_value=None),
) as load_session,
patch.object(sessions.turn_runtime, "runtime_fallback_allowed", return_value=True),
):
loaded, review_principal = await sessions._load_review_session_or_404(
sess.session_id,
super_admin,
)
self.assertIs(loaded, sess)
self.assertEqual(review_principal.role, Role.ADMIN)
self.assertEqual(load_session.await_count, 2)
async def test_super_admin_primary_learner_review_loaders_use_admin_principal(self) -> None:
owner = _principal(
user_id="00000000-0000-0000-0000-000000000105",
)
super_admin = Principal(
user_id="00000000-0000-0000-0000-000000000305",
role=Role.LEARNER,
admin_access=True,
super_admin=True,
email="yunchan@twentyoz.kr",
display_name="Yun Chan",
)
sess = _session(
session_id="00000000-0000-0000-0000-00000000a105",
learner_id=owner.user_id,
)
sess.ended = True
sess.ended_at = 1_800_000_120.0
sess.turns.extend(
[
_turn(seq=1, speaker="counselor", text="admin-visible learner turn"),
_turn(seq=2, speaker="client", text="admin-visible client turn"),
]
)
store.put(sess)
with (
patch.object(
sessions.session_persistence,
"load_session",
AsyncMock(return_value=None),
),
patch.object(sessions.turn_runtime, "runtime_fallback_allowed", return_value=True),
patch.object(
sessions.session_persistence,
"load_session_evaluation",
AsyncMock(return_value=(None, False)),
) as load_session_evaluation,
patch.object(
sessions.session_persistence,
"load_case_worksheet",
AsyncMock(return_value=(None, False)),
) as load_case_worksheet,
patch.object(
sessions.session_persistence,
"load_session_review_status",
AsyncMock(
return_value=(
{
"status": "viewed",
"reviewer_id": "00000000-0000-0000-0000-000000000999",
},
True,
)
),
) as load_session_review_status,
):
response = await sessions.get_session_review(sess.session_id, super_admin)
load_session_evaluation.assert_awaited_once()
load_case_worksheet.assert_awaited_once()
load_session_review_status.assert_awaited_once()
evaluation_principal = load_session_evaluation.await_args.args[1]
worksheet_principal = load_case_worksheet.await_args.args[1]
review_status_principal = load_session_review_status.await_args.args[1]
self.assertEqual(load_session_evaluation.await_args.args[0], sess.session_id)
self.assertEqual(load_case_worksheet.await_args.args[0], sess.session_id)
self.assertEqual(load_session_review_status.await_args.args[0], sess.session_id)
self.assertIs(evaluation_principal, worksheet_principal)
self.assertIs(evaluation_principal, review_status_principal)
self.assertEqual(evaluation_principal.user_id, super_admin.user_id)
self.assertEqual(evaluation_principal.role, Role.ADMIN)
self.assertTrue(evaluation_principal.super_admin)
self.assertEqual(response.session_id, sess.session_id)
self.assertIsNotNone(response.teacherReview)
self.assertEqual(response.teacherReview.status, "viewed")
async def test_admin_access_flag_alone_does_not_review_other_learner_session(self) -> None:
owner = _principal(
user_id="00000000-0000-0000-0000-000000000104",
)
delegated_admin = Principal(
user_id="00000000-0000-0000-0000-000000000304",
role=Role.LEARNER,
admin_access=True,
super_admin=False,
email="delegate@example.test",
display_name="Delegated Admin",
)
sess = _session(
session_id="00000000-0000-0000-0000-00000000a104",
learner_id=owner.user_id,
)
store.put(sess)
with (
patch.object(
sessions.session_persistence,
"load_session",
AsyncMock(return_value=None),
),
patch.object(sessions.turn_runtime, "runtime_fallback_allowed", return_value=True),
):
with self.assertRaises(HTTPException) as caught:
await sessions._load_review_session_or_404(sess.session_id, delegated_admin)
self.assertEqual(caught.exception.status_code, 403)
self.assertIn("does not belong", caught.exception.detail)
async def test_session_detail_filters_evaluator_only_turns(self) -> None:
owner = _principal(
user_id="00000000-0000-0000-0000-000000000111",