package hostbox import ( "bytes" "context" "database/sql" "github.com/carlosframework/platform/internal/hostguard" "os" "path/filepath" "testing" ) // A pool replacement must not open a different inode under a checked name. // Regression: the replaced database must remain untouched when a pool reopens. func TestPlan9JournalReplacementRemainsUntouched(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "launches.db") replacement := filepath.Join(dir, "unrelated.db") db, err := sql.Open("sqlite", replacement) if err != nil { t.Fatal(err) } if _, err = db.Exec("CREATE TABLE launches (identity TEXT PRIMARY KEY, body BLOB NOT NULL)"); err != nil { t.Fatal(err) } if err = db.Close(); err != nil { t.Fatal(err) } before, err := os.ReadFile(replacement) if err != nil { t.Fatal(err) } j, err := OpenLaunchJournal(path) if err != nil { t.Fatal(err) } defer j.Close() j.db.SetMaxIdleConns(0) if err := os.Rename(path, path+".pinned"); err != nil { t.Fatal(err) } if err := os.Rename(replacement, path); err != nil { t.Fatal(err) } in := LaunchInventory{Target: hostguard.FenceTarget{Host: "app.example", Incarnation: "inc", Stamp: 1, Session: 1, Box: "box", Enrollment: "enrollment", BootID: "boot", ControllerID: "controller"}, ControllerPID: 1, ProcessStart: "1", NoState: true, Groups: []LaunchGroup{{RelativePath: "session", Device: 1, Inode: 2}}} writeErr := j.Create(context.Background(), in) after, err := os.ReadFile(path) if err != nil { t.Fatal(err) } if !bytes.Equal(before, after) { t.Fatalf("replacement database modified: write error=%v", writeErr) } if writeErr == nil { t.Fatal("replacement inode accepted") } }