160 lines
4.2 KiB
Go
160 lines
4.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"log/slog"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
embeddedpostgres "github.com/fergusstrange/embedded-postgres"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
|
|
"github.com/iliaivanov/spec-kit-remote/cmd/dev-pod-api/internal/model"
|
|
"github.com/iliaivanov/spec-kit-remote/cmd/dev-pod-api/internal/store"
|
|
)
|
|
|
|
var testPool *pgxpool.Pool
|
|
|
|
func TestMain(m *testing.M) {
|
|
runtimePath := filepath.Join(os.TempDir(), "embedded-postgres-main-test")
|
|
|
|
postgres := embeddedpostgres.NewDatabase(
|
|
embeddedpostgres.DefaultConfig().
|
|
Port(15437).
|
|
Database("testbootstrap").
|
|
RuntimePath(runtimePath),
|
|
)
|
|
if err := postgres.Start(); err != nil {
|
|
log.Fatalf("start embedded postgres: %v", err)
|
|
}
|
|
|
|
ctx := context.Background()
|
|
pool, err := pgxpool.New(ctx, "postgres://postgres:postgres@localhost:15437/testbootstrap?sslmode=disable")
|
|
if err != nil {
|
|
postgres.Stop()
|
|
log.Fatalf("connect to test db: %v", err)
|
|
}
|
|
testPool = pool
|
|
|
|
code := m.Run()
|
|
|
|
pool.Close()
|
|
if err := postgres.Stop(); err != nil {
|
|
log.Printf("stop embedded postgres: %v", err)
|
|
}
|
|
os.Exit(code)
|
|
}
|
|
|
|
func newTestStore(t *testing.T) *store.Store {
|
|
t.Helper()
|
|
ctx := context.Background()
|
|
st := store.New(testPool)
|
|
if err := st.Migrate(ctx); err != nil {
|
|
t.Fatalf("migrate: %v", err)
|
|
}
|
|
// Clean tables for test isolation
|
|
for _, table := range []string{"runners", "usage_records", "api_keys", "users"} {
|
|
if _, err := testPool.Exec(ctx, fmt.Sprintf("DELETE FROM %s", table)); err != nil {
|
|
t.Fatalf("clean %s: %v", table, err)
|
|
}
|
|
}
|
|
return st
|
|
}
|
|
|
|
func TestBootstrapAdmin_CreatesAdminUser(t *testing.T) {
|
|
st := newTestStore(t)
|
|
ctx := context.Background()
|
|
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))
|
|
|
|
// Ensure admin doesn't exist yet
|
|
_, err := st.GetUser(ctx, "admin")
|
|
if err == nil {
|
|
t.Fatal("admin user should not exist before bootstrap")
|
|
}
|
|
|
|
// Run bootstrap
|
|
bootstrapAdmin(ctx, st, logger)
|
|
|
|
// Verify admin user was created
|
|
user, err := st.GetUser(ctx, "admin")
|
|
if err != nil {
|
|
t.Fatalf("get admin user: %v", err)
|
|
}
|
|
if user.ID != "admin" {
|
|
t.Errorf("expected user ID %q, got %q", "admin", user.ID)
|
|
}
|
|
if user.Quota.MaxConcurrentPods != 10 {
|
|
t.Errorf("expected MaxConcurrentPods 10, got %d", user.Quota.MaxConcurrentPods)
|
|
}
|
|
}
|
|
|
|
func TestBootstrapAdmin_SkipsIfExists(t *testing.T) {
|
|
st := newTestStore(t)
|
|
ctx := context.Background()
|
|
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))
|
|
|
|
// Pre-create admin with default quota
|
|
_, err := st.CreateUser(ctx, "admin", model.DefaultQuota())
|
|
if err != nil {
|
|
t.Fatalf("create admin: %v", err)
|
|
}
|
|
|
|
// Bootstrap should not fail or change anything
|
|
bootstrapAdmin(ctx, st, logger)
|
|
|
|
user, err := st.GetUser(ctx, "admin")
|
|
if err != nil {
|
|
t.Fatalf("get admin: %v", err)
|
|
}
|
|
// Should still have default quota (not elevated)
|
|
if user.Quota.MaxConcurrentPods != model.DefaultQuota().MaxConcurrentPods {
|
|
t.Errorf("expected MaxConcurrentPods %d, got %d",
|
|
model.DefaultQuota().MaxConcurrentPods, user.Quota.MaxConcurrentPods)
|
|
}
|
|
}
|
|
|
|
func TestBootstrapAdmin_UsesEnvKey(t *testing.T) {
|
|
st := newTestStore(t)
|
|
ctx := context.Background()
|
|
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))
|
|
|
|
// Set a known bootstrap key
|
|
t.Setenv("ADMIN_BOOTSTRAP_KEY", "dpk_testbootstrapkey12345678")
|
|
|
|
bootstrapAdmin(ctx, st, logger)
|
|
|
|
// Validate using the known key
|
|
user, role, err := st.ValidateKey(ctx, "dpk_testbootstrapkey12345678")
|
|
if err != nil {
|
|
t.Fatalf("validate key: %v", err)
|
|
}
|
|
if user.ID != "admin" {
|
|
t.Errorf("expected user %q, got %q", "admin", user.ID)
|
|
}
|
|
if role != model.RoleAdmin {
|
|
t.Errorf("expected role %q, got %q", model.RoleAdmin, role)
|
|
}
|
|
}
|
|
|
|
func TestBootstrapAdmin_AutoGeneratesKey(t *testing.T) {
|
|
st := newTestStore(t)
|
|
ctx := context.Background()
|
|
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))
|
|
|
|
// Ensure no bootstrap key is set
|
|
t.Setenv("ADMIN_BOOTSTRAP_KEY", "")
|
|
|
|
bootstrapAdmin(ctx, st, logger)
|
|
|
|
// Admin user should exist
|
|
user, err := st.GetUser(ctx, "admin")
|
|
if err != nil {
|
|
t.Fatalf("get admin: %v", err)
|
|
}
|
|
if user.ID != "admin" {
|
|
t.Errorf("expected user %q, got %q", "admin", user.ID)
|
|
}
|
|
}
|