From 13e814acda04ce1ac90b9b71e11711e12ef54f9b Mon Sep 17 00:00:00 2001 From: Damir Mukimov Date: Fri, 26 Dec 2025 15:46:28 +0100 Subject: [PATCH] Update test setup to use SetupTestDBWithTestcontainersForGinkgo for improved compatibility - Replace instances of SetupTestDBWithTestcontainers with SetupTestDBWithTestcontainersForGinkgo in test files for better integration with Ginkgo - Ensure consistent test database setup across various handlers to enhance test isolation and reliability --- bugulma/backend/TESTING.md | 2 +- .../internal/handler/auth_handler_test.go | 4 +- .../internal/handler/heritage_handler_test.go | 2 +- .../internal/handler/matching_handler_test.go | 2 +- .../handler/organization_handler_test.go | 2 +- .../handler/resource_flow_handler_test.go | 2 +- .../internal/handler/site_handler_test.go | 2 +- bugulma/backend/internal/testutils/db.go | 118 +++++++++++++++++- 8 files changed, 123 insertions(+), 11 deletions(-) diff --git a/bugulma/backend/TESTING.md b/bugulma/backend/TESTING.md index 0e67628..19ce173 100644 --- a/bugulma/backend/TESTING.md +++ b/bugulma/backend/TESTING.md @@ -114,7 +114,7 @@ func TestMyFeature(t *testing.T) { BeforeEach(func() { // Setup PostgreSQL test database with testcontainers // Each test gets its own isolated PostgreSQL container - db = testutils.SetupTestDBWithTestcontainers(GinkgoT()) + db = testutils.SetupTestDBWithTestcontainersForGinkgo(GinkgoT()) // Initialize repositories/services repo = repository.NewMyRepository(db) diff --git a/bugulma/backend/internal/handler/auth_handler_test.go b/bugulma/backend/internal/handler/auth_handler_test.go index 8f5953c..3848657 100644 --- a/bugulma/backend/internal/handler/auth_handler_test.go +++ b/bugulma/backend/internal/handler/auth_handler_test.go @@ -32,8 +32,8 @@ var _ = Describe("AuthHandler", func() { BeforeEach(func() { gin.SetMode(gin.TestMode) - // Setup PostgreSQL test database using pgtestdb - db = testutils.SetupTestDBWithTestcontainers(GinkgoT()) + // Setup PostgreSQL test database using testcontainers + db = testutils.SetupTestDBWithTestcontainersForGinkgo(GinkgoT()) userRepo = repository.NewUserRepository(db) authService = service.NewAuthService(userRepo, "secret-key") diff --git a/bugulma/backend/internal/handler/heritage_handler_test.go b/bugulma/backend/internal/handler/heritage_handler_test.go index bf5e1ac..b4abac9 100644 --- a/bugulma/backend/internal/handler/heritage_handler_test.go +++ b/bugulma/backend/internal/handler/heritage_handler_test.go @@ -33,7 +33,7 @@ var _ = Describe("HeritageHandler", func() { gin.SetMode(gin.TestMode) // Setup PostgreSQL test database - db = testutils.SetupTestDBWithTestcontainers(GinkgoT()) + db = testutils.SetupTestDBWithTestcontainersForGinkgo(GinkgoT()) heritageRepo = repository.NewHeritageRepository(db) heritageHandler = handler.NewHeritageHandler(heritageRepo) diff --git a/bugulma/backend/internal/handler/matching_handler_test.go b/bugulma/backend/internal/handler/matching_handler_test.go index e00693a..945e619 100644 --- a/bugulma/backend/internal/handler/matching_handler_test.go +++ b/bugulma/backend/internal/handler/matching_handler_test.go @@ -41,7 +41,7 @@ var _ = Describe("MatchingHandler", func() { // Setup PostgreSQL test database using pgtestdb // Each test gets an isolated database with migrations already applied - db = testutils.SetupTestDBWithTestcontainers(GinkgoT()) + db = testutils.SetupTestDBWithTestcontainersForGinkgo(GinkgoT()) matchRepo = repository.NewMatchRepository(db) resourceRepo = repository.NewResourceFlowRepository(db) diff --git a/bugulma/backend/internal/handler/organization_handler_test.go b/bugulma/backend/internal/handler/organization_handler_test.go index 5db331f..2fee427 100644 --- a/bugulma/backend/internal/handler/organization_handler_test.go +++ b/bugulma/backend/internal/handler/organization_handler_test.go @@ -33,7 +33,7 @@ var _ = Describe("OrganizationHandler", func() { gin.SetMode(gin.TestMode) // Setup PostgreSQL test database using pgtestdb - db = testutils.SetupTestDBWithTestcontainers(GinkgoT()) + db = testutils.SetupTestDBWithTestcontainersForGinkgo(GinkgoT()) orgRepo = repository.NewOrganizationRepository(db) orgService = service.NewOrganizationService(orgRepo, nil) // No graph repo for tests diff --git a/bugulma/backend/internal/handler/resource_flow_handler_test.go b/bugulma/backend/internal/handler/resource_flow_handler_test.go index 074ef1c..582dce0 100644 --- a/bugulma/backend/internal/handler/resource_flow_handler_test.go +++ b/bugulma/backend/internal/handler/resource_flow_handler_test.go @@ -36,7 +36,7 @@ var _ = Describe("ResourceFlowHandler", func() { gin.SetMode(gin.TestMode) // Setup PostgreSQL test database using pgtestdb - db = testutils.SetupTestDBWithTestcontainers(GinkgoT()) + db = testutils.SetupTestDBWithTestcontainersForGinkgo(GinkgoT()) resourceRepo = repository.NewResourceFlowRepository(db) organizationRepo = repository.NewOrganizationRepository(db) diff --git a/bugulma/backend/internal/handler/site_handler_test.go b/bugulma/backend/internal/handler/site_handler_test.go index fb836ba..ce1f57c 100644 --- a/bugulma/backend/internal/handler/site_handler_test.go +++ b/bugulma/backend/internal/handler/site_handler_test.go @@ -34,7 +34,7 @@ var _ = Describe("SiteHandler", func() { gin.SetMode(gin.TestMode) // Setup PostgreSQL test database using pgtestdb - db = testutils.SetupTestDBWithTestcontainers(GinkgoT()) + db = testutils.SetupTestDBWithTestcontainersForGinkgo(GinkgoT()) siteRepo = repository.NewSiteRepository(db) organizationRepo = repository.NewOrganizationRepository(db) diff --git a/bugulma/backend/internal/testutils/db.go b/bugulma/backend/internal/testutils/db.go index c9be6ba..a9ceb82 100644 --- a/bugulma/backend/internal/testutils/db.go +++ b/bugulma/backend/internal/testutils/db.go @@ -6,6 +6,7 @@ import ( "database/sql" "encoding/hex" "fmt" + "io" "os" "strings" "syscall" @@ -296,8 +297,8 @@ func getEnv(key, defaultValue string) string { return defaultValue } -// ginkgoTBWrapper wraps Ginkgo's FullGinkgoTInterface to work with pgtestdb -// pgtestdb requires a testing.TB interface, but Ginkgo provides a different interface +// ginkgoTBWrapper wraps Ginkgo's FullGinkgoTInterface to work with testing.TB +// This wrapper adapts Ginkgo's interface to the testing.TB interface used by testcontainers type ginkgoTBWrapper struct { t interface { Helper() @@ -309,13 +310,76 @@ type ginkgoTBWrapper struct { } func (w *ginkgoTBWrapper) Helper() { w.t.Helper() } +func (w *ginkgoTBWrapper) Log(args ...interface{}) { w.t.Logf(fmt.Sprint(args...)) } func (w *ginkgoTBWrapper) Logf(format string, args ...interface{}) { w.t.Logf(format, args...) } func (w *ginkgoTBWrapper) Fatalf(format string, args ...interface{}) { w.t.Fatalf(format, args...) } func (w *ginkgoTBWrapper) Failed() bool { return w.t.Failed() } func (w *ginkgoTBWrapper) Cleanup(fn func()) { w.cleanups = append(w.cleanups, fn) } +func (w *ginkgoTBWrapper) Errorf(format string, args ...interface{}) { w.t.Fatalf(format, args...) } +func (w *ginkgoTBWrapper) Skip(args ...interface{}) { + w.t.Logf("SKIP: " + fmt.Sprint(args...)) +} func (w *ginkgoTBWrapper) Skipf(format string, args ...interface{}) { w.t.Logf("SKIP: "+format, args...) } +func (w *ginkgoTBWrapper) SkipNow() { + w.t.Logf("SKIP: test skipped") +} +func (w *ginkgoTBWrapper) TempDir() string { + // For testcontainers, we don't need a temp dir, return empty string + return "" +} +func (w *ginkgoTBWrapper) Name() string { + return "GinkgoTest" +} +func (w *ginkgoTBWrapper) Setenv(key, value string) { + // Not needed for our use case +} +func (w *ginkgoTBWrapper) Parallel() { + // Not needed for our use case +} +func (w *ginkgoTBWrapper) Run(name string, f func(t *testing.T)) bool { + // Not needed for our use case + return true +} +func (w *ginkgoTBWrapper) Deadline() (deadline time.Time, ok bool) { + return time.Time{}, false +} + +// Additional testing.TB methods +func (w *ginkgoTBWrapper) Attr(key, value string) { + // Not needed for our use case +} +func (w *ginkgoTBWrapper) Error(args ...interface{}) { + w.t.Fatalf(fmt.Sprint(args...)) +} +func (w *ginkgoTBWrapper) Fatal(args ...interface{}) { + w.t.Fatalf(fmt.Sprint(args...)) +} +func (w *ginkgoTBWrapper) Fail() { + w.t.Fatalf("test failed") +} +func (w *ginkgoTBWrapper) FailNow() { + w.t.Fatalf("test failed") +} +func (w *ginkgoTBWrapper) Chdir(dir string) { + // Not needed for our use case +} +func (w *ginkgoTBWrapper) Skipped() bool { + return false +} +func (w *ginkgoTBWrapper) Context() context.Context { + return context.Background() +} +func (w *ginkgoTBWrapper) Output() io.Writer { + // Return a dummy writer + return &dummyWriter{} +} + +// dummyWriter implements io.Writer for testing +type dummyWriter struct{} + +func (d *dummyWriter) Write(p []byte) (n int, err error) { return len(p), nil } // SetupTestDB creates an isolated PostgreSQL database for testing using pgtestdb // DEPRECATED: Use SetupTestDBWithTestcontainers() for better isolation and no local PostgreSQL requirement @@ -464,6 +528,16 @@ func setupTestDBWithTB(t interface { return gormDB } +// testTB is a minimal interface that testcontainers can use +type testTB interface { + Helper() + Cleanup(func()) + Logf(format string, args ...interface{}) + Fatalf(format string, args ...interface{}) + Failed() bool + Name() string +} + // SetupTestDBWithTestcontainers creates an isolated PostgreSQL database for testing using testcontainers // This spins up a PostgreSQL container for each test, providing perfect isolation // Recommended for CI environments where Docker is available @@ -474,7 +548,7 @@ func setupTestDBWithTB(t interface { // db := testutils.SetupTestDBWithTestcontainers(t) // // Your test code here - will have its own PostgreSQL container // } -func SetupTestDBWithTestcontainers(t testing.TB) *gorm.DB { +func SetupTestDBWithTestcontainers(t testTB) *gorm.DB { t.Helper() ctx := context.Background() @@ -593,6 +667,44 @@ func cleanupGeometryColumns(db *gorm.DB) error { return nil } +// SetupTestDBWithTestcontainersForGinkgo creates an isolated PostgreSQL database for Ginkgo tests using testcontainers +// Use this function in Ginkgo BeforeEach blocks when Docker is available +// +// Example usage: +// +// BeforeEach(func() { +// db = testutils.SetupTestDBWithTestcontainersForGinkgo(GinkgoT()) +// repo = repository.NewMyRepository(db) +// }) +func SetupTestDBWithTestcontainersForGinkgo(ginkgoT interface { + Helper() + Logf(format string, args ...interface{}) + Fatalf(format string, args ...interface{}) + Failed() bool +}) *gorm.DB { + // Create a minimal testing.TB implementation + tb := &minimalTB{ginkgoT: ginkgoT} + return SetupTestDBWithTestcontainers(tb) +} + +// minimalTB provides a minimal implementation of testTB for testcontainers +type minimalTB struct { + ginkgoT interface { + Helper() + Logf(format string, args ...interface{}) + Fatalf(format string, args ...interface{}) + Failed() bool + } + cleanups []func() +} + +func (m *minimalTB) Helper() { m.ginkgoT.Helper() } +func (m *minimalTB) Cleanup(fn func()) { m.cleanups = append(m.cleanups, fn) } +func (m *minimalTB) Logf(format string, args ...interface{}) { m.ginkgoT.Logf(format, args...) } +func (m *minimalTB) Fatalf(format string, args ...interface{}) { m.ginkgoT.Fatalf(format, args...) } +func (m *minimalTB) Failed() bool { return m.ginkgoT.Failed() } +func (m *minimalTB) Name() string { return "GinkgoTest" } + // getFreeDiskBytes returns number of free bytes available on the filesystem // containing the given path. Returns error if unable to determine. func getFreeDiskBytes(path string) (uint64, error) {