package config import ( "os" "testing" "time" ) func TestConfigValidate(t *testing.T) { tests := []struct { name string cfg Config wantErr bool }{ { name: "valid config", cfg: Config{ TodoistAPIKey: "todoist-key", TrelloAPIKey: "trello-key", TrelloToken: "trello-token", }, wantErr: false, }, { name: "missing todoist key", cfg: Config{ TrelloAPIKey: "trello-key", TrelloToken: "trello-token", }, wantErr: true, }, { name: "missing trello key", cfg: Config{ TodoistAPIKey: "todoist-key", TrelloToken: "trello-token", }, wantErr: true, }, { name: "missing trello token", cfg: Config{ TodoistAPIKey: "todoist-key", TrelloAPIKey: "trello-key", }, wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { err := tc.cfg.Validate() if (err != nil) != tc.wantErr { t.Errorf("Validate() error = %v, wantErr %v", err, tc.wantErr) } }) } } func TestConfigHasMethods(t *testing.T) { cfg := Config{ PlanToEatAPIKey: "pte-key", TrelloAPIKey: "trello-key", TrelloToken: "trello-token", GoogleCredentialsFile: "/path/to/creds.json", GoogleTasksListID: "@default", } if !cfg.HasPlanToEat() { t.Error("HasPlanToEat should return true") } if !cfg.HasTrello() { t.Error("HasTrello should return true") } if !cfg.HasGoogleCalendar() { t.Error("HasGoogleCalendar should return true") } if !cfg.HasGoogleTasks() { t.Error("HasGoogleTasks should return true") } // Test with empty config emptyCfg := Config{} if emptyCfg.HasPlanToEat() { t.Error("HasPlanToEat should return false for empty config") } if emptyCfg.HasTrello() { t.Error("HasTrello should return false for empty config") } if emptyCfg.HasGoogleCalendar() { t.Error("HasGoogleCalendar should return false for empty config") } if emptyCfg.HasGoogleTasks() { t.Error("HasGoogleTasks should return false for empty config") } // Test session check sessionCfg := Config{PlanToEatSession: "session-cookie"} if !sessionCfg.HasPlanToEat() { t.Error("HasPlanToEat should return true for session") } if !sessionCfg.HasPlanToEatSession() { t.Error("HasPlanToEatSession should return true") } } func TestGetEnvWithDefault(t *testing.T) { // Test with set env var os.Setenv("TEST_CONFIG_VAR", "test_value") defer os.Unsetenv("TEST_CONFIG_VAR") if val := getEnvWithDefault("TEST_CONFIG_VAR", "default"); val != "test_value" { t.Errorf("Expected 'test_value', got '%s'", val) } // Test with unset env var if val := getEnvWithDefault("UNSET_CONFIG_VAR", "default"); val != "default" { t.Errorf("Expected 'default', got '%s'", val) } } func TestGetEnvAsInt(t *testing.T) { // Test with valid int os.Setenv("TEST_INT_VAR", "42") defer os.Unsetenv("TEST_INT_VAR") if val := getEnvAsInt("TEST_INT_VAR", 10); val != 42 { t.Errorf("Expected 42, got %d", val) } // Test with invalid int os.Setenv("TEST_INVALID_INT", "not_a_number") defer os.Unsetenv("TEST_INVALID_INT") if val := getEnvAsInt("TEST_INVALID_INT", 10); val != 10 { t.Errorf("Expected default 10 for invalid int, got %d", val) } // Test with unset var if val := getEnvAsInt("UNSET_INT_VAR", 10); val != 10 { t.Errorf("Expected default 10, got %d", val) } } func TestGetEnvAsBool(t *testing.T) { // Test with true values os.Setenv("TEST_BOOL_TRUE", "true") defer os.Unsetenv("TEST_BOOL_TRUE") if val := getEnvAsBool("TEST_BOOL_TRUE", false); !val { t.Error("Expected true") } // Test with false values os.Setenv("TEST_BOOL_FALSE", "false") defer os.Unsetenv("TEST_BOOL_FALSE") if val := getEnvAsBool("TEST_BOOL_FALSE", true); val { t.Error("Expected false") } // Test with invalid bool os.Setenv("TEST_INVALID_BOOL", "maybe") defer os.Unsetenv("TEST_INVALID_BOOL") if val := getEnvAsBool("TEST_INVALID_BOOL", true); !val { t.Error("Expected default true for invalid bool") } // Test with unset var if val := getEnvAsBool("UNSET_BOOL_VAR", true); !val { t.Error("Expected default true") } } // Timezone tests func TestGetDisplayTimezone(t *testing.T) { // Before SetDisplayTimezone is called, should return UTC loc := GetDisplayTimezone() if loc == nil { t.Fatal("GetDisplayTimezone should not return nil") } } func TestNow(t *testing.T) { now := Now() // Just verify it returns a valid time if now.IsZero() { t.Error("Now() should not return zero time") } } func TestToday(t *testing.T) { today := Today() // Today should have zero hours, minutes, seconds if today.Hour() != 0 || today.Minute() != 0 || today.Second() != 0 { t.Error("Today() should return midnight") } } func TestParseDateInDisplayTZ(t *testing.T) { parsed, err := ParseDateInDisplayTZ("2024-01-15") if err != nil { t.Fatalf("ParseDateInDisplayTZ failed: %v", err) } if parsed.Year() != 2024 || parsed.Month() != time.January || parsed.Day() != 15 { t.Errorf("Unexpected date: %v", parsed) } // Test invalid date _, err = ParseDateInDisplayTZ("invalid") if err == nil { t.Error("Expected error for invalid date") } } func TestParseDateTimeInDisplayTZ(t *testing.T) { parsed, err := ParseDateTimeInDisplayTZ("2006-01-02 15:04", "2024-01-15 14:30") if err != nil { t.Fatalf("ParseDateTimeInDisplayTZ failed: %v", err) } if parsed.Hour() != 14 || parsed.Minute() != 30 { t.Errorf("Unexpected time: %v", parsed) } } func TestToDisplayTZ(t *testing.T) { utcTime := time.Date(2024, 1, 15, 12, 0, 0, 0, time.UTC) converted := ToDisplayTZ(utcTime) // Just verify it doesn't panic and returns a valid time if converted.IsZero() { t.Error("ToDisplayTZ should not return zero time") } } func TestLoad(t *testing.T) { // Set up required env vars os.Setenv("TODOIST_API_KEY", "test-todoist-key") os.Setenv("TRELLO_API_KEY", "test-trello-key") os.Setenv("TRELLO_TOKEN", "test-trello-token") os.Setenv("PORT", "9999") os.Setenv("CACHE_TTL_MINUTES", "10") os.Setenv("DEBUG", "true") defer func() { os.Unsetenv("TODOIST_API_KEY") os.Unsetenv("TRELLO_API_KEY") os.Unsetenv("TRELLO_TOKEN") os.Unsetenv("PORT") os.Unsetenv("CACHE_TTL_MINUTES") os.Unsetenv("DEBUG") }() cfg, err := Load() if err != nil { t.Fatalf("Load failed: %v", err) } if cfg.TodoistAPIKey != "test-todoist-key" { t.Errorf("Expected TodoistAPIKey 'test-todoist-key', got '%s'", cfg.TodoistAPIKey) } if cfg.Port != "9999" { t.Errorf("Expected Port '9999', got '%s'", cfg.Port) } if cfg.CacheTTLMinutes != 10 { t.Errorf("Expected CacheTTLMinutes 10, got %d", cfg.CacheTTLMinutes) } if !cfg.Debug { t.Error("Expected Debug to be true") } } func TestConfig_ClaudomatorURL_Default(t *testing.T) { os.Unsetenv("CLAUDOMATOR_URL") os.Setenv("TODOIST_API_KEY", "test-todoist-key") os.Setenv("TRELLO_API_KEY", "test-trello-key") os.Setenv("TRELLO_TOKEN", "test-trello-token") defer func() { os.Unsetenv("TODOIST_API_KEY") os.Unsetenv("TRELLO_API_KEY") os.Unsetenv("TRELLO_TOKEN") }() cfg, err := Load() if err != nil { t.Fatalf("Load failed: %v", err) } if cfg.ClaudomatorURL != "http://127.0.0.1:8484" { t.Errorf("Expected default ClaudomatorURL 'http://127.0.0.1:8484', got '%s'", cfg.ClaudomatorURL) } } func TestConfig_ClaudomatorURL_EnvOverride(t *testing.T) { os.Setenv("CLAUDOMATOR_URL", "http://1.2.3.4:9000") os.Setenv("TODOIST_API_KEY", "test-todoist-key") os.Setenv("TRELLO_API_KEY", "test-trello-key") os.Setenv("TRELLO_TOKEN", "test-trello-token") defer func() { os.Unsetenv("CLAUDOMATOR_URL") os.Unsetenv("TODOIST_API_KEY") os.Unsetenv("TRELLO_API_KEY") os.Unsetenv("TRELLO_TOKEN") }() cfg, err := Load() if err != nil { t.Fatalf("Load failed: %v", err) } if cfg.ClaudomatorURL != "http://1.2.3.4:9000" { t.Errorf("Expected ClaudomatorURL 'http://1.2.3.4:9000', got '%s'", cfg.ClaudomatorURL) } } func TestLoad_ValidationError(t *testing.T) { // Clear required env vars to trigger validation error os.Unsetenv("TODOIST_API_KEY") os.Unsetenv("TRELLO_API_KEY") os.Unsetenv("TRELLO_TOKEN") _, err := Load() if err == nil { t.Error("Expected validation error when required env vars are missing") } }