From a6a0e583402d7505b1a2242556cbde7662a12ca1 Mon Sep 17 00:00:00 2001 From: long-agent Date: Thu, 9 Apr 2026 14:00:42 +0800 Subject: [PATCH] test: add more UserHandler tests for RBAC coverage Add tests for UserHandler permission checks: - TestUserHandler_UpdateUserStatus_RequiresAdmin - TestUserHandler_GetUserRoles_Success - TestUserHandler_AssignRoles_RequiresAdmin - TestUserHandler_BatchUpdateStatus_RequiresAdmin - TestUserHandler_BatchDelete_RequiresAdmin - TestUserHandler_BatchDelete_EmptyIDs_RequiresAdmin These tests verify that admin-only endpoints properly return 403 for non-admin users (RBAC security validation). --- internal/api/handler/handler_test.go | 102 +++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/internal/api/handler/handler_test.go b/internal/api/handler/handler_test.go index e2478bb..bae464c 100644 --- a/internal/api/handler/handler_test.go +++ b/internal/api/handler/handler_test.go @@ -480,6 +480,108 @@ func TestUserHandler_SearchUsers_Success(t *testing.T) { } } +func TestUserHandler_UpdateUserStatus_RequiresAdmin(t *testing.T) { + server, cleanup := setupHandlerTestServer(t) + defer cleanup() + + registerUser(server.URL, "statususer", "statususer@test.com", "UserPass123!") + token := getToken(server.URL, "statususer", "UserPass123!") + + resp, _ := doPut(server.URL+"/api/v1/users/1/status", token, map[string]interface{}{ + "status": "inactive", + }) + defer resp.Body.Close() + + // Requires admin permission (user:manage) + if resp.StatusCode != http.StatusForbidden { + t.Errorf("expected status %d for non-admin user, got %d", http.StatusForbidden, resp.StatusCode) + } +} + +func TestUserHandler_GetUserRoles_Success(t *testing.T) { + server, cleanup := setupHandlerTestServer(t) + defer cleanup() + + registerUser(server.URL, "rolesadmin", "rolesadmin@test.com", "AdminPass123!") + token := getToken(server.URL, "rolesadmin", "AdminPass123!") + + resp, _ := doGet(server.URL+"/api/v1/users/1/roles", token) + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + t.Errorf("expected status %d, got %d", http.StatusOK, resp.StatusCode) + } +} + +func TestUserHandler_AssignRoles_RequiresAdmin(t *testing.T) { + server, cleanup := setupHandlerTestServer(t) + defer cleanup() + + registerUser(server.URL, "assignuser", "assignuser@test.com", "UserPass123!") + token := getToken(server.URL, "assignuser", "UserPass123!") + + resp, _ := doPut(server.URL+"/api/v1/users/1/roles", token, map[string]interface{}{ + "role_ids": []int64{1}, + }) + defer resp.Body.Close() + + // Requires admin permission (user:manage) + if resp.StatusCode != http.StatusForbidden { + t.Errorf("expected status %d for non-admin user, got %d", http.StatusForbidden, resp.StatusCode) + } +} + +func TestUserHandler_BatchUpdateStatus_RequiresAdmin(t *testing.T) { + server, cleanup := setupHandlerTestServer(t) + defer cleanup() + + registerUser(server.URL, "batchuser", "batchuser@test.com", "UserPass123!") + token := getToken(server.URL, "batchuser", "UserPass123!") + + resp, _ := doPut(server.URL+"/api/v1/users/batch/status", token, map[string]interface{}{ + "user_ids": []int64{2, 3}, + "status": "inactive", + }) + defer resp.Body.Close() + + // Requires admin permission (user:manage) + if resp.StatusCode != http.StatusForbidden { + t.Errorf("expected status %d for non-admin user, got %d", http.StatusForbidden, resp.StatusCode) + } +} + +func TestUserHandler_BatchDelete_RequiresAdmin(t *testing.T) { + server, cleanup := setupHandlerTestServer(t) + defer cleanup() + + registerUser(server.URL, "deluser", "deluser@test.com", "UserPass123!") + token := getToken(server.URL, "deluser", "UserPass123!") + + resp, _ := doDelete(server.URL+"/api/v1/users/batch?ids=2,3", token) + defer resp.Body.Close() + + // Requires admin permission (user:delete) + if resp.StatusCode != http.StatusForbidden { + t.Errorf("expected status %d for non-admin user, got %d", http.StatusForbidden, resp.StatusCode) + } +} + +func TestUserHandler_BatchDelete_EmptyIDs_RequiresAdmin(t *testing.T) { + server, cleanup := setupHandlerTestServer(t) + defer cleanup() + + registerUser(server.URL, "emptyidsuser", "emptyidsuser@test.com", "UserPass123!") + token := getToken(server.URL, "emptyidsuser", "UserPass123!") + + resp, _ := doDelete(server.URL+"/api/v1/users/batch", token) + defer resp.Body.Close() + + // Requires admin permission (user:delete) - validation happens after auth check + if resp.StatusCode != http.StatusForbidden { + t.Errorf("expected status %d for non-admin user, got %d", http.StatusForbidden, resp.StatusCode) + } +} + // ============================================================================= // Device Handler Tests // =============================================================================