| new file mode 100644 |
| index 0000000..bf52cac |
| --- /dev/null |
| +++ b/internal/docs/content_maintenance_test.go |
| @@ -0,0 +1,96 @@ |
| +package docs |
| + |
| +import ( |
| + "bytes" |
| + "context" |
| + "encoding/base64" |
| + "errors" |
| + "os" |
| + "path/filepath" |
| + "testing" |
| + |
| + "github.com/carlosframework/rastrillo/db" |
| +) |
| + |
| +func TestContentMaintenanceRequiresExpectedKeyBeforeOpeningDatabase(t *testing.T) { |
| + path := filepath.Join(t.TempDir(), "missing.db") |
| + encoded := base64.StdEncoding.EncodeToString(bytes.Repeat([]byte{62}, 32)) |
| + c, err := newContentCipher(encoded, "production") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + for _, fingerprint := range []string{"", "incorrect"} { |
| + err := MaintainContent(context.Background(), ContentMaintenanceConfig{DBPath: path, ContentKey: encoded, ExpectedFingerprint: fingerprint}) |
| + if !errors.Is(err, errContentKey) { |
| + t.Fatalf("fingerprint %q: %v", fingerprint, err) |
| + } |
| + } |
| + err = MaintainContent(context.Background(), ContentMaintenanceConfig{DBPath: path, ContentKey: encoded, ExpectedFingerprint: c.fingerprint}) |
| + if !errors.Is(err, os.ErrNotExist) { |
| + t.Fatalf("missing database: %v", err) |
| + } |
| + if _, err := os.Stat(path); !errors.Is(err, os.ErrNotExist) { |
| + t.Fatalf("maintenance created missing database: %v", err) |
| + } |
| +} |
| + |
| +func TestContentMaintenancePreservesContentInRestoredCopy(t *testing.T) { |
| + ctx := context.Background() |
| + path := filepath.Join(t.TempDir(), "source.db") |
| + app, err := New(Config{Env: "development", DBPath: path}) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + doc, err := app.Store().Create(ctx, app.TestPerson(t, "owner@example.com"), "Restore") |
| + if err != nil { |
| + app.Close() |
| + t.Fatal(err) |
| + } |
| + if _, err := app.Store().ApplyUpdate(ctx, doc, app.TestActorFor(t, doc), validUpdate(t)); err != nil { |
| + app.Close() |
| + t.Fatal(err) |
| + } |
| + if err := app.Close(); err != nil { |
| + t.Fatal(err) |
| + } |
| + encoded := base64.StdEncoding.EncodeToString(bytes.Repeat([]byte{63}, 32)) |
| + c, err := newContentCipher(encoded, "production") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + cfg := ContentMaintenanceConfig{DBPath: path, ContentKey: encoded, ExpectedFingerprint: c.fingerprint, Scrub: true} |
| + if err := MaintainContent(ctx, cfg); err != nil { |
| + t.Fatal(err) |
| + } |
| + snapshot, err := os.ReadFile(path) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + restoredPath := filepath.Join(t.TempDir(), "restored.db") |
| + if err := os.WriteFile(restoredPath, snapshot, 0600); err != nil { |
| + t.Fatal(err) |
| + } |
| + cfg.DBPath = restoredPath |
| + if err := MaintainContent(ctx, cfg); err != nil { |
| + t.Fatalf("restored maintenance: %v", err) |
| + } |
| + d, err := db.Open(restoredPath, nil) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + defer d.Close() |
| + state, err := readContentSealing(ctx, d.G, c) |
| + if err != nil || state == nil || !state.Scrubbed { |
| + t.Fatalf("restored state: %#v, %v", state, err) |
| + } |
| + var row DocUpdate |
| + if err := d.G.Where("doc_id = ?", doc.ID).First(&row).Error; err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := c.openUpdate(doc.GUID, &row); err != nil { |
| + t.Fatal(err) |
| + } |
| + if !bytes.Equal(row.UpdateBin, validUpdate(t)) { |
| + t.Fatal("restored document changed") |
| + } |
| +} |