package handlers import ( "net/http" "net/http/httptest" "testing" ) // TestToggleLength tests the ToggleLength handler func TestToggleLength(t *testing.T) { handler := newTestCVHandler(t, "localhost:8080", nil) tests := []struct { name string currentLength string expectedToggle string }{ { name: "Toggle from short to long", currentLength: "short", expectedToggle: "long", }, { name: "Toggle from long to short", currentLength: "long", expectedToggle: "short", }, { name: "Toggle from extended (migrated) to short", currentLength: "extended", expectedToggle: "short", // extended becomes long, then toggles to short }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/toggle-length", nil) // Set current length cookie if tt.currentLength != "" { req.AddCookie(&http.Cookie{ Name: "cv-length", Value: tt.currentLength, }) } w := httptest.NewRecorder() handler.ToggleLength(w, req) // 204 No Content - frontend uses hx-swap="none" so response body is ignored if w.Code != http.StatusNoContent { t.Errorf("Expected status No Content (204), got %d", w.Code) } // Check that response sets the toggled cookie cookies := w.Result().Cookies() found := false for _, cookie := range cookies { if cookie.Name == "cv-length" { found = true // Note: We can't easily verify the exact value without parsing the template // But we can verify the cookie was set } } if !found { t.Error("Expected cv-length cookie to be set in response") } }) } } // TestToggleIcons tests the ToggleIcons handler func TestToggleIcons(t *testing.T) { handler := newTestCVHandler(t, "localhost:8080", nil) tests := []struct { name string currentIcons string }{ { name: "Toggle from show to hide", currentIcons: "show", }, { name: "Toggle from hide to show", currentIcons: "hide", }, { name: "Toggle from true (migrated)", currentIcons: "true", }, { name: "Toggle from false (migrated)", currentIcons: "false", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/toggle-icons", nil) if tt.currentIcons != "" { req.AddCookie(&http.Cookie{ Name: "cv-icons", Value: tt.currentIcons, }) } w := httptest.NewRecorder() handler.ToggleIcons(w, req) // 204 No Content - frontend uses hx-swap="none" so response body is ignored if w.Code != http.StatusNoContent { t.Errorf("Expected status No Content (204), got %d", w.Code) } }) } } // TestSwitchLanguage tests the SwitchLanguage handler func TestSwitchLanguage(t *testing.T) { handler := newTestCVHandler(t, "localhost:8080", nil) tests := []struct { name string lang string expectStatus int }{ { name: "Switch to English", lang: "en", expectStatus: http.StatusOK, }, { name: "Switch to Spanish", lang: "es", expectStatus: http.StatusOK, }, { name: "Invalid language", lang: "fr", expectStatus: http.StatusBadRequest, }, { name: "Default to English", lang: "", expectStatus: http.StatusOK, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/switch-language?lang="+tt.lang, nil) w := httptest.NewRecorder() handler.SwitchLanguage(w, req) if w.Code != tt.expectStatus { t.Errorf("Expected status %d, got %d", tt.expectStatus, w.Code) } if tt.expectStatus == http.StatusOK { // Verify language cookie was set cookies := w.Result().Cookies() found := false for _, cookie := range cookies { if cookie.Name == "cv-language" { found = true } } if !found { t.Error("Expected cv-language cookie to be set") } } }) } } // TestToggleTheme tests the ToggleTheme handler func TestToggleTheme(t *testing.T) { handler := newTestCVHandler(t, "localhost:8080", nil) tests := []struct { name string currentTheme string }{ { name: "Toggle from default to clean", currentTheme: "default", }, { name: "Toggle from clean to default", currentTheme: "clean", }, { name: "Toggle with no cookie (default)", currentTheme: "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/toggle-theme", nil) if tt.currentTheme != "" { req.AddCookie(&http.Cookie{ Name: "cv-theme", Value: tt.currentTheme, }) } w := httptest.NewRecorder() handler.ToggleTheme(w, req) // 204 No Content - frontend uses hx-swap="none" so response body is ignored if w.Code != http.StatusNoContent { t.Errorf("Expected status No Content (204), got %d", w.Code) } // Verify theme cookie was set cookies := w.Result().Cookies() found := false for _, cookie := range cookies { if cookie.Name == "cv-theme" { found = true } } if !found { t.Error("Expected cv-theme cookie to be set") } }) } } // TestHTMXHandlersRequirePost tests that all HTMX handlers reject GET requests func TestHTMXHandlersRequirePost(t *testing.T) { handler := newTestCVHandler(t, "localhost:8080", nil) tests := []struct { name string handlerFunc func(http.ResponseWriter, *http.Request) endpoint string }{ { name: "ToggleLength rejects GET", handlerFunc: handler.ToggleLength, endpoint: "/toggle-length", }, { name: "ToggleIcons rejects GET", handlerFunc: handler.ToggleIcons, endpoint: "/toggle-icons", }, { name: "ToggleTheme rejects GET", handlerFunc: handler.ToggleTheme, endpoint: "/toggle-theme", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { req := httptest.NewRequest(http.MethodGet, tt.endpoint, nil) w := httptest.NewRecorder() tt.handlerFunc(w, req) if w.Code != http.StatusMethodNotAllowed { t.Errorf("Expected status MethodNotAllowed (405), got %d", w.Code) } }) } }