ci: add postgres backend boot smoke test (#1398)

This commit is contained in:
Carlos Florêncio
2026-06-16 22:50:14 +01:00
committed by GitHub
parent 327f7d4ec0
commit 9702cffff5
2 changed files with 181 additions and 0 deletions
+31
View File
@@ -152,6 +152,37 @@ jobs:
apps/backend/test-results.json
retention-days: 30
postgres-boot:
name: Backend Postgres boot
runs-on: ubuntu-latest
timeout-minutes: 10
container: ghcr.io/kdlbs/kandev-ci:build-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: kandev
POSTGRES_PASSWORD: kandev
POSTGRES_DB: kandev_admin
options: >-
--health-cmd "pg_isready -U kandev -d kandev_admin"
--health-interval 5s
--health-timeout 5s
--health-retries 12
defaults:
run:
shell: bash
working-directory: apps/backend
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Run Postgres boot smoke test
env:
KANDEV_TEST_POSTGRES_DSN: host=postgres port=5432 user=kandev password=kandev dbname=kandev_admin sslmode=disable
run: go test -v -race -run TestPostgresBootInitializesRepositories ./cmd/kandev
test-windows:
name: Backend (windows)
runs-on: windows-latest
@@ -0,0 +1,150 @@
package main
import (
"fmt"
"net/url"
"os"
"strings"
"testing"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgconn"
_ "github.com/jackc/pgx/v5/stdlib"
"github.com/jmoiron/sqlx"
"github.com/kandev/kandev/internal/common/config"
"github.com/kandev/kandev/internal/testutil"
)
func TestPostgresBootInitializesRepositories(t *testing.T) {
adminDSN := testutil.PostgresDSNFromEnv(t)
adminInfo, err := parsePostgresConnInfo(adminDSN)
if err != nil {
t.Fatalf("parse postgres dsn: %v", err)
}
admin := openPostgresAdmin(t, adminDSN)
dbName := "kandev_boot_" + strings.ReplaceAll(uuid.NewString(), "-", "")
if _, err := admin.Exec("CREATE DATABASE " + dbName); err != nil {
t.Fatalf("create postgres database %s: %v", dbName, err)
}
t.Cleanup(func() {
_, _ = admin.Exec("DROP DATABASE IF EXISTS " + dbName + " WITH (FORCE)")
})
cfg := &config.Config{
HomeDir: t.TempDir(),
Database: config.DatabaseConfig{
Driver: "postgres",
Host: adminInfo.Host,
Port: adminInfo.Port,
User: adminInfo.User,
Password: adminInfo.Password,
DBName: dbName,
SSLMode: adminInfo.SSLMode,
MaxConns: 5,
MinConns: 1,
},
}
log := newTestLogger()
pool, repos, cleanups, err := provideRepositories(cfg, log, "test-postgres-boot")
if err != nil {
t.Fatalf("provide repositories with postgres: %v", err)
}
if pool == nil || repos == nil {
t.Fatal("provideRepositories returned nil pool or repositories")
}
t.Cleanup(func() {
for i := len(cleanups) - 1; i >= 0; i-- {
if cleanups[i] != nil {
_ = cleanups[i]()
}
}
})
}
type postgresConnInfo struct {
Host string
Port int
User string
Password string
SSLMode string
}
func openPostgresAdmin(t *testing.T, dsn string) *sqlx.DB {
t.Helper()
db, err := sqlx.Open("pgx", dsn)
if err != nil {
t.Fatalf("open postgres admin connection: %v", err)
}
t.Cleanup(func() {
_ = db.Close()
})
if err := db.Ping(); err != nil {
t.Fatalf("ping postgres admin connection: %v", err)
}
return db
}
func parsePostgresConnInfo(dsn string) (postgresConnInfo, error) {
cfg, err := pgconn.ParseConfig(dsn)
if err != nil {
return postgresConnInfo{}, err
}
info := postgresConnInfo{
Host: cfg.Host,
Port: int(cfg.Port),
User: cfg.User,
Password: cfg.Password,
SSLMode: postgresSSLMode(dsn),
}
if info.Host == "" {
info.Host = "localhost"
}
if info.Port == 0 {
info.Port = 5432
}
return info, nil
}
func postgresSSLMode(dsn string) string {
if parsed, err := url.Parse(dsn); err == nil && parsed.Scheme != "" {
if mode := parsed.Query().Get("sslmode"); mode != "" {
return mode
}
}
for _, field := range strings.Fields(dsn) {
key, value, ok := strings.Cut(field, "=")
if ok && strings.EqualFold(key, "sslmode") {
return strings.Trim(value, "'\"")
}
}
if mode := os.Getenv("PGSSLMODE"); mode != "" {
return mode
}
return "disable"
}
func TestParsePostgresConnInfo(t *testing.T) {
info, err := parsePostgresConnInfo(
"host=postgres port=5433 user=kandev password=secret dbname=kandev_test sslmode=require",
)
if err != nil {
t.Fatalf("parse conn info: %v", err)
}
want := postgresConnInfo{
Host: "postgres",
Port: 5433,
User: "kandev",
Password: "secret",
SSLMode: "require",
}
if info != want {
t.Fatalf("conn info = %s, want %s", formatConnInfo(info), formatConnInfo(want))
}
}
func formatConnInfo(info postgresConnInfo) string {
return fmt.Sprintf("{host:%s port:%d user:%s sslmode:%s}", info.Host, info.Port, info.User, info.SSLMode)
}