package config import ( "errors" "log/slog" "strings" "testing" "time" ) func TestDefaults(t *testing.T) { cfg, err := Load(func(k string) string { if k == "OLLAMA_URL" { return "http://127.0.0.1:11435" } return "" }) if err != nil { t.Fatal(err) } if cfg.ListenOllama != ":11434" || cfg.ListenComfy != ":8188" { t.Fatalf("listen addrs = %s %s", cfg.ListenOllama, cfg.ListenComfy) } if cfg.ComfyURL != "" { t.Fatalf("ComfyURL default = %q, want empty (disabled)", cfg.ComfyURL) } if cfg.UnloadTimeout != time.Minute || cfg.JobTimeout != 15*time.Minute { t.Fatalf("timeouts = %v %v", cfg.UnloadTimeout, cfg.JobTimeout) } if !cfg.AutoUpdate || cfg.UpdateInterval != 6*time.Hour { t.Fatalf("update = %v %v", cfg.AutoUpdate, cfg.UpdateInterval) } if cfg.LogLevel != slog.LevelWarn { t.Fatalf("log level = %v", cfg.LogLevel) } } func TestLoadRequiresConsumer(t *testing.T) { _, err := Load(func(string) string { return "" }) if err == nil || !strings.Contains(err.Error(), "OLLAMA_URL") { t.Fatalf("err = %v, want missing-consumer error", err) } if !errors.Is(err, ErrNoConsumer) { t.Fatalf("err = %v, want errors.Is(err, ErrNoConsumer)", err) } } func TestAppVersion(t *testing.T) { load := func(appVer string) (Config, error) { return Load(func(k string) string { switch k { case "OLLAMA_URL": return "http://127.0.0.1:11435" case "APP_VER": return appVer } return "" }) } cfg, err := load("") if err != nil || cfg.AppVersion != "stable" { t.Fatalf("default AppVersion = %q, err %v; want stable", cfg.AppVersion, err) } for _, v := range []string{"dev", "stable"} { if cfg, err := load(v); err != nil || cfg.AppVersion != v { t.Fatalf("APP_VER=%s: got %q, err %v", v, cfg.AppVersion, err) } } if cfg, err := load("1.2.3"); err != nil || cfg.AppVersion != "v1.2.3" { t.Fatalf("APP_VER=1.2.3: got %q, err %v; want normalized v1.2.3", cfg.AppVersion, err) } if _, err := load("nightly"); err == nil { t.Fatal("APP_VER=nightly: want validation error") } } func TestComfyCmdRequiresURL(t *testing.T) { _, err := Load(func(k string) string { if k == "COMFY_CMD" { return "python main.py" } return "" }) if err == nil || !strings.Contains(err.Error(), "COMFY_CMD requires COMFY_URL") { t.Fatalf("err = %v, want COMFY_CMD/COMFY_URL validation error", err) } if _, err := Load(func(k string) string { switch k { case "COMFY_CMD": return "python main.py" case "COMFY_URL": return "http://127.0.0.1:8188" } return "" }); err != nil { t.Fatalf("COMFY_CMD with COMFY_URL must load: %v", err) } } func TestParseEnvFile(t *testing.T) { input := `# comment OLLAMA_URL=http://host:11435 LOGLEVEL=debug SPACED = value with spaces ` values, err := ParseEnvFile(strings.NewReader(input)) if err != nil { t.Fatal(err) } if values["OLLAMA_URL"] != "http://host:11435" { t.Fatalf("OLLAMA_URL = %q", values["OLLAMA_URL"]) } if values["LOGLEVEL"] != "debug" { t.Fatalf("LOGLEVEL = %q", values["LOGLEVEL"]) } if values["SPACED"] != "value with spaces" { t.Fatalf("SPACED = %q", values["SPACED"]) } } func TestParseEnvFileMalformed(t *testing.T) { _, err := ParseEnvFile(strings.NewReader("OK=1\nNOT_A_PAIR\n")) if err == nil || !strings.Contains(err.Error(), "line 2") { t.Fatalf("err = %v, want line 2 error", err) } } func TestEnvOverridesFile(t *testing.T) { file := map[string]string{"OLLAMA_URL": "http://file:1", "UNLOAD_TIMEOUT": "42s"} env := map[string]string{"OLLAMA_URL": "http://env:2"} getenv := func(k string) string { if v := env[k]; v != "" { return v } return file[k] } cfg, err := Load(getenv) if err != nil { t.Fatal(err) } if cfg.OllamaURL != "http://env:2" { t.Fatalf("OllamaURL = %q, want env value", cfg.OllamaURL) } if cfg.UnloadTimeout != 42*time.Second { t.Fatalf("UnloadTimeout = %v, want file value", cfg.UnloadTimeout) } } func TestLoadErrors(t *testing.T) { for _, tc := range []struct{ key, value string }{ {"UNLOAD_TIMEOUT", "bogus"}, {"PROMPT_CAPTURE_LIMIT", "-5"}, {"AUTO_UPDATE", "maybe"}, {"LOGLEVEL", "shouty"}, {"LOG_FORMAT", "yaml"}, {"LLM_BUSY_MODE", "bogus"}, {"LLM_BUSY_STATUS", "200"}, {"BUSY_RETRY_AFTER", "0"}, {"GPU_FOREIGN_VRAM_MB", "-1"}, {"GAME_POLL_INTERVAL", "bogus"}, } { _, err := Load(func(k string) string { if k == tc.key { return tc.value } if k == "OLLAMA_URL" { return "http://127.0.0.1:11435" } return "" }) if err == nil { t.Errorf("%s=%s: expected error", tc.key, tc.value) } } } func TestGameDetectionSettings(t *testing.T) { cfg, err := Load(func(k string) string { switch k { case "OLLAMA_URL": return "http://127.0.0.1:11435" case "GAME_PROCS": return " cyberpunk2077.exe, hl2.exe ,, " case "GPU_FOREIGN_VRAM_MB": return "1024" case "GPU_IGNORE_PROCS": return "ollama, my-trainer" } return "" }) if err != nil { t.Fatal(err) } if len(cfg.GameProcs) != 2 || cfg.GameProcs[0] != "cyberpunk2077.exe" || cfg.GameProcs[1] != "hl2.exe" { t.Fatalf("GameProcs = %v", cfg.GameProcs) } if cfg.GPUForeignVRAMMB != 1024 { t.Fatalf("GPUForeignVRAMMB = %d", cfg.GPUForeignVRAMMB) } if len(cfg.GPUIgnoreProcs) != 2 || cfg.GPUIgnoreProcs[1] != "my-trainer" { t.Fatalf("GPUIgnoreProcs = %v", cfg.GPUIgnoreProcs) } if cfg.GamePollInterval != 15*time.Second { t.Fatalf("GamePollInterval = %v, want 15s default", cfg.GamePollInterval) } // Defaults: both detection paths off, ignore list covers our consumers. cfg, err = Load(func(k string) string { if k == "OLLAMA_URL" { return "http://127.0.0.1:11435" } return "" }) if err != nil { t.Fatal(err) } if len(cfg.GameProcs) != 0 || cfg.GPUForeignVRAMMB != 0 { t.Fatalf("detection must be off by default: %v %d", cfg.GameProcs, cfg.GPUForeignVRAMMB) } if len(cfg.GPUIgnoreProcs) == 0 { t.Fatal("GPUIgnoreProcs default must not be empty") } } func TestComfyDirOnlyEnablesManaged(t *testing.T) { // COMFY_DIR without COMFY_CMD and without COMFY_URL is a mistake. _, err := Load(func(k string) string { if k == "COMFY_DIR" { return `C:\ComfyUI` } return "" }) if err == nil || !strings.Contains(err.Error(), "COMFY_DIR") { t.Fatalf("err = %v, want COMFY_DIR/COMFY_URL validation error", err) } // With COMFY_URL it loads — the launch command is derived from the dir. if _, err := Load(func(k string) string { switch k { case "COMFY_DIR": return `C:\ComfyUI` case "COMFY_URL": return "http://127.0.0.1:8189" } return "" }); err != nil { t.Fatalf("COMFY_DIR with COMFY_URL must load: %v", err) } }