返回 DeepSeek-Reasonix
rebuild_resume_test.go
根目录 / internal / projectiondb / rebuild_resume_test.go
1 package projectiondb
2
3 import (
4 "context"
5 "database/sql"
6 "errors"
7 "os"
8 "path/filepath"
9 "testing"
10 )
11
12 func TestRebuildResumesCanceledGenerationWithoutPublishingPrefix(t *testing.T) {
13 for _, mode := range []string{"same-source", "replaced-source", "missing-fence", "failed-build"} {
14 t.Run(mode, func(t *testing.T) {
15 opts := OpenOptions{Path: filepath.Join(t.TempDir(), "cache.sqlite"), Migrations: testMigrations(), RequireDisk: true, ResumeKey: "first"}
16 old, err := Open(t.Context(), opts)
17 if err != nil {
18 t.Fatal(err)
19 }
20 if _, err := old.DB.Exec(`INSERT INTO values_table VALUES('old-published')`); err != nil {
21 t.Fatal(err)
22 }
23 if err := old.DB.Close(); err != nil {
24 t.Fatal(err)
25 }
26 ctx, cancel := context.WithCancel(t.Context())
27 err = Rebuild(ctx, opts, func(ctx context.Context, db *sql.DB) error {
28 if _, err := db.ExecContext(ctx, `INSERT INTO values_table VALUES('durable-prefix')`); err != nil {
29 return err
30 }
31 cancel()
32 return ctx.Err()
33 })
34 cancel()
35 if !errors.Is(err, context.Canceled) {
36 t.Fatalf("cancellation missing: %v", err)
37 }
38 old, err = Open(t.Context(), opts)
39 if err != nil {
40 t.Fatal(err)
41 }
42 var published string
43 err = old.DB.QueryRow(`SELECT value FROM values_table`).Scan(&published)
44 _ = old.DB.Close()
45 if err != nil || published != "old-published" {
46 t.Fatalf("partial generation became readable: %q %v", published, err)
47 }
48 if mode == "replaced-source" {
49 opts.ResumeKey = "second"
50 }
51 if mode == "missing-fence" {
52 pending := opts
53 pending.Path += ".rebuild-pending"
54 handle, err := Open(t.Context(), pending)
55 if err != nil {
56 t.Fatal(err)
57 }
58 _, err = handle.DB.Exec(`DELETE FROM projection_rebuild_resume`)
59 _ = handle.DB.Close()
60 if err != nil {
61 t.Fatal(err)
62 }
63 }
64 if mode == "failed-build" {
65 failure := errors.New("invalid source")
66 if err := Rebuild(t.Context(), opts, func(context.Context, *sql.DB) error { return failure }); !errors.Is(err, failure) {
67 t.Fatalf("source failure missing: %v", err)
68 }
69 if _, err := os.Stat(opts.Path + ".rebuild-pending"); !os.IsNotExist(err) {
70 t.Fatalf("failed generation retained: %v", err)
71 }
72 }
73 err = Rebuild(t.Context(), opts, func(ctx context.Context, db *sql.DB) error {
74 var count int
75 if err := db.QueryRowContext(ctx, `SELECT count(*) FROM values_table`).Scan(&count); err != nil {
76 return err
77 }
78 want := 1
79 if mode != "same-source" {
80 want = 0
81 }
82 if count != want {
83 t.Fatalf("resume key did not fence progress: %d want %d", count, want)
84 }
85 _, err := db.ExecContext(ctx, `INSERT INTO values_table VALUES('completed')`)
86 return err
87 })
88 if err != nil {
89 t.Fatal(err)
90 }
91 if _, err := os.Stat(opts.Path + ".rebuild-pending"); !os.IsNotExist(err) {
92 t.Fatalf("published generation left a staging database: %v", err)
93 }
94 })
95 }
96 }
97
97 lines GO