Some checks failed
CI / v3 / Check ui (pull_request) Failing after 15s
CI / v3 / Test backend (pull_request) Failing after 16s
CI / v3 / Docker / backend (pull_request) Has been skipped
CI / v3 / Docker / runner (pull_request) Has been skipped
CI / v3 / Docker / ui (pull_request) Has been skipped
- Remove all pre-v3 code: scraper, ui-v2, backend v1, ios v1+v2, legacy CI workflows - Flatten v3/ contents to repo root - Add Doppler secrets management (project=libnovel, config=prd) - Add justfile with doppler run wrappers for all docker compose commands - Strip hardcoded env fallbacks from docker-compose.yml - Add minimal README.md - Clean up .gitignore
128 lines
4.1 KiB
Go
128 lines
4.1 KiB
Go
package config_test
|
|
|
|
import (
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/libnovel/backend/internal/config"
|
|
)
|
|
|
|
func TestLoad_Defaults(t *testing.T) {
|
|
// Unset all relevant vars so we test pure defaults.
|
|
unset := []string{
|
|
"LOG_LEVEL",
|
|
"POCKETBASE_URL", "POCKETBASE_ADMIN_EMAIL", "POCKETBASE_ADMIN_PASSWORD",
|
|
"MINIO_ENDPOINT", "MINIO_PUBLIC_ENDPOINT", "MINIO_ACCESS_KEY", "MINIO_SECRET_KEY",
|
|
"MINIO_USE_SSL", "MINIO_PUBLIC_USE_SSL",
|
|
"MINIO_BUCKET_CHAPTERS", "MINIO_BUCKET_AUDIO", "MINIO_BUCKET_AVATARS",
|
|
"KOKORO_URL", "KOKORO_VOICE",
|
|
"BACKEND_HTTP_ADDR",
|
|
"RUNNER_POLL_INTERVAL", "RUNNER_MAX_CONCURRENT_SCRAPE", "RUNNER_MAX_CONCURRENT_AUDIO",
|
|
"RUNNER_WORKER_ID", "RUNNER_WORKERS", "RUNNER_TIMEOUT",
|
|
}
|
|
for _, k := range unset {
|
|
t.Setenv(k, "")
|
|
}
|
|
|
|
cfg := config.Load()
|
|
|
|
if cfg.LogLevel != "info" {
|
|
t.Errorf("LogLevel: want info, got %q", cfg.LogLevel)
|
|
}
|
|
if cfg.PocketBase.URL != "http://localhost:8090" {
|
|
t.Errorf("PocketBase.URL: want http://localhost:8090, got %q", cfg.PocketBase.URL)
|
|
}
|
|
if cfg.MinIO.BucketChapters != "chapters" {
|
|
t.Errorf("MinIO.BucketChapters: want chapters, got %q", cfg.MinIO.BucketChapters)
|
|
}
|
|
if cfg.MinIO.UseSSL != false {
|
|
t.Errorf("MinIO.UseSSL: want false, got %v", cfg.MinIO.UseSSL)
|
|
}
|
|
if cfg.MinIO.PublicUseSSL != true {
|
|
t.Errorf("MinIO.PublicUseSSL: want true, got %v", cfg.MinIO.PublicUseSSL)
|
|
}
|
|
if cfg.Kokoro.DefaultVoice != "af_bella" {
|
|
t.Errorf("Kokoro.DefaultVoice: want af_bella, got %q", cfg.Kokoro.DefaultVoice)
|
|
}
|
|
if cfg.HTTP.Addr != ":8080" {
|
|
t.Errorf("HTTP.Addr: want :8080, got %q", cfg.HTTP.Addr)
|
|
}
|
|
if cfg.Runner.PollInterval != 30*time.Second {
|
|
t.Errorf("Runner.PollInterval: want 30s, got %v", cfg.Runner.PollInterval)
|
|
}
|
|
if cfg.Runner.MaxConcurrentScrape != 1 {
|
|
t.Errorf("Runner.MaxConcurrentScrape: want 1, got %d", cfg.Runner.MaxConcurrentScrape)
|
|
}
|
|
if cfg.Runner.MaxConcurrentAudio != 1 {
|
|
t.Errorf("Runner.MaxConcurrentAudio: want 1, got %d", cfg.Runner.MaxConcurrentAudio)
|
|
}
|
|
}
|
|
|
|
func TestLoad_EnvOverride(t *testing.T) {
|
|
t.Setenv("LOG_LEVEL", "debug")
|
|
t.Setenv("POCKETBASE_URL", "https://pb.libnovel.cc")
|
|
t.Setenv("MINIO_USE_SSL", "true")
|
|
t.Setenv("MINIO_PUBLIC_USE_SSL", "false")
|
|
t.Setenv("RUNNER_POLL_INTERVAL", "1m")
|
|
t.Setenv("RUNNER_MAX_CONCURRENT_SCRAPE", "5")
|
|
t.Setenv("RUNNER_WORKER_ID", "homelab-01")
|
|
t.Setenv("BACKEND_HTTP_ADDR", ":9090")
|
|
t.Setenv("KOKORO_URL", "https://kokoro.libnovel.cc")
|
|
|
|
cfg := config.Load()
|
|
|
|
if cfg.LogLevel != "debug" {
|
|
t.Errorf("LogLevel: want debug, got %q", cfg.LogLevel)
|
|
}
|
|
if cfg.PocketBase.URL != "https://pb.libnovel.cc" {
|
|
t.Errorf("PocketBase.URL: want https://pb.libnovel.cc, got %q", cfg.PocketBase.URL)
|
|
}
|
|
if !cfg.MinIO.UseSSL {
|
|
t.Error("MinIO.UseSSL: want true")
|
|
}
|
|
if cfg.MinIO.PublicUseSSL {
|
|
t.Error("MinIO.PublicUseSSL: want false")
|
|
}
|
|
if cfg.Runner.PollInterval != time.Minute {
|
|
t.Errorf("Runner.PollInterval: want 1m, got %v", cfg.Runner.PollInterval)
|
|
}
|
|
if cfg.Runner.MaxConcurrentScrape != 5 {
|
|
t.Errorf("Runner.MaxConcurrentScrape: want 5, got %d", cfg.Runner.MaxConcurrentScrape)
|
|
}
|
|
if cfg.Runner.WorkerID != "homelab-01" {
|
|
t.Errorf("Runner.WorkerID: want homelab-01, got %q", cfg.Runner.WorkerID)
|
|
}
|
|
if cfg.HTTP.Addr != ":9090" {
|
|
t.Errorf("HTTP.Addr: want :9090, got %q", cfg.HTTP.Addr)
|
|
}
|
|
if cfg.Kokoro.URL != "https://kokoro.libnovel.cc" {
|
|
t.Errorf("Kokoro.URL: want https://kokoro.libnovel.cc, got %q", cfg.Kokoro.URL)
|
|
}
|
|
}
|
|
|
|
func TestLoad_InvalidInt_FallsToDefault(t *testing.T) {
|
|
t.Setenv("RUNNER_MAX_CONCURRENT_SCRAPE", "notanumber")
|
|
cfg := config.Load()
|
|
if cfg.Runner.MaxConcurrentScrape != 1 {
|
|
t.Errorf("want default 1, got %d", cfg.Runner.MaxConcurrentScrape)
|
|
}
|
|
}
|
|
|
|
func TestLoad_InvalidDuration_FallsToDefault(t *testing.T) {
|
|
t.Setenv("RUNNER_POLL_INTERVAL", "notaduration")
|
|
cfg := config.Load()
|
|
if cfg.Runner.PollInterval != 30*time.Second {
|
|
t.Errorf("want default 30s, got %v", cfg.Runner.PollInterval)
|
|
}
|
|
}
|
|
|
|
func TestLoad_WorkerID_FallsToHostname(t *testing.T) {
|
|
t.Setenv("RUNNER_WORKER_ID", "")
|
|
cfg := config.Load()
|
|
host, _ := os.Hostname()
|
|
if host != "" && cfg.Runner.WorkerID != host {
|
|
t.Errorf("want hostname %q, got %q", host, cfg.Runner.WorkerID)
|
|
}
|
|
}
|