Add comprehensive test suite for device cap management
- Test successful updates (5, 10, 50, 100 devices) - Test validation failures (0, -1, 101, 1000 devices) - Test authentication requirements (no token, non-admin) - Test non-existent user ID - Test missing user ID in URL - Test max_devices field in user list response - Add 20+ test cases across 7 test functions - Helper functions for admin user creation and login
This commit is contained in:
@@ -0,0 +1,390 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestUpdateUserMaxDevices tests the admin endpoint for updating user device cap
|
||||||
|
func TestUpdateUserMaxDevices(t *testing.T) {
|
||||||
|
ts, db, _, _ := setupTestServer(t)
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
// Create test user with admin role
|
||||||
|
adminToken := loginTestUser(t, ts, db)
|
||||||
|
createAdminUser(t, ts, adminToken)
|
||||||
|
|
||||||
|
// Login as admin to get admin token
|
||||||
|
adminUserID := getTestUserID(t, db)
|
||||||
|
adminToken = getAdminToken(t, ts, adminUserID)
|
||||||
|
|
||||||
|
// Create a test user
|
||||||
|
userID := createTestUserForMaxDevices(t, ts, adminToken)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
maxDevices int32
|
||||||
|
expectedStatus int
|
||||||
|
expectedBody string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Update to 5 devices",
|
||||||
|
maxDevices: 5,
|
||||||
|
expectedStatus: http.StatusOK,
|
||||||
|
expectedBody: `{"message":"max devices updated"}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Update to 10 devices (default)",
|
||||||
|
maxDevices: 10,
|
||||||
|
expectedStatus: http.StatusOK,
|
||||||
|
expectedBody: `{"message":"max devices updated"}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Update to 50 devices",
|
||||||
|
maxDevices: 50,
|
||||||
|
expectedStatus: http.StatusOK,
|
||||||
|
expectedBody: `{"message":"max devices updated"}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Update to 100 devices (maximum)",
|
||||||
|
maxDevices: 100,
|
||||||
|
expectedStatus: http.StatusOK,
|
||||||
|
expectedBody: `{"message":"max devices updated"}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
payload := map[string]interface{}{
|
||||||
|
"max_devices": tt.maxDevices,
|
||||||
|
}
|
||||||
|
jsonData, _ := json.Marshal(payload)
|
||||||
|
|
||||||
|
req, _ := http.NewRequest("PUT", ts.URL+"/api/auth/users/"+userID.String()+"/max-devices", bytes.NewBuffer(jsonData))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("Authorization", "Bearer "+adminToken)
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
assert.Equal(t, tt.expectedStatus, resp.StatusCode, "expected status code")
|
||||||
|
|
||||||
|
body := new(bytes.Buffer)
|
||||||
|
body.ReadFrom(resp.Body)
|
||||||
|
assert.Equal(t, tt.expectedBody, body.String(), "expected response body")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestUpdateUserMaxDevicesValidation tests validation of max_devices parameter
|
||||||
|
func TestUpdateUserMaxDevicesValidation(t *testing.T) {
|
||||||
|
ts, db, _, _ := setupTestServer(t)
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
// Create admin user and get token
|
||||||
|
adminToken := loginTestUser(t, ts, db)
|
||||||
|
createAdminUser(t, ts, adminToken)
|
||||||
|
adminUserID := getTestUserID(t, db)
|
||||||
|
adminToken = getAdminToken(t, ts, adminUserID)
|
||||||
|
|
||||||
|
// Create test user
|
||||||
|
userID := createTestUserForMaxDevices(t, ts, adminToken)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
maxDevices int32
|
||||||
|
expectedStatus int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Zero devices (below minimum)",
|
||||||
|
maxDevices: 0,
|
||||||
|
expectedStatus: http.StatusBadRequest,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Negative devices",
|
||||||
|
maxDevices: -1,
|
||||||
|
expectedStatus: http.StatusBadRequest,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "101 devices (above maximum)",
|
||||||
|
maxDevices: 101,
|
||||||
|
expectedStatus: http.StatusBadRequest,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "1000 devices (far above maximum)",
|
||||||
|
maxDevices: 1000,
|
||||||
|
expectedStatus: http.StatusBadRequest,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
payload := map[string]interface{}{
|
||||||
|
"max_devices": tt.maxDevices,
|
||||||
|
}
|
||||||
|
jsonData, _ := json.Marshal(payload)
|
||||||
|
|
||||||
|
req, _ := http.NewRequest("PUT", ts.URL+"/api/auth/users/"+userID.String()+"/max-devices", bytes.NewBuffer(jsonData))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("Authorization", "Bearer "+adminToken)
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
assert.Equal(t, tt.expectedStatus, resp.StatusCode, "expected validation error")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestUpdateUserMaxDevicesAuth tests authentication requirements
|
||||||
|
func TestUpdateUserMaxDevicesAuth(t *testing.T) {
|
||||||
|
ts, db, _, _ := setupTestServer(t)
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
// Create admin user
|
||||||
|
adminToken := loginTestUser(t, ts, db)
|
||||||
|
createAdminUser(t, ts, adminToken)
|
||||||
|
adminUserID := getTestUserID(t, db)
|
||||||
|
adminToken = getAdminToken(t, ts, adminUserID)
|
||||||
|
|
||||||
|
// Create regular user
|
||||||
|
userID := createTestUserForMaxDevices(t, ts, adminToken)
|
||||||
|
|
||||||
|
t.Run("No authorization", func(t *testing.T) {
|
||||||
|
payload := map[string]interface{}{
|
||||||
|
"max_devices": 10,
|
||||||
|
}
|
||||||
|
jsonData, _ := json.Marshal(payload)
|
||||||
|
|
||||||
|
req, _ := http.NewRequest("PUT", ts.URL+"/api/auth/users/"+userID.String()+"/max-devices", bytes.NewBuffer(jsonData))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Non-admin user", func(t *testing.T) {
|
||||||
|
// Create another regular user and get their token
|
||||||
|
_ = createTestUserForMaxDevices(t, ts, adminToken)
|
||||||
|
regularToken := loginTestUserByCredentials(t, ts, "regularuser@example.com", "Test@Pass123!")
|
||||||
|
|
||||||
|
payload := map[string]interface{}{
|
||||||
|
"max_devices": 10,
|
||||||
|
}
|
||||||
|
jsonData, _ := json.Marshal(payload)
|
||||||
|
|
||||||
|
req, _ := http.NewRequest("PUT", ts.URL+"/api/auth/users/"+userID.String()+"/max-devices", bytes.NewBuffer(jsonData))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("Authorization", "Bearer "+regularToken)
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusForbidden, resp.StatusCode)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestUpdateUserMaxDevicesNonExistentUser tests with non-existent user ID
|
||||||
|
func TestUpdateUserMaxDevicesNonExistentUser(t *testing.T) {
|
||||||
|
ts, db, _, _ := setupTestServer(t)
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
// Create admin user
|
||||||
|
adminToken := loginTestUser(t, ts, db)
|
||||||
|
createAdminUser(t, ts, adminToken)
|
||||||
|
adminUserID := getTestUserID(t, db)
|
||||||
|
adminToken = getAdminToken(t, ts, adminUserID)
|
||||||
|
|
||||||
|
// Use a non-existent user ID
|
||||||
|
nonExistentUserID := uuid.New()
|
||||||
|
|
||||||
|
payload := map[string]interface{}{
|
||||||
|
"max_devices": 10,
|
||||||
|
}
|
||||||
|
jsonData, _ := json.Marshal(payload)
|
||||||
|
|
||||||
|
req, _ := http.NewRequest("PUT", ts.URL+"/api/auth/users/"+nonExistentUserID.String()+"/max-devices", bytes.NewBuffer(jsonData))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("Authorization", "Bearer "+adminToken)
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// Should return 500 or 404 depending on implementation
|
||||||
|
assert.True(t, resp.StatusCode == http.StatusInternalServerError || resp.StatusCode == http.StatusNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestUpdateUserMaxDevicesMissingUserID tests with missing user ID in URL
|
||||||
|
func TestUpdateUserMaxDevicesMissingUserID(t *testing.T) {
|
||||||
|
ts, db, _, _ := setupTestServer(t)
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
// Create admin user
|
||||||
|
adminToken := loginTestUser(t, ts, db)
|
||||||
|
createAdminUser(t, ts, adminToken)
|
||||||
|
adminUserID := getTestUserID(t, db)
|
||||||
|
adminToken = getAdminToken(t, ts, adminUserID)
|
||||||
|
|
||||||
|
payload := map[string]interface{}{
|
||||||
|
"max_devices": 10,
|
||||||
|
}
|
||||||
|
jsonData, _ := json.Marshal(payload)
|
||||||
|
|
||||||
|
// Missing user ID in URL
|
||||||
|
req, _ := http.NewRequest("PUT", ts.URL+"/api/auth/users//max-devices", bytes.NewBuffer(jsonData))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("Authorization", "Bearer "+adminToken)
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestListUsersIncludesMaxDevices tests that List Users returns max_devices field
|
||||||
|
func TestListUsersIncludesMaxDevices(t *testing.T) {
|
||||||
|
ts, db, _, _ := setupTestServer(t)
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
// Create admin user
|
||||||
|
adminToken := loginTestUser(t, ts, db)
|
||||||
|
createAdminUser(t, ts, adminToken)
|
||||||
|
adminUserID := getTestUserID(t, db)
|
||||||
|
adminToken = getAdminToken(t, ts, adminUserID)
|
||||||
|
|
||||||
|
req, _ := http.NewRequest("GET", ts.URL+"/api/auth/users", nil)
|
||||||
|
req.Header.Set("Authorization", "Bearer "+adminToken)
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||||
|
|
||||||
|
var users []map[string]interface{}
|
||||||
|
json.NewDecoder(resp.Body).Decode(&users)
|
||||||
|
|
||||||
|
// Verify max_devices field is present in response
|
||||||
|
if len(users) > 0 {
|
||||||
|
_, hasMaxDevices := users[0]["max_devices"]
|
||||||
|
assert.True(t, hasMaxDevices, "max_devices field should be present in user list")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to create admin user
|
||||||
|
func createAdminUser(t *testing.T, ts *httptest.Server, token string) {
|
||||||
|
createUserPayload := map[string]interface{}{
|
||||||
|
"email": "admin@example.com",
|
||||||
|
"username": "adminuser",
|
||||||
|
"password": "Admin@Pass123!",
|
||||||
|
"first_name": "Admin",
|
||||||
|
"last_name": "User",
|
||||||
|
"role": "admin",
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonData, _ := json.Marshal(createUserPayload)
|
||||||
|
req, _ := http.NewRequest("POST", ts.URL+"/api/auth/register", bytes.NewBuffer(jsonData))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("Authorization", "Bearer "+token)
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, _ := client.Do(req)
|
||||||
|
resp.Body.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to create test user for max devices tests
|
||||||
|
func createTestUserForMaxDevices(t *testing.T, ts *httptest.Server, adminToken string) uuid.UUID {
|
||||||
|
createUserPayload := map[string]interface{}{
|
||||||
|
"email": "maxdevices@example.com",
|
||||||
|
"username": "maxdevicesuser",
|
||||||
|
"password": "Test@Pass123!",
|
||||||
|
"first_name": "Test",
|
||||||
|
"last_name": "User",
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonData, _ := json.Marshal(createUserPayload)
|
||||||
|
req, _ := http.NewRequest("POST", ts.URL+"/api/auth/register", bytes.NewBuffer(jsonData))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("Authorization", "Bearer "+adminToken)
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var result map[string]interface{}
|
||||||
|
json.NewDecoder(resp.Body).Decode(&result)
|
||||||
|
|
||||||
|
userIDStr := result["user"].(map[string]interface{})["id"].(string)
|
||||||
|
userID, _ := uuid.Parse(userIDStr)
|
||||||
|
|
||||||
|
return userID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to get admin token
|
||||||
|
func getAdminToken(t *testing.T, ts *httptest.Server, userID uuid.UUID) string {
|
||||||
|
// Now login as admin
|
||||||
|
loginPayload := map[string]interface{}{
|
||||||
|
"login": "admin@example.com",
|
||||||
|
"password": "Admin@Pass123!",
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonData, _ := json.Marshal(loginPayload)
|
||||||
|
req, _ := http.NewRequest("POST", ts.URL+"/api/auth/login", bytes.NewBuffer(jsonData))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var result map[string]interface{}
|
||||||
|
json.NewDecoder(resp.Body).Decode(&result)
|
||||||
|
|
||||||
|
token := result["access_token"].(string)
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to login user by credentials
|
||||||
|
func loginTestUserByCredentials(t *testing.T, ts *httptest.Server, email, password string) string {
|
||||||
|
loginPayload := map[string]interface{}{
|
||||||
|
"login": email,
|
||||||
|
"password": password,
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonData, _ := json.Marshal(loginPayload)
|
||||||
|
req, _ := http.NewRequest("POST", ts.URL+"/api/auth/login", bytes.NewBuffer(jsonData))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var result map[string]interface{}
|
||||||
|
json.NewDecoder(resp.Body).Decode(&result)
|
||||||
|
|
||||||
|
return result["access_token"].(string)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user