fix: scrub training public import response
This commit is contained in:
@@ -96,7 +96,7 @@ class TrainingCaseService:
|
||||
return _sanitize_public_export(export_training_cases(cases))
|
||||
|
||||
def import_cases(self, payload: dict) -> dict:
|
||||
return import_training_cases(payload, repository=self.repository)
|
||||
return _sanitize_public_export(import_training_cases(payload, repository=self.repository))
|
||||
|
||||
|
||||
def _sanitize_public_export(value):
|
||||
|
||||
@@ -260,6 +260,75 @@ def _public_import_package() -> dict:
|
||||
}
|
||||
|
||||
|
||||
def _nested_public_import_package() -> dict:
|
||||
return {
|
||||
"version": 1,
|
||||
"items": [
|
||||
{
|
||||
"external_id": "m19-import-ref " + INTERNAL_FIELD,
|
||||
"title": "Imported response privacy",
|
||||
"scenario": {
|
||||
"instrument_id": 1,
|
||||
"timeframe": "15m",
|
||||
"case_type": "sweep",
|
||||
"start_ts": "2026-04-22T09:00:00Z",
|
||||
"decision_ts": "2026-04-22T10:00:00Z",
|
||||
"end_ts": "2026-04-22T11:00:00Z",
|
||||
"source_event_refs": [
|
||||
{
|
||||
"kind": "sweep_event",
|
||||
"id": 19,
|
||||
"answer": NESTED_PRIVATE_MARKERS[0],
|
||||
INTERNAL_FIELD: NESTED_PRIVATE_MARKERS[1],
|
||||
},
|
||||
{"kind": "context", "id": 20, "label": PRIVATE_FIELD_TEXT},
|
||||
],
|
||||
},
|
||||
"skill_tags": ["sweep", "context_reading"],
|
||||
"difficulty": "medium",
|
||||
"status": "draft",
|
||||
"answer": {
|
||||
"side": NESTED_PRIVATE_MARKERS[0],
|
||||
"nested": {
|
||||
"close": NESTED_PRIVATE_MARKERS[1],
|
||||
INTERNAL_FIELD: NESTED_PRIVATE_MARKERS[2],
|
||||
},
|
||||
},
|
||||
"rubric": [
|
||||
{
|
||||
"category": NESTED_PRIVATE_MARKERS[2],
|
||||
"max_score": 5,
|
||||
"answer": NESTED_PRIVATE_MARKERS[0],
|
||||
INTERNAL_FIELD: {"detail": NESTED_PRIVATE_MARKERS[1]},
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"task_type": "short_answer",
|
||||
"skill_tag": "sweep",
|
||||
"prompt": "Describe the public confirmation.",
|
||||
"required_annotation_types": ["text"],
|
||||
"max_score": 5,
|
||||
"meta": {
|
||||
"public_hint": "Focus on context.",
|
||||
"answer": NESTED_PRIVATE_MARKERS[0],
|
||||
INTERNAL_FIELD: NESTED_PRIVATE_MARKERS[1],
|
||||
"nested": {"answer": NESTED_PRIVATE_MARKERS[2]},
|
||||
},
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"chapter_ref": "chapter_19",
|
||||
"public_label": "visible",
|
||||
"answer": NESTED_PRIVATE_MARKERS[0],
|
||||
INTERNAL_FIELD: NESTED_PRIVATE_MARKERS[1],
|
||||
"nested": {"answer": NESTED_PRIVATE_MARKERS[2]},
|
||||
},
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def _dict_keys(value) -> list[str]:
|
||||
if isinstance(value, dict):
|
||||
keys = []
|
||||
@@ -411,6 +480,59 @@ class TrainingPublicApiContractM8Tests(unittest.TestCase):
|
||||
with self.subTest(private_text=private_text):
|
||||
self.assertNotIn(private_text, serialized)
|
||||
|
||||
def test_public_training_import_route_scrubs_nested_solution_material_from_response(self) -> None:
|
||||
repository = _PublicTrainingRepository()
|
||||
service = TrainingCaseService(repository=repository)
|
||||
dependencies = _training_dependencies(service)
|
||||
|
||||
status, payload = server.route_request(
|
||||
"/training/cases/import",
|
||||
{},
|
||||
dependencies=dependencies,
|
||||
method="POST",
|
||||
body=_nested_public_import_package(),
|
||||
)
|
||||
|
||||
self.assertEqual(status, 201)
|
||||
self.assertEqual(payload["imported"], 1)
|
||||
self.assertEqual(payload["case_ids"], [41])
|
||||
item = payload["items"][0]
|
||||
self.assertEqual(
|
||||
set(item),
|
||||
{"id", "title", "scenario", "skill_tags", "difficulty", "status", "external_id"},
|
||||
)
|
||||
self.assertEqual(item["id"], 41)
|
||||
self.assertEqual(item["title"], "Imported response privacy")
|
||||
self.assertEqual(item["external_id"], "m19-import-ref private solution field")
|
||||
self.assertEqual(item["skill_tags"], ["sweep", "context_reading"])
|
||||
self.assertEqual(item["difficulty"], "medium")
|
||||
self.assertEqual(item["status"], "draft")
|
||||
self.assertEqual(
|
||||
item["scenario"],
|
||||
{
|
||||
"instrument_id": 1,
|
||||
"timeframe": "15m",
|
||||
"case_type": "sweep",
|
||||
"start_ts": "2026-04-22T09:00:00Z",
|
||||
"decision_ts": "2026-04-22T10:00:00Z",
|
||||
"end_ts": "2026-04-22T11:00:00Z",
|
||||
"source_event_refs": [
|
||||
{"id": 19, "kind": "sweep_event"},
|
||||
{"id": 20, "kind": "context", "label": "public text mentions private solution field"},
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
serialized = json.dumps(payload, sort_keys=True)
|
||||
self.assertNotIn(INTERNAL_FIELD, serialized)
|
||||
for private_text in NESTED_PRIVATE_MARKERS:
|
||||
with self.subTest(private_text=private_text):
|
||||
self.assertNotIn(private_text, serialized)
|
||||
|
||||
keys = _dict_keys(payload)
|
||||
self.assertNotIn("answer", keys)
|
||||
self.assertNotIn(INTERNAL_FIELD, keys)
|
||||
|
||||
def test_public_training_list_detail_routes_scrub_nested_solution_material(self) -> None:
|
||||
repository = _NestedPublicTrainingRepository()
|
||||
service = TrainingCaseService(repository=repository)
|
||||
|
||||
Reference in New Issue
Block a user