Skip to content

Commit 917ba00

Browse files
committed
loopdb: switch from docker to embedded-postgres
embedded-postgres is faster and doesn't depend on docker.
1 parent e79b1d4 commit 917ba00

File tree

3 files changed

+93
-80
lines changed

3 files changed

+93
-80
lines changed

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ require (
1111
github.com/btcsuite/btcwallet/wtxmgr v1.5.6
1212
github.com/davecgh/go-spew v1.1.1
1313
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0
14+
github.com/fergusstrange/embedded-postgres v1.25.0
1415
github.com/fortytw2/leaktest v1.3.0
1516
github.com/golang-migrate/migrate/v4 v4.17.0
1617
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0
@@ -32,7 +33,6 @@ require (
3233
github.com/lightningnetwork/lnd/ticker v1.1.1
3334
github.com/lightningnetwork/lnd/tlv v1.3.2
3435
github.com/lightningnetwork/lnd/tor v1.1.6
35-
github.com/ory/dockertest/v3 v3.10.0
3636
github.com/stretchr/testify v1.10.0
3737
github.com/urfave/cli-docs/v3 v3.1.1-0.20251020101624-bec07369b4f6
3838
github.com/urfave/cli/v3 v3.4.1
@@ -81,7 +81,6 @@ require (
8181
github.com/docker/go-connections v0.5.0 // indirect
8282
github.com/docker/go-units v0.5.0 // indirect
8383
github.com/dustin/go-humanize v1.0.1 // indirect
84-
github.com/fergusstrange/embedded-postgres v1.25.0 // indirect
8584
github.com/go-logr/logr v1.4.3 // indirect
8685
github.com/go-logr/stdr v1.2.2 // indirect
8786
github.com/go-macaroon-bakery/macaroonpb v1.0.0 // indirect
@@ -142,6 +141,7 @@ require (
142141
github.com/opencontainers/go-digest v1.0.0 // indirect
143142
github.com/opencontainers/image-spec v1.1.0 // indirect
144143
github.com/opencontainers/runc v1.2.8 // indirect
144+
github.com/ory/dockertest/v3 v3.10.0 // indirect
145145
github.com/pkg/errors v0.9.1 // indirect
146146
github.com/pmezard/go-difflib v1.0.0 // indirect
147147
github.com/prometheus/client_golang v1.14.0 // indirect

loopdb/postgres.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ const (
2020

2121
var (
2222
// DefaultPostgresFixtureLifetime is the default maximum time a Postgres
23-
// test fixture is being kept alive. After that time the docker
24-
// container will be terminated forcefully, even if the tests aren't
25-
// fully executed yet. So this time needs to be chosen correctly to be
26-
// longer than the longest expected individual test run time.
23+
// test fixture is being kept alive. After that time the embedded
24+
// Postgres process will be terminated forcefully, even if the tests
25+
// aren't fully executed yet. So this time needs to be chosen correctly
26+
// to be longer than the longest expected individual test run time.
2727
DefaultPostgresFixtureLifetime = 10 * time.Minute
2828
)
2929

loopdb/postgres_fixture.go

Lines changed: 87 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -3,101 +3,86 @@ package loopdb
33
import (
44
"context"
55
"database/sql"
6-
"fmt"
7-
"strconv"
8-
"strings"
6+
"net"
7+
"os"
8+
"path/filepath"
9+
"sync"
910
"testing"
1011
"time"
1112

13+
embeddedpostgres "github.com/fergusstrange/embedded-postgres"
1214
_ "github.com/lib/pq"
13-
"github.com/ory/dockertest/v3"
14-
"github.com/ory/dockertest/v3/docker"
1515
"github.com/stretchr/testify/require"
1616
)
1717

1818
const (
1919
testPgUser = "test"
2020
testPgPass = "test"
2121
testPgDBName = "test"
22-
PostgresTag = "11"
2322
)
2423

25-
// TestPgFixture is a test fixture that starts a Postgres 11 instance in a
26-
// docker container.
24+
// TestPgFixture is a test fixture that starts a Postgres 11 instance using an
25+
// embedded Postgres runtime.
2726
type TestPgFixture struct {
28-
db *sql.DB
29-
pool *dockertest.Pool
30-
resource *dockertest.Resource
31-
host string
32-
port int
27+
db *sql.DB
28+
pg *embeddedpostgres.EmbeddedPostgres
29+
host string
30+
port int
31+
expiryTimer *time.Timer
32+
stopOnce sync.Once
3333
}
3434

35-
// NewTestPgFixture constructs a new TestPgFixture starting up a docker
36-
// container running Postgres 11. The started container will expire in after
37-
// the passed duration.
35+
// NewTestPgFixture constructs a new TestPgFixture starting up an embedded
36+
// Postgres 11 server. The process will be auto-stopped after the specified
37+
// expiry if TearDown is not called first.
3838
func NewTestPgFixture(t *testing.T, expiry time.Duration) *TestPgFixture {
39-
// Use a sensible default on Windows (tcp/http) and linux/osx (socket)
40-
// by specifying an empty endpoint.
41-
pool, err := dockertest.NewPool("")
42-
require.NoError(t, err, "Could not connect to docker")
43-
44-
// Pulls an image, creates a container based on it and runs it.
45-
resource, err := pool.RunWithOptions(&dockertest.RunOptions{
46-
Repository: "postgres",
47-
Tag: PostgresTag,
48-
Env: []string{
49-
fmt.Sprintf("POSTGRES_USER=%v", testPgUser),
50-
fmt.Sprintf("POSTGRES_PASSWORD=%v", testPgPass),
51-
fmt.Sprintf("POSTGRES_DB=%v", testPgDBName),
52-
"listen_addresses='*'",
53-
},
54-
Cmd: []string{
55-
"postgres",
56-
"-c", "log_statement=all",
57-
"-c", "log_destination=stderr",
58-
},
59-
}, func(config *docker.HostConfig) {
60-
// Set AutoRemove to true so that stopped container goes away
61-
// by itself.
62-
config.AutoRemove = true
63-
config.RestartPolicy = docker.RestartPolicy{Name: "no"}
64-
})
65-
require.NoError(t, err, "Could not start resource")
66-
67-
hostAndPort := resource.GetHostPort("5432/tcp")
68-
parts := strings.Split(hostAndPort, ":")
69-
host := parts[0]
70-
port, err := strconv.ParseInt(parts[1], 10, 64)
71-
require.NoError(t, err)
39+
port := getFreePort(t)
40+
runtimePath := t.TempDir()
41+
logDir := filepath.Join(runtimePath, "logs")
42+
require.NoError(t, os.MkdirAll(logDir, 0o755))
43+
config := embeddedpostgres.DefaultConfig().
44+
Version(embeddedpostgres.V11).
45+
Database(testPgDBName).
46+
Username(testPgUser).
47+
Password(testPgPass).
48+
Port(uint32(port)).
49+
RuntimePath(runtimePath).
50+
StartParameters(map[string]string{
51+
"listen_addresses": "127.0.0.1",
52+
53+
// Logging collector only captures stderr output, so
54+
// keep this destination in sync with the log file
55+
// settings below.
56+
"log_statement": "all",
57+
"log_destination": "stderr",
58+
"logging_collector": "on",
59+
"log_directory": logDir,
60+
"log_filename": "postgres.log",
61+
})
62+
63+
pg := embeddedpostgres.NewDatabase(config)
64+
require.NoError(t, pg.Start(), "Could not start embedded Postgres")
7265

7366
fixture := &TestPgFixture{
74-
host: host,
75-
port: int(port),
67+
host: "127.0.0.1",
68+
port: port,
69+
pg: pg,
7670
}
77-
databaseURL := fixture.GetDSN()
78-
log.Infof("Connecting to Postgres fixture: %v\n", databaseURL)
79-
80-
// Tell docker to hard kill the container in "expiry" seconds.
81-
require.NoError(t, resource.Expire(uint(expiry.Seconds())))
8271

83-
// Exponential backoff-retry, because the application in the container
84-
// might not be ready to accept connections yet.
85-
pool.MaxWait = 120 * time.Second
72+
if expiry > 0 {
73+
fixture.expiryTimer = time.AfterFunc(expiry, func() {
74+
log.Warnf("Postgres fixture exceeded lifetime; tearing down")
75+
fixture.TearDown(t)
76+
})
77+
}
8678

87-
var testDB *sql.DB
88-
err = pool.Retry(func() error {
89-
testDB, err = sql.Open("postgres", databaseURL)
90-
if err != nil {
91-
return err
92-
}
93-
return testDB.Ping()
94-
})
95-
require.NoError(t, err, "Could not connect to docker")
79+
databaseURL := fixture.GetDSN()
80+
log.Infof("Connecting to Postgres fixture: %v\n", databaseURL)
9681

97-
// Now fill in the rest of the fixture.
82+
testDB, err := sql.Open("postgres", databaseURL)
83+
require.NoError(t, err, "Could not open connection to Postgres fixture")
84+
require.NoError(t, testDB.Ping(), "Could not connect to embedded Postgres")
9885
fixture.db = testDB
99-
fixture.pool = pool
100-
fixture.resource = resource
10186

10287
return fixture
10388
}
@@ -119,10 +104,26 @@ func (f *TestPgFixture) GetConfig() *PostgresConfig {
119104
}
120105
}
121106

122-
// TearDown stops the underlying docker container.
107+
// TearDown stops the embedded Postgres process and releases resources.
123108
func (f *TestPgFixture) TearDown(t *testing.T) {
124-
err := f.pool.Purge(f.resource)
125-
require.NoError(t, err, "Could not purge resource")
109+
if f.expiryTimer != nil {
110+
f.expiryTimer.Stop()
111+
}
112+
113+
f.stopOnce.Do(func() {
114+
if f.db != nil {
115+
err := f.db.Close()
116+
require.NoErrorf(t, err, "failed to close postgres")
117+
}
118+
119+
if f.pg != nil {
120+
err := f.pg.Stop()
121+
require.NoErrorf(t, err, "failed to stop postgres")
122+
}
123+
124+
f.db = nil
125+
f.pg = nil
126+
})
126127
}
127128

128129
// ClearDB clears the database.
@@ -137,3 +138,15 @@ func (f *TestPgFixture) ClearDB(t *testing.T) {
137138
)
138139
require.NoError(t, err)
139140
}
141+
142+
// getFreePort returns an available TCP port on localhost.
143+
func getFreePort(t *testing.T) int {
144+
listener, err := net.Listen("tcp", "127.0.0.1:0")
145+
require.NoError(t, err)
146+
147+
defer func() {
148+
require.NoError(t, listener.Close())
149+
}()
150+
151+
return listener.Addr().(*net.TCPAddr).Port
152+
}

0 commit comments

Comments
 (0)