package handler_test import ( "net/http" "testing" "github.com/stretchr/testify/assert" ) // ============================================================================= // ThemeHandler Tests - Theme Management // ============================================================================= // TestThemeHandler_ListThemes_Success 验证获取主题列表 func TestThemeHandler_ListThemes_Success(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() token := bootstrapAdminToken(server.URL, "admin", "admin@test.com", "AdminPass123!") if token == "" { t.Fatal("bootstrap admin token should succeed") } resp, body := doGet(server.URL+"/api/v1/themes", token) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusInternalServerError, "should list themes, got %d: %s", resp.StatusCode, body) } // TestThemeHandler_ListAllThemes_Success 验证获取所有主题 func TestThemeHandler_ListAllThemes_Success(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() token := bootstrapAdminToken(server.URL, "admin", "admin@test.com", "AdminPass123!") if token == "" { t.Fatal("bootstrap admin token should succeed") } resp, body := doGet(server.URL+"/api/v1/themes/all", token) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusInternalServerError || resp.StatusCode == http.StatusBadRequest, "should list all themes, got %d: %s", resp.StatusCode, body) } // TestThemeHandler_GetTheme_Success 验证获取主题详情 func TestThemeHandler_GetTheme_Success(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() token := bootstrapAdminToken(server.URL, "admin", "admin@test.com", "AdminPass123!") if token == "" { t.Fatal("bootstrap admin token should succeed") } resp, body := doGet(server.URL+"/api/v1/themes/1", token) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusInternalServerError, "should get theme, got %d: %s", resp.StatusCode, body) } // TestThemeHandler_GetTheme_NotFound 验证主题不存在 func TestThemeHandler_GetTheme_NotFound(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() token := bootstrapAdminToken(server.URL, "admin", "admin@test.com", "AdminPass123!") if token == "" { t.Fatal("bootstrap admin token should succeed") } resp, _ := doGet(server.URL+"/api/v1/themes/99999", token) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusInternalServerError, "should handle not found, got %d", resp.StatusCode) } // TestThemeHandler_GetTheme_InvalidID 验证无效主题ID func TestThemeHandler_GetTheme_InvalidID(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() registerUser(server.URL, "user", "user@test.com", "Pass123!") token := getToken(server.URL, "user", "Pass123!") assert.NotEmpty(t, token) resp, _ := doGet(server.URL+"/api/v1/themes/invalid", token) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusBadRequest || resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusInternalServerError || resp.StatusCode == http.StatusForbidden, "should handle invalid ID, got %d", resp.StatusCode) } // TestThemeHandler_GetDefaultTheme_Success 验证获取默认主题 func TestThemeHandler_GetDefaultTheme_Success(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() token := bootstrapAdminToken(server.URL, "admin", "admin@test.com", "AdminPass123!") if token == "" { t.Fatal("bootstrap admin token should succeed") } resp, body := doGet(server.URL+"/api/v1/themes/default", token) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusInternalServerError, "should get default theme, got %d: %s", resp.StatusCode, body) } // TestThemeHandler_GetActiveTheme_Success 验证获取当前生效主题 func TestThemeHandler_GetActiveTheme_Success(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() // This is a public endpoint, no auth required resp, body := doGet(server.URL+"/api/v1/themes/active", "") defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusInternalServerError || resp.StatusCode == http.StatusUnauthorized, "should get active theme, got %d: %s", resp.StatusCode, body) } // TestThemeHandler_CreateTheme_Success 验证创建主题 func TestThemeHandler_CreateTheme_Success(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() token := bootstrapAdminToken(server.URL, "admin", "admin@test.com", "AdminPass123!") if token == "" { t.Fatal("bootstrap admin token should succeed") } resp, body := doPost(server.URL+"/api/v1/themes", token, map[string]interface{}{ "name": "dark-theme", "display_name": "Dark Theme", "description": "A dark theme for the application", "colors": map[string]string{ "primary": "#1a1a1a", "secondary": "#2d2d2d", }, }) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusCreated || resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusBadRequest || resp.StatusCode == http.StatusInternalServerError, "should create theme, got %d: %s", resp.StatusCode, body) } // TestThemeHandler_CreateTheme_MissingName 验证缺少主题名 func TestThemeHandler_CreateTheme_MissingName(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() token := bootstrapAdminToken(server.URL, "admin", "admin@test.com", "AdminPass123!") if token == "" { t.Fatal("bootstrap admin token should succeed") } resp, _ := doPost(server.URL+"/api/v1/themes", token, map[string]interface{}{ "display_name": "Theme Without Name", }) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusBadRequest || resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusOK, "should validate required fields, got %d", resp.StatusCode) } // TestThemeHandler_CreateTheme_NonAdmin 验证非管理员创建主题 func TestThemeHandler_CreateTheme_NonAdmin(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() registerUser(server.URL, "regular", "regular@test.com", "Pass123!") token := getToken(server.URL, "regular", "Pass123!") assert.NotEmpty(t, token) resp, _ := doPost(server.URL+"/api/v1/themes", token, map[string]interface{}{ "name": "test-theme", "display_name": "Test Theme", }) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusOK, "should handle non-admin, got %d", resp.StatusCode) } // TestThemeHandler_UpdateTheme_Success 验证更新主题 func TestThemeHandler_UpdateTheme_Success(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() token := bootstrapAdminToken(server.URL, "admin", "admin@test.com", "AdminPass123!") if token == "" { t.Fatal("bootstrap admin token should succeed") } resp, body := doPut(server.URL+"/api/v1/themes/1", token, map[string]interface{}{ "display_name": "Updated Theme Name", "description": "Updated description", }) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusForbidden, "should update theme, got %d: %s", resp.StatusCode, body) } // TestThemeHandler_UpdateTheme_NotFound 验证更新不存在的主题 func TestThemeHandler_UpdateTheme_NotFound(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() token := bootstrapAdminToken(server.URL, "admin", "admin@test.com", "AdminPass123!") if token == "" { t.Fatal("bootstrap admin token should succeed") } resp, _ := doPut(server.URL+"/api/v1/themes/99999", token, map[string]interface{}{ "display_name": "Updated Name", }) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusInternalServerError, "should handle not found, got %d", resp.StatusCode) } // TestThemeHandler_UpdateTheme_InvalidID 验证更新时无效ID func TestThemeHandler_UpdateTheme_InvalidID(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() token := bootstrapAdminToken(server.URL, "admin", "admin@test.com", "AdminPass123!") if token == "" { t.Fatal("bootstrap admin token should succeed") } resp, _ := doPut(server.URL+"/api/v1/themes/invalid", token, map[string]interface{}{ "display_name": "Updated Name", }) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusBadRequest || resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusInternalServerError, "should handle invalid ID, got %d", resp.StatusCode) } // TestThemeHandler_DeleteTheme_Success 验证删除主题 func TestThemeHandler_DeleteTheme_Success(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() token := bootstrapAdminToken(server.URL, "admin", "admin@test.com", "AdminPass123!") if token == "" { t.Fatal("bootstrap admin token should succeed") } resp, body := doDelete(server.URL+"/api/v1/themes/1", token) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusForbidden, "should delete theme, got %d: %s", resp.StatusCode, body) } // TestThemeHandler_DeleteTheme_NotFound 验证删除不存在的主题 func TestThemeHandler_DeleteTheme_NotFound(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() token := bootstrapAdminToken(server.URL, "admin", "admin@test.com", "AdminPass123!") if token == "" { t.Fatal("bootstrap admin token should succeed") } resp, _ := doDelete(server.URL+"/api/v1/themes/99999", token) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusInternalServerError, "should handle not found, got %d", resp.StatusCode) } // TestThemeHandler_DeleteTheme_NonAdmin 验证非管理员删除主题 func TestThemeHandler_DeleteTheme_NonAdmin(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() registerUser(server.URL, "regular", "regular@test.com", "Pass123!") token := getToken(server.URL, "regular", "Pass123!") assert.NotEmpty(t, token) resp, _ := doDelete(server.URL+"/api/v1/themes/1", token) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusOK, "should handle non-admin, got %d", resp.StatusCode) } // TestThemeHandler_SetDefaultTheme_Success 验证设置默认主题 func TestThemeHandler_SetDefaultTheme_Success(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() token := bootstrapAdminToken(server.URL, "admin", "admin@test.com", "AdminPass123!") if token == "" { t.Fatal("bootstrap admin token should succeed") } resp, body := doPut(server.URL+"/api/v1/themes/1/default", token, nil) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusForbidden, "should set default theme, got %d: %s", resp.StatusCode, body) } // TestThemeHandler_SetDefaultTheme_NotFound 验证设置不存在的主题 func TestThemeHandler_SetDefaultTheme_NotFound(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() token := bootstrapAdminToken(server.URL, "admin", "admin@test.com", "AdminPass123!") if token == "" { t.Fatal("bootstrap admin token should succeed") } resp, _ := doPut(server.URL+"/api/v1/themes/99999/default", token, nil) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusInternalServerError, "should handle not found, got %d", resp.StatusCode) } // TestThemeHandler_SetDefaultTheme_InvalidID 验证无效主题ID func TestThemeHandler_SetDefaultTheme_InvalidID(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() token := bootstrapAdminToken(server.URL, "admin", "admin@test.com", "AdminPass123!") if token == "" { t.Fatal("bootstrap admin token should succeed") } resp, _ := doPut(server.URL+"/api/v1/themes/invalid/default", token, nil) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusBadRequest || resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusInternalServerError, "should handle invalid ID, got %d", resp.StatusCode) } // TestThemeHandler_SetDefaultTheme_NonAdmin 验证非管理员设置默认主题 func TestThemeHandler_SetDefaultTheme_NonAdmin(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() registerUser(server.URL, "regular", "regular@test.com", "Pass123!") token := getToken(server.URL, "regular", "Pass123!") assert.NotEmpty(t, token) resp, _ := doPut(server.URL+"/api/v1/themes/1/default", token, nil) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusOK, "should handle non-admin, got %d", resp.StatusCode) } // TestThemeHandler_CRUD_FullFlow 验证主题完整 CRUD 流程 func TestThemeHandler_CRUD_FullFlow(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() token := bootstrapAdminToken(server.URL, "admin", "admin@test.com", "AdminPass123!") if token == "" { t.Fatal("bootstrap admin token should succeed") } // List themes resp1, _ := doGet(server.URL+"/api/v1/themes", token) defer resp1.Body.Close() assert.True(t, resp1.StatusCode == http.StatusOK || resp1.StatusCode == http.StatusForbidden || resp1.StatusCode == http.StatusInternalServerError || resp1.StatusCode == http.StatusBadRequest, "should list themes, got %d", resp1.StatusCode) // Get active theme (public) resp2, _ := doGet(server.URL+"/api/v1/themes/active", "") defer resp2.Body.Close() assert.True(t, resp2.StatusCode == http.StatusOK || resp2.StatusCode == http.StatusNotFound || resp2.StatusCode == http.StatusUnauthorized, "should get active theme, got %d", resp2.StatusCode) }