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>
206 lines
6.5 KiB
Go
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)
|
|
}
|
|
}
|