Skip to content

Commit 0ab0417

Browse files
committed
Add WithOrderedInitScripts for Postgres testcontainers
1 parent 322a057 commit 0ab0417

3 files changed

Lines changed: 87 additions & 9 deletions

File tree

modules/postgres/postgres.go

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -94,24 +94,42 @@ func WithDatabase(dbName string) testcontainers.CustomizeRequestOption {
9494
}
9595
}
9696

97-
// WithInitScripts sets the init scripts to be run when the container starts
97+
// WithInitScripts sets the init scripts to be run when the container starts.
98+
// These init scripts will be executed in sorted name order as defined by the current locale, which defaults to en_US.utf8.
99+
// If you need to run your scripts in a specific order, consider using `WithOrderedInitScripts` instead.
98100
func WithInitScripts(scripts ...string) testcontainers.CustomizeRequestOption {
99101
return func(req *testcontainers.GenericContainerRequest) error {
100-
initScripts := []testcontainers.ContainerFile{}
101102
for _, script := range scripts {
102-
cf := testcontainers.ContainerFile{
103-
HostFilePath: script,
104-
ContainerFilePath: "/docker-entrypoint-initdb.d/" + filepath.Base(script),
105-
FileMode: 0o755,
106-
}
107-
initScripts = append(initScripts, cf)
103+
filename := filepath.Base(script)
104+
appendInitScript(req, script, filename)
108105
}
109-
req.Files = append(req.Files, initScripts...)
106+
return nil
107+
}
108+
}
110109

110+
// WithOrderedInitScripts sets the init scripts to be run when the container starts.
111+
// The scripts will be run in the order that they are provided in this function.
112+
func WithOrderedInitScripts(scripts ...string) testcontainers.CustomizeRequestOption {
113+
return func(req *testcontainers.GenericContainerRequest) error {
114+
for idx, script := range scripts {
115+
filename := filepath.Base(script)
116+
containerFilePath := fmt.Sprintf("%06d-%s", idx, filename)
117+
appendInitScript(req, script, containerFilePath)
118+
}
111119
return nil
112120
}
113121
}
114122

123+
// Adds an initialization script to the Postgres container
124+
func appendInitScript(req *testcontainers.GenericContainerRequest, hostFilePath string, containerFilePath string) {
125+
cf := testcontainers.ContainerFile{
126+
HostFilePath: hostFilePath,
127+
ContainerFilePath: "/docker-entrypoint-initdb.d/" + containerFilePath,
128+
FileMode: 0o755,
129+
}
130+
req.Files = append(req.Files, cf)
131+
}
132+
115133
// WithPassword sets the initial password of the user to be created when the container starts
116134
// It is required for you to use the PostgreSQL image. It must not be empty or undefined.
117135
// This environment variable sets the superuser password for PostgreSQL.

modules/postgres/postgres_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import (
55
"database/sql"
66
"errors"
77
"fmt"
8+
"io"
89
"os"
910
"path/filepath"
11+
"strings"
1012
"testing"
1113
"time"
1214

@@ -19,6 +21,7 @@ import (
1921
"github.com/stretchr/testify/require"
2022

2123
"github.com/testcontainers/testcontainers-go"
24+
tcexec "github.com/testcontainers/testcontainers-go/exec"
2225
"github.com/testcontainers/testcontainers-go/modules/postgres"
2326
"github.com/testcontainers/testcontainers-go/wait"
2427
)
@@ -288,6 +291,58 @@ func TestWithInitScript(t *testing.T) {
288291
require.NotNil(t, result)
289292
}
290293

294+
func TestWithOrderedInitScript(t *testing.T) {
295+
ctx := context.Background()
296+
297+
ctr, err := postgres.Run(ctx,
298+
"postgres:15.2-alpine",
299+
// Executes first the init-user-db shell-script, then the do-insert-user SQL script
300+
// Using WithInitScripts, this would not work as
301+
postgres.WithOrderedInitScripts(
302+
filepath.Join("testdata", "init-user-db.sh"),
303+
filepath.Join("testdata", "aaaa-insert-user.sql"),
304+
),
305+
postgres.WithDatabase(dbname),
306+
postgres.WithUsername(user),
307+
postgres.WithPassword(password),
308+
postgres.BasicWaitStrategies(),
309+
)
310+
testcontainers.CleanupContainer(t, ctr)
311+
require.NoError(t, err)
312+
313+
// Test that init scripts have been correctly renamed
314+
c, reader, err := ctr.Exec(ctx, []string{"ls", "-l", "/docker-entrypoint-initdb.d"}, tcexec.Multiplexed())
315+
require.NoError(t, err)
316+
require.Equal(t, 0, c, "Expected to read init scripts from the container")
317+
318+
buf := new(strings.Builder)
319+
_, err = io.Copy(buf, reader)
320+
require.NoError(t, err)
321+
322+
initScripts := buf.String()
323+
strings.Contains(initScripts, "000000-init-user-db.sh")
324+
strings.Contains(initScripts, "000001-aaaa-insert-user.sql")
325+
326+
// explicitly set sslmode=disable because the container is not configured to use TLS
327+
connStr, err := ctr.ConnectionString(ctx, "sslmode=disable")
328+
require.NoError(t, err)
329+
330+
db, err := sql.Open("postgres", connStr)
331+
require.NoError(t, err)
332+
require.NotNil(t, db)
333+
defer db.Close()
334+
335+
// database created in init script. See testdata/init-user-db.sh
336+
rows, err := db.Query("SELECT COUNT(*) FROM testdb;")
337+
require.NoError(t, err)
338+
require.NotNil(t, rows)
339+
for rows.Next() {
340+
var count int
341+
rows.Scan(&count)
342+
require.Equal(t, 2, count)
343+
}
344+
}
345+
291346
func TestSnapshot(t *testing.T) {
292347
tests := []struct {
293348
name string
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-- Do not rename this file, it is named like this
2+
-- to test correct ordering behavior.
3+
--
4+
-- See TestWithOrderedInitScripts.
5+
INSERT INTO testdb (id, name) VALUES (2, 'second user')

0 commit comments

Comments
 (0)