182 lines
5.1 KiB
Go
182 lines
5.1 KiB
Go
package examples_test
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"testing"
|
|
|
|
_ "github.com/lib/pq"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/testcontainers/testcontainers-go"
|
|
"github.com/testcontainers/testcontainers-go/modules/postgres"
|
|
)
|
|
|
|
// TestPostgresSnapshot demonstrates using snapshots for test isolation
|
|
// This is useful when you want to run multiple tests against the same initial state
|
|
func TestPostgresSnapshot(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
// Start PostgreSQL container with a custom database (required for snapshots)
|
|
// Note: Cannot snapshot the default 'postgres' system database
|
|
pgContainer, err := postgres.Run(
|
|
ctx,
|
|
"postgres:16-alpine",
|
|
postgres.WithDatabase("snapshotdb"),
|
|
postgres.BasicWaitStrategies(),
|
|
)
|
|
testcontainers.CleanupContainer(t, pgContainer)
|
|
require.NoError(t, err)
|
|
|
|
connStr, err := pgContainer.ConnectionString(ctx, "sslmode=disable")
|
|
require.NoError(t, err)
|
|
|
|
db, err := sql.Open("postgres", connStr)
|
|
require.NoError(t, err)
|
|
defer db.Close()
|
|
|
|
// Create initial schema and data
|
|
_, err = db.Exec(`
|
|
CREATE TABLE products (
|
|
id SERIAL PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
price DECIMAL(10, 2) NOT NULL
|
|
)
|
|
`)
|
|
require.NoError(t, err)
|
|
|
|
_, err = db.Exec(`INSERT INTO products (name, price) VALUES ($1, $2)`, "Widget", 9.99)
|
|
require.NoError(t, err)
|
|
|
|
// Close connection before snapshot (PostgreSQL can't snapshot a database with active connections)
|
|
db.Close()
|
|
|
|
// Take a snapshot of the initial state
|
|
err = pgContainer.Snapshot(ctx, postgres.WithSnapshotName("initial_state"))
|
|
require.NoError(t, err)
|
|
|
|
t.Log("Snapshot created with initial state")
|
|
|
|
// Reconnect to modify the database
|
|
db, err = sql.Open("postgres", connStr)
|
|
require.NoError(t, err)
|
|
|
|
// Modify the database
|
|
_, err = db.Exec(`INSERT INTO products (name, price) VALUES ($1, $2)`, "Gadget", 19.99)
|
|
require.NoError(t, err)
|
|
|
|
// Verify we have 2 products
|
|
var count int
|
|
err = db.QueryRow(`SELECT COUNT(*) FROM products`).Scan(&count)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 2, count)
|
|
|
|
t.Log("Added second product, count is now 2")
|
|
|
|
// Close connection before restore
|
|
db.Close()
|
|
|
|
// Restore to the snapshot
|
|
err = pgContainer.Restore(ctx, postgres.WithSnapshotName("initial_state"))
|
|
require.NoError(t, err)
|
|
|
|
t.Log("Restored to initial snapshot")
|
|
|
|
// Reconnect after restore
|
|
db, err = sql.Open("postgres", connStr)
|
|
require.NoError(t, err)
|
|
defer db.Close()
|
|
|
|
// Verify we're back to 1 product
|
|
err = db.QueryRow(`SELECT COUNT(*) FROM products`).Scan(&count)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, count, "After restore, should have only 1 product")
|
|
|
|
// Verify it's the original product
|
|
var name string
|
|
var price float64
|
|
err = db.QueryRow(`SELECT name, price FROM products WHERE id = 1`).Scan(&name, &price)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "Widget", name)
|
|
require.Equal(t, 9.99, price)
|
|
|
|
t.Log("Successfully restored to initial state")
|
|
}
|
|
|
|
// TestPostgresMultipleSnapshots demonstrates using multiple named snapshots
|
|
func TestPostgresMultipleSnapshots(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
// Use a custom database name (not 'postgres') for snapshots to work properly
|
|
pgContainer, err := postgres.Run(
|
|
ctx,
|
|
"postgres:16-alpine",
|
|
postgres.WithDatabase("testdb"),
|
|
postgres.BasicWaitStrategies(),
|
|
)
|
|
testcontainers.CleanupContainer(t, pgContainer)
|
|
require.NoError(t, err)
|
|
|
|
connStr, err := pgContainer.ConnectionString(ctx, "sslmode=disable")
|
|
require.NoError(t, err)
|
|
|
|
db, err := sql.Open("postgres", connStr)
|
|
require.NoError(t, err)
|
|
defer db.Close()
|
|
|
|
// Create table
|
|
_, err = db.Exec(`CREATE TABLE counters (id INT PRIMARY KEY, value INT)`)
|
|
require.NoError(t, err)
|
|
|
|
// State 1: Empty table
|
|
// Close connection before snapshot (PostgreSQL can't snapshot a database with active connections)
|
|
db.Close()
|
|
err = pgContainer.Snapshot(ctx, postgres.WithSnapshotName("empty"))
|
|
require.NoError(t, err)
|
|
t.Log("Snapshot 'empty' created")
|
|
|
|
// Reconnect to make changes
|
|
db, err = sql.Open("postgres", connStr)
|
|
require.NoError(t, err)
|
|
|
|
// State 2: One record
|
|
_, err = db.Exec(`INSERT INTO counters (id, value) VALUES (1, 10)`)
|
|
require.NoError(t, err)
|
|
|
|
// Close and snapshot
|
|
db.Close()
|
|
err = pgContainer.Snapshot(ctx, postgres.WithSnapshotName("one_record"))
|
|
require.NoError(t, err)
|
|
t.Log("Snapshot 'one_record' created")
|
|
|
|
// Reconnect to make changes
|
|
db, err = sql.Open("postgres", connStr)
|
|
require.NoError(t, err)
|
|
|
|
// State 3: Two records
|
|
_, err = db.Exec(`INSERT INTO counters (id, value) VALUES (2, 20)`)
|
|
require.NoError(t, err)
|
|
|
|
// Close and snapshot
|
|
db.Close()
|
|
err = pgContainer.Snapshot(ctx, postgres.WithSnapshotName("two_records"))
|
|
require.NoError(t, err)
|
|
t.Log("Snapshot 'two_records' created")
|
|
|
|
// Now restore to "one_record" state
|
|
err = pgContainer.Restore(ctx, postgres.WithSnapshotName("one_record"))
|
|
require.NoError(t, err)
|
|
|
|
// Reconnect after restore
|
|
db, err = sql.Open("postgres", connStr)
|
|
require.NoError(t, err)
|
|
defer db.Close()
|
|
|
|
// Verify we have exactly 1 record
|
|
var count int
|
|
err = db.QueryRow(`SELECT COUNT(*) FROM counters`).Scan(&count)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, count, "After restoring to 'one_record', should have 1 record")
|
|
|
|
t.Log("Successfully restored to 'one_record' snapshot")
|
|
}
|