@@ -20,7 +20,13 @@ const (
2020 postgresImage = "docker.io/postgres:16.0-alpine"
2121)
2222
23- var postgresDataSource string
23+ // Connection strings for the three databases created in [TestMain]
24+ // These are used in other tests
25+ var (
26+ postgresDataSource string
27+ postgresDataSourceRR1 string
28+ postgresDataSourceRR2 string
29+ )
2430
2531func withSchemaSQL () testcontainers.CustomizeRequestOption {
2632 return func (req * testcontainers.GenericContainerRequest ) error {
@@ -34,24 +40,74 @@ func withSchemaSQL() testcontainers.CustomizeRequestOption {
3440}
3541
3642func TestMain (m * testing.M ) {
37- var cleanups [] func ()
38- cleanup := func ( fn func ()) {
39- cleanups = append ( cleanups , fn )
40- }
41- fatal := func ( args ... any ) {
42- fmt . Fprintln ( os . Stderr , args ... )
43- for _ , fn := range cleanups {
44- fn ()
43+ ctx := context . Background ()
44+ cleanups , err := createPostgresContainers ( ctx )
45+ if err != nil {
46+ fmt . Printf ( "did not create postgres containers: %v \n " , err )
47+ for _ , cleanup := range cleanups {
48+ if cleanup != nil {
49+ cleanup ()
50+ }
4551 }
4652 os .Exit (1 )
4753 }
4854
49- ctx := context .Background ()
55+ // nolint:gocritic // The docs for Run specify that the returned int is to be passed to os.Exit
56+ retCode := m .Run ()
57+ for _ , cleanup := range cleanups {
58+ if cleanup != nil {
59+ cleanup ()
60+ }
61+ }
62+ os .Exit (retCode )
63+ }
64+
65+ // createPostgresContainers creates 3 databases as containers. The first is intended to mimic a master database and
66+ // the last 2 are intended to mimic read replicas.
67+ // A []func() is returned to cleanups.
68+ // Package-level connection strings are set.
69+ func createPostgresContainers (ctx context.Context ) ([]func (), error ) {
70+ // Database connection strings are the same, except the port
71+ connString := func (port string ) string {
72+ return fmt .Sprintf ("postgres://%s:%s@localhost:%s/%s?sslmode=disable&application_name=test" , dbUser , dbPassword , port , dbName )
73+ }
74+
75+ var cleanups []func ()
76+
77+ // Create the master database
78+ cM , mpM , err := createPostgresContainer (ctx , "init-db.sh" )
79+ cleanups = append (cleanups , cM )
80+ if err != nil {
81+ return cleanups , err
82+ }
83+ postgresDataSource = connString (mpM )
84+
85+ // Create first read replica
86+ cRR1 , mpRR1 , err := createPostgresContainer (ctx , "init-db-rr.sh" )
87+ cleanups = append (cleanups , cRR1 )
88+ if err != nil {
89+ return cleanups , err
90+ }
91+ postgresDataSourceRR1 = connString (mpRR1 )
92+
93+ // Create second read replica
94+ cRR2 , mpRR2 , err := createPostgresContainer (ctx , "init-db-rr.sh" )
95+ cleanups = append (cleanups , cRR2 )
96+ if err != nil {
97+ return cleanups , err
98+ }
99+ postgresDataSourceRR2 = connString (mpRR2 )
100+
101+ return cleanups , nil
102+ }
103+
104+ // createPostgresContainer creates a single postgres container on the specified port
105+ func createPostgresContainer (ctx context.Context , initFilename string ) (func (), string , error ) {
50106 postgresContainer , err := postgres .Run (ctx , postgresImage ,
51107 postgres .WithDatabase (dbName ),
52108 postgres .WithUsername (dbUser ),
53109 postgres .WithPassword (dbPassword ),
54- postgres .WithInitScripts (filepath .Join ("testdata" , "init-db.sh" )),
110+ postgres .WithInitScripts (filepath .Join ("testdata" , initFilename )),
55111 withSchemaSQL (),
56112 testcontainers .WithWaitStrategy (
57113 wait .ForLog ("database system is ready to accept connections" ).
@@ -60,28 +116,27 @@ func TestMain(m *testing.M) {
60116 ),
61117 )
62118 if err != nil {
63- fatal ("error creating postgres container:" , err )
119+ return nil , "" , fmt . Errorf ("error creating postgres container: %w " , err )
64120 }
65- cleanup (func () {
121+
122+ cleanup := func () {
66123 if err := postgresContainer .Terminate (ctx ); err != nil {
67124 fmt .Fprintln (os .Stderr , "error terminating postgres:" , err )
68125 }
69- })
126+ }
70127
71128 postgresState , err := postgresContainer .State (ctx )
72129 if err != nil {
73- fatal ( err )
130+ return cleanup , "" , fmt . Errorf ( "checking container state: %w" , err )
74131 }
75132 if ! postgresState .Running {
76- fatal ("Postgres status: " , postgresState .Status )
133+ return cleanup , "" , fmt . Errorf ("Postgres status %q is not \" running \" " , postgresState .Status )
77134 }
78135
79- postgresPort , err := postgresContainer .MappedPort (ctx , "5432/tcp" )
136+ mp , err := postgresContainer .MappedPort (ctx , "5432/tcp" )
80137 if err != nil {
81- fatal ( err )
138+ return cleanup , "" , fmt . Errorf ( "mapped port 5432/tcp does not seem to be available: %w" , err )
82139 }
83140
84- postgresDataSource = fmt .Sprintf ("postgres://%s:%s@localhost:%s/%s?sslmode=disable&application_name=test" , dbUser , dbPassword , postgresPort .Port (), dbName )
85-
86- os .Exit (m .Run ())
141+ return cleanup , mp .Port (), nil
87142}
0 commit comments