Skip to content

Commit e7af3a0

Browse files
committed
chore: replace test.sh with patch content containing diff markers
1 parent 91cf3ee commit e7af3a0

1 file changed

Lines changed: 178 additions & 59 deletions

File tree

test.sh

Lines changed: 178 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,178 @@
1-
#!/usr/bin/env bash
2-
set -euo pipefail
3-
4-
OUTPUT_PATH=""
5-
MODE=""
6-
7-
while [[ $# -gt 0 ]]; do
8-
case "$1" in
9-
--output_path)
10-
OUTPUT_PATH="$2"
11-
shift 2
12-
;;
13-
base|new)
14-
MODE="$1"
15-
shift
16-
;;
17-
*)
18-
shift
19-
;;
20-
esac
21-
done
22-
23-
if [[ -z "$OUTPUT_PATH" ]]; then
24-
echo "Error: --output_path is required" >&2
25-
exit 1
26-
fi
27-
28-
if [[ -z "$MODE" ]]; then
29-
echo "Error: mode (base or new) is required" >&2
30-
exit 1
31-
fi
32-
33-
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
34-
cd "$REPO_ROOT/modules/postgres"
35-
36-
case "$MODE" in
37-
base)
38-
# Regression check: existing tests covering the postgres module's wait-strategy
39-
# path and basic container startup. Excludes the new reuse test so this mode
40-
# is independent of the solution patch.
41-
go test -v -count=1 -timeout 10m \
42-
-run "^(TestContainerWithWaitForSQL|TestWithConfigFile|TestWithInitScript|TestWithOrderedInitScript)$" \
43-
./... 2>&1 \
44-
| go-junit-report -set-exit-code > "$OUTPUT_PATH"
45-
;;
46-
new)
47-
# Regression test for false-positive ready signal on reused containers.
48-
# Fails on the base commit (log wait satisfied by stale logs before crash
49-
# recovery completes); passes once BasicWaitStrategies adds a live-state probe.
50-
go test -v -count=1 -timeout 10m \
51-
-run "^TestBasicWaitStrategies_reusedContainer$" \
52-
./... 2>&1 \
53-
| go-junit-report -set-exit-code > "$OUTPUT_PATH"
54-
;;
55-
*)
56-
echo "Error: mode must be 'base' or 'new'" >&2
57-
exit 1
58-
;;
59-
esac
1+
diff --git a/modules/postgres/wait_strategies_test.go b/modules/postgres/wait_strategies_test.go
2+
new file mode 100644
3+
index 0000000..b6e9ff2
4+
--- /dev/null
5+
+++ b/modules/postgres/wait_strategies_test.go
6+
@@ -0,0 +1,107 @@
7+
+package postgres_test
8+
+
9+
+import (
10+
+ "context"
11+
+ "database/sql"
12+
+ "fmt"
13+
+ "io"
14+
+ "strings"
15+
+ "testing"
16+
+ "time"
17+
+
18+
+ _ "github.com/jackc/pgx/v5/stdlib"
19+
+ "github.com/stretchr/testify/require"
20+
+
21+
+ "github.com/testcontainers/testcontainers-go"
22+
+ "github.com/testcontainers/testcontainers-go/modules/postgres"
23+
+)
24+
+
25+
+// TestBasicWaitStrategies_reusedContainer reproduces the false-positive ready
26+
+// signal reported in https://github.com/testcontainers/testcontainers-go/issues/3671:
27+
+// container logs survive restarts, so on a reused container a log-based wait
28+
+// strategy can be satisfied by the output of a previous run and unblock before
29+
+// the current postgres process accepts connections.
30+
+func TestBasicWaitStrategies_reusedContainer(t *testing.T) {
31+
+ ctx := context.Background()
32+
+
33+
+ reuseName := fmt.Sprintf("postgres-reused-wait-%d", time.Now().UnixNano())
34+
+
35+
+ run := func() *postgres.PostgresContainer {
36+
+ t.Helper()
37+
+ ctr, err := postgres.Run(ctx, "postgres:16-alpine",
38+
+ postgres.WithDatabase(dbname),
39+
+ postgres.WithUsername(user),
40+
+ postgres.WithPassword(password),
41+
+ postgres.BasicWaitStrategies(),
42+
+ testcontainers.WithReuseByName(reuseName),
43+
+ )
44+
+ testcontainers.CleanupContainer(t, ctr)
45+
+ require.NoError(t, err)
46+
+ return ctr
47+
+ }
48+
+
49+
+ connect := func(c *postgres.PostgresContainer) *sql.DB {
50+
+ t.Helper()
51+
+ connStr, err := c.ConnectionString(ctx, "sslmode=disable")
52+
+ require.NoError(t, err)
53+
+ db, err := sql.Open("pgx", connStr)
54+
+ require.NoError(t, err)
55+
+ t.Cleanup(func() { db.Close() })
56+
+ return db
57+
+ }
58+
+
59+
+ // recoveries counts how many crash recoveries the container has logged.
60+
+ // Logs accumulate across restarts of the same container, which is the very
61+
+ // property that makes log-based waits unsafe with reuse.
62+
+ recoveries := func(c *postgres.PostgresContainer) int {
63+
+ t.Helper()
64+
+ rc, err := c.Logs(ctx)
65+
+ require.NoError(t, err)
66+
+ defer rc.Close()
67+
+ logs, err := io.ReadAll(rc)
68+
+ require.NoError(t, err)
69+
+ return strings.Count(string(logs), "database system was not properly shut down")
70+
+ }
71+
+
72+
+ const rowCount = 3_000_000
73+
+
74+
+ ctr := run()
75+
+
76+
+ db := connect(ctr)
77+
+ _, err := db.ExecContext(ctx, "CREATE TABLE reuse_wait (v int)")
78+
+ require.NoError(t, err)
79+
+
80+
+ for i := 0; i < 2; i++ {
81+
+ // Generate WAL so that the unclean restart below forces crash recovery,
82+
+ // during which postgres does not accept connections yet.
83+
+ _, err = db.ExecContext(ctx, "TRUNCATE reuse_wait")
84+
+ require.NoError(t, err)
85+
+ _, err = db.ExecContext(ctx, fmt.Sprintf("INSERT INTO reuse_wait SELECT generate_series(1, %d)", rowCount))
86+
+ require.NoError(t, err)
87+
+
88+
+ // Stop with a zero timeout follows SIGTERM with an immediate SIGKILL.
89+
+ // The session above is kept open on purpose: postgres waits for it on
90+
+ // SIGTERM, so the SIGKILL always interrupts an unclean shutdown and the
91+
+ // next start is guaranteed to run crash recovery.
92+
+ noGrace := time.Duration(0)
93+
+ require.NoError(t, ctr.Stop(ctx, &noGrace))
94+
+
95+
+ ctr = run()
96+
+
97+
+ // The wait strategy must not unblock before postgres accepts connections:
98+
+ // a single attempt with no retries must succeed.
99+
+ db = connect(ctr)
100+
+ var rows int
101+
+ require.NoErrorf(t,
102+
+ db.QueryRowContext(ctx, "SELECT count(*) FROM reuse_wait").Scan(&rows),
103+
+ "reuse cycle %d: container reported ready before postgres accepted connections", i,
104+
+ )
105+
+ require.Equal(t, rowCount, rows)
106+
+
107+
+ // Guard the reproducer itself: every cycle must have gone through crash
108+
+ // recovery, otherwise the scenario above degraded silently.
109+
+ require.Equalf(t, i+1, recoveries(ctr),
110+
+ "reuse cycle %d: expected the restart to run crash recovery", i,
111+
+ )
112+
+ }
113+
+}
114+
diff --git a/test.sh b/test.sh
115+
new file mode 100755
116+
index 0000000..f57cb3f
117+
--- /dev/null
118+
+++ b/test.sh
119+
@@ -0,0 +1,59 @@
120+
+#!/usr/bin/env bash
121+
+set -euo pipefail
122+
+
123+
+OUTPUT_PATH=""
124+
+MODE=""
125+
+
126+
+while [[ $# -gt 0 ]]; do
127+
+ case "$1" in
128+
+ --output_path)
129+
+ OUTPUT_PATH="$2"
130+
+ shift 2
131+
+ ;;
132+
+ base|new)
133+
+ MODE="$1"
134+
+ shift
135+
+ ;;
136+
+ *)
137+
+ shift
138+
+ ;;
139+
+ esac
140+
+done
141+
+
142+
+if [[ -z "$OUTPUT_PATH" ]]; then
143+
+ echo "Error: --output_path is required" >&2
144+
+ exit 1
145+
+fi
146+
+
147+
+if [[ -z "$MODE" ]]; then
148+
+ echo "Error: mode (base or new) is required" >&2
149+
+ exit 1
150+
+fi
151+
+
152+
+REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
153+
+cd "$REPO_ROOT/modules/postgres"
154+
+
155+
+case "$MODE" in
156+
+ base)
157+
+ # Regression check: existing tests covering the postgres module's wait-strategy
158+
+ # path and basic container startup. Excludes the new reuse test so this mode
159+
+ # is independent of the solution patch.
160+
+ go test -v -count=1 -timeout 10m \
161+
+ -run "^(TestContainerWithWaitForSQL|TestWithConfigFile|TestWithInitScript|TestWithOrderedInitScript)$" \
162+
+ ./... 2>&1 \
163+
+ | go-junit-report -set-exit-code > "$OUTPUT_PATH"
164+
+ ;;
165+
+ new)
166+
+ # Regression test for false-positive ready signal on reused containers.
167+
+ # Fails on the base commit (log wait satisfied by stale logs before crash
168+
+ # recovery completes); passes once BasicWaitStrategies adds a live-state probe.
169+
+ go test -v -count=1 -timeout 10m \
170+
+ -run "^TestBasicWaitStrategies_reusedContainer$" \
171+
+ ./... 2>&1 \
172+
+ | go-junit-report -set-exit-code > "$OUTPUT_PATH"
173+
+ ;;
174+
+ *)
175+
+ echo "Error: mode must be 'base' or 'new'" >&2
176+
+ exit 1
177+
+ ;;
178+
+esac

0 commit comments

Comments
 (0)