package server import ( "encoding/json" "net/http" "net/http/httptest" "testing" "chat-agent-system/internal/docgov" "chat-agent-system/internal/docrevision" ) func TestWriteControlledContentError_MapsTypedCodes(t *testing.T) { cases := []struct { err error status int code string }{ {&docgov.ContentError{Code: docgov.ErrCodeDocumentNotFound, Message: "missing record"}, http.StatusNotFound, docgov.ErrCodeDocumentNotFound}, {&docgov.ContentError{Code: docgov.ErrCodeProjectionMissing, Message: "no file", NeedsAdmin: true, Retryable: true}, http.StatusConflict, docgov.ErrCodeProjectionMissing}, {&docgov.ContentError{Code: docgov.ErrCodeProjectionEmpty, Message: "empty", NeedsAdmin: true}, http.StatusConflict, docgov.ErrCodeProjectionEmpty}, {&docgov.ContentError{Code: docgov.ErrCodeIntegrityMismatch, Message: "mismatch", NeedsAdmin: true}, http.StatusConflict, docgov.ErrCodeIntegrityMismatch}, {&docgov.ContentError{Code: docgov.ErrCodeProjectionDrift, Message: "drift", NeedsAdmin: true, Class: "LEGACY_CANONICALIZATION_DRIFT"}, http.StatusConflict, docgov.ErrCodeProjectionDrift}, } for _, tc := range cases { rec := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/api/v1/docgov/documents/x/content", nil) req.Header.Set("X-Request-ID", "corr-1") writeControlledContentError(rec, req, tc.err) if rec.Code != tc.status { t.Fatalf("%s status %d want %d", tc.code, rec.Code, tc.status) } var body map[string]any if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil { t.Fatal(err) } if body["error_code"] != tc.code { t.Fatalf("code %v", body["error_code"]) } if body["correlation_id"] != "corr-1" { t.Fatalf("correlation %v", body["correlation_id"]) } if tc.err.(*docgov.ContentError).Class != "" && body["projection_class"] != tc.err.(*docgov.ContentError).Class { t.Fatalf("projection_class %v", body["projection_class"]) } } } func TestServeLedgerHeadOrConflict_ServesLedgerOnDrift(t *testing.T) { raw := "条目一 \n条目二 \n" head := docrevision.LegacyDestructiveCanonicalizeMarkdown(raw) class := docrevision.ClassifyProjection([]byte(raw), head) if class.Class != docrevision.ProjectionLegacyCanonicalizationDrift { t.Fatalf("class=%s", class.Class) } gotDrift, err := serveLedgerHeadOrConflict("doc_x", 1, false, head, docrevision.SHA256Hex(head), docrevision.SHA256Hex(head), class) if err != nil { t.Fatal(err) } if gotDrift.Consistency != "drift" || gotDrift.Markdown != head { t.Fatalf("drift must still serve ledger: %+v", gotDrift) } aligned := docrevision.ClassifyProjection([]byte(raw), docrevision.CanonicalizeMarkdown(raw)) got, err := serveLedgerHeadOrConflict("doc_x", 1, false, docrevision.CanonicalizeMarkdown(raw), aligned.CanonSHA, aligned.CanonSHA, aligned) if err != nil { t.Fatal(err) } if got.Markdown != docrevision.CanonicalizeMarkdown(raw) { t.Fatalf("aligned must serve preserved-hard-break head, got %q", got.Markdown) } }