fix: 우연히 지켜지던 멱등성을 회귀로 못박고, 틀린 문장의 출처를 고친다
저장 멱등 키가 필드 순서에 안 흔들리는 것은 sort_keys=True 덕이고, 그것은 JSON 을 안정적으로 만들려고 넣은 것이지 멱등성을 노린 것이 아니다. 다시 직렬화한 재시도가 새 저장으로 잡히지 않게 지키고 있었는데 그 성질을 명시한 시험이 없었다. sort_keys 를 빼도 아무 시험이 안 깨졌다. 의도하지 않은 성질에 기대는 코드는 회귀로 못박지 않으면 다음 사람이 지운다. 대조군도 함께 넣었다. 순서에 안 흔들린다고 내용에도 안 흔들리면 그건 다른 결함이다. 그리고 「Decision 은 계약에 삭제 경로가 없다」의 출처가 스킬이었다. 내가 코드 주석과 보고서로 옮긴 문장이 거기서 왔다. 다섯 종류 전부의 삭제 경로를 세어 표로 바꾸고 확인한 리비전(tech-log-backend @ a000f87)을 함께 적었다 — 서버를 서술할 때 리비전이 없으면 서버가 바뀌어도 아무도 모른다. 지워지지 않는 것은 따로 있다. 게시한 적이 있으면 DOCUMENT_PUBLISHED, 관계로 참조되면 DOCUMENT_IN_USE 다. 그것이 원래 말하려던 것이다. 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
4e9226a57d
commit
aae43f4f99
@@ -128,9 +128,25 @@ python3 scripts/verify-tech-log-tree.py <프로젝트>
|
|||||||
|
|
||||||
## 시험용 초안을 남기지 않는다
|
## 시험용 초안을 남기지 않는다
|
||||||
|
|
||||||
확인하려고 만든 작업본은 지운다. 다만 **Decision 은 계약에 삭제 경로가 없다** — 확인용으로
|
확인하려고 만든 작업본은 지운다. **다섯 종류 전부 삭제 경로가 있다** —
|
||||||
만들지 않는 편이 낫다. 관계로 참조된 문서도 지워지지 않는다(`DOCUMENT_IN_USE`). 참조하는 쪽의
|
`tech-log-backend` @ `a000f87` 의 `ManagementDocumentController` 에서 센 것이다.
|
||||||
관계를 먼저 끊는다.
|
|
||||||
|
| 종류 | 경로 |
|
||||||
|
|---|---|
|
||||||
|
| Case | `DELETE /v1/studio/cases/{id}` (`:72`) |
|
||||||
|
| Reference | `DELETE /v1/studio/references/{id}` (`:81`) |
|
||||||
|
| Concept | `DELETE /v1/studio/concepts/{id}` (`:95`) |
|
||||||
|
| Question | `DELETE /v1/studio/questions/{id}` (`:114`) |
|
||||||
|
| Decision | `DELETE /v1/studio/projects/{id}/decisions/{decisionId}` (`:123`) |
|
||||||
|
|
||||||
|
**전에 이 자리에 「Decision 은 계약에 삭제 경로가 없다」고 적혀 있었고 그것은 틀렸다.**
|
||||||
|
확인 없이 적힌 문장이 옮겨 다녔다 — 이 배치에서 그 문장을 코드 주석과 보고서로 다시 옮긴
|
||||||
|
일이 있었다. **서버를 서술할 때는 확인한 리비전을 함께 적는다.** 리비전이 없으면 서버가
|
||||||
|
바뀌어도 아무도 모른다.
|
||||||
|
|
||||||
|
**지워지지 않는 것은 따로 있다.** 게시한 적이 있으면 `DOCUMENT_PUBLISHED` 로 409 가 나고
|
||||||
|
(「공개된 기록은 삭제할 수 없습니다」), 관계로 참조된 문서는 `DOCUMENT_IN_USE` 로 막힌다
|
||||||
|
(「이 기록을 참조하는 곳이 있어 삭제할 수 없습니다」). 참조하는 쪽의 관계를 먼저 끊는다.
|
||||||
|
|
||||||
## 실패했을 때
|
## 실패했을 때
|
||||||
|
|
||||||
|
|||||||
@@ -533,3 +533,37 @@ class HarnessTestPlanTest(unittest.TestCase):
|
|||||||
for s in steps:
|
for s in steps:
|
||||||
self.assertNotIn("/publish", s["path"])
|
self.assertNotIn("/publish", s["path"])
|
||||||
self.assertNotIn("/unpublish", s["path"])
|
self.assertNotIn("/unpublish", s["path"])
|
||||||
|
|
||||||
|
|
||||||
|
class KeyStabilityTest(unittest.TestCase):
|
||||||
|
"""저장 멱등 키가 **필드 순서에 안 흔들린다.**
|
||||||
|
|
||||||
|
`idempotency_key` 가 `json.dumps(..., sort_keys=True)` 로 본문을 직렬화한다.
|
||||||
|
**그 효과를 노리고 넣은 것이 아니라** JSON 을 안정적으로 만들려고 넣은 것이 멱등성을
|
||||||
|
지켰다 — 다시 직렬화한 재시도(같은 내용, 다른 필드 순서)가 새 저장으로 잡히지 않는다.
|
||||||
|
|
||||||
|
**의도하지 않은 성질에 기대는 코드는 회귀로 못박지 않으면 다음 사람이 지운다.**
|
||||||
|
`sort_keys` 를 빼면 이 시험이 깨진다.
|
||||||
|
"""
|
||||||
|
|
||||||
|
REL = "docs/p/tech-log-studio/t/case/x.md"
|
||||||
|
|
||||||
|
def test_field_order_does_not_change_the_save_key(self):
|
||||||
|
one = {"expectedVersion": 3,
|
||||||
|
"document": {"kind": "CASE", "title": "가", "slug": "x"}}
|
||||||
|
two = {"document": {"slug": "x", "title": "가", "kind": "CASE"},
|
||||||
|
"expectedVersion": 3}
|
||||||
|
self.assertEqual(ss.idempotency_key("save", self.REL, one),
|
||||||
|
ss.idempotency_key("save", self.REL, two))
|
||||||
|
|
||||||
|
def test_content_still_changes_the_save_key(self):
|
||||||
|
"""대조군. 순서에 안 흔들린다고 내용에도 안 흔들리면 그건 다른 결함이다."""
|
||||||
|
one = {"expectedVersion": 3, "document": {"title": "가"}}
|
||||||
|
two = {"expectedVersion": 3, "document": {"title": "나"}}
|
||||||
|
self.assertNotEqual(ss.idempotency_key("save", self.REL, one),
|
||||||
|
ss.idempotency_key("save", self.REL, two))
|
||||||
|
|
||||||
|
def test_the_create_key_does_not_depend_on_a_payload(self):
|
||||||
|
"""만들기 키는 경로만으로 만든다. 재시도가 문서를 둘 만들지 않는다."""
|
||||||
|
self.assertEqual(ss.idempotency_key("create", self.REL),
|
||||||
|
ss.idempotency_key("create", self.REL))
|
||||||
|
|||||||
Reference in New Issue
Block a user