Files
upaas/internal/service/deploy/deploy_container_test.go
user e2522f2017
All checks were successful
Check / check (pull_request) Successful in 1m53s
feat: add custom health check commands per app
Add configurable health check commands per app via a new
'healthcheck_command' field. When set, the command is passed
to Docker as a CMD-SHELL health check on the container.
When empty, the image's default health check is used.

Changes:
- Add migration 007 for healthcheck_command column on apps table
- Add HealthcheckCommand field to App model with full CRUD support
- Add buildHealthcheck() to docker client for CMD-SHELL config
- Pass health check command through CreateContainerOptions
- Add health check command input to app create/edit UI forms
- Extract optionalNullString helper to reduce handler complexity
- Update README features list

closes #81
2026-03-17 02:11:08 -07:00

108 lines
2.5 KiB
Go

package deploy_test
import (
"context"
"database/sql"
"log/slog"
"os"
"testing"
"sneak.berlin/go/upaas/internal/database"
"sneak.berlin/go/upaas/internal/docker"
"sneak.berlin/go/upaas/internal/models"
"sneak.berlin/go/upaas/internal/service/deploy"
)
func TestBuildContainerOptionsUsesImageID(t *testing.T) {
t.Parallel()
db := database.NewTestDatabase(t)
app := models.NewApp(db)
app.Name = "myapp"
err := app.Save(context.Background())
if err != nil {
t.Fatalf("failed to save app: %v", err)
}
log := slog.New(slog.NewTextHandler(os.Stderr, nil))
svc := deploy.NewTestService(log)
const expectedImageID = docker.ImageID("sha256:abc123def456")
opts, err := svc.BuildContainerOptionsExported(context.Background(), app, expectedImageID)
if err != nil {
t.Fatalf("buildContainerOptions returned error: %v", err)
}
if opts.Image != expectedImageID.String() {
t.Errorf("expected Image=%q, got %q", expectedImageID, opts.Image)
}
if opts.Name != "upaas-myapp" {
t.Errorf("expected Name=%q, got %q", "upaas-myapp", opts.Name)
}
}
func TestBuildContainerOptionsHealthcheckSet(t *testing.T) {
t.Parallel()
db := database.NewTestDatabase(t)
app := models.NewApp(db)
app.Name = "hc-app"
app.HealthcheckCommand = sql.NullString{
String: "curl -f http://localhost:8080/healthz || exit 1",
Valid: true,
}
err := app.Save(context.Background())
if err != nil {
t.Fatalf("failed to save app: %v", err)
}
log := slog.New(slog.NewTextHandler(os.Stderr, nil))
svc := deploy.NewTestService(log)
opts, err := svc.BuildContainerOptionsExported(
context.Background(), app, "sha256:test",
)
if err != nil {
t.Fatalf("buildContainerOptions returned error: %v", err)
}
expected := "curl -f http://localhost:8080/healthz || exit 1"
if opts.HealthcheckCommand != expected {
t.Errorf("expected HealthcheckCommand=%q, got %q", expected, opts.HealthcheckCommand)
}
}
func TestBuildContainerOptionsHealthcheckEmpty(t *testing.T) {
t.Parallel()
db := database.NewTestDatabase(t)
app := models.NewApp(db)
app.Name = "no-hc-app"
err := app.Save(context.Background())
if err != nil {
t.Fatalf("failed to save app: %v", err)
}
log := slog.New(slog.NewTextHandler(os.Stderr, nil))
svc := deploy.NewTestService(log)
opts, err := svc.BuildContainerOptionsExported(
context.Background(), app, "sha256:test",
)
if err != nil {
t.Fatalf("buildContainerOptions returned error: %v", err)
}
if opts.HealthcheckCommand != "" {
t.Errorf("expected empty HealthcheckCommand, got %q", opts.HealthcheckCommand)
}
}