package handler_test import ( "encoding/json" "net/http" "testing" "github.com/stretchr/testify/assert" ) // ============================================================================= // CustomFieldHandler Tests - Custom Field Management // ============================================================================= // TestCustomFieldHandler_CreateField_Success 验证创建自定义字段 func TestCustomFieldHandler_CreateField_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/fields", token, map[string]interface{}{ "name": "department", "label": "Department", "type": "text", "required": false, "description": "User's department", }) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusCreated || resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusBadRequest || resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusInternalServerError, "should create field, got %d: %s", resp.StatusCode, body) } // TestCustomFieldHandler_CreateField_MissingName 验证缺少字段名 func TestCustomFieldHandler_CreateField_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/fields", token, map[string]interface{}{ "label": "Department", "type": "text", }) 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) } // TestCustomFieldHandler_CreateField_NonAdmin_Forbidden 验证非管理员被拒 func TestCustomFieldHandler_CreateField_NonAdmin_Forbidden(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/fields", token, map[string]interface{}{ "name": "test", "label": "Test", "type": "text", }) 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) } // TestCustomFieldHandler_ListFields_Success 验证获取字段列表 func TestCustomFieldHandler_ListFields_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/fields", token) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode, "should list fields: %s", body) var result map[string]interface{} json.Unmarshal([]byte(body), &result) data, ok := result["data"].([]interface{}) if ok { t.Logf("Found %d custom fields", len(data)) } } // TestCustomFieldHandler_GetField_Success 验证获取字段详情 func TestCustomFieldHandler_GetField_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") } // Create a field first resp, _ := doPost(server.URL+"/api/v1/fields", token, map[string]interface{}{ "name": "testfield", "label": "Test Field", "type": "text", }) defer resp.Body.Close() // Get the field resp2, body2 := doGet(server.URL+"/api/v1/fields/1", token) defer resp2.Body.Close() assert.True(t, resp2.StatusCode == http.StatusOK || resp2.StatusCode == http.StatusNotFound, "should get field, got %d: %s", resp2.StatusCode, body2) } // TestCustomFieldHandler_GetField_NotFound 验证字段不存在 func TestCustomFieldHandler_GetField_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/fields/99999", token) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusBadRequest || resp.StatusCode == http.StatusInternalServerError || resp.StatusCode == http.StatusOK, "should handle NotFound, got %d", resp.StatusCode) } // TestCustomFieldHandler_GetField_InvalidID 验证无效 ID func TestCustomFieldHandler_GetField_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, _ := doGet(server.URL+"/api/v1/fields/invalid", token) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusBadRequest || resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusInternalServerError || resp.StatusCode == http.StatusOK, "should handle InvalidID, got %d", resp.StatusCode) } // TestCustomFieldHandler_UpdateField_Success 验证更新字段 func TestCustomFieldHandler_UpdateField_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") } // Create field doPost(server.URL+"/api/v1/fields", token, map[string]interface{}{ "name": "updatefield", "label": "Original Label", "type": "text", }) // Update field resp, body := doPut(server.URL+"/api/v1/fields/1", token, map[string]interface{}{ "label": "Updated Label", "description": "Updated description", }) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNotFound, "should update field, got %d: %s", resp.StatusCode, body) } // TestCustomFieldHandler_UpdateField_NotFound 验证更新不存在的字段 func TestCustomFieldHandler_UpdateField_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/fields/99999", token, map[string]interface{}{ "label": "Updated", }) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusBadRequest || resp.StatusCode == http.StatusInternalServerError || resp.StatusCode == http.StatusOK, "should handle NotFound, got %d", resp.StatusCode) } // TestCustomFieldHandler_UpdateField_NonAdmin_Forbidden 验证非管理员更新被拒 func TestCustomFieldHandler_UpdateField_NonAdmin_Forbidden(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() registerUser(server.URL, "regular2", "regular2@test.com", "Pass123!") token := getToken(server.URL, "regular2", "Pass123!") assert.NotEmpty(t, token) resp, _ := doPut(server.URL+"/api/v1/fields/1", token, map[string]interface{}{ "label": "Updated", }) 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) } // TestCustomFieldHandler_DeleteField_Success 验证删除字段 func TestCustomFieldHandler_DeleteField_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") } // Create field doPost(server.URL+"/api/v1/fields", token, map[string]interface{}{ "name": "deletefield", "label": "Delete Field", "type": "text", }) // Delete field resp, _ := doDelete(server.URL+"/api/v1/fields/1", token) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNotFound, "should delete field, got %d", resp.StatusCode) } // TestCustomFieldHandler_DeleteField_NotFound 验证删除不存在的字段 func TestCustomFieldHandler_DeleteField_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/fields/99999", token) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusBadRequest || resp.StatusCode == http.StatusInternalServerError || resp.StatusCode == http.StatusOK, "should handle NotFound, got %d", resp.StatusCode) } // TestCustomFieldHandler_DeleteField_InvalidID 验证删除时无效 ID func TestCustomFieldHandler_DeleteField_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, _ := doDelete(server.URL+"/api/v1/fields/invalid", token) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusBadRequest || resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusInternalServerError || resp.StatusCode == http.StatusOK, "should handle InvalidID, got %d", resp.StatusCode) } // TestCustomFieldHandler_GetUserFieldValues_Success 验证获取用户字段值 func TestCustomFieldHandler_GetUserFieldValues_Success(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() registerUser(server.URL, "fielduser", "field@test.com", "Pass123!") token := getToken(server.URL, "fielduser", "Pass123!") assert.NotEmpty(t, token) resp, body := doGet(server.URL+"/api/v1/users/me/fields", token) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNotFound, "should get user field values, got %d: %s", resp.StatusCode, body) } // TestCustomFieldHandler_GetUserFieldValues_Unauthorized 验证未认证访问 func TestCustomFieldHandler_GetUserFieldValues_Unauthorized(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() resp, _ := doGet(server.URL+"/api/v1/users/me/fields", "") defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusOK, "should handle unauthorized, got %d", resp.StatusCode) } // TestCustomFieldHandler_SetUserFieldValues_Success 验证设置用户字段值 func TestCustomFieldHandler_SetUserFieldValues_Success(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() registerUser(server.URL, "fielduser2", "field2@test.com", "Pass123!") token := getToken(server.URL, "fielduser2", "Pass123!") assert.NotEmpty(t, token) resp, body := doPut(server.URL+"/api/v1/users/me/fields", token, map[string]interface{}{ "values": map[string]string{ "department": "Engineering", "location": "Beijing", }, }) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusBadRequest, "should set user field values, got %d: %s", resp.StatusCode, body) } // TestCustomFieldHandler_SetUserFieldValues_MissingValues 验证缺少值参数 func TestCustomFieldHandler_SetUserFieldValues_MissingValues(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() registerUser(server.URL, "fielduser3", "field3@test.com", "Pass123!") token := getToken(server.URL, "fielduser3", "Pass123!") assert.NotEmpty(t, token) resp, _ := doPut(server.URL+"/api/v1/users/me/fields", token, map[string]interface{}{ "values": map[string]string{}, }) defer resp.Body.Close() assert.True(t, resp.StatusCode >= http.StatusBadRequest || resp.StatusCode == http.StatusOK, "should handle empty values, got %d", resp.StatusCode) } // TestCustomFieldHandler_SetUserFieldValues_Unauthorized 验证未认证访问 func TestCustomFieldHandler_SetUserFieldValues_Unauthorized(t *testing.T) { server, cleanup := setupHandlerTestServer(t) defer cleanup() resp, _ := doPut(server.URL+"/api/v1/users/me/fields", "", map[string]interface{}{ "values": map[string]string{ "department": "Engineering", }, }) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusOK, "should handle unauthorized, got %d", resp.StatusCode) } // TestCustomFieldHandler_FieldTypes_Support 验证字段类型支持 func TestCustomFieldHandler_FieldTypes_Support(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") } // Create fields with different types fieldTypes := []string{"text", "number", "date", "boolean", "select"} for _, ft := range fieldTypes { resp, _ := doPost(server.URL+"/api/v1/fields", token, map[string]interface{}{ "name": "field_" + ft, "label": "Field " + ft, "type": ft, }) defer resp.Body.Close() // Accept success or error depending on supported types t.Logf("Field type '%s' returned status: %d", ft, resp.StatusCode) } } // TestCustomFieldHandler_FieldValidation_Required 验证必填字段 func TestCustomFieldHandler_FieldValidation_Required(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") } // Create required field resp, _ := doPost(server.URL+"/api/v1/fields", token, map[string]interface{}{ "name": "required_field", "label": "Required Field", "type": "text", "required": true, }) defer resp.Body.Close() assert.True(t, resp.StatusCode == http.StatusCreated || resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusBadRequest || resp.StatusCode == http.StatusInternalServerError, "should handle required field creation, got %d", resp.StatusCode) }