package testutil import ( "context" "path/filepath" "strings" "testing" "sub2api-cn-relay-manager/internal/store/sqlite" ) func TestSQLiteTestDSN(t *testing.T) { tests := []struct { name string fileName string disableForeignKeys bool wantContains []string }{ { name: "with foreign keys enabled", fileName: "test.db", disableForeignKeys: false, wantContains: []string{ "file:", "test.db", "_busy_timeout=5000", }, }, { name: "with foreign keys disabled", fileName: "test_fk.db", disableForeignKeys: true, wantContains: []string{ "file:", "test_fk.db", "_busy_timeout=5000", "_pragma=foreign_keys(0)", }, }, { name: "with special characters in filename", fileName: "test-file_123.db", disableForeignKeys: false, wantContains: []string{ "test-file_123.db", }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { dsn := SQLiteTestDSN(t, tt.fileName, tt.disableForeignKeys) for _, want := range tt.wantContains { if !strings.Contains(dsn, want) { t.Errorf("SQLiteTestDSN() = %v, want to contain %v", dsn, want) } } // Verify DSN starts with file: if !strings.HasPrefix(dsn, "file:") { t.Errorf("SQLiteTestDSN() = %v, want to start with 'file:'", dsn) } }) } } func TestSQLiteTestDSN_CreatesUniquePaths(t *testing.T) { dsn1 := SQLiteTestDSN(t, "test.db", false) dsn2 := SQLiteTestDSN(t, "test.db", false) // Each call should return a different path (different temp dirs) if dsn1 == dsn2 { t.Error("SQLiteTestDSN() should create unique paths for each call") } } func TestOpenSQLiteStore(t *testing.T) { dsn := SQLiteTestDSN(t, "open_test.db", false) store := OpenSQLiteStore(t, dsn) if store == nil { t.Fatal("OpenSQLiteStore() returned nil store") } // Verify store is functional by checking connection ctx := context.Background() if err := store.SQLDB().PingContext(ctx); err != nil { t.Errorf("store.SQLDB().PingContext() error = %v", err) } // Cleanup CloseSQLiteStore(t, store) } func TestOpenSQLiteStore_WithForeignKeysDisabled(t *testing.T) { dsn := SQLiteTestDSN(t, "open_test_nofk.db", true) store := OpenSQLiteStore(t, dsn) if store == nil { t.Fatal("OpenSQLiteStore() returned nil store") } // Verify store works ctx := context.Background() if err := store.SQLDB().PingContext(ctx); err != nil { t.Errorf("store.SQLDB().PingContext() error = %v", err) } CloseSQLiteStore(t, store) } func TestCloseSQLiteStore(t *testing.T) { dsn := SQLiteTestDSN(t, "close_test.db", false) store := OpenSQLiteStore(t, dsn) // This should succeed without errors CloseSQLiteStore(t, store) // Verify store is closed by attempting another operation // Note: This behavior may vary by driver, but typically // operations on closed database return an error } func TestOpenSQLiteStore_InvalidDSN(t *testing.T) { // Create a mock testing.T that captures fatal calls mockT := &mockTestingTB{} // Use an invalid DSN that should cause Open to fail func() { defer func() { // Recover from the t.Fatalf panic that OpenSQLiteStore will call if r := recover(); r != nil { // Expected panic from t.Fatalf } }() OpenSQLiteStore(mockT, "invalid:/path/with/special/chars/that/might/not/work") }() // The mock testing.TB should have recorded the fatal call if !mockT.fatalfCalled { t.Error("OpenSQLiteStore() should call t.Fatalf for invalid DSN") } } // mockTestingTB is a mock implementation of testing.TB for testing failure cases type mockTestingTB struct { testing.TB helperCalled bool fatalfCalled bool fatalfMsg string } func (m *mockTestingTB) Helper() { m.helperCalled = true } func (m *mockTestingTB) Fatalf(format string, args ...interface{}) { m.fatalfCalled = true m.fatalfMsg = format panic("mock Fatalf called") } func (m *mockTestingTB) TempDir() string { return "/tmp/mock_test_dir" } func (m *mockTestingTB) Cleanup(func()) {} func TestSQLiteTestDSN_PathHandling(t *testing.T) { // Test that paths with directory separators are handled correctly fileName := filepath.Join("subdir", "deep", "test.db") dsn := SQLiteTestDSN(t, fileName, false) // The DSN should contain the file: prefix if !strings.HasPrefix(dsn, "file:") { t.Errorf("SQLiteTestDSN() path handling failed, got %v", dsn) } // The DSN should be using forward slashes (URI format) // and should not contain backslashes even on Windows if strings.Contains(dsn, "\\") { t.Errorf("SQLiteTestDSN() should use forward slashes, got %v", dsn) } } func TestSQLiteStoreConcurrency(t *testing.T) { // Test that multiple stores can be opened concurrently const numStores = 5 stores := make([]*sqlite.DB, numStores) for i := 0; i < numStores; i++ { dsn := SQLiteTestDSN(t, "concurrent_test.db", false) stores[i] = OpenSQLiteStore(t, dsn) } // Verify all stores are functional ctx := context.Background() for i, store := range stores { if err := store.SQLDB().PingContext(ctx); err != nil { t.Errorf("store[%d].SQLDB().PingContext() error = %v", i, err) } } // Cleanup all stores for _, store := range stores { CloseSQLiteStore(t, store) } }