From e4b080151c873a9e77e785721464fb89fc05b3d5 Mon Sep 17 00:00:00 2001 From: Krystian Chmura Date: Tue, 29 Oct 2024 16:49:34 +0100 Subject: [PATCH] test: move all db tests to dbtest package prerequisite before moving to a separate go module. Tests, which were not executed in a real db were kept in the same place as before --- Taskfile.yml | 2 +- .../clickhouse_test.go | 2 +- .../cockroachdb_test.go | 7 +- dbtests/common_test.go | 549 ++++++++++++++++++ mysql_test.go => dbtests/mysql_test.go | 5 +- .../postgresql_test.go | 9 +- spanner_test.go => dbtests/spanner_test.go | 6 +- sqlite_test.go => dbtests/sqlite_test.go | 2 +- .../sqlserver_test.go | 5 +- .../testdata}/fixtures/assets.yml | 0 .../testdata}/fixtures/comments.yml | 0 .../testdata}/fixtures/posts.yml | 0 .../testdata}/fixtures/posts_tags.yml | 0 .../testdata}/fixtures/tags.yml | 0 .../testdata}/fixtures/users.yml | 0 .../testdata}/fixtures/votes.yml | 0 .../fixtures_dirs/fixtures1/comments.yml | 0 .../fixtures_dirs/fixtures1/posts.yml | 0 .../fixtures_dirs/fixtures1/posts_tags.yml | 0 .../fixtures_dirs/fixtures2/tags.yml | 0 .../fixtures_dirs/fixtures2/users.yml | 0 .../fixtures_multi_tables/assets.yml | 0 .../fixtures_multi_tables/posts_comments.yml | 0 .../fixtures_multi_tables/posts_tags.yml | 0 .../testdata}/fixtures_multi_tables/tags.yml | 0 .../testdata}/fixtures_multi_tables/users.yml | 0 .../testdata}/schema/clickhouse.sql | 0 .../testdata}/schema/cockroachdb.sql | 0 .../testdata}/schema/mysql.sql | 0 .../testdata}/schema/postgresql.sql | 0 .../testdata}/schema/spanner.sql | 0 .../testdata}/schema/sqlite.sql | 0 .../testdata}/schema/sqlserver.sql | 0 testfixtures_test.go | 542 ----------------- 34 files changed, 570 insertions(+), 559 deletions(-) rename clickhouse_test.go => dbtests/clickhouse_test.go (93%) rename cockroachdb_test.go => dbtests/cockroachdb_test.go (71%) create mode 100644 dbtests/common_test.go rename mysql_test.go => dbtests/mysql_test.go (77%) rename postgresql_test.go => dbtests/postgresql_test.go (67%) rename spanner_test.go => dbtests/spanner_test.go (95%) rename sqlite_test.go => dbtests/sqlite_test.go (92%) rename sqlserver_test.go => dbtests/sqlserver_test.go (81%) rename {testdata => dbtests/testdata}/fixtures/assets.yml (100%) rename {testdata => dbtests/testdata}/fixtures/comments.yml (100%) rename {testdata => dbtests/testdata}/fixtures/posts.yml (100%) rename {testdata => dbtests/testdata}/fixtures/posts_tags.yml (100%) rename {testdata => dbtests/testdata}/fixtures/tags.yml (100%) rename {testdata => dbtests/testdata}/fixtures/users.yml (100%) rename {testdata => dbtests/testdata}/fixtures/votes.yml (100%) rename {testdata => dbtests/testdata}/fixtures_dirs/fixtures1/comments.yml (100%) rename {testdata => dbtests/testdata}/fixtures_dirs/fixtures1/posts.yml (100%) rename {testdata => dbtests/testdata}/fixtures_dirs/fixtures1/posts_tags.yml (100%) rename {testdata => dbtests/testdata}/fixtures_dirs/fixtures2/tags.yml (100%) rename {testdata => dbtests/testdata}/fixtures_dirs/fixtures2/users.yml (100%) rename {testdata => dbtests/testdata}/fixtures_multi_tables/assets.yml (100%) rename {testdata => dbtests/testdata}/fixtures_multi_tables/posts_comments.yml (100%) rename {testdata => dbtests/testdata}/fixtures_multi_tables/posts_tags.yml (100%) rename {testdata => dbtests/testdata}/fixtures_multi_tables/tags.yml (100%) rename {testdata => dbtests/testdata}/fixtures_multi_tables/users.yml (100%) rename {testdata => dbtests/testdata}/schema/clickhouse.sql (100%) rename {testdata => dbtests/testdata}/schema/cockroachdb.sql (100%) rename {testdata => dbtests/testdata}/schema/mysql.sql (100%) rename {testdata => dbtests/testdata}/schema/postgresql.sql (100%) rename {testdata => dbtests/testdata}/schema/spanner.sql (100%) rename {testdata => dbtests/testdata}/schema/sqlite.sql (100%) rename {testdata => dbtests/testdata}/schema/sqlserver.sql (100%) diff --git a/Taskfile.yml b/Taskfile.yml index 75fb2fb..ab95f6c 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -79,4 +79,4 @@ tasks: docker:test: cmds: - docker compose down -v --remove-orphans - - docker compose run testfixtures go test -v -tags 'postgresql sqlite mysql sqlserver cockroachdb clickhouse spanner' + - docker compose run testfixtures go test -v -tags 'postgresql sqlite mysql sqlserver cockroachdb clickhouse spanner' ./... diff --git a/clickhouse_test.go b/dbtests/clickhouse_test.go similarity index 93% rename from clickhouse_test.go rename to dbtests/clickhouse_test.go index 997af7f..0023c3c 100644 --- a/clickhouse_test.go +++ b/dbtests/clickhouse_test.go @@ -1,6 +1,6 @@ //go:build clickhouse -package testfixtures +package dbtests import ( "os" diff --git a/cockroachdb_test.go b/dbtests/cockroachdb_test.go similarity index 71% rename from cockroachdb_test.go rename to dbtests/cockroachdb_test.go index 6e7543e..af44217 100644 --- a/cockroachdb_test.go +++ b/dbtests/cockroachdb_test.go @@ -1,11 +1,12 @@ //go:build cockroachdb -package testfixtures +package dbtests import ( "os" "testing" + "github.com/go-testfixtures/testfixtures/v3" _ "github.com/jackc/pgx/v4/stdlib" _ "github.com/lib/pq" ) @@ -18,8 +19,8 @@ func TestCockroachDB(t *testing.T) { t, db, dialect, - DangerousSkipTestDatabaseCheck(), - UseDropConstraint(), + testfixtures.DangerousSkipTestDatabaseCheck(), + testfixtures.UseDropConstraint(), ) } } diff --git a/dbtests/common_test.go b/dbtests/common_test.go new file mode 100644 index 0000000..4db683a --- /dev/null +++ b/dbtests/common_test.go @@ -0,0 +1,549 @@ +package dbtests + +import ( + "bytes" + "database/sql" + "embed" + "fmt" + "os" + "testing" + "time" + + "github.com/go-testfixtures/testfixtures/v3" + _ "github.com/joho/godotenv/autoload" +) + +//go:embed testdata +var fixtures embed.FS //nolint:unused + +func openDB(t *testing.T, dialect, connStr string) *sql.DB { //nolint:unused + t.Helper() + db, err := sql.Open(dialect, connStr) + if err != nil { + t.Errorf("failed to open database: %v", err) + } + t.Cleanup(func() { + _ = db.Close() + }) + + if err := db.Ping(); err != nil { + t.Errorf("failed to connect to database: %v", err) + } + return db +} + +func loadSchemaInOneQuery(t *testing.T, db *sql.DB, schemaFilePath string) { //nolint:unused + t.Helper() + schema, err := os.ReadFile(schemaFilePath) + if err != nil { + t.Errorf("cannot read schema file: %v", err) + return + } + loadSchemaInBatches(t, db, [][]byte{schema}) +} + +func loadSchemaInBatchesBySplitter(t *testing.T, db *sql.DB, schemaFilePath string, splitter []byte) { //nolint:unused + t.Helper() + schema, err := os.ReadFile(schemaFilePath) + if err != nil { + t.Errorf("cannot read schema file: %v", err) + return + } + batches := bytes.Split(schema, splitter) + loadSchemaInBatches(t, db, batches) +} + +func loadSchemaInBatches(t *testing.T, db *sql.DB, batches [][]byte) { //nolint:unused + t.Helper() + for _, b := range batches { + if len(b) == 0 { + continue + } + if _, err := db.Exec(string(b)); err != nil { + t.Errorf("cannot load schema: %v", err) + return + } + } +} + +func testLoader(t *testing.T, db *sql.DB, dialect string, additionalOptions ...func(*testfixtures.Loader) error) { //nolint:unused + t.Run("LoadFromDirectory", func(t *testing.T) { + options := append( + []func(*testfixtures.Loader) error{ + testfixtures.Database(db), + testfixtures.Dialect(dialect), + testfixtures.Template(), + testfixtures.TemplateData(map[string]interface{}{ + "PostIds": []int{1, 2}, + "TagIds": []int{1, 2, 3}, + }), + testfixtures.Directory("testdata/fixtures"), + }, + additionalOptions..., + ) + l, err := testfixtures.New(options...) + if err != nil { + t.Errorf("failed to create Loader: %v", err) + return + } + if err := l.Load(); err != nil { + t.Errorf("cannot load fixtures: %v", err) + } + + // Call load again to test against a database with existing data. + if err := l.Load(); err != nil { + t.Errorf("cannot load fixtures: %v", err) + } + + assertFixturesLoaded(t, db) + }) + + t.Run("LoadFromDirectory with SkipTableChecksumComputation", func(t *testing.T) { + options := append( + []func(*testfixtures.Loader) error{ + testfixtures.Database(db), + testfixtures.Dialect(dialect), + testfixtures.Template(), + testfixtures.TemplateData(map[string]interface{}{ + "PostIds": []int{1, 2}, + "TagIds": []int{1, 2, 3}, + }), + testfixtures.Directory("testdata/fixtures"), + testfixtures.SkipTableChecksumComputation(), + }, + additionalOptions..., + ) + l, err := testfixtures.New(options...) + if err != nil { + t.Errorf("failed to create Loader: %v", err) + return + } + if err := l.Load(); err != nil { + t.Errorf("cannot load fixtures: %v", err) + } + + // Call load again to test against a database with existing data. + if err := l.Load(); err != nil { + t.Errorf("cannot load fixtures: %v", err) + } + + assertFixturesLoaded(t, db) + }) + + t.Run("LoadFromDirectory-Multiple", func(t *testing.T) { + options := append( + []func(*testfixtures.Loader) error{ + testfixtures.Database(db), + testfixtures.Dialect(dialect), + testfixtures.Template(), + testfixtures.TemplateData(map[string]interface{}{ + "PostIds": []int{1, 2}, + "TagIds": []int{1, 2, 3}, + }), + testfixtures.Directory("testdata/fixtures_dirs/fixtures1"), + testfixtures.Directory("testdata/fixtures_dirs/fixtures2"), + }, + additionalOptions..., + ) + l, err := testfixtures.New(options...) + if err != nil { + t.Errorf("failed to create Loader: %v", err) + return + } + if err := l.Load(); err != nil { + t.Errorf("cannot load fixtures: %v", err) + } + assertFixturesLoaded(t, db) + }) + + t.Run("LoadFromFiles", func(t *testing.T) { + options := append( + []func(*testfixtures.Loader) error{ + testfixtures.Database(db), + testfixtures.Dialect(dialect), + testfixtures.Template(), + testfixtures.TemplateData(map[string]interface{}{ + "PostIds": []int{1, 2}, + "TagIds": []int{1, 2, 3}, + }), + testfixtures.Files( + "testdata/fixtures/posts.yml", + "testdata/fixtures/comments.yml", + "testdata/fixtures/tags.yml", + "testdata/fixtures/posts_tags.yml", + "testdata/fixtures/users.yml", + "testdata/fixtures/assets.yml", + ), + }, + additionalOptions..., + ) + l, err := testfixtures.New(options...) + if err != nil { + t.Errorf("failed to create Loader: %v", err) + return + } + if err := l.Load(); err != nil { + t.Errorf("cannot load fixtures: %v", err) + } + assertFixturesLoaded(t, db) + }) + + t.Run("LoadFromFiles-Multiple", func(t *testing.T) { + options := append( + []func(*testfixtures.Loader) error{ + testfixtures.Database(db), + testfixtures.Dialect(dialect), + testfixtures.Template(), + testfixtures.TemplateData(map[string]interface{}{ + "PostIds": []int{1, 2}, + "TagIds": []int{1, 2, 3}, + }), + testfixtures.Files( + "testdata/fixtures/posts.yml", + "testdata/fixtures/comments.yml", + ), + testfixtures.Files( + "testdata/fixtures/tags.yml", + "testdata/fixtures/posts_tags.yml", + "testdata/fixtures/users.yml", + "testdata/fixtures/assets.yml", + ), + }, + additionalOptions..., + ) + l, err := testfixtures.New(options...) + if err != nil { + t.Errorf("failed to create Loader: %v", err) + return + } + if err := l.Load(); err != nil { + t.Errorf("cannot load fixtures: %v", err) + } + assertFixturesLoaded(t, db) + }) + + t.Run("LoadFromFiles-MultiTables", func(t *testing.T) { + options := append( + []func(*testfixtures.Loader) error{ + testfixtures.Database(db), + testfixtures.Dialect(dialect), + testfixtures.Template(), + testfixtures.TemplateData(map[string]interface{}{ + "PostIds": []int{1, 2}, + "TagIds": []int{1, 2, 3}, + }), + testfixtures.FilesMultiTables( + "testdata/fixtures_multi_tables/posts_comments.yml", + "testdata/fixtures_multi_tables/tags.yml", + "testdata/fixtures_multi_tables/users.yml", + "testdata/fixtures_multi_tables/posts_tags.yml", + "testdata/fixtures_multi_tables/assets.yml", + ), + }, + additionalOptions..., + ) + l, err := testfixtures.New(options...) + if err != nil { + t.Errorf("failed to create Loader: %v", err) + return + } + if err := l.Load(); err != nil { + t.Errorf("cannot load fixtures: %v", err) + } + assertFixturesLoaded(t, db) + }) + + t.Run("LoadFromFiles-MultiTablesWithFS", func(t *testing.T) { + options := append( + []func(*testfixtures.Loader) error{ + testfixtures.Database(db), + testfixtures.Dialect(dialect), + testfixtures.Template(), + testfixtures.TemplateData(map[string]interface{}{ + "PostIds": []int{1, 2}, + "TagIds": []int{1, 2, 3}, + }), + testfixtures.FS(fixtures), + testfixtures.FilesMultiTables( + "testdata/fixtures_multi_tables/posts_comments.yml", + "testdata/fixtures_multi_tables/tags.yml", + "testdata/fixtures_multi_tables/users.yml", + "testdata/fixtures_multi_tables/posts_tags.yml", + "testdata/fixtures_multi_tables/assets.yml", + ), + }, + additionalOptions..., + ) + l, err := testfixtures.New(options...) + if err != nil { + t.Errorf("failed to create Loader: %v", err) + return + } + if err := l.Load(); err != nil { + t.Errorf("cannot load fixtures: %v", err) + } + assertFixturesLoaded(t, db) + }) + + t.Run("LoadFromDirectoryAndFiles", func(t *testing.T) { + options := append( + []func(*testfixtures.Loader) error{ + testfixtures.Database(db), + testfixtures.Dialect(dialect), + testfixtures.Template(), + testfixtures.TemplateData(map[string]interface{}{ + "PostIds": []int{1, 2}, + "TagIds": []int{1, 2, 3}, + }), + testfixtures.Directory("testdata/fixtures_dirs/fixtures1"), + testfixtures.Files( + "testdata/fixtures/tags.yml", + "testdata/fixtures/users.yml", + ), + }, + additionalOptions..., + ) + l, err := testfixtures.New(options...) + if err != nil { + t.Errorf("failed to create Loader: %v", err) + return + } + if err := l.Load(); err != nil { + t.Errorf("cannot load fixtures: %v", err) + } + assertFixturesLoaded(t, db) + }) + + t.Run("LoadFromDirectoryAndFilesWithFS", func(t *testing.T) { + options := append( + []func(*testfixtures.Loader) error{ + testfixtures.Database(db), + testfixtures.Dialect(dialect), + testfixtures.Template(), + testfixtures.TemplateData(map[string]interface{}{ + "PostIds": []int{1, 2}, + "TagIds": []int{1, 2, 3}, + }), + testfixtures.FS(fixtures), + testfixtures.Directory("testdata/fixtures_dirs/fixtures1"), + testfixtures.Files( + "testdata/fixtures/tags.yml", + "testdata/fixtures/users.yml", + ), + }, + additionalOptions..., + ) + l, err := testfixtures.New(options...) + if err != nil { + t.Errorf("failed to create Loader: %v", err) + return + } + if err := l.Load(); err != nil { + t.Errorf("cannot load fixtures: %v", err) + } + assertFixturesLoaded(t, db) + }) + + t.Run("LoadFromPaths", func(t *testing.T) { + options := append( + []func(*testfixtures.Loader) error{ + testfixtures.Database(db), + testfixtures.Dialect(dialect), + testfixtures.Template(), + testfixtures.TemplateData(map[string]interface{}{ + "PostIds": []int{1, 2}, + "TagIds": []int{1, 2, 3}, + }), + testfixtures.Paths( + "testdata/fixtures_dirs/fixtures1", + "testdata/fixtures_dirs/fixtures2/tags.yml", + "testdata/fixtures_dirs/fixtures2/users.yml", + ), + }, + additionalOptions..., + ) + l, err := testfixtures.New(options...) + if err != nil { + t.Errorf("failed to create Loader: %v", err) + return + } + if err := l.Load(); err != nil { + t.Errorf("cannot load fixtures: %v", err) + } + assertFixturesLoaded(t, db) + }) + + t.Run("LoadFromPathsWithFS", func(t *testing.T) { + options := append( + []func(*testfixtures.Loader) error{ + testfixtures.Database(db), + testfixtures.Dialect(dialect), + testfixtures.Template(), + testfixtures.TemplateData(map[string]interface{}{ + "PostIds": []int{1, 2}, + "TagIds": []int{1, 2, 3}, + }), + testfixtures.FS(fixtures), + testfixtures.Paths( + "testdata/fixtures_dirs/fixtures1", + "testdata/fixtures_dirs/fixtures2/tags.yml", + "testdata/fixtures_dirs/fixtures2/users.yml", + ), + }, + additionalOptions..., + ) + l, err := testfixtures.New(options...) + if err != nil { + t.Errorf("failed to create Loader: %v", err) + return + } + if err := l.Load(); err != nil { + t.Errorf("cannot load fixtures: %v", err) + } + assertFixturesLoaded(t, db) + }) + + t.Run("LoadFromPaths-OnlyFiles", func(t *testing.T) { + options := append( + []func(*testfixtures.Loader) error{ + testfixtures.Database(db), + testfixtures.Dialect(dialect), + testfixtures.Template(), + testfixtures.TemplateData(map[string]interface{}{ + "PostIds": []int{1, 2}, + "TagIds": []int{1, 2, 3}, + }), + testfixtures.Paths( + "testdata/fixtures/posts.yml", + "testdata/fixtures/comments.yml", + "testdata/fixtures/tags.yml", + "testdata/fixtures/posts_tags.yml", + "testdata/fixtures/users.yml", + "testdata/fixtures/assets.yml", + ), + }, + additionalOptions..., + ) + l, err := testfixtures.New(options...) + if err != nil { + t.Errorf("failed to create Loader: %v", err) + return + } + if err := l.Load(); err != nil { + t.Errorf("cannot load fixtures: %v", err) + } + assertFixturesLoaded(t, db) + }) + + t.Run("LoadFromPaths-OnlyDirs", func(t *testing.T) { + options := append( + []func(*testfixtures.Loader) error{ + testfixtures.Database(db), + testfixtures.Dialect(dialect), + testfixtures.Template(), + testfixtures.TemplateData(map[string]interface{}{ + "PostIds": []int{1, 2}, + "TagIds": []int{1, 2, 3}, + }), + testfixtures.Paths( + "testdata/fixtures_dirs/fixtures1", + "testdata/fixtures_dirs/fixtures2", + ), + }, + additionalOptions..., + ) + l, err := testfixtures.New(options...) + if err != nil { + t.Errorf("failed to create Loader: %v", err) + return + } + if err := l.Load(); err != nil { + t.Errorf("cannot load fixtures: %v", err) + } + assertFixturesLoaded(t, db) + }) + + t.Run("GenerateAndLoad", func(t *testing.T) { + dir, err := os.MkdirTemp(os.TempDir(), "testfixtures_test") + if err != nil { + t.Errorf("cannot create temp dir: %v", err) + return + } + dumper, err := testfixtures.NewDumper( + testfixtures.DumpDatabase(db), + testfixtures.DumpDialect(dialect), + testfixtures.DumpDirectory(dir), + ) + if err != nil { + t.Errorf("could not create dumper: %v", err) + return + } + if err := dumper.Dump(); err != nil { + t.Errorf("cannot generate fixtures: %v", err) + return + } + + options := append( + []func(*testfixtures.Loader) error{ + testfixtures.Database(db), + testfixtures.Dialect(dialect), + testfixtures.Directory(dir), + }, + additionalOptions..., + ) + l, err := testfixtures.New(options...) + if err != nil { + t.Errorf("failed to create Loader: %v", err) + return + } + if err := l.Load(); err != nil { + t.Error(err) + } + }) + + t.Run("InsertAfterLoad", func(t *testing.T) { + // This test was originally written to catch a bug where it + // wasn't possible to insert a record on PostgreSQL due + // sequence issues. + + var sql string + switch dialect { + case "postgres", "pgx", "clickhouse": + sql = "INSERT INTO posts (title, content, created_at, updated_at) VALUES ($1, $2, $3, $4)" + case "mysql", "sqlite3", "mssql": + sql = "INSERT INTO posts (title, content, created_at, updated_at) VALUES (?, ?, ?, ?)" + case "sqlserver", "spanner": + sql = "INSERT INTO posts (title, content, created_at, updated_at) VALUES (@p1, @p2, @p3, @p4)" + default: + t.Fatalf("undefined param type for %s dialect, modify switch statement", dialect) + } + + _, err := db.Exec(sql, "Post title", "Post content", time.Now(), time.Now()) + if err != nil { + t.Errorf("cannot insert post: %v", err) + } + }) +} + +func assertFixturesLoaded(t *testing.T, db *sql.DB) { //nolint + assertCount(t, db, "posts", 2) + assertCount(t, db, "comments", 4) + assertCount(t, db, "tags", 3) + assertCount(t, db, "posts_tags", 6) + assertCount(t, db, "users", 2) + assertCount(t, db, "assets", 1) +} + +func assertCount(t *testing.T, db *sql.DB, table string, expectedCount int) { //nolint + count := 0 + sql := fmt.Sprintf("SELECT COUNT(*) FROM %s", table) + + row := db.QueryRow(sql) + if err := row.Scan(&count); err != nil { + t.Errorf("cannot query table: %v", err) + } + + if count != expectedCount { + t.Errorf("%s should have %d, but has %d", table, expectedCount, count) + } +} diff --git a/mysql_test.go b/dbtests/mysql_test.go similarity index 77% rename from mysql_test.go rename to dbtests/mysql_test.go index 61fec87..919ec9c 100644 --- a/mysql_test.go +++ b/dbtests/mysql_test.go @@ -1,12 +1,13 @@ //go:build mysql -package testfixtures +package dbtests import ( "os" "testing" _ "github.com/go-sql-driver/mysql" + "github.com/go-testfixtures/testfixtures/v3" ) func TestMySQL(t *testing.T) { @@ -18,5 +19,5 @@ func TestMySQL(t *testing.T) { func TestMySQLWithMultipleStatementsSupport(t *testing.T) { db := openDB(t, "mysql", os.Getenv("MYSQL_CONN_STRING")+"?multiStatements=true") loadSchemaInOneQuery(t, db, "testdata/schema/mysql.sql") - testLoader(t, db, "mysql", AllowMultipleStatementsInOneQuery()) + testLoader(t, db, "mysql", testfixtures.AllowMultipleStatementsInOneQuery()) } diff --git a/postgresql_test.go b/dbtests/postgresql_test.go similarity index 67% rename from postgresql_test.go rename to dbtests/postgresql_test.go index f980d19..a1cceda 100644 --- a/postgresql_test.go +++ b/dbtests/postgresql_test.go @@ -1,11 +1,12 @@ //go:build postgresql -package testfixtures +package dbtests import ( "os" "testing" + "github.com/go-testfixtures/testfixtures/v3" _ "github.com/jackc/pgx/v4/stdlib" _ "github.com/lib/pq" ) @@ -15,14 +16,14 @@ func TestPostgreSQL(t *testing.T) { } func TestPostgreSQLWithAlterConstraint(t *testing.T) { - testPostgreSQL(t, UseAlterConstraint()) + testPostgreSQL(t, testfixtures.UseAlterConstraint()) } func TestPostgreSQLWithDropConstraint(t *testing.T) { - testPostgreSQL(t, UseDropConstraint()) + testPostgreSQL(t, testfixtures.UseDropConstraint()) } -func testPostgreSQL(t *testing.T, additionalOptions ...func(*Loader) error) { +func testPostgreSQL(t *testing.T, additionalOptions ...func(*testfixtures.Loader) error) { t.Helper() for _, dialect := range []string{"postgres", "pgx"} { db := openDB(t, dialect, os.Getenv("PG_CONN_STRING")) diff --git a/spanner_test.go b/dbtests/spanner_test.go similarity index 95% rename from spanner_test.go rename to dbtests/spanner_test.go index f6bc173..0e80f2c 100644 --- a/spanner_test.go +++ b/dbtests/spanner_test.go @@ -1,7 +1,7 @@ //go:build spanner // +build spanner -package testfixtures +package dbtests import ( "context" @@ -13,7 +13,7 @@ import ( "cloud.google.com/go/spanner/admin/database/apiv1/databasepb" instance "cloud.google.com/go/spanner/admin/instance/apiv1" "cloud.google.com/go/spanner/admin/instance/apiv1/instancepb" - + "github.com/go-testfixtures/testfixtures/v3" _ "github.com/googleapis/go-sql-spanner" ) @@ -22,7 +22,7 @@ func TestSpanner(t *testing.T) { db := openDB(t, "spanner", os.Getenv("SPANNER_CONN_STRING")) loadSchemaInBatchesBySplitter(t, db, "testdata/schema/spanner.sql", []byte(";\n")) - testLoader(t, db, "spanner", DangerousSkipTestDatabaseCheck()) + testLoader(t, db, "spanner", testfixtures.DangerousSkipTestDatabaseCheck()) } func prepareSpannerDB(t *testing.T) { diff --git a/sqlite_test.go b/dbtests/sqlite_test.go similarity index 92% rename from sqlite_test.go rename to dbtests/sqlite_test.go index e8d01d9..3c79999 100644 --- a/sqlite_test.go +++ b/dbtests/sqlite_test.go @@ -1,6 +1,6 @@ //go:build sqlite -package testfixtures +package dbtests import ( "os" diff --git a/sqlserver_test.go b/dbtests/sqlserver_test.go similarity index 81% rename from sqlserver_test.go rename to dbtests/sqlserver_test.go index 70356a8..f708513 100644 --- a/sqlserver_test.go +++ b/dbtests/sqlserver_test.go @@ -1,12 +1,13 @@ //go:build sqlserver -package testfixtures +package dbtests import ( "os" "testing" _ "github.com/denisenkom/go-mssqldb" + "github.com/go-testfixtures/testfixtures/v3" ) func TestSQLServer(t *testing.T) { @@ -25,6 +26,6 @@ func testSQLServer(t *testing.T, dialect string) { t, db, dialect, - DangerousSkipTestDatabaseCheck(), + testfixtures.DangerousSkipTestDatabaseCheck(), ) } diff --git a/testdata/fixtures/assets.yml b/dbtests/testdata/fixtures/assets.yml similarity index 100% rename from testdata/fixtures/assets.yml rename to dbtests/testdata/fixtures/assets.yml diff --git a/testdata/fixtures/comments.yml b/dbtests/testdata/fixtures/comments.yml similarity index 100% rename from testdata/fixtures/comments.yml rename to dbtests/testdata/fixtures/comments.yml diff --git a/testdata/fixtures/posts.yml b/dbtests/testdata/fixtures/posts.yml similarity index 100% rename from testdata/fixtures/posts.yml rename to dbtests/testdata/fixtures/posts.yml diff --git a/testdata/fixtures/posts_tags.yml b/dbtests/testdata/fixtures/posts_tags.yml similarity index 100% rename from testdata/fixtures/posts_tags.yml rename to dbtests/testdata/fixtures/posts_tags.yml diff --git a/testdata/fixtures/tags.yml b/dbtests/testdata/fixtures/tags.yml similarity index 100% rename from testdata/fixtures/tags.yml rename to dbtests/testdata/fixtures/tags.yml diff --git a/testdata/fixtures/users.yml b/dbtests/testdata/fixtures/users.yml similarity index 100% rename from testdata/fixtures/users.yml rename to dbtests/testdata/fixtures/users.yml diff --git a/testdata/fixtures/votes.yml b/dbtests/testdata/fixtures/votes.yml similarity index 100% rename from testdata/fixtures/votes.yml rename to dbtests/testdata/fixtures/votes.yml diff --git a/testdata/fixtures_dirs/fixtures1/comments.yml b/dbtests/testdata/fixtures_dirs/fixtures1/comments.yml similarity index 100% rename from testdata/fixtures_dirs/fixtures1/comments.yml rename to dbtests/testdata/fixtures_dirs/fixtures1/comments.yml diff --git a/testdata/fixtures_dirs/fixtures1/posts.yml b/dbtests/testdata/fixtures_dirs/fixtures1/posts.yml similarity index 100% rename from testdata/fixtures_dirs/fixtures1/posts.yml rename to dbtests/testdata/fixtures_dirs/fixtures1/posts.yml diff --git a/testdata/fixtures_dirs/fixtures1/posts_tags.yml b/dbtests/testdata/fixtures_dirs/fixtures1/posts_tags.yml similarity index 100% rename from testdata/fixtures_dirs/fixtures1/posts_tags.yml rename to dbtests/testdata/fixtures_dirs/fixtures1/posts_tags.yml diff --git a/testdata/fixtures_dirs/fixtures2/tags.yml b/dbtests/testdata/fixtures_dirs/fixtures2/tags.yml similarity index 100% rename from testdata/fixtures_dirs/fixtures2/tags.yml rename to dbtests/testdata/fixtures_dirs/fixtures2/tags.yml diff --git a/testdata/fixtures_dirs/fixtures2/users.yml b/dbtests/testdata/fixtures_dirs/fixtures2/users.yml similarity index 100% rename from testdata/fixtures_dirs/fixtures2/users.yml rename to dbtests/testdata/fixtures_dirs/fixtures2/users.yml diff --git a/testdata/fixtures_multi_tables/assets.yml b/dbtests/testdata/fixtures_multi_tables/assets.yml similarity index 100% rename from testdata/fixtures_multi_tables/assets.yml rename to dbtests/testdata/fixtures_multi_tables/assets.yml diff --git a/testdata/fixtures_multi_tables/posts_comments.yml b/dbtests/testdata/fixtures_multi_tables/posts_comments.yml similarity index 100% rename from testdata/fixtures_multi_tables/posts_comments.yml rename to dbtests/testdata/fixtures_multi_tables/posts_comments.yml diff --git a/testdata/fixtures_multi_tables/posts_tags.yml b/dbtests/testdata/fixtures_multi_tables/posts_tags.yml similarity index 100% rename from testdata/fixtures_multi_tables/posts_tags.yml rename to dbtests/testdata/fixtures_multi_tables/posts_tags.yml diff --git a/testdata/fixtures_multi_tables/tags.yml b/dbtests/testdata/fixtures_multi_tables/tags.yml similarity index 100% rename from testdata/fixtures_multi_tables/tags.yml rename to dbtests/testdata/fixtures_multi_tables/tags.yml diff --git a/testdata/fixtures_multi_tables/users.yml b/dbtests/testdata/fixtures_multi_tables/users.yml similarity index 100% rename from testdata/fixtures_multi_tables/users.yml rename to dbtests/testdata/fixtures_multi_tables/users.yml diff --git a/testdata/schema/clickhouse.sql b/dbtests/testdata/schema/clickhouse.sql similarity index 100% rename from testdata/schema/clickhouse.sql rename to dbtests/testdata/schema/clickhouse.sql diff --git a/testdata/schema/cockroachdb.sql b/dbtests/testdata/schema/cockroachdb.sql similarity index 100% rename from testdata/schema/cockroachdb.sql rename to dbtests/testdata/schema/cockroachdb.sql diff --git a/testdata/schema/mysql.sql b/dbtests/testdata/schema/mysql.sql similarity index 100% rename from testdata/schema/mysql.sql rename to dbtests/testdata/schema/mysql.sql diff --git a/testdata/schema/postgresql.sql b/dbtests/testdata/schema/postgresql.sql similarity index 100% rename from testdata/schema/postgresql.sql rename to dbtests/testdata/schema/postgresql.sql diff --git a/testdata/schema/spanner.sql b/dbtests/testdata/schema/spanner.sql similarity index 100% rename from testdata/schema/spanner.sql rename to dbtests/testdata/schema/spanner.sql diff --git a/testdata/schema/sqlite.sql b/dbtests/testdata/schema/sqlite.sql similarity index 100% rename from testdata/schema/sqlite.sql rename to dbtests/testdata/schema/sqlite.sql diff --git a/testdata/schema/sqlserver.sql b/dbtests/testdata/schema/sqlserver.sql similarity index 100% rename from testdata/schema/sqlserver.sql rename to dbtests/testdata/schema/sqlserver.sql diff --git a/testfixtures_test.go b/testfixtures_test.go index c8c6daa..b3d395f 100644 --- a/testfixtures_test.go +++ b/testfixtures_test.go @@ -1,21 +1,11 @@ package testfixtures import ( - "bytes" "database/sql" - "embed" "errors" - "fmt" - "os" "testing" - "time" - - _ "github.com/joho/godotenv/autoload" ) -//go:embed testdata -var fixtures embed.FS //nolint:unused - func TestFixtureFile(t *testing.T) { f := &fixtureFile{fileName: "posts.yml"} file := f.fileNameWithoutExtension() @@ -40,538 +30,6 @@ func TestRequiredOptions(t *testing.T) { }) } -func openDB(t *testing.T, dialect, connStr string) *sql.DB { //nolint:unused - t.Helper() - db, err := sql.Open(dialect, connStr) - if err != nil { - t.Errorf("failed to open database: %v", err) - } - t.Cleanup(func() { - _ = db.Close() - }) - - if err := db.Ping(); err != nil { - t.Errorf("failed to connect to database: %v", err) - } - return db -} - -func loadSchemaInOneQuery(t *testing.T, db *sql.DB, schemaFilePath string) { //nolint:unused - t.Helper() - schema, err := os.ReadFile(schemaFilePath) - if err != nil { - t.Errorf("cannot read schema file: %v", err) - return - } - loadSchemaInBatches(t, db, [][]byte{schema}) -} - -func loadSchemaInBatchesBySplitter(t *testing.T, db *sql.DB, schemaFilePath string, splitter []byte) { //nolint:unused - t.Helper() - schema, err := os.ReadFile(schemaFilePath) - if err != nil { - t.Errorf("cannot read schema file: %v", err) - return - } - batches := bytes.Split(schema, splitter) - loadSchemaInBatches(t, db, batches) -} - -func loadSchemaInBatches(t *testing.T, db *sql.DB, batches [][]byte) { //nolint:unused - t.Helper() - for _, b := range batches { - if len(b) == 0 { - continue - } - if _, err := db.Exec(string(b)); err != nil { - t.Errorf("cannot load schema: %v", err) - return - } - } -} - -func testLoader(t *testing.T, db *sql.DB, dialect string, additionalOptions ...func(*Loader) error) { //nolint:unused - t.Run("LoadFromDirectory", func(t *testing.T) { - options := append( - []func(*Loader) error{ - Database(db), - Dialect(dialect), - Template(), - TemplateData(map[string]interface{}{ - "PostIds": []int{1, 2}, - "TagIds": []int{1, 2, 3}, - }), - Directory("testdata/fixtures"), - }, - additionalOptions..., - ) - l, err := New(options...) - if err != nil { - t.Errorf("failed to create Loader: %v", err) - return - } - if err := l.Load(); err != nil { - t.Errorf("cannot load fixtures: %v", err) - } - - // Call load again to test against a database with existing data. - if err := l.Load(); err != nil { - t.Errorf("cannot load fixtures: %v", err) - } - - assertFixturesLoaded(t, db) - }) - - t.Run("LoadFromDirectory with SkipTableChecksumComputation", func(t *testing.T) { - options := append( - []func(*Loader) error{ - Database(db), - Dialect(dialect), - Template(), - TemplateData(map[string]interface{}{ - "PostIds": []int{1, 2}, - "TagIds": []int{1, 2, 3}, - }), - Directory("testdata/fixtures"), - SkipTableChecksumComputation(), - }, - additionalOptions..., - ) - l, err := New(options...) - if err != nil { - t.Errorf("failed to create Loader: %v", err) - return - } - if err := l.Load(); err != nil { - t.Errorf("cannot load fixtures: %v", err) - } - - // Call load again to test against a database with existing data. - if err := l.Load(); err != nil { - t.Errorf("cannot load fixtures: %v", err) - } - - assertFixturesLoaded(t, db) - }) - - t.Run("LoadFromDirectory-Multiple", func(t *testing.T) { - options := append( - []func(*Loader) error{ - Database(db), - Dialect(dialect), - Template(), - TemplateData(map[string]interface{}{ - "PostIds": []int{1, 2}, - "TagIds": []int{1, 2, 3}, - }), - Directory("testdata/fixtures_dirs/fixtures1"), - Directory("testdata/fixtures_dirs/fixtures2"), - }, - additionalOptions..., - ) - l, err := New(options...) - if err != nil { - t.Errorf("failed to create Loader: %v", err) - return - } - if err := l.Load(); err != nil { - t.Errorf("cannot load fixtures: %v", err) - } - assertFixturesLoaded(t, db) - }) - - t.Run("LoadFromFiles", func(t *testing.T) { - options := append( - []func(*Loader) error{ - Database(db), - Dialect(dialect), - Template(), - TemplateData(map[string]interface{}{ - "PostIds": []int{1, 2}, - "TagIds": []int{1, 2, 3}, - }), - Files( - "testdata/fixtures/posts.yml", - "testdata/fixtures/comments.yml", - "testdata/fixtures/tags.yml", - "testdata/fixtures/posts_tags.yml", - "testdata/fixtures/users.yml", - "testdata/fixtures/assets.yml", - ), - }, - additionalOptions..., - ) - l, err := New(options...) - if err != nil { - t.Errorf("failed to create Loader: %v", err) - return - } - if err := l.Load(); err != nil { - t.Errorf("cannot load fixtures: %v", err) - } - assertFixturesLoaded(t, db) - }) - - t.Run("LoadFromFiles-Multiple", func(t *testing.T) { - options := append( - []func(*Loader) error{ - Database(db), - Dialect(dialect), - Template(), - TemplateData(map[string]interface{}{ - "PostIds": []int{1, 2}, - "TagIds": []int{1, 2, 3}, - }), - Files( - "testdata/fixtures/posts.yml", - "testdata/fixtures/comments.yml", - ), - Files( - "testdata/fixtures/tags.yml", - "testdata/fixtures/posts_tags.yml", - "testdata/fixtures/users.yml", - "testdata/fixtures/assets.yml", - ), - }, - additionalOptions..., - ) - l, err := New(options...) - if err != nil { - t.Errorf("failed to create Loader: %v", err) - return - } - if err := l.Load(); err != nil { - t.Errorf("cannot load fixtures: %v", err) - } - assertFixturesLoaded(t, db) - }) - - t.Run("LoadFromFiles-MultiTables", func(t *testing.T) { - options := append( - []func(*Loader) error{ - Database(db), - Dialect(dialect), - Template(), - TemplateData(map[string]interface{}{ - "PostIds": []int{1, 2}, - "TagIds": []int{1, 2, 3}, - }), - FilesMultiTables( - "testdata/fixtures_multi_tables/posts_comments.yml", - "testdata/fixtures_multi_tables/tags.yml", - "testdata/fixtures_multi_tables/users.yml", - "testdata/fixtures_multi_tables/posts_tags.yml", - "testdata/fixtures_multi_tables/assets.yml", - ), - }, - additionalOptions..., - ) - l, err := New(options...) - if err != nil { - t.Errorf("failed to create Loader: %v", err) - return - } - if err := l.Load(); err != nil { - t.Errorf("cannot load fixtures: %v", err) - } - assertFixturesLoaded(t, db) - }) - - t.Run("LoadFromFiles-MultiTablesWithFS", func(t *testing.T) { - options := append( - []func(*Loader) error{ - Database(db), - Dialect(dialect), - Template(), - TemplateData(map[string]interface{}{ - "PostIds": []int{1, 2}, - "TagIds": []int{1, 2, 3}, - }), - FS(fixtures), - FilesMultiTables( - "testdata/fixtures_multi_tables/posts_comments.yml", - "testdata/fixtures_multi_tables/tags.yml", - "testdata/fixtures_multi_tables/users.yml", - "testdata/fixtures_multi_tables/posts_tags.yml", - "testdata/fixtures_multi_tables/assets.yml", - ), - }, - additionalOptions..., - ) - l, err := New(options...) - if err != nil { - t.Errorf("failed to create Loader: %v", err) - return - } - if err := l.Load(); err != nil { - t.Errorf("cannot load fixtures: %v", err) - } - assertFixturesLoaded(t, db) - }) - - t.Run("LoadFromDirectoryAndFiles", func(t *testing.T) { - options := append( - []func(*Loader) error{ - Database(db), - Dialect(dialect), - Template(), - TemplateData(map[string]interface{}{ - "PostIds": []int{1, 2}, - "TagIds": []int{1, 2, 3}, - }), - Directory("testdata/fixtures_dirs/fixtures1"), - Files( - "testdata/fixtures/tags.yml", - "testdata/fixtures/users.yml", - ), - }, - additionalOptions..., - ) - l, err := New(options...) - if err != nil { - t.Errorf("failed to create Loader: %v", err) - return - } - if err := l.Load(); err != nil { - t.Errorf("cannot load fixtures: %v", err) - } - assertFixturesLoaded(t, db) - }) - - t.Run("LoadFromDirectoryAndFilesWithFS", func(t *testing.T) { - options := append( - []func(*Loader) error{ - Database(db), - Dialect(dialect), - Template(), - TemplateData(map[string]interface{}{ - "PostIds": []int{1, 2}, - "TagIds": []int{1, 2, 3}, - }), - FS(fixtures), - Directory("testdata/fixtures_dirs/fixtures1"), - Files( - "testdata/fixtures/tags.yml", - "testdata/fixtures/users.yml", - ), - }, - additionalOptions..., - ) - l, err := New(options...) - if err != nil { - t.Errorf("failed to create Loader: %v", err) - return - } - if err := l.Load(); err != nil { - t.Errorf("cannot load fixtures: %v", err) - } - assertFixturesLoaded(t, db) - }) - - t.Run("LoadFromPaths", func(t *testing.T) { - options := append( - []func(*Loader) error{ - Database(db), - Dialect(dialect), - Template(), - TemplateData(map[string]interface{}{ - "PostIds": []int{1, 2}, - "TagIds": []int{1, 2, 3}, - }), - Paths( - "testdata/fixtures_dirs/fixtures1", - "testdata/fixtures_dirs/fixtures2/tags.yml", - "testdata/fixtures_dirs/fixtures2/users.yml", - ), - }, - additionalOptions..., - ) - l, err := New(options...) - if err != nil { - t.Errorf("failed to create Loader: %v", err) - return - } - if err := l.Load(); err != nil { - t.Errorf("cannot load fixtures: %v", err) - } - assertFixturesLoaded(t, db) - }) - - t.Run("LoadFromPathsWithFS", func(t *testing.T) { - options := append( - []func(*Loader) error{ - Database(db), - Dialect(dialect), - Template(), - TemplateData(map[string]interface{}{ - "PostIds": []int{1, 2}, - "TagIds": []int{1, 2, 3}, - }), - FS(fixtures), - Paths( - "testdata/fixtures_dirs/fixtures1", - "testdata/fixtures_dirs/fixtures2/tags.yml", - "testdata/fixtures_dirs/fixtures2/users.yml", - ), - }, - additionalOptions..., - ) - l, err := New(options...) - if err != nil { - t.Errorf("failed to create Loader: %v", err) - return - } - if err := l.Load(); err != nil { - t.Errorf("cannot load fixtures: %v", err) - } - assertFixturesLoaded(t, db) - }) - - t.Run("LoadFromPaths-OnlyFiles", func(t *testing.T) { - options := append( - []func(*Loader) error{ - Database(db), - Dialect(dialect), - Template(), - TemplateData(map[string]interface{}{ - "PostIds": []int{1, 2}, - "TagIds": []int{1, 2, 3}, - }), - Paths( - "testdata/fixtures/posts.yml", - "testdata/fixtures/comments.yml", - "testdata/fixtures/tags.yml", - "testdata/fixtures/posts_tags.yml", - "testdata/fixtures/users.yml", - "testdata/fixtures/assets.yml", - ), - }, - additionalOptions..., - ) - l, err := New(options...) - if err != nil { - t.Errorf("failed to create Loader: %v", err) - return - } - if err := l.Load(); err != nil { - t.Errorf("cannot load fixtures: %v", err) - } - assertFixturesLoaded(t, db) - }) - - t.Run("LoadFromPaths-OnlyDirs", func(t *testing.T) { - options := append( - []func(*Loader) error{ - Database(db), - Dialect(dialect), - Template(), - TemplateData(map[string]interface{}{ - "PostIds": []int{1, 2}, - "TagIds": []int{1, 2, 3}, - }), - Paths( - "testdata/fixtures_dirs/fixtures1", - "testdata/fixtures_dirs/fixtures2", - ), - }, - additionalOptions..., - ) - l, err := New(options...) - if err != nil { - t.Errorf("failed to create Loader: %v", err) - return - } - if err := l.Load(); err != nil { - t.Errorf("cannot load fixtures: %v", err) - } - assertFixturesLoaded(t, db) - }) - - t.Run("GenerateAndLoad", func(t *testing.T) { - dir, err := os.MkdirTemp(os.TempDir(), "testfixtures_test") - if err != nil { - t.Errorf("cannot create temp dir: %v", err) - return - } - dumper, err := NewDumper( - DumpDatabase(db), - DumpDialect(dialect), - DumpDirectory(dir), - ) - if err != nil { - t.Errorf("could not create dumper: %v", err) - return - } - if err := dumper.Dump(); err != nil { - t.Errorf("cannot generate fixtures: %v", err) - return - } - - options := append( - []func(*Loader) error{ - Database(db), - Dialect(dialect), - Directory(dir), - }, - additionalOptions..., - ) - l, err := New(options...) - if err != nil { - t.Errorf("failed to create Loader: %v", err) - return - } - if err := l.Load(); err != nil { - t.Error(err) - } - }) - - t.Run("InsertAfterLoad", func(t *testing.T) { - // This test was originally written to catch a bug where it - // wasn't possible to insert a record on PostgreSQL due - // sequence issues. - - var sql string - switch dialect { - case "postgres", "pgx", "clickhouse": - sql = "INSERT INTO posts (title, content, created_at, updated_at) VALUES ($1, $2, $3, $4)" - case "mysql", "sqlite3", "mssql": - sql = "INSERT INTO posts (title, content, created_at, updated_at) VALUES (?, ?, ?, ?)" - case "sqlserver", "spanner": - sql = "INSERT INTO posts (title, content, created_at, updated_at) VALUES (@p1, @p2, @p3, @p4)" - default: - t.Fatalf("undefined param type for %s dialect, modify switch statement", dialect) - } - - _, err := db.Exec(sql, "Post title", "Post content", time.Now(), time.Now()) - if err != nil { - t.Errorf("cannot insert post: %v", err) - } - }) -} - -func assertFixturesLoaded(t *testing.T, db *sql.DB) { //nolint - assertCount(t, db, "posts", 2) - assertCount(t, db, "comments", 4) - assertCount(t, db, "tags", 3) - assertCount(t, db, "posts_tags", 6) - assertCount(t, db, "users", 2) - assertCount(t, db, "assets", 1) -} - -func assertCount(t *testing.T, db *sql.DB, table string, expectedCount int) { //nolint - count := 0 - sql := fmt.Sprintf("SELECT COUNT(*) FROM %s", table) - - row := db.QueryRow(sql) - if err := row.Scan(&count); err != nil { - t.Errorf("cannot query table: %v", err) - } - - if count != expectedCount { - t.Errorf("%s should have %d, but has %d", table, expectedCount, count) - } -} - func TestQuoteKeyword(t *testing.T) { tests := []struct { helper helper