package config import ( "os" "path/filepath" "testing" ) func TestDefault(t *testing.T) { cfg := Default() if cfg.Auth.APIURL != "https://torrentclaw.com" { t.Errorf("default APIURL = %q, want https://torrentclaw.com", cfg.Auth.APIURL) } if cfg.Download.PreferredMethod != "auto" { t.Errorf("default PreferredMethod = %q, want auto", cfg.Download.PreferredMethod) } if cfg.Download.MaxConcurrent != 3 { t.Errorf("default MaxConcurrent = %d, want 3", cfg.Download.MaxConcurrent) } if cfg.General.Country != "US" { t.Errorf("default Country = %q, want US", cfg.General.Country) } if cfg.Daemon.StatusInterval != "" { t.Errorf("default StatusInterval = %q, want empty", cfg.Daemon.StatusInterval) } } func TestLoadMissingFile(t *testing.T) { cfg, err := Load("/nonexistent/path/config.toml") if err != nil { t.Fatalf("Load nonexistent should return defaults, got err: %v", err) } if cfg.Auth.APIURL != "https://torrentclaw.com" { t.Errorf("missing file should return default APIURL, got %q", cfg.Auth.APIURL) } } func TestSaveAndLoad(t *testing.T) { tmp := t.TempDir() path := filepath.Join(tmp, "config.toml") cfg := Default() cfg.Auth.APIKey = "tc_test123" cfg.Auth.APIURL = "https://custom.example.com" cfg.General.Country = "ES" cfg.Download.Dir = "/media/downloads" cfg.Agent.ID = "agent-uuid-123" cfg.Agent.Name = "Test Machine" if err := Save(cfg, path); err != nil { t.Fatalf("Save failed: %v", err) } // File should exist if _, err := os.Stat(path); os.IsNotExist(err) { t.Fatal("config file was not created") } // No .tmp file left behind if _, err := os.Stat(path + ".tmp"); !os.IsNotExist(err) { t.Error("temp file was not cleaned up") } // Load it back loaded, err := Load(path) if err != nil { t.Fatalf("Load failed: %v", err) } if loaded.Auth.APIKey != "tc_test123" { t.Errorf("APIKey = %q, want tc_test123", loaded.Auth.APIKey) } if loaded.Auth.APIURL != "https://custom.example.com" { t.Errorf("APIURL = %q, want https://custom.example.com", loaded.Auth.APIURL) } if loaded.General.Country != "ES" { t.Errorf("Country = %q, want ES", loaded.General.Country) } if loaded.Download.Dir != "/media/downloads" { t.Errorf("Dir = %q, want /media/downloads", loaded.Download.Dir) } if loaded.Agent.ID != "agent-uuid-123" { t.Errorf("AgentID = %q, want agent-uuid-123", loaded.Agent.ID) } if loaded.Agent.Name != "Test Machine" { t.Errorf("AgentName = %q, want Test Machine", loaded.Agent.Name) } } func TestLoadPreservesDefaults(t *testing.T) { tmp := t.TempDir() path := filepath.Join(tmp, "config.toml") // Write partial config (only auth section) os.WriteFile(path, []byte(`[auth] api_key = "tc_partial" `), 0o644) cfg, err := Load(path) if err != nil { t.Fatalf("Load failed: %v", err) } if cfg.Auth.APIKey != "tc_partial" { t.Errorf("APIKey = %q, want tc_partial", cfg.Auth.APIKey) } // Defaults should be preserved for missing sections if cfg.Auth.APIURL != "https://torrentclaw.com" { t.Errorf("APIURL should default, got %q", cfg.Auth.APIURL) } if cfg.Download.MaxConcurrent != 3 { t.Errorf("MaxConcurrent should default to 3, got %d", cfg.Download.MaxConcurrent) } if cfg.General.Country != "US" { t.Errorf("Country should default to US, got %q", cfg.General.Country) } } func TestApplyEnvOverrides(t *testing.T) { cfg := Default() t.Setenv("UNARR_API_KEY", "tc_env_key") t.Setenv("UNARR_API_URL", "https://env.example.com") t.Setenv("UNARR_COUNTRY", "DE") t.Setenv("UNARR_DOWNLOAD_DIR", "/env/downloads") cfg.ApplyEnvOverrides() if cfg.Auth.APIKey != "tc_env_key" { t.Errorf("APIKey = %q, want tc_env_key", cfg.Auth.APIKey) } if cfg.Auth.APIURL != "https://env.example.com" { t.Errorf("APIURL = %q, want https://env.example.com", cfg.Auth.APIURL) } if cfg.General.Country != "DE" { t.Errorf("Country = %q, want DE", cfg.General.Country) } if cfg.Download.Dir != "/env/downloads" { t.Errorf("Dir = %q, want /env/downloads", cfg.Download.Dir) } } func TestSaveCreatesDirectory(t *testing.T) { tmp := t.TempDir() path := filepath.Join(tmp, "nested", "deep", "config.toml") cfg := Default() if err := Save(cfg, path); err != nil { t.Fatalf("Save with nested dir failed: %v", err) } if _, err := os.Stat(path); os.IsNotExist(err) { t.Error("config file was not created in nested dir") } } func TestParseSpeed(t *testing.T) { tests := []struct { input string want int64 }{ {"0", 0}, {"", 0}, {"10MB", 10 * 1024 * 1024}, {"500KB", 500 * 1024}, {"1GB", 1024 * 1024 * 1024}, {"1.5MB", int64(1.5 * 1024 * 1024)}, {"10mb", 10 * 1024 * 1024}, {"1024", 1024}, } for _, tt := range tests { got, err := ParseSpeed(tt.input) if err != nil { t.Errorf("ParseSpeed(%q) error: %v", tt.input, err) continue } if got != tt.want { t.Errorf("ParseSpeed(%q) = %d, want %d", tt.input, got, tt.want) } } // Error cases if _, err := ParseSpeed("abc"); err == nil { t.Error("ParseSpeed(\"abc\") should error") } if _, err := ParseSpeed("-5MB"); err == nil { t.Error("ParseSpeed(\"-5MB\") should error") } } func TestLoadMinimalTOMLAppliesStreamingDefaults(t *testing.T) { tmp := t.TempDir() path := filepath.Join(tmp, "config.toml") // Minimal config — only auth + agent. Nothing about webrtc / transcode. os.WriteFile(path, []byte(`[auth] api_key = "tc_minimal" [agent] id = "agent-uuid" name = "Test" `), 0o644) cfg, err := Load(path) if err != nil { t.Fatalf("Load failed: %v", err) } // Transcode should be on by default. if !cfg.Download.Transcode.Enabled { t.Error("Transcode.Enabled should default to true when [downloads.transcode] is absent") } if cfg.Download.Transcode.HWAccel != "auto" { t.Errorf("Transcode.HWAccel = %q, want auto", cfg.Download.Transcode.HWAccel) } if cfg.Download.Transcode.Preset != "" { // Default is now empty — engine.ResolveEncoderProfile picks // "superfast" on libx264 for first-start latency. Users // wanting better quality override in config.toml. t.Errorf("Transcode.Preset = %q, want empty", cfg.Download.Transcode.Preset) } if cfg.Download.Transcode.MaxConcurrent != 2 { t.Errorf("Transcode.MaxConcurrent = %d, want 2", cfg.Download.Transcode.MaxConcurrent) } } func TestLoadRespectsExplicitlyDisabledStreaming(t *testing.T) { tmp := t.TempDir() path := filepath.Join(tmp, "config.toml") // User explicitly opted out of transcode. Defaults must NOT override // it — that would silently re-enable a feature the user disabled. os.WriteFile(path, []byte(`[downloads.transcode] enabled = false `), 0o644) cfg, err := Load(path) if err != nil { t.Fatalf("Load failed: %v", err) } if cfg.Download.Transcode.Enabled { t.Error("Transcode.Enabled = true, want false (user explicitly disabled)") } } func TestLoadInvalidTOML(t *testing.T) { tmp := t.TempDir() path := filepath.Join(tmp, "config.toml") os.WriteFile(path, []byte(`not valid toml [[[`), 0o644) _, err := Load(path) if err == nil { t.Error("expected error for invalid TOML, got nil") } }