Files
Agent-System/internal/docquery/service_test.go
weichenandCursor c7e3e99303 feat(artifactvault): extract audited FAV with union-deny reads
Goose 00036 is a renumbered copy of the vault tables. Degraded read stays off by default and only runs after authorization; tombstones and unpublished artifacts fail closed.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-14 15:59:36 +00:00

206 lines
6.5 KiB
Go

package docquery
import (
"context"
"errors"
"os"
"path/filepath"
"testing"
"chat-agent-system/internal/docgov"
"chat-agent-system/internal/docrevision"
)
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)
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)
}
}