(t *testing.T)
| 279 | } |
| 280 | |
| 281 | func TestVFSFile_PendingIndexReplacementRemovesStalePages(t *testing.T) { |
| 282 | client := newMockReplicaClient() |
| 283 | client.addFixture(t, buildLTXFixtureWithPages(t, 1, 4096, []uint32{1, 2, 3, 4}, 'a')) |
| 284 | |
| 285 | f := NewVFSFile(client, "pending-replace.db", slog.Default()) |
| 286 | if err := f.Open(); err != nil { |
| 287 | t.Fatalf("open vfs file: %v", err) |
| 288 | } |
| 289 | |
| 290 | if err := f.Lock(sqlite3vfs.LockShared); err != nil { |
| 291 | t.Fatalf("lock shared: %v", err) |
| 292 | } |
| 293 | |
| 294 | client.addFixture(t, buildLTXFixtureWithPages(t, 2, 4096, []uint32{1, 2}, 'b')) |
| 295 | if err := f.pollReplicaClient(context.Background()); err != nil { |
| 296 | t.Fatalf("poll replica: %v", err) |
| 297 | } |
| 298 | |
| 299 | f.mu.Lock() |
| 300 | if _, ok := f.index[4]; !ok { |
| 301 | t.Fatalf("expected stale page to remain in main index while lock is held") |
| 302 | } |
| 303 | if !f.pendingReplace { |
| 304 | t.Fatalf("expected pending replacement flag set") |
| 305 | } |
| 306 | f.mu.Unlock() |
| 307 | |
| 308 | if err := f.Unlock(sqlite3vfs.LockNone); err != nil { |
| 309 | t.Fatalf("unlock: %v", err) |
| 310 | } |
| 311 | |
| 312 | size, err := f.FileSize() |
| 313 | if err != nil { |
| 314 | t.Fatalf("file size: %v", err) |
| 315 | } |
| 316 | if size != int64(2*4096) { |
| 317 | t.Fatalf("unexpected file size after pending replacement applied: got %d want %d", size, 2*4096) |
| 318 | } |
| 319 | |
| 320 | buf := make([]byte, 4096) |
| 321 | lockOffset := int64(3-1) * 4096 |
| 322 | if _, err := f.ReadAt(buf, lockOffset); err == nil || !strings.Contains(err.Error(), "page not found") { |
| 323 | t.Fatalf("expected missing page after pending replacement applied, got %v", err) |
| 324 | } |
| 325 | } |
| 326 | |
| 327 | func TestVFSFile_CorruptedPageIndexRecovery(t *testing.T) { |
| 328 | client := newMockReplicaClient() |
nothing calls this directly
no test coverage detected