Skip to content

Commit 693c335

Browse files
committed
feat: add native database support for e2e tests without Docker
Add a new native package (internal/sqltest/native/) that can install and start PostgreSQL and MySQL servers directly on Linux systems without requiring Docker. This enables running end-to-end tests in environments where Docker is not available (e.g., CI environments without Docker). Key changes: - Add internal/sqltest/native/ package with support for: - PostgreSQL installation and startup via apt-get and systemctl/pg_ctlcluster - MySQL installation and startup via apt-get and systemctl/service - Graceful fallback when installation fails (e.g., no network) - Update internal/sqltest/local/ to fall back to native installation when Docker is not available - Update internal/endtoend/endtoend_test.go to: - Check environment variables first (POSTGRESQL_SERVER_URI, MYSQL_SERVER_URI) - Fall back to Docker, then native installation - Skip database-specific tests when that database is unavailable - Support partial database availability (run with just PostgreSQL) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent a8b20cc commit 693c335

File tree

6 files changed

+573
-21
lines changed

6 files changed

+573
-21
lines changed

internal/endtoend/endtoend_test.go

Lines changed: 71 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/sqlc-dev/sqlc/internal/config"
1919
"github.com/sqlc-dev/sqlc/internal/opts"
2020
"github.com/sqlc-dev/sqlc/internal/sqltest/docker"
21+
"github.com/sqlc-dev/sqlc/internal/sqltest/native"
2122
)
2223

