Bootstraps Vellum as a single Go binary with one SQLite database. - core: env-driven config, slog logging, SQLite (WAL) with pooled pragmas, embedded migration runner keyed on PRAGMA user_version - migrations/0001_core.sql: identity, media, user state, infra. Media items are global; all per-user state (progress, favorites, grants) is a join. One polymorphic progress table covers every media kind. - httpapi: request-ID, structured request logging, panic recovery, JSON endpoints for /healthz and /api/v1/version - multi-arch Dockerfile (pure-Go SQLite, CGO_ENABLED=0) with an entrypoint that honours PUID/PGID like the rest of the self-hosted stack - tests for migration idempotency, FK enforcement, WAL, config, middleware Media does not exist yet. Nothing is exposed beyond health and version.
77 lines
1.9 KiB
Go
77 lines
1.9 KiB
Go
package core
|
|
|
|
import (
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestLoadConfigDefaults(t *testing.T) {
|
|
// No environment set: every field should have a usable default.
|
|
cfg, err := LoadConfig()
|
|
if err != nil {
|
|
t.Fatalf("LoadConfig: %v", err)
|
|
}
|
|
if cfg.Listen != ":7788" {
|
|
t.Errorf("Listen = %q, want :7788", cfg.Listen)
|
|
}
|
|
if !filepath.IsAbs(cfg.DataDir) {
|
|
t.Errorf("DataDir = %q, want absolute", cfg.DataDir)
|
|
}
|
|
if cfg.DBPath != filepath.Join(cfg.DataDir, "vellum.db") {
|
|
t.Errorf("DBPath = %q, want <DataDir>/vellum.db", cfg.DBPath)
|
|
}
|
|
if cfg.LogLevel != "info" {
|
|
t.Errorf("LogLevel = %q, want info", cfg.LogLevel)
|
|
}
|
|
}
|
|
|
|
func TestLoadConfigFromEnv(t *testing.T) {
|
|
t.Setenv("VELLUM_LISTEN", "127.0.0.1:9999")
|
|
t.Setenv("VELLUM_DATA_DIR", "/tmp/vellum-data")
|
|
t.Setenv("VELLUM_MEDIA_DIRS", "/media/a:/media/b")
|
|
t.Setenv("VELLUM_SESSION_TTL", "48h")
|
|
|
|
cfg, err := LoadConfig()
|
|
if err != nil {
|
|
t.Fatalf("LoadConfig: %v", err)
|
|
}
|
|
if cfg.Listen != "127.0.0.1:9999" {
|
|
t.Errorf("Listen = %q", cfg.Listen)
|
|
}
|
|
if cfg.DataDir != "/tmp/vellum-data" {
|
|
t.Errorf("DataDir = %q", cfg.DataDir)
|
|
}
|
|
if len(cfg.MediaDirs) != 2 || cfg.MediaDirs[0] != "/media/a" {
|
|
t.Errorf("MediaDirs = %v", cfg.MediaDirs)
|
|
}
|
|
if cfg.SessionTTL.Hours() != 48 {
|
|
t.Errorf("SessionTTL = %v, want 48h", cfg.SessionTTL)
|
|
}
|
|
}
|
|
|
|
func TestValidateRejectsMissingMediaDir(t *testing.T) {
|
|
cfg := Config{
|
|
DataDir: t.TempDir(),
|
|
ConfigDir: t.TempDir(),
|
|
MediaDirs: []string{"/definitely/not/a/real/path"},
|
|
}
|
|
err := cfg.Validate()
|
|
if err == nil {
|
|
t.Fatal("expected error for missing media dir")
|
|
}
|
|
if !strings.Contains(err.Error(), "/definitely/not/a/real/path") {
|
|
t.Errorf("error %q should name the offending path", err)
|
|
}
|
|
}
|
|
|
|
func TestValidateCreatesDirs(t *testing.T) {
|
|
base := t.TempDir()
|
|
cfg := Config{
|
|
DataDir: filepath.Join(base, "data"),
|
|
ConfigDir: filepath.Join(base, "config"),
|
|
}
|
|
if err := cfg.Validate(); err != nil {
|
|
t.Fatalf("Validate: %v", err)
|
|
}
|
|
}
|