package docquery import ( "context" "errors" "os" "path/filepath" "testing" "chat-agent-system/internal/docgov" "chat-agent-system/internal/docrevision" "chat-agent-system/internal/workspace" ) func setupLedger(t *testing.T, docID, body, workDir string) (*Service, *docgov.GovernanceEngine, *docrevision.Service) { t.Helper() ctx := context.Background() gov := docgov.NewGovernanceEngine(nil) if _, err := gov.CreateOrGetDocument(ctx, docID, 7, "intake", "t", "V0.5_feasibility", workDir, 1, "A", 2, "R", 3, "P"); err != nil { t.Fatal(err) } rev := docrevision.NewService(nil) rev.SetDeliverableStore(workspace.DirDeliverable{}) if _, err := rev.OpenTemplateVersion(ctx, docID, body, docrevision.ActorSnapshot{}, 7, workDir); err != nil { t.Fatal(err) } svc := &Service{ Gov: gov, Rev: rev, RepoRoot: "", Authorize: func(context.Context, Principal, *docgov.DocumentRecord) error { return nil }, } return svc, gov, rev } func TestGetServesLedgerWhenProjectionMissing(t *testing.T) { ctx := context.Background() dir := t.TempDir() svc, _, _ := setupLedger(t, "doc_q_missing", "# ledger\n", dir) view, err := svc.Get(ctx, Principal{UserID: 1}, DocRef{DocID: "doc_q_missing"}) if err != nil { t.Fatal(err) } if view.Markdown != "# ledger\n" || view.Consistency != ConsistencyMissing { t.Fatalf("%+v", view) } } func TestGetAlignedWhenProjectionMatches(t *testing.T) { ctx := context.Background() dir := t.TempDir() body := "# ledger\n" if err := os.WriteFile(filepath.Join(dir, "output.md"), []byte(body), 0o644); err != nil { t.Fatal(err) } svc, _, _ := setupLedger(t, "doc_q_aligned", body, dir) view, err := svc.Get(ctx, Principal{UserID: 1}, DocRef{DocID: "doc_q_aligned"}) if err != nil { t.Fatal(err) } if view.Consistency != ConsistencyAligned || view.Markdown != body { t.Fatalf("%+v", view) } } func TestGetDriftStillServesLedger(t *testing.T) { ctx := context.Background() dir := t.TempDir() if err := os.WriteFile(filepath.Join(dir, "output.md"), []byte("# file fork\n"), 0o644); err != nil { t.Fatal(err) } svc, _, _ := setupLedger(t, "doc_q_drift", "# ledger\n", dir) view, err := svc.Get(ctx, Principal{UserID: 1}, DocRef{DocID: "doc_q_drift"}) if err != nil { t.Fatal(err) } if view.Consistency != ConsistencyDrift || view.Markdown != "# ledger\n" { t.Fatalf("%+v", view) } } func TestGetEmptyProjectionStillServesLedger(t *testing.T) { ctx := context.Background() dir := t.TempDir() if err := os.WriteFile(filepath.Join(dir, "output.md"), []byte{}, 0o644); err != nil { t.Fatal(err) } svc, _, _ := setupLedger(t, "doc_q_empty", "# ledger\n", dir) view, err := svc.Get(ctx, Principal{UserID: 1}, DocRef{DocID: "doc_q_empty"}) if err != nil { t.Fatal(err) } if view.Consistency != ConsistencyEmpty || view.Markdown != "# ledger\n" { t.Fatalf("%+v", view) } } func TestGetLedgerMissing(t *testing.T) { ctx := context.Background() gov := docgov.NewGovernanceEngine(nil) if _, err := gov.CreateOrGetDocument(ctx, "doc_q_noleger", 7, "intake", "t", "V0.5", "", 1, "A", 2, "R", 3, "P"); err != nil { t.Fatal(err) } svc := &Service{Gov: gov, Rev: docrevision.NewService(nil), Authorize: func(context.Context, Principal, *docgov.DocumentRecord) error { return nil }} _, err := svc.Get(ctx, Principal{UserID: 1}, DocRef{DocID: "doc_q_noleger"}) if err != ErrLedgerMissing { t.Fatalf("got %v", err) } } func TestGetIsReadOnly(t *testing.T) { ctx := context.Background() dir := t.TempDir() svc, _, _ := setupLedger(t, "doc_q_ro", "# ledger\n", dir) before, err := os.ReadDir(dir) if err != nil { t.Fatal(err) } if _, err := svc.Get(ctx, Principal{UserID: 1}, DocRef{DocID: "doc_q_ro"}); err != nil { t.Fatal(err) } after, err := os.ReadDir(dir) if err != nil { t.Fatal(err) } if len(after) != len(before) { t.Fatalf("GET wrote files: before=%d after=%d", len(before), len(after)) } } func TestGetFailClosed(t *testing.T) { ctx := context.Background() svc, _, _ := setupLedger(t, "doc_q_deny", "# ledger\n", t.TempDir()) if _, err := svc.Get(ctx, Principal{}, DocRef{DocID: "doc_q_deny"}); err != ErrClosed { t.Fatalf("anon: %v", err) } svc.Authorize = func(context.Context, Principal, *docgov.DocumentRecord) error { return ErrClosed } if _, err := svc.Get(ctx, Principal{UserID: 99}, DocRef{DocID: "doc_q_deny"}); err != ErrClosed { t.Fatalf("denied: %v", err) } if _, err := svc.Get(ctx, Principal{UserID: 1}, DocRef{DocID: "doc_missing"}); err != ErrClosed { t.Fatalf("unknown: %v", err) } } func TestBatchGetLimitsAndCrossProject(t *testing.T) { ctx := context.Background() svc, gov, _ := setupLedger(t, "doc_q_b1", "# a\n", t.TempDir()) if _, err := gov.CreateOrGetDocument(ctx, "doc_q_b2", 8, "intake", "t", "V0.5", "", 1, "A", 2, "R", 3, "P"); err != nil { t.Fatal(err) } refs := make([]DocRef, 101) for i := range refs { refs[i] = DocRef{DocID: "doc_q_b1"} } if _, err := svc.BatchGet(ctx, Principal{UserID: 1}, refs); err != ErrBatchLimit { t.Fatalf("101: %v", err) } if _, err := svc.BatchGet(ctx, Principal{UserID: 1}, []DocRef{{DocID: "doc_q_b1"}, {DocID: "doc_q_b2"}}); err != ErrCrossProject { t.Fatalf("cross project: %v", err) } got, err := svc.BatchGet(ctx, Principal{UserID: 1}, []DocRef{{DocID: "doc_q_b1"}, {DocID: "doc_q_b1"}}) if err != nil || len(got) != 1 { t.Fatalf("dedupe: %v n=%d", err, len(got)) } } func TestFavDegradedReadDefaultOff(t *testing.T) { if FavDegradedReadEnabled() { t.Fatal("FAV_DEGRADED_READ must default false") } } type countingVault struct { calls int err error } func (v *countingVault) ReadVerified(context.Context, int64, string) (string, string, error) { v.calls++ if v.err != nil { return "", "", v.err } return "# from vault\n", "abc", nil } func TestFavDegradedReadOnlyAfterAuthorize(t *testing.T) { ctx := context.Background() gov := docgov.NewGovernanceEngine(nil) _, _ = gov.CreateOrGetDocument(ctx, "doc_fav", 9, "intake", "t", "V0.5_feasibility", "", 1, "A", 2, "R", 3, "P") vault := &countingVault{err: errors.New("artifactvault: union-deny")} svc := &Service{ Gov: gov, FavDegradedRead: true, Vault: vault, Authorize: func(context.Context, Principal, *docgov.DocumentRecord) error { return ErrClosed }, } if _, err := svc.Get(ctx, Principal{UserID: 1}, DocRef{DocID: "doc_fav"}); err != ErrClosed { t.Fatalf("auth must win: %v", err) } if vault.calls != 0 { t.Fatal("vault must not run before authorization") } svc.Authorize = func(context.Context, Principal, *docgov.DocumentRecord) error { return nil } if _, err := svc.Get(ctx, Principal{UserID: 1}, DocRef{DocID: "doc_fav"}); err == nil { t.Fatal("union-deny must fail closed") } if vault.calls != 1 { t.Fatalf("calls=%d", vault.calls) } }