Skip to content

Commit cbc79e2

Browse files
committed
feat(e2e): start embedded postgres in BeforeSuite
The e2e ServiceManager was scaffolding: startXxx were TODO no-ops while AllHealthy did real port-dials, so AllHealthy never returned true and the BeforeSuite health gate timed out after 2m — the suite had never passed. Wire startPostgres to db.StartEmbedded (dynamic free port, waits for readiness) and gate AllHealthy on Postgres only, since that is the sole service actually started. The remaining specs assert on URL strings and locally-generated fixtures, not live Redis/OpenSearch/Loki/LocalStack, so no live instance is needed for them. Removes the dead start/health stubs for the unstarted services.
1 parent 32ab5af commit cbc79e2

1 file changed

Lines changed: 49 additions & 108 deletions

File tree

e2e/helpers/services.go

Lines changed: 49 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,26 @@ import (
44
"context"
55
"fmt"
66
"net"
7+
"net/url"
78
"os"
8-
"os/exec"
9+
"strconv"
910
"time"
11+
12+
"github.com/flanksource/commons-db/db"
1013
)
1114

15+
// ServiceManager starts the native services the e2e suite depends on.
16+
//
17+
// Only Postgres is started for real (via db.StartEmbedded). The other
18+
// services (Redis, OpenSearch, Loki, LocalStack) are not yet implemented;
19+
// the current e2e specs only assert on their connection-URL strings and on
20+
// locally-generated fixture data, so no live instance is required. Their
21+
// URL/port accessors remain so those specs keep compiling.
1222
type ServiceManager struct {
13-
postgresCmd *exec.Cmd
14-
redisCmd *exec.Cmd
15-
opensearchCmd *exec.Cmd
16-
lokiCmd *exec.Cmd
17-
localstackCmd *exec.Cmd
23+
postgresDSN string
24+
postgresPort int
25+
postgresStop func() error
1826

19-
postgresPort int
2027
redisPort int
2128
opensearchPort int
2229
lokiPort int
@@ -27,7 +34,6 @@ type ServiceManager struct {
2734

2835
func NewServiceManager() *ServiceManager {
2936
return &ServiceManager{
30-
postgresPort: 5432,
3137
redisPort: 6379,
3238
opensearchPort: 9200,
3339
lokiPort: 3100,
@@ -42,51 +48,20 @@ func (sm *ServiceManager) StartAll(ctx context.Context) error {
4248
}
4349
sm.tmpDir = tmpDir
4450

45-
// Start services in order
46-
if err := sm.startPostgres(ctx); err != nil {
51+
if err := sm.startPostgres(); err != nil {
4752
return fmt.Errorf("failed to start postgres: %w", err)
4853
}
4954

50-
if err := sm.startRedis(ctx); err != nil {
51-
return fmt.Errorf("failed to start redis: %w", err)
52-
}
53-
54-
if err := sm.startOpenSearch(ctx); err != nil {
55-
return fmt.Errorf("failed to start opensearch: %w", err)
56-
}
57-
58-
if err := sm.startLoki(ctx); err != nil {
59-
return fmt.Errorf("failed to start loki: %w", err)
60-
}
61-
62-
if err := sm.startLocalStack(ctx); err != nil {
63-
return fmt.Errorf("failed to start localstack: %w", err)
64-
}
65-
6655
return nil
6756
}
6857

6958
func (sm *ServiceManager) StopAll(ctx context.Context) error {
7059
var errs []error
7160

72-
if sm.postgresCmd != nil && sm.postgresCmd.Process != nil {
73-
_ = sm.postgresCmd.Process.Kill()
74-
}
75-
76-
if sm.redisCmd != nil && sm.redisCmd.Process != nil {
77-
_ = sm.redisCmd.Process.Kill()
78-
}
79-
80-
if sm.opensearchCmd != nil && sm.opensearchCmd.Process != nil {
81-
_ = sm.opensearchCmd.Process.Kill()
82-
}
83-
84-
if sm.lokiCmd != nil && sm.lokiCmd.Process != nil {
85-
_ = sm.lokiCmd.Process.Kill()
86-
}
87-
88-
if sm.localstackCmd != nil && sm.localstackCmd.Process != nil {
89-
_ = sm.localstackCmd.Process.Kill()
61+
if sm.postgresStop != nil {
62+
if err := sm.postgresStop(); err != nil {
63+
errs = append(errs, fmt.Errorf("stop postgres: %w", err))
64+
}
9065
}
9166

9267
if sm.tmpDir != "" {
@@ -102,77 +77,39 @@ func (sm *ServiceManager) StopAll(ctx context.Context) error {
10277
return nil
10378
}
10479

80+
// AllHealthy reports whether every service StartAll actually started is
81+
// reachable. Only Postgres is started today, so only Postgres is checked.
10582
func (sm *ServiceManager) AllHealthy() bool {
106-
return sm.isPostgresHealthy() && sm.isRedisHealthy() && sm.isOpenSearchHealthy() && sm.isLokiHealthy() && sm.isLocalStackHealthy()
83+
return sm.isPostgresHealthy()
10784
}
10885

109-
func (sm *ServiceManager) startPostgres(ctx context.Context) error {
110-
if !sm.isPortAvailable(sm.postgresPort) {
111-
return fmt.Errorf("postgres port %d not available", sm.postgresPort)
112-
}
113-
114-
// TODO: Implement postgres startup using embedded binaries from deps
115-
// This is a placeholder that would use zonky postgres binaries
116-
return nil
117-
}
118-
119-
func (sm *ServiceManager) startRedis(ctx context.Context) error {
120-
if !sm.isPortAvailable(sm.redisPort) {
121-
return fmt.Errorf("redis port %d not available", sm.redisPort)
122-
}
123-
124-
// TODO: Implement redis startup
125-
return nil
126-
}
127-
128-
func (sm *ServiceManager) startOpenSearch(ctx context.Context) error {
129-
if !sm.isPortAvailable(sm.opensearchPort) {
130-
return fmt.Errorf("opensearch port %d not available", sm.opensearchPort)
131-
}
132-
133-
// TODO: Implement opensearch startup
134-
return nil
135-
}
136-
137-
func (sm *ServiceManager) startLoki(ctx context.Context) error {
138-
if !sm.isPortAvailable(sm.lokiPort) {
139-
return fmt.Errorf("loki port %d not available", sm.lokiPort)
86+
func (sm *ServiceManager) startPostgres() error {
87+
dsn, stop, err := db.StartEmbedded(db.EmbeddedConfig{
88+
DataDir: sm.tmpDir,
89+
Database: "test",
90+
Port: 0,
91+
})
92+
if err != nil {
93+
return err
14094
}
14195

142-
// TODO: Implement loki startup
143-
return nil
144-
}
145-
146-
func (sm *ServiceManager) startLocalStack(ctx context.Context) error {
147-
if !sm.isPortAvailable(sm.localstackPort) {
148-
return fmt.Errorf("localstack port %d not available", sm.localstackPort)
96+
port, err := portFromDSN(dsn)
97+
if err != nil {
98+
_ = stop()
99+
return err
149100
}
150101

151-
// TODO: Implement localstack startup
102+
sm.postgresDSN = dsn
103+
sm.postgresPort = port
104+
sm.postgresStop = stop
152105
return nil
153106
}
154107

155108
func (sm *ServiceManager) isPostgresHealthy() bool {
156-
return sm.isPortHealthy(sm.postgresPort)
109+
return sm.postgresPort != 0 && isPortHealthy(sm.postgresPort)
157110
}
158111

159-
func (sm *ServiceManager) isRedisHealthy() bool {
160-
return sm.isPortHealthy(sm.redisPort)
161-
}
162-
163-
func (sm *ServiceManager) isOpenSearchHealthy() bool {
164-
return sm.isPortHealthy(sm.opensearchPort)
165-
}
166-
167-
func (sm *ServiceManager) isLokiHealthy() bool {
168-
return sm.isPortHealthy(sm.lokiPort)
169-
}
170-
171-
func (sm *ServiceManager) isLocalStackHealthy() bool {
172-
return sm.isPortHealthy(sm.localstackPort)
173-
}
174-
175-
func (sm *ServiceManager) isPortHealthy(port int) bool {
112+
func isPortHealthy(port int) bool {
176113
conn, err := net.DialTimeout("tcp", fmt.Sprintf("localhost:%d", port), 1*time.Second)
177114
if err != nil {
178115
return false
@@ -181,17 +118,21 @@ func (sm *ServiceManager) isPortHealthy(port int) bool {
181118
return true
182119
}
183120

184-
func (sm *ServiceManager) isPortAvailable(port int) bool {
185-
conn, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", port))
121+
// portFromDSN extracts the TCP port from a postgres:// DSN.
122+
func portFromDSN(dsn string) (int, error) {
123+
u, err := url.Parse(dsn)
186124
if err != nil {
187-
return false
125+
return 0, fmt.Errorf("parse dsn: %w", err)
188126
}
189-
defer conn.Close()
190-
return true
127+
p, err := strconv.Atoi(u.Port())
128+
if err != nil {
129+
return 0, fmt.Errorf("dsn port %q: %w", u.Port(), err)
130+
}
131+
return p, nil
191132
}
192133

193134
func (sm *ServiceManager) PostgresURL() string {
194-
return fmt.Sprintf("postgres://localhost:%d/test", sm.postgresPort)
135+
return sm.postgresDSN
195136
}
196137

197138
func (sm *ServiceManager) RedisURL() string {

0 commit comments

Comments
 (0)