feat(studio-save): 시험 초안 계획에 두 번째 저장과 정리 단계를 넣는다
만들기만 하면 이 런에서 expectedVersion·VERSION_CONFLICT 경로가 한 번도 안 돈다. 새로
만든 뒤 한 번 더 저장해 낙관적 락을 태운다. expectedVersion 은 만들기 응답이 준 version
이고, 모른 채 보내면 서버가 0 으로 채워 늘 충돌한다.
만든 시험 초안을 그 자리에서 지운다. DELETE /api/v1/studio/cases/{id} 가 실재하는 것을
소스와 openapi 에서 확인했고 게시 가드에 안 걸리는 것도 확인했다. 시험 초안의 종류를
CASE 로 고른 이유가 그것이다 — Decision 은 계약에 삭제 경로가 없다.
정리 단계는 --harness-test 일 때만 낸다. 진짜 기록에는 삭제 요청을 만들지 않는다.
삭제가 409 를 낼 수 있다 — 공개된 기록이거나 참조하는 곳이 있는 경우다. 둘 다 사람이
화면에서 처리하고, 계획에 그렇게 적었다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4vKjQo9KKBBokzxqXLCfk
This commit is contained in:
co-authored by
Claude Opus 5
parent
f16785b93e
commit
c66eee6cb2
@@ -489,3 +489,44 @@ class PublishedRecordGuardTest(unittest.TestCase):
|
||||
def test_the_harness_prefix_is_defined(self):
|
||||
"""시험 초안을 사람이 눈으로 가릴 수 있어야 한다."""
|
||||
self.assertEqual("[HARNESS-TEST]", ss.HARNESS_PREFIX)
|
||||
|
||||
|
||||
class HarnessTestPlanTest(unittest.TestCase):
|
||||
"""시험 초안 계획. 만든 것을 그 자리에서 지운다."""
|
||||
|
||||
def test_cleanup_only_appears_for_a_harness_test(self):
|
||||
"""진짜 기록에는 삭제 요청을 만들지 않는다."""
|
||||
plain = ss.plan_requests("docs/p/t/case/x.md", dict(SENT), None, None)
|
||||
self.assertNotIn("cleanup", [s["op"] for s in plain])
|
||||
harness = ss.plan_requests("docs/p/t/case/x.md", dict(SENT), None, None,
|
||||
harness_test=True)
|
||||
self.assertIn("cleanup", [s["op"] for s in harness])
|
||||
|
||||
def test_the_cleanup_path_is_the_case_delete_endpoint(self):
|
||||
"""`DELETE /api/v1/studio/cases/{id}` 는 실재한다
|
||||
(`ManagementDocumentController.java:72`). Decision 은 삭제 경로가 없어서
|
||||
시험 초안의 종류를 CASE 로 고른다."""
|
||||
steps = ss.plan_requests("docs/p/t/case/x.md", dict(SENT), None, None,
|
||||
harness_test=True)
|
||||
cleanup = next(s for s in steps if s["op"] == "cleanup")
|
||||
self.assertEqual("DELETE", cleanup["method"])
|
||||
self.assertIn("/api/v1/studio/cases/", cleanup["path"])
|
||||
|
||||
def test_the_cleanup_path_does_not_trip_the_publish_guard(self):
|
||||
"""삭제 경로가 게시 가드에 걸리면 정리를 못 한다."""
|
||||
ss._reject_forbidden_paths([{"path": "/api/v1/studio/cases/abc"}])
|
||||
|
||||
def test_a_second_save_exercises_the_optimistic_lock(self):
|
||||
"""이 단계가 없으면 이 런에서 `expectedVersion` 경로가 안 돈다."""
|
||||
steps = ss.plan_requests("docs/p/t/case/x.md", dict(SENT), None, None,
|
||||
harness_test=True)
|
||||
save = next(s for s in steps if s["op"] == "save-after-create")
|
||||
self.assertIn("expectedVersion", save["body"])
|
||||
self.assertIn("VERSION_CONFLICT", save["expect"]["onConflict"])
|
||||
|
||||
def test_a_harness_plan_still_has_no_publish_path(self):
|
||||
steps = ss.plan_requests("docs/p/t/case/x.md", dict(SENT), None, None,
|
||||
harness_test=True)
|
||||
for s in steps:
|
||||
self.assertNotIn("/publish", s["path"])
|
||||
self.assertNotIn("/unpublish", s["path"])
|
||||
|
||||
Reference in New Issue
Block a user