diff --git a/server/Makefile b/server/Makefile index 28dbbc0772..6256cfc242 100644 --- a/server/Makefile +++ b/server/Makefile @@ -19,7 +19,7 @@ init-submodule: git submodule update test: # run go tests for this application - go test -timeout 90s -coverprofile=coverage.out ./... + go test -timeout 150s -coverprofile=coverage.out ./... vet: ## run vet tool to analyze the code for suspicious, abnormal, or useless code go vet -structtag=false ./... diff --git a/server/config/configresource/config_resource_test.go b/server/config/configresource/config_resource_test.go index 2a2ef36d93..aeee3f1d5b 100644 --- a/server/config/configresource/config_resource_test.go +++ b/server/config/configresource/config_resource_test.go @@ -38,9 +38,8 @@ func TestPublishing(t *testing.T) { publisher.On("Publish", configresource.ResourceID, updated) - db := testmock.MustGetRawTestingDatabase() repo := configresource.NewRepository( - testmock.MustCreateRandomMigratedDatabase(db), + testmock.CreateMigratedDatabase(), configresource.WithPublisher(publisher), ) @@ -52,13 +51,12 @@ func TestPublishing(t *testing.T) { } func TestIsAnalyticsEnabled(t *testing.T) { - db := testmock.MustGetRawTestingDatabase() t.Run("DefaultValues", func(t *testing.T) { restore := cleanEnv() defer restore() repo := configresource.NewRepository( - testmock.MustCreateRandomMigratedDatabase(db), + testmock.CreateMigratedDatabase(), ) cfg := repo.Current(context.TODO()) @@ -70,7 +68,7 @@ func TestIsAnalyticsEnabled(t *testing.T) { restore := cleanEnv() defer restore() repo := configresource.NewRepository( - testmock.MustCreateRandomMigratedDatabase(db), + testmock.CreateMigratedDatabase(), ) repo.Update(context.TODO(), configresource.Config{ AnalyticsEnabled: false, @@ -84,7 +82,7 @@ func TestIsAnalyticsEnabled(t *testing.T) { restore := cleanEnv() defer restore() repo := configresource.NewRepository( - testmock.MustCreateRandomMigratedDatabase(db), + testmock.CreateMigratedDatabase(), ) repo.Update(context.TODO(), configresource.Config{ AnalyticsEnabled: true, @@ -99,14 +97,11 @@ func TestIsAnalyticsEnabled(t *testing.T) { } func TestConfigResource(t *testing.T) { - - db := testmock.MustGetRawTestingDatabase() - rmtests.TestResourceType(t, rmtests.ResourceTypeTest{ ResourceTypeSingular: configresource.ResourceName, ResourceTypePlural: configresource.ResourceNamePlural, RegisterManagerFn: func(router *mux.Router) resourcemanager.Manager { - db := testmock.MustCreateRandomMigratedDatabase(db) + db := testmock.CreateMigratedDatabase() configRepo := configresource.NewRepository(db) manager := resourcemanager.New[configresource.Config]( diff --git a/server/config/configresource/main_test.go b/server/config/configresource/main_test.go new file mode 100644 index 0000000000..0cd8821da4 --- /dev/null +++ b/server/config/configresource/main_test.go @@ -0,0 +1,18 @@ +package configresource_test + +import ( + "os" + "testing" + + "github.com/kubeshop/tracetest/server/testmock" +) + +func TestMain(m *testing.M) { + testmock.StartTestEnvironment() + + exitVal := m.Run() + + testmock.StopTestEnvironment() + + os.Exit(exitVal) +} diff --git a/server/config/demoresource/demo_resource_test.go b/server/config/demoresource/demo_resource_test.go index 1ba32f9cc7..c6a971bc83 100644 --- a/server/config/demoresource/demo_resource_test.go +++ b/server/config/demoresource/demo_resource_test.go @@ -13,7 +13,6 @@ import ( ) func TestPokeshopDemoResource(t *testing.T) { - db := testmock.MustGetRawTestingDatabase() sampleDemo := demoresource.Demo{ ID: "1", Name: "dev", @@ -51,7 +50,7 @@ func TestPokeshopDemoResource(t *testing.T) { ResourceTypeSingular: demoresource.ResourceName, ResourceTypePlural: demoresource.ResourceNamePlural, RegisterManagerFn: func(router *mux.Router) resourcemanager.Manager { - db := testmock.MustCreateRandomMigratedDatabase(db) + db := testmock.CreateMigratedDatabase() demoRepository := demoresource.NewRepository(db) manager := resourcemanager.New[demoresource.Demo]( @@ -108,7 +107,6 @@ func TestPokeshopDemoResource(t *testing.T) { } func TestOpenTelemetryStoreDemoResource(t *testing.T) { - db := testmock.MustGetRawTestingDatabase() sampleDemo := demoresource.Demo{ ID: "1", Name: "dev", @@ -152,7 +150,7 @@ func TestOpenTelemetryStoreDemoResource(t *testing.T) { ResourceTypeSingular: demoresource.ResourceName, ResourceTypePlural: demoresource.ResourceNamePlural, RegisterManagerFn: func(router *mux.Router) resourcemanager.Manager { - db := testmock.MustCreateRandomMigratedDatabase(db) + db := testmock.CreateMigratedDatabase() demoRepository := demoresource.NewRepository(db) manager := resourcemanager.New[demoresource.Demo]( diff --git a/server/config/demoresource/main_test.go b/server/config/demoresource/main_test.go new file mode 100644 index 0000000000..a423a1156a --- /dev/null +++ b/server/config/demoresource/main_test.go @@ -0,0 +1,18 @@ +package demoresource_test + +import ( + "os" + "testing" + + "github.com/kubeshop/tracetest/server/testmock" +) + +func TestMain(m *testing.M) { + testmock.StartTestEnvironment() + + exitVal := m.Run() + + testmock.StopTestEnvironment() + + os.Exit(exitVal) +} diff --git a/server/executor/main_test.go b/server/executor/main_test.go new file mode 100644 index 0000000000..b4db43fdec --- /dev/null +++ b/server/executor/main_test.go @@ -0,0 +1,18 @@ +package executor_test + +import ( + "os" + "testing" + + "github.com/kubeshop/tracetest/server/testmock" +) + +func TestMain(m *testing.M) { + testmock.StartTestEnvironment() + + exitVal := m.Run() + + testmock.StopTestEnvironment() + + os.Exit(exitVal) +} diff --git a/server/executor/pollingprofile/main_test.go b/server/executor/pollingprofile/main_test.go new file mode 100644 index 0000000000..1f8fdee506 --- /dev/null +++ b/server/executor/pollingprofile/main_test.go @@ -0,0 +1,18 @@ +package pollingprofile_test + +import ( + "os" + "testing" + + "github.com/kubeshop/tracetest/server/testmock" +) + +func TestMain(m *testing.M) { + testmock.StartTestEnvironment() + + exitVal := m.Run() + + testmock.StopTestEnvironment() + + os.Exit(exitVal) +} diff --git a/server/executor/pollingprofile/polling_profile_resource_test.go b/server/executor/pollingprofile/polling_profile_resource_test.go index c27d2ec7fe..16b5209c56 100644 --- a/server/executor/pollingprofile/polling_profile_resource_test.go +++ b/server/executor/pollingprofile/polling_profile_resource_test.go @@ -12,23 +12,11 @@ import ( ) func TestPollingProfileResource(t *testing.T) { - db := testmock.MustGetRawTestingDatabase() - // sampleProfile := pollingprofile.PollingProfile{ - // ID: "1", - // Name: "test", - // Default: true, - // Strategy: pollingprofile.Periodic, - // Periodic: &pollingprofile.PeriodicPollingConfig{ - // RetryDelay: "10s", - // Timeout: "30m", - // }, - // } - rmtests.TestResourceType(t, rmtests.ResourceTypeTest{ ResourceTypeSingular: pollingprofile.ResourceName, ResourceTypePlural: pollingprofile.ResourceNamePlural, RegisterManagerFn: func(router *mux.Router) resourcemanager.Manager { - db := testmock.MustCreateRandomMigratedDatabase(db) + db := testmock.CreateMigratedDatabase() pollingProfileRepo := pollingprofile.NewRepository(db) manager := resourcemanager.New[pollingprofile.PollingProfile]( @@ -42,17 +30,6 @@ func TestPollingProfileResource(t *testing.T) { return manager }, - // Prepare: func(t *testing.T, op rmtests.Operation, manager resourcemanager.Manager) { - // pollingProfileRepo := manager.Handler().(*pollingprofile.Repository) - // switch op { - // case rmtests.OperationGetSuccess, - // pollingProfileRepo.Update(context.TODO(), sampleProfile) - // case rmtests.OperationListPaginatedSuccess: - // pollingProfileRepo.Create(context.TODO(), sampleProfile) - // pollingProfileRepo.Create(context.TODO(), secondSampleProfile) - // pollingProfileRepo.Create(context.TODO(), thirdSampleProfile) - // } - // }, SampleJSON: `{ "type": "PollingProfile", "spec": { @@ -80,7 +57,6 @@ func TestPollingProfileResource(t *testing.T) { } }`, }, - // TODO: remove this when we support multiple profiles rmtests.ExcludeOperations( rmtests.OperationGetNotFound, rmtests.OperationUpdateNotFound, diff --git a/server/executor/transaction_runner_test.go b/server/executor/transaction_runner_test.go index 5cf21cffdd..086b88668b 100644 --- a/server/executor/transaction_runner_test.go +++ b/server/executor/transaction_runner_test.go @@ -96,13 +96,10 @@ func TestTransactionRunner(t *testing.T) { } func getDB() (model.Repository, func()) { - db, err := testmock.GetTestingDatabase() - if err != nil { - panic(err) - } + db := testmock.GetTestingDatabase() clean := func() { - err = db.Drop() + err := db.Drop() if err != nil { panic(err) } diff --git a/server/integration/main_test.go b/server/integration/main_test.go new file mode 100644 index 0000000000..b4f4bb1351 --- /dev/null +++ b/server/integration/main_test.go @@ -0,0 +1,18 @@ +package integration_test + +import ( + "os" + "testing" + + "github.com/kubeshop/tracetest/server/testmock" +) + +func TestMain(m *testing.M) { + testmock.StartTestEnvironment() + + exitVal := m.Run() + + testmock.StopTestEnvironment() + + os.Exit(exitVal) +} diff --git a/server/migrations/main_test.go b/server/migrations/main_test.go new file mode 100644 index 0000000000..773312d1f6 --- /dev/null +++ b/server/migrations/main_test.go @@ -0,0 +1,18 @@ +package migrations_test + +import ( + "os" + "testing" + + "github.com/kubeshop/tracetest/server/testmock" +) + +func TestMain(m *testing.M) { + testmock.StartTestEnvironment() + + exitVal := m.Run() + + testmock.StopTestEnvironment() + + os.Exit(exitVal) +} diff --git a/server/migrations/migrations_test.go b/server/migrations/migrations_test.go index a0a91fc2a4..a7848adac3 100644 --- a/server/migrations/migrations_test.go +++ b/server/migrations/migrations_test.go @@ -17,16 +17,15 @@ import ( ) func TestMigrations(t *testing.T) { - db, err := testmock.GetRawTestingDatabase() - require.NoError(t, err) + db := testmock.GetRawTestingDatabase() t.Run("applying migrations", func(t *testing.T) { - _, err = testdb.Postgres(testdb.WithDB(db)) + _, err := testdb.Postgres(testdb.WithDB(db)) require.NoError(t, err, "postgres migrations up should not fail") }) t.Run("rolling back migrations", func(t *testing.T) { - err = rollback(db) + err := rollback(db) assert.NoError(t, err, "rollback should not fail") }) } diff --git a/server/provisioning/main_test.go b/server/provisioning/main_test.go new file mode 100644 index 0000000000..bc60d44a4a --- /dev/null +++ b/server/provisioning/main_test.go @@ -0,0 +1,18 @@ +package provisioning_test + +import ( + "os" + "testing" + + "github.com/kubeshop/tracetest/server/testmock" +) + +func TestMain(m *testing.M) { + testmock.StartTestEnvironment() + + exitVal := m.Run() + + testmock.StopTestEnvironment() + + os.Exit(exitVal) +} diff --git a/server/provisioning/provisioning_test.go b/server/provisioning/provisioning_test.go index 17e426af1d..fd95c4e406 100644 --- a/server/provisioning/provisioning_test.go +++ b/server/provisioning/provisioning_test.go @@ -30,7 +30,7 @@ func TestFromFile(t *testing.T) { assert.ErrorIs(t, err, provisioning.ErrFileNotExists) }) - db := testmock.MustGetRawTestingDatabase() + db := testmock.GetRawTestingDatabase() for _, c := range cases { t.Run(c.name, func(t *testing.T) { @@ -50,7 +50,7 @@ func TestFromFile(t *testing.T) { } func TestFromEnv(t *testing.T) { - db := testmock.MustGetRawTestingDatabase() + db := testmock.GetRawTestingDatabase() t.Run("Empty", func(t *testing.T) { provisioner := provisioning.New() diff --git a/server/resourcemanager/testutil/test_resource.go b/server/resourcemanager/testutil/test_resource.go index 3cbd134115..d27d62a029 100644 --- a/server/resourcemanager/testutil/test_resource.go +++ b/server/resourcemanager/testutil/test_resource.go @@ -110,7 +110,6 @@ func testOperation(t *testing.T, op operationTester, rt ResourceTypeTest) { for _, ct := range contentTypeConverters { t.Run(ct.name, func(t *testing.T) { ct := ct - t.Parallel() testOperationForContentType(t, op, ct, rt) }) diff --git a/server/testdb/data_stores_test.go b/server/testdb/data_stores_test.go index eb7949e2c2..7577ec074d 100644 --- a/server/testdb/data_stores_test.go +++ b/server/testdb/data_stores_test.go @@ -174,14 +174,11 @@ func TestGetDataStores(t *testing.T) { } func TestDataStoreProvisioner(t *testing.T) { - - db := testmock.MustGetRawTestingDatabase() - rmtests.TestResourceType(t, rmtests.ResourceTypeTest{ ResourceTypeSingular: testdb.DataStoreResourceName, ResourceTypePlural: testdb.DataStoreResourceName, RegisterManagerFn: func(router *mux.Router) resourcemanager.Manager { - db := testmock.MustCreateRandomMigratedDatabase(db) + db := testmock.CreateMigratedDatabase() dsRepo, err := testdb.Postgres(testdb.WithDB(db)) require.NoError(t, err) diff --git a/server/testdb/main_test.go b/server/testdb/main_test.go new file mode 100644 index 0000000000..4227b7fd6f --- /dev/null +++ b/server/testdb/main_test.go @@ -0,0 +1,18 @@ +package testdb_test + +import ( + "os" + "testing" + + "github.com/kubeshop/tracetest/server/testmock" +) + +func TestMain(m *testing.M) { + testmock.StartTestEnvironment() + + exitVal := m.Run() + + testmock.StopTestEnvironment() + + os.Exit(exitVal) +} diff --git a/server/testdb/postgres_test.go b/server/testdb/postgres_test.go index aa1a3f2ff0..cfab8c1247 100644 --- a/server/testdb/postgres_test.go +++ b/server/testdb/postgres_test.go @@ -11,14 +11,11 @@ import ( ) func getDB() (model.Repository, func()) { - db, err := testmock.GetTestingDatabase() - if err != nil { - panic(err) - } + db := testmock.GetTestingDatabase() clean := func() { defer db.Close() - err = db.Drop() + err := db.Drop() if err != nil { panic(err) } diff --git a/server/testmock/app.go b/server/testmock/app.go index 93292ccc0b..3206fd2d4a 100644 --- a/server/testmock/app.go +++ b/server/testmock/app.go @@ -24,10 +24,18 @@ func GetTestingApp(options ...TestingAppOption) (*app.App, error) { for _, option := range options { option(cfg) } - err := ConfigureDB(cfg) - if err != nil { - panic(err) - } + + ConfigureDB(cfg) return app.New(cfg) } + +func ConfigureDB(cfg *config.Config) { + db := getTestDatabaseEnvironment() + + cfg.Set("postgres.host", db.container.Host) + cfg.Set("postgres.user", "tracetest") + cfg.Set("postgres.password", "tracetest") + cfg.Set("postgres.dbname", "postgres") + cfg.Set("postgres.port", db.container.DefaultPort()) +} diff --git a/server/testmock/database.go b/server/testmock/database.go index ccf0a7621f..b2bb4534ac 100644 --- a/server/testmock/database.go +++ b/server/testmock/database.go @@ -4,53 +4,100 @@ import ( "database/sql" "fmt" "math/rand" + "sync" "time" - "github.com/kubeshop/tracetest/server/config" "github.com/kubeshop/tracetest/server/model" "github.com/kubeshop/tracetest/server/testdb" "github.com/orlangure/gnomock" "github.com/orlangure/gnomock/preset/postgres" ) -var pgContainer *gnomock.Container +const baseDatabaseName = "tracetest" -func GetTestingDatabase() (model.Repository, error) { - db, err := GetRawTestingDatabase() - if err != nil { - return nil, err +var singletonTestDatabaseEnvironment *testDatabaseEnvironment + +type testDatabaseEnvironment struct { + container *gnomock.Container + mainConnection *sql.DB + + mutex sync.Mutex +} + +func getTestDatabaseEnvironment() *testDatabaseEnvironment { + if singletonTestDatabaseEnvironment == nil { + panic(fmt.Errorf("testing database environment not started")) } - return testdb.Postgres(testdb.WithDB(db)) + return singletonTestDatabaseEnvironment } -func ConfigureDB(cfg *config.Config) error { - pgContainer, err := getPostgresContainer() +func StartTestEnvironment() { + if singletonTestDatabaseEnvironment != nil { + return // Already started + } + + db := &testDatabaseEnvironment{ + mutex: sync.Mutex{}, + } + + db.mutex.Lock() + defer db.mutex.Unlock() + + container, err := getPostgresContainer() + if err != nil { + panic(err) + } + db.container = container + + connection, err := getMainDatabaseConnection(db.container) if err != nil { - return err + panic(err) } + db.mainConnection = connection - cfg.Set("postgres.host", pgContainer.Host) - cfg.Set("postgres.user", "tracetest") - cfg.Set("postgres.password", "tracetest") - cfg.Set("postgres.dbname", "postgres") - cfg.Set("postgres.port", pgContainer.DefaultPort()) + // Starts this singleton only here, to guarantee that we + // will only initiate this singleton when starting the environment + singletonTestDatabaseEnvironment = db +} - return nil +func StopTestEnvironment() { + db := getTestDatabaseEnvironment() + db.mutex.Lock() + defer db.mutex.Unlock() + + // Close main connection + if db.mainConnection != nil { + err := db.mainConnection.Close() + if err != nil { + panic(err) + } + + db.mainConnection = nil + } + + if db.container != nil { + err := gnomock.Stop(db.container) + if err != nil { + panic(err) + } + } } -func MustGetRawTestingDatabase() *sql.DB { - db, err := GetRawTestingDatabase() +func GetTestingDatabase() model.Repository { + dbConnection := GetRawTestingDatabase() + + testingDatabase, err := testdb.Postgres(testdb.WithDB(dbConnection)) if err != nil { panic(err) } - return db + return testingDatabase } -func MustCreateRandomMigratedDatabase(db *sql.DB) *sql.DB { - newConn, err := createRandomDatabaseForTest(db, "tracetest") +func CreateMigratedDatabase() *sql.DB { + newConn, err := createRandomDatabaseForTest(baseDatabaseName) if err != nil { panic(err) } @@ -62,63 +109,36 @@ func MustCreateRandomMigratedDatabase(db *sql.DB) *sql.DB { } return newConn - } -func GetRawTestingDatabase() (*sql.DB, error) { - pgContainer, err := getPostgresContainer() - if err != nil { - return nil, err - } - db, err := getMainDatabaseConnection(pgContainer) - if err != nil { - return nil, err - } - newDbConnection, err := createRandomDatabaseForTest(db, "tracetest") +func GetRawTestingDatabase() *sql.DB { + newDbConnection, err := createRandomDatabaseForTest(baseDatabaseName) if err != nil { - return nil, err + panic(err) } - return newDbConnection, nil -} - -func getMainDatabaseConnection(container *gnomock.Container) (*sql.DB, error) { - connStr := fmt.Sprintf( - "host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", - container.Host, container.DefaultPort(), "tracetest", "tracetest", "postgres", - ) - - return sql.Open("postgres", connStr) + return newDbConnection } -func randomInt() int { - rand.Seed(time.Now().UnixNano()) - min := 1 - max := 1000000 - return rand.Intn(max-min) + min -} +func createRandomDatabaseForTest(baseDatabase string) (*sql.DB, error) { + db := getTestDatabaseEnvironment() -func createRandomDatabaseForTest(db *sql.DB, baseDatabase string) (*sql.DB, error) { newDatabaseName := fmt.Sprintf("%s_%d%d%d", baseDatabase, randomInt(), randomInt(), randomInt()) - _, err := db.Exec(fmt.Sprintf("CREATE DATABASE %s WITH TEMPLATE %s", newDatabaseName, baseDatabase)) + _, err := db.mainConnection.Exec(fmt.Sprintf("CREATE DATABASE %s WITH TEMPLATE %s", newDatabaseName, baseDatabase)) if err != nil { return nil, fmt.Errorf("could not create database %s: %w", newDatabaseName, err) } connStr := fmt.Sprintf( "host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", - pgContainer.Host, pgContainer.DefaultPort(), "tracetest", "tracetest", newDatabaseName, + db.container.Host, db.container.DefaultPort(), "tracetest", "tracetest", newDatabaseName, ) return sql.Open("postgres", connStr) } func getPostgresContainer() (*gnomock.Container, error) { - if pgContainer != nil { - return pgContainer, nil - } - preset := postgres.Preset( postgres.WithUser("tracetest", "tracetest"), postgres.WithDatabase("tracetest"), @@ -129,7 +149,21 @@ func getPostgresContainer() (*gnomock.Container, error) { return nil, fmt.Errorf("could not start postgres container") } - pgContainer = dbContainer - return dbContainer, nil } + +func getMainDatabaseConnection(container *gnomock.Container) (*sql.DB, error) { + connStr := fmt.Sprintf( + "host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", + container.Host, container.DefaultPort(), "tracetest", "tracetest", "postgres", + ) + + return sql.Open("postgres", connStr) +} + +func randomInt() int { + rand.Seed(time.Now().UnixNano()) + min := 1 + max := 1000000 + return rand.Intn(max-min) + min +}