package service import ( "context" "errors" "strings" "testing" "sourcecraft.dev/bigbes/sr-ht-spec/core" ) func TestReadDocumentResolvesTheApprovedHeadByDefault(t *testing.T) { svc, root := newService(t) sp := newSpace(t, root, 1) ctx := context.Background() first := commitFiles(t, sp, sp.ApprovedBranch(), 1, map[string][]byte{ "specs/0007-storage.md": doc("SPEC-0007", "Storage model", "first"), }) second := commitFiles(t, sp, sp.ApprovedBranch(), 2, map[string][]byte{ "specs/0007-storage.md": doc("SPEC-0007", "Storage model", "second"), }) got, err := svc.ReadDocument(ctx, sp, ApprovedRev, "specs/0007-storage.md") if err != nil { t.Fatalf("ReadDocument: %v", err) } if !contains(got.Data, "second") { t.Errorf("approved read returned %q, want the newest revision", got.Data) } if got.Rev != second.String() { t.Errorf("Rev = %s, want the resolved approved head %s", got.Rev, second) } if got.Blob == "" { t.Error("Blob is empty; it is the render cache key") } // The same call with a pinned revision is the same code path with a // different revision — one storage tier, no checkout. pinned, err := svc.ReadDocument(ctx, sp, first.String(), "specs/0007-storage.md") if err != nil { t.Fatalf("pinned ReadDocument: %v", err) } if !contains(pinned.Data, "first") { t.Errorf("pinned read returned %q, want the pinned revision", pinned.Data) } if pinned.Blob == got.Blob { t.Error("two revisions of one document share a blob sha") } } // A draft must never be served by default: doing so would poison every // downstream agent context with unreviewed text. func TestReadDocumentDoesNotServeAProposalByDefault(t *testing.T) { svc, root := newService(t) sp := newSpace(t, root, 1) ctx := context.Background() commitFiles(t, sp, sp.ApprovedBranch(), 1, map[string][]byte{ "specs/0007-storage.md": doc("SPEC-0007", "Storage model", "approved"), }) cutBranch(t, sp, "proposals/1", sp.ApprovedBranch()) commitFiles(t, sp, "proposals/1", 2, map[string][]byte{ "specs/0007-storage.md": doc("SPEC-0007", "Storage model", "draft"), }) approved, err := svc.ReadDocument(ctx, sp, ApprovedRev, "specs/0007-storage.md") if err != nil { t.Fatalf("ReadDocument: %v", err) } if !contains(approved.Data, "approved") { t.Fatalf("default read returned the draft: %q", approved.Data) } // The read plane refuses a ref name outright — that is the guard that stops // a crafted ?rev= from serving unreviewed text as approved. if _, err := svc.ReadDocument(ctx, sp, "proposals/1", "specs/0007-storage.md"); !errors.Is(err, ErrBadReadRev) { t.Fatalf("ReadDocument accepted a ref name: err = %v, want ErrBadReadRev", err) } // The review path reaches it deliberately, by a differently-named method. draft, err := svc.ReadDocumentAtRef(ctx, sp, "proposals/1", "specs/0007-storage.md") if err != nil { t.Fatalf("ReadDocumentAtRef at the proposal branch: %v", err) } if !contains(draft.Data, "draft") { t.Errorf("proposal read returned %q", draft.Data) } } func TestReadDocumentMapsAbsenceOntoErrNotFound(t *testing.T) { svc, root := newService(t) sp := newSpace(t, root, 1) ctx := context.Background() tests := []struct { name string rev string path string }{ {"missing document", ApprovedRev, "specs/nope.md"}, {"unknown revision", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", "specs/nope.md"}, // Revision arithmetic is not a usable revision; it must not be // distinguishable from an absent one by probing. {"revision arithmetic", "main^2", "specs/nope.md"}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { if _, err := svc.ReadDocument(ctx, sp, tc.rev, tc.path); !errors.Is(err, ErrNotFound) { t.Fatalf("err = %v, want ErrNotFound", err) } }) } } func TestListDocumentsReturnsEveryDocumentAtARevision(t *testing.T) { svc, root := newService(t) sp := newSpace(t, root, 1) ctx := context.Background() commitFiles(t, sp, sp.ApprovedBranch(), 1, map[string][]byte{ "specs/0007-storage.md": doc("SPEC-0007", "Storage", "body"), "notes/scratch.md": doc("NOTE-0001", "Scratch", "body"), "attachment.png": []byte("\x89PNG not a document"), core.PolicyFile: []byte("review:\n auto_merge: [notes/**]\n"), }) docs, err := svc.ListDocuments(ctx, sp, ApprovedRev) if err != nil { t.Fatalf("ListDocuments: %v", err) } var paths []string for _, d := range docs { paths = append(paths, d.Path) } want := []string{"notes/scratch.md", "specs/0007-storage.md"} if len(paths) != len(want) { t.Fatalf("paths = %v, want %v (documents only)", paths, want) } for i := range want { if paths[i] != want[i] { t.Fatalf("paths = %v, want %v", paths, want) } } } func TestPolicyReadsTheVersionedSpecYml(t *testing.T) { svc, root := newService(t) sp := newSpace(t, root, 1) ctx := context.Background() // A space with no .spec.yml gets the house contract and nothing // auto-merged: the fail-closed direction. pol, err := svc.Policy(ctx, sp, ApprovedRev) if err != nil { t.Fatalf("Policy: %v", err) } if pol.AutoMerges("notes/x.md") { t.Error("a space with no policy auto-merged a path") } if len(pol.Schema.Required) == 0 { t.Error("default policy carries no required keys") } before := commitFiles(t, sp, sp.ApprovedBranch(), 1, map[string][]byte{ core.PolicyFile: []byte("review:\n auto_merge: [notes/**]\n"), }) pol, err = svc.Policy(ctx, sp, ApprovedRev) if err != nil { t.Fatalf("Policy: %v", err) } if !pol.AutoMerges("notes/x.md") || pol.AutoMerges("specs/x.md") { t.Errorf("auto_merge = %v", pol.Review.AutoMerge) } // Policy is read at a revision, which is what makes a policy change // reviewable like any other change. commitFiles(t, sp, sp.ApprovedBranch(), 2, map[string][]byte{ core.PolicyFile: []byte("review:\n auto_merge: []\n"), }) pinned, err := svc.Policy(ctx, sp, before.String()) if err != nil { t.Fatalf("pinned Policy: %v", err) } if !pinned.AutoMerges("notes/x.md") { t.Error("a pinned policy read returned the newer policy") } } func TestPolicyFailsOnAnUnparseableSpecYml(t *testing.T) { svc, root := newService(t) sp := newSpace(t, root, 1) commitFiles(t, sp, sp.ApprovedBranch(), 1, map[string][]byte{ core.PolicyFile: []byte("review:\n auto-merge: [notes/**]\n"), }) _, err := svc.Policy(context.Background(), sp, ApprovedRev) if !errors.Is(err, core.ErrInvalidPolicy) { t.Fatalf("err = %v, want core.ErrInvalidPolicy — a typo that silently does nothing is worse", err) } } func TestResolveRevPinsTheApprovedHead(t *testing.T) { svc, root := newService(t) sp := newSpace(t, root, 1) head := commitFiles(t, sp, sp.ApprovedBranch(), 1, map[string][]byte{ "specs/0007-storage.md": doc("SPEC-0007", "Storage", "body"), }) got, err := svc.ResolveRev(context.Background(), sp, ApprovedRev) if err != nil { t.Fatalf("ResolveRev: %v", err) } if got != head.String() { t.Errorf("ResolveRev = %s, want %s", got, head) } } func contains(data []byte, want string) bool { return strings.Contains(string(data), want) }