2324
func lineEndings() cmp.Option {
@@ -113,23 +114,63 @@ func TestReplay(t *testing.T) {
113114
ctx := context.Background()
114115

115116
var mysqlURI, postgresURI string
116-
if err := docker.Installed(); err == nil {
117-
{
118-
host, err := docker.StartPostgreSQLServer(ctx)
119-
if err != nil {
120-
t.Fatalf("starting postgresql failed: %s", err)
117+
118+
// First, check environment variables
119+
if uri := os.Getenv("POSTGRESQL_SERVER_URI"); uri != "" {
120+
postgresURI = uri
121+
}
122+
if uri := os.Getenv("MYSQL_SERVER_URI"); uri != "" {
123+
mysqlURI = uri
124+
}
125+
126+
// Try Docker for any missing databases
127+
if postgresURI == "" || mysqlURI == "" {
128+
if err := docker.Installed(); err == nil {
129+
if postgresURI == "" {
130+
host, err := docker.StartPostgreSQLServer(ctx)
131+
if err != nil {
132+
t.Logf("docker postgresql startup failed: %s", err)
133+
} else {
134+
postgresURI = host
135+
}
136+
}
137+
if mysqlURI == "" {
138+
host, err := docker.StartMySQLServer(ctx)
139+
if err != nil {
140+
t.Logf("docker mysql startup failed: %s", err)
141+
} else {
142+
mysqlURI = host
143+
}
121144
}
122-
postgresURI = host
123145
}
124-
{
125-
host, err := docker.StartMySQLServer(ctx)
126-
if err != nil {
127-
t.Fatalf("starting mysql failed: %s", err)
146+
}
147+
148+
// Try native installation for any missing databases (Linux only)
149+
if postgresURI == "" || mysqlURI == "" {
150+
if err := native.Supported(); err == nil {
151+
if postgresURI == "" {
152+
host, err := native.StartPostgreSQLServer(ctx)
153+
if err != nil {
154+
t.Logf("native postgresql startup failed: %s", err)
155+
} else {
156+
postgresURI = host
157+
}
158+
}
159+
if mysqlURI == "" {
160+
host, err := native.StartMySQLServer(ctx)
161+
if err != nil {
162+
t.Logf("native mysql startup failed: %s", err)
163+
} else {
164+
mysqlURI = host
165+
}
128166
}
129-
mysqlURI = host
130167
}
131168
}
132169

170+
// Log which databases are available
171+
t.Logf("PostgreSQL available: %v (URI: %s)", postgresURI != "", postgresURI)
172+
t.Logf("MySQL available: %v (URI: %s)", mysqlURI != "", mysqlURI)
173+
133174
contexts := map[string]textContext{
134175
"base": {
135176
Mutate: func(t *testing.T, path string) func(*config.Config) { return func(c *config.Config) {} },
@@ -138,26 +179,37 @@ func TestReplay(t *testing.T) {
138179
"managed-db": {
139180
Mutate: func(t *testing.T, path string) func(*config.Config) {
140181
return func(c *config.Config) {
141-
c.Servers = []config.Server{
142-
{
182+
// Only add servers that are available
183+
var servers []config.Server
184+
if postgresURI != "" {
185+
servers = append(servers, config.Server{
143186
Name: "postgres",
144187
Engine: config.EnginePostgreSQL,
145188
URI: postgresURI,
146-
},
147-
148-
{
189+
})
190+
}
191+
if mysqlURI != "" {
192+
servers = append(servers, config.Server{
149193
Name: "mysql",
150194
Engine: config.EngineMySQL,
151195
URI: mysqlURI,
152-
},
196+
})
153197
}
198+
c.Servers = servers
199+
154200
for i := range c.SQL {
155201
switch c.SQL[i].Engine {
156202
case config.EnginePostgreSQL:
203+
if postgresURI == "" {
204+
t.Skipf("PostgreSQL not available")
205+
}
157206
c.SQL[i].Database = &config.Database{
158207
Managed: true,
159208
}
160209
case config.EngineMySQL:
210+
if mysqlURI == "" {
211+
t.Skipf("MySQL not available")
212+
}
161213
c.SQL[i].Database = &config.Database{
162214
Managed: true,
163215
}
@@ -172,8 +224,8 @@ func TestReplay(t *testing.T) {
172224
}
173225
},
174226
Enabled: func() bool {
175-
err := docker.Installed()
176-
return err == nil
227+
// Enable if at least one database is available
228+
return postgresURI != "" || mysqlURI != ""
177229
},
178230
},
179231
}

internal/sqltest/local/mysql.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
migrate "github.com/sqlc-dev/sqlc/internal/migrations"
1515
"github.com/sqlc-dev/sqlc/internal/sql/sqlpath"
1616
"github.com/sqlc-dev/sqlc/internal/sqltest/docker"
17+
"github.com/sqlc-dev/sqlc/internal/sqltest/native"
1718
)
1819

1920
var mysqlSync sync.Once
@@ -31,8 +32,15 @@ func MySQL(t *testing.T, migrations []string) string {
3132
t.Fatal(err)
3233
}
3334
dburi = u
35+
} else if ierr := native.Supported(); ierr == nil {
36+
// Fall back to native installation when Docker is not available
37+
u, err := native.StartMySQLServer(ctx)
38+
if err != nil {
39+
t.Fatal(err)
40+
}
41+
dburi = u
3442
} else {
35-
t.Skip("MYSQL_SERVER_URI is empty")
43+
t.Skip("MYSQL_SERVER_URI is empty and neither Docker nor native installation is available")
3644
}
3745
}
3846

internal/sqltest/local/postgres.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/sqlc-dev/sqlc/internal/pgx/poolcache"
1717
"github.com/sqlc-dev/sqlc/internal/sql/sqlpath"
1818
"github.com/sqlc-dev/sqlc/internal/sqltest/docker"
19+
"github.com/sqlc-dev/sqlc/internal/sqltest/native"
1920
)
2021

2122
var flight singleflight.Group
@@ -41,8 +42,15 @@ func postgreSQL(t *testing.T, migrations []string, rw bool) string {
4142
t.Fatal(err)
4243
}
4344
dburi = u
45+
} else if ierr := native.Supported(); ierr == nil {
46+
// Fall back to native installation when Docker is not available
47+
u, err := native.StartPostgreSQLServer(ctx)
48+
if err != nil {
49+
t.Fatal(err)
50+
}
51+
dburi = u
4452
} else {
45-
t.Skip("POSTGRESQL_SERVER_URI is empty")
53+
t.Skip("POSTGRESQL_SERVER_URI is empty and neither Docker nor native installation is available")
4654
}
4755
}
4856

internal/sqltest/native/enabled.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package native
2+
3+
import (
4+
"fmt"
5+
"os/exec"
6+
"runtime"
7+
)
8+
9+
// Supported returns nil if native database installation is supported on this platform.
10+
// Currently only Linux (Ubuntu/Debian) is supported.
11+
func Supported() error {
12+
if runtime.GOOS != "linux" {
13+
return fmt.Errorf("native database installation only supported on linux, got %s", runtime.GOOS)
14+
}
15+
// Check if apt-get is available (Debian/Ubuntu)
16+
if _, err := exec.LookPath("apt-get"); err != nil {
17+
return fmt.Errorf("apt-get not found: %w", err)
18+
}
19+
return nil
20+
}

0 commit comments

Comments
 (0)