개선관리 요구사항과 Google 로그인을 완료
This commit is contained in:
parent
cc0a15b7c6
commit
2a39636163
112 changed files with 10166 additions and 527 deletions
|
|
@ -211,6 +211,48 @@ class DeliberatePracticeStoreAsyncTests(unittest.IsolatedAsyncioTestCase):
|
|||
episode=self.pack.cases[0].episodes[0],
|
||||
)
|
||||
|
||||
async def test_attempt_rejects_disabled_prescription_source_before_replay(
|
||||
self,
|
||||
) -> None:
|
||||
principal = _principal(Role.LEARNER)
|
||||
case = self.pack.cases[0]
|
||||
episode_payload = case.episodes[0].model_dump(mode="python")
|
||||
for attempt in episode_payload["attempts"]:
|
||||
for ref in attempt["evidence_refs"]:
|
||||
ref["ref_id"] = str(uuid4())
|
||||
for ref in attempt["criterion"]["evidence_refs"]:
|
||||
ref["ref_id"] = str(uuid4())
|
||||
episode = type(case.episodes[0]).model_validate(episode_payload)
|
||||
source_session_id = uuid4()
|
||||
conn = AsyncMock()
|
||||
conn.fetchrow.return_value = {
|
||||
"prescription_record_id": uuid4(),
|
||||
"session_id": source_session_id,
|
||||
"prescription_payload": {},
|
||||
"created_at": datetime.now(timezone.utc),
|
||||
"source_learner_feedback_enabled": False,
|
||||
}
|
||||
|
||||
@asynccontextmanager
|
||||
async def fake_acquire(**_kwargs):
|
||||
yield conn
|
||||
|
||||
with patch.object(deliberate_practice_store.db, "acquire", fake_acquire):
|
||||
with self.assertRaises(
|
||||
deliberate_practice_store.DeliberatePracticeFeedbackDisabledError
|
||||
):
|
||||
await deliberate_practice_store.append_learner_attempt_submission(
|
||||
principal=principal,
|
||||
submission_id=uuid4(),
|
||||
prescription_id=episode.prescription_id,
|
||||
episode=episode,
|
||||
)
|
||||
|
||||
self.assertEqual(conn.fetchrow.await_count, 1)
|
||||
source_query = conn.fetchrow.await_args.args[0]
|
||||
self.assertIn("source_session.learner_feedback_enabled", source_query)
|
||||
self.assertNotIn("app.practice_episode_submission", source_query)
|
||||
|
||||
async def test_runtime_observer_derives_attempt_from_later_ready_session(
|
||||
self,
|
||||
) -> None:
|
||||
|
|
@ -388,6 +430,71 @@ class DeliberatePracticeStoreAsyncTests(unittest.IsolatedAsyncioTestCase):
|
|||
self.assertIsNone(result["competency_graph"])
|
||||
self.assertFalse(result["clinical_claim_allowed"])
|
||||
|
||||
async def test_learner_read_rejects_any_disabled_source_snapshot(self) -> None:
|
||||
principal = _principal(Role.LEARNER)
|
||||
conn = AsyncMock()
|
||||
conn.fetch.side_effect = [
|
||||
[
|
||||
{
|
||||
"prescription_record_id": uuid4(),
|
||||
"session_id": uuid4(),
|
||||
"source_learner_feedback_enabled": False,
|
||||
}
|
||||
],
|
||||
[],
|
||||
]
|
||||
conn.fetchrow.return_value = None
|
||||
|
||||
@asynccontextmanager
|
||||
async def fake_acquire(**_kwargs):
|
||||
yield conn
|
||||
|
||||
with patch.object(deliberate_practice_store.db, "acquire", fake_acquire):
|
||||
with self.assertRaises(
|
||||
deliberate_practice_store.DeliberatePracticeFeedbackDisabledError
|
||||
):
|
||||
await deliberate_practice_store.read_deliberate_practice(
|
||||
principal=principal
|
||||
)
|
||||
|
||||
prescription_query = conn.fetch.await_args_list[0].args[0]
|
||||
episode_query = conn.fetch.await_args_list[1].args[0]
|
||||
self.assertIn("source_session.learner_feedback_enabled", prescription_query)
|
||||
self.assertIn("source_session.learner_feedback_enabled", episode_query)
|
||||
|
||||
async def test_teacher_read_keeps_disabled_source_snapshot_visible(self) -> None:
|
||||
principal = _principal(Role.TEACHER)
|
||||
learner_id = uuid4()
|
||||
conn = AsyncMock()
|
||||
conn.fetchval.return_value = True
|
||||
conn.fetch.side_effect = [
|
||||
[
|
||||
{
|
||||
"prescription_record_id": uuid4(),
|
||||
"session_id": uuid4(),
|
||||
"source_learner_feedback_enabled": False,
|
||||
}
|
||||
],
|
||||
[],
|
||||
]
|
||||
conn.fetchrow.return_value = None
|
||||
|
||||
@asynccontextmanager
|
||||
async def fake_acquire(**_kwargs):
|
||||
yield conn
|
||||
|
||||
with patch.object(deliberate_practice_store.db, "acquire", fake_acquire):
|
||||
result = await deliberate_practice_store.read_deliberate_practice(
|
||||
principal=principal,
|
||||
learner_id=learner_id,
|
||||
)
|
||||
|
||||
self.assertEqual(len(result["prescriptions"]), 1)
|
||||
self.assertNotIn(
|
||||
"source_learner_feedback_enabled",
|
||||
result["prescriptions"][0],
|
||||
)
|
||||
|
||||
async def test_learner_read_cannot_target_another_learner(self) -> None:
|
||||
with self.assertRaises(
|
||||
deliberate_practice_store.DeliberatePracticeNotFoundError
|
||||
|
|
@ -439,6 +546,32 @@ class DeliberatePracticeStoreAsyncTests(unittest.IsolatedAsyncioTestCase):
|
|||
)
|
||||
self.assertEqual(captured.exception.status_code, 409)
|
||||
|
||||
async def test_attempt_route_maps_disabled_source_snapshot_to_403(self) -> None:
|
||||
case = self.pack.cases[0]
|
||||
body = deliberate_practices.PracticeAttemptSubmissionRequest(
|
||||
submission_id=uuid4(),
|
||||
episode=case.episodes[0],
|
||||
)
|
||||
with patch.object(
|
||||
deliberate_practices.deliberate_practice_store,
|
||||
"append_learner_attempt_submission",
|
||||
AsyncMock(
|
||||
side_effect=deliberate_practice_store.DeliberatePracticeFeedbackDisabledError(
|
||||
"source session feedback disabled"
|
||||
)
|
||||
),
|
||||
) as append:
|
||||
with self.assertRaises(HTTPException) as captured:
|
||||
await deliberate_practices.create_practice_attempt(
|
||||
prescription_id=case.episodes[0].prescription_id,
|
||||
body=body,
|
||||
principal=_principal(Role.LEARNER),
|
||||
)
|
||||
|
||||
self.assertEqual(captured.exception.status_code, 403)
|
||||
self.assertEqual(captured.exception.detail, "learner_feedback_disabled")
|
||||
append.assert_awaited_once()
|
||||
|
||||
async def test_runtime_practice_route_forwards_completed_session_identity(
|
||||
self,
|
||||
) -> None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue