package data import ( "io" "log/slog" "testing" "time" "github.com/glebarez/sqlite" "gorm.io/gorm" ) func openDataScopeAuditTestDB(t *testing.T, name string) *gorm.DB { t.Helper() db, err := gorm.Open(sqlite.Open("file:"+name+"?mode=memory&cache=shared"), &gorm.Config{}) if err != nil { t.Fatal(err) } if err = db.AutoMigrate(&dataAccessLogPO{}); err != nil { t.Fatal(err) } // The in-memory SQLite URI is intentionally named so the audit writer can // use a second GORM handle. Clear rows left by a prior -count iteration so // each test starts with an isolated logical database. if err = db.Unscoped().Where("1 = 1").Delete(&dataAccessLogPO{}).Error; err != nil { t.Fatal(err) } return db } func waitForDataScopeAuditCount(t *testing.T, db *gorm.DB, want int64) { t.Helper() deadline := time.Now().Add(time.Second) for time.Now().Before(deadline) { var count int64 if err := db.Model(&dataAccessLogPO{}).Count(&count).Error; err != nil { t.Fatal(err) } if count == want { return } time.Sleep(5 * time.Millisecond) } t.Fatalf("data access log count did not reach %d", want) } func TestDataScopeAuditWriterFlushesBatch(t *testing.T) { db := openDataScopeAuditTestDB(t, "data-scope-audit-batch") d := &Data{gormDB: newReloadableDB(db, nil)} w := newDataScopeAuditWriterWithOptions(d, slog.New(slog.NewTextHandler(io.Discard, nil)), 8, 2, time.Hour) d.auditLog = w t.Cleanup(w.Close) d.enqueueDataScopeAudit(dataAccessLogPO{EventType: "no_identity", TargetTable: "example"}) d.enqueueDataScopeAudit(dataAccessLogPO{EventType: "blocked_write", TargetTable: "example"}) waitForDataScopeAuditCount(t, db, 2) } func TestDataScopeAuditWriterUsesReloadedDatabase(t *testing.T) { first := openDataScopeAuditTestDB(t, "data-scope-audit-first") second := openDataScopeAuditTestDB(t, "data-scope-audit-second") d := &Data{gormDB: newReloadableDB(first, nil)} w := newDataScopeAuditWriterWithOptions(d, slog.New(slog.NewTextHandler(io.Discard, nil)), 8, 100, 10*time.Millisecond) d.auditLog = w t.Cleanup(w.Close) d.enqueueDataScopeAudit(dataAccessLogPO{EventType: "no_identity", TargetTable: "example"}) d.gormDB.replace(second) waitForDataScopeAuditCount(t, second, 1) var firstCount int64 if err := first.Model(&dataAccessLogPO{}).Count(&firstCount).Error; err != nil { t.Fatal(err) } if firstCount != 0 { t.Fatalf("audit log was written to retired database: %d", firstCount) } }