Files
user-system/internal/api/handler/device_handler_test.go

511 lines
17 KiB
Go
Raw Normal View History

package handler_test
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"testing"
)
func TestDeviceHandler_ListDevices(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
registerUser(server.URL, "devicelistuser", "devicelist@test.com", "UserPass123!")
token := getToken(server.URL, "devicelistuser", "UserPass123!")
resp, body := doGet(server.URL+"/api/v1/devices", token)
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatalf("expected status %d, got %d, body: %s", http.StatusOK, resp.StatusCode, body)
}
var result map[string]interface{}
if err := json.Unmarshal([]byte(body), &result); err != nil {
t.Fatalf("failed to parse response: %v", err)
}
if result["code"] != float64(0) {
t.Errorf("expected code 0, got %v", result["code"])
}
}
func TestDeviceHandler_ListDevices_Unauthorized(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
resp, _ := doGet(server.URL+"/api/v1/devices", "")
defer resp.Body.Close()
if resp.StatusCode != http.StatusUnauthorized {
t.Errorf("expected status %d, got %d", http.StatusUnauthorized, resp.StatusCode)
}
}
func TestDeviceHandler_CreateDevice(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
registerUser(server.URL, "devicecreateuser", "devicecreate@test.com", "UserPass123!")
token := getToken(server.URL, "devicecreateuser", "UserPass123!")
resp, body := doPost(server.URL+"/api/v1/devices", token, map[string]interface{}{
"name": "Test Device",
"device_id": "device-test-001",
"device_type": 3,
"device_os": "Windows 10",
})
defer resp.Body.Close()
if resp.StatusCode != http.StatusCreated {
t.Fatalf("expected status %d, got %d, body: %s", http.StatusCreated, resp.StatusCode, body)
}
var result map[string]interface{}
if err := json.Unmarshal([]byte(body), &result); err != nil {
t.Fatalf("failed to parse response: %v", err)
}
if result["code"] != float64(0) {
t.Errorf("expected code 0, got %v", result["code"])
}
}
func TestDeviceHandler_CreateDevice_InvalidBody(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
registerUser(server.URL, "devicecreatebad", "devicecreatebad@test.com", "UserPass123!")
token := getToken(server.URL, "devicecreatebad", "UserPass123!")
req, _ := http.NewRequest("POST", server.URL+"/api/v1/devices", bytes.NewReader([]byte("not json")))
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
t.Fatalf("request failed: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusBadRequest {
t.Errorf("expected status %d for invalid body, got %d", http.StatusBadRequest, resp.StatusCode)
}
}
func TestDeviceHandler_GetDevice(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
registerUser(server.URL, "devicegetuser", "deviceget@test.com", "UserPass123!")
token := getToken(server.URL, "devicegetuser", "UserPass123!")
deviceID := createDeviceForHandlerTest(t, server.URL, token, "device-get-001", "Get Device")
resp, body := doGet(fmt.Sprintf("%s/api/v1/devices/%d", server.URL, deviceID), token)
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatalf("expected status %d, got %d, body: %s", http.StatusOK, resp.StatusCode, body)
}
var result map[string]interface{}
if err := json.Unmarshal([]byte(body), &result); err != nil {
t.Fatalf("failed to parse response: %v", err)
}
if result["code"] != float64(0) {
t.Errorf("expected code 0, got %v", result["code"])
}
}
func TestDeviceHandler_GetDevice_NotFound(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
registerUser(server.URL, "devicegetnf", "devicegetnf@test.com", "UserPass123!")
token := getToken(server.URL, "devicegetnf", "UserPass123!")
resp, body := doGet(server.URL+"/api/v1/devices/99999", token)
defer resp.Body.Close()
if resp.StatusCode != http.StatusNotFound {
t.Errorf("expected status %d, got %d, body: %s", http.StatusNotFound, resp.StatusCode, body)
}
}
func TestDeviceHandler_GetDevice_InvalidID(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
registerUser(server.URL, "devicegetinv", "devicegetinv@test.com", "UserPass123!")
token := getToken(server.URL, "devicegetinv", "UserPass123!")
resp, body := doGet(server.URL+"/api/v1/devices/invalid", token)
defer resp.Body.Close()
if resp.StatusCode != http.StatusBadRequest {
t.Errorf("expected status %d, got %d, body: %s", http.StatusBadRequest, resp.StatusCode, body)
}
}
func TestDeviceHandler_UpdateDevice(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
registerUser(server.URL, "deviceupdateuser", "deviceupdate@test.com", "UserPass123!")
token := getToken(server.URL, "deviceupdateuser", "UserPass123!")
deviceID := createDeviceForHandlerTest(t, server.URL, token, "device-update-001", "Original Name")
resp, body := doPut(fmt.Sprintf("%s/api/v1/devices/%d", server.URL, deviceID), token, map[string]interface{}{
"device_name": "Updated Name",
})
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatalf("expected status %d, got %d, body: %s", http.StatusOK, resp.StatusCode, body)
}
var result map[string]interface{}
if err := json.Unmarshal([]byte(body), &result); err != nil {
t.Fatalf("failed to parse response: %v", err)
}
if result["code"] != float64(0) {
t.Errorf("expected code 0, got %v", result["code"])
}
}
func TestDeviceHandler_UpdateDevice_NotFound(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
registerUser(server.URL, "deviceupdatenf", "deviceupdatenf@test.com", "UserPass123!")
token := getToken(server.URL, "deviceupdatenf", "UserPass123!")
resp, body := doPut(server.URL+"/api/v1/devices/99999", token, map[string]interface{}{
"device_name": "Updated Name",
})
defer resp.Body.Close()
if resp.StatusCode != http.StatusNotFound {
t.Errorf("expected status %d, got %d, body: %s", http.StatusNotFound, resp.StatusCode, body)
}
}
func TestDeviceHandler_DeleteDevice(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
registerUser(server.URL, "devicedeluser", "devicedel@test.com", "UserPass123!")
token := getToken(server.URL, "devicedeluser", "UserPass123!")
deviceID := createDeviceForHandlerTest(t, server.URL, token, "device-del-001", "Delete Device")
resp, body := doDelete(fmt.Sprintf("%s/api/v1/devices/%d", server.URL, deviceID), token)
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatalf("expected status %d, got %d, body: %s", http.StatusOK, resp.StatusCode, body)
}
// Verify deletion
getResp, _ := doGet(fmt.Sprintf("%s/api/v1/devices/%d", server.URL, deviceID), token)
defer getResp.Body.Close()
if getResp.StatusCode != http.StatusNotFound {
t.Errorf("expected device to be deleted, got status %d", getResp.StatusCode)
}
}
func TestDeviceHandler_DeleteDevice_NotFound(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
registerUser(server.URL, "devicedelnf", "devicedelnf@test.com", "UserPass123!")
token := getToken(server.URL, "devicedelnf", "UserPass123!")
resp, body := doDelete(server.URL+"/api/v1/devices/99999", token)
defer resp.Body.Close()
if resp.StatusCode != http.StatusNotFound {
t.Errorf("expected status %d, got %d, body: %s", http.StatusNotFound, resp.StatusCode, body)
}
}
func TestDeviceHandler_UpdateDeviceStatus(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
registerUser(server.URL, "devicestatususer", "devicestatus@test.com", "UserPass123!")
token := getToken(server.URL, "devicestatususer", "UserPass123!")
deviceID := createDeviceForHandlerTest(t, server.URL, token, "device-status-001", "Status Device")
resp, body := doPut(fmt.Sprintf("%s/api/v1/devices/%d/status", server.URL, deviceID), token, map[string]interface{}{
"status": "inactive",
})
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatalf("expected status %d, got %d, body: %s", http.StatusOK, resp.StatusCode, body)
}
}
func TestDeviceHandler_UpdateDeviceStatus_InvalidStatus(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
registerUser(server.URL, "devicestatusinv", "devicestatusinv@test.com", "UserPass123!")
token := getToken(server.URL, "devicestatusinv", "UserPass123!")
deviceID := createDeviceForHandlerTest(t, server.URL, token, "device-status-inv-001", "Status Device")
resp, body := doPut(fmt.Sprintf("%s/api/v1/devices/%d/status", server.URL, deviceID), token, map[string]interface{}{
"status": "invalid_status",
})
defer resp.Body.Close()
if resp.StatusCode != http.StatusBadRequest {
t.Errorf("expected status %d, got %d, body: %s", http.StatusBadRequest, resp.StatusCode, body)
}
}
func TestDeviceHandler_TrustDevice(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
registerUser(server.URL, "devicetrustuser", "devicetrust@test.com", "UserPass123!")
token := getToken(server.URL, "devicetrustuser", "UserPass123!")
deviceID := createDeviceForHandlerTest(t, server.URL, token, "device-trust-001", "Trust Device")
resp, body := doPost(fmt.Sprintf("%s/api/v1/devices/%d/trust", server.URL, deviceID), token, map[string]interface{}{
"trust_duration": "24h",
})
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatalf("expected status %d, got %d, body: %s", http.StatusOK, resp.StatusCode, body)
}
}
func TestDeviceHandler_UntrustDevice(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
registerUser(server.URL, "deviceuntrustuser", "deviceuntrust@test.com", "UserPass123!")
token := getToken(server.URL, "deviceuntrustuser", "UserPass123!")
deviceID := createDeviceForHandlerTest(t, server.URL, token, "device-untrust-001", "Untrust Device")
// First trust the device
trustResp, trustBody := doPost(fmt.Sprintf("%s/api/v1/devices/%d/trust", server.URL, deviceID), token, map[string]interface{}{
"trust_duration": "24h",
})
defer trustResp.Body.Close()
if trustResp.StatusCode != http.StatusOK {
t.Fatalf("expected trust status %d, got %d, body: %s", http.StatusOK, trustResp.StatusCode, trustBody)
}
// Then untrust
resp, body := doDelete(fmt.Sprintf("%s/api/v1/devices/%d/trust", server.URL, deviceID), token)
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatalf("expected status %d, got %d, body: %s", http.StatusOK, resp.StatusCode, body)
}
}
func TestDeviceHandler_GetMyTrustedDevices(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
registerUser(server.URL, "devicetrusteduser", "devicetrusted@test.com", "UserPass123!")
token := getToken(server.URL, "devicetrusteduser", "UserPass123!")
deviceID := createDeviceForHandlerTest(t, server.URL, token, "device-trusted-001", "Trusted Device")
// Trust the device first
trustResp, trustBody := doPost(fmt.Sprintf("%s/api/v1/devices/%d/trust", server.URL, deviceID), token, map[string]interface{}{
"trust_duration": "24h",
})
defer trustResp.Body.Close()
if trustResp.StatusCode != http.StatusOK {
t.Fatalf("expected trust status %d, got %d, body: %s", http.StatusOK, trustResp.StatusCode, trustBody)
}
resp, body := doGet(server.URL+"/api/v1/devices/me/trusted", token)
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatalf("expected status %d, got %d, body: %s", http.StatusOK, resp.StatusCode, body)
}
var result map[string]interface{}
if err := json.Unmarshal([]byte(body), &result); err != nil {
t.Fatalf("failed to parse response: %v", err)
}
if result["code"] != float64(0) {
t.Errorf("expected code 0, got %v", result["code"])
}
}
func TestDeviceHandler_LogoutAllOtherDevices(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
registerUser(server.URL, "devicelogoutuser", "devicelogout@test.com", "UserPass123!")
token := getToken(server.URL, "devicelogoutuser", "UserPass123!")
deviceID := createDeviceForHandlerTest(t, server.URL, token, "device-logout-001", "Logout Device")
req, _ := http.NewRequest("POST", server.URL+"/api/v1/devices/me/logout-others", nil)
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("X-Device-ID", fmt.Sprintf("%d", deviceID))
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
t.Fatalf("request failed: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
bodyBytes, _ := json.Marshal(resp.Body)
t.Fatalf("expected status %d, got %d, body: %s", http.StatusOK, resp.StatusCode, string(bodyBytes))
}
}
func TestDeviceHandler_LogoutAllOtherDevices_MissingDeviceID(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
registerUser(server.URL, "devicelogoutbad", "devicelogoutbad@test.com", "UserPass123!")
token := getToken(server.URL, "devicelogoutbad", "UserPass123!")
resp, body := doPost(server.URL+"/api/v1/devices/me/logout-others", token, nil)
defer resp.Body.Close()
if resp.StatusCode != http.StatusBadRequest {
t.Errorf("expected status %d, got %d, body: %s", http.StatusBadRequest, resp.StatusCode, body)
}
}
func TestDeviceHandler_GetUserDevices_AdminCanViewOthers(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
t.Setenv("BOOTSTRAP_SECRET", "handler-bootstrap-secret")
adminToken := bootstrapAdmin(server.URL, "handler-bootstrap-secret", "deviceadmin", "deviceadmin@test.com", "AdminPass123!")
registerUser(server.URL, "deviceuserview", "deviceuserview@test.com", "UserPass123!")
if adminToken == "" {
t.Fatal("bootstrap admin should return access token")
}
resp, body := doGet(server.URL+"/api/v1/devices/users/2", adminToken)
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatalf("expected status %d, got %d, body: %s", http.StatusOK, resp.StatusCode, body)
}
}
func TestDeviceHandler_GetUserDevices_NonAdminForbidden(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
registerUser(server.URL, "deviceuser1", "deviceuser1@test.com", "UserPass123!")
registerUser(server.URL, "deviceuser2", "deviceuser2@test.com", "UserPass123!")
token := getToken(server.URL, "deviceuser1", "UserPass123!")
resp, body := doGet(server.URL+"/api/v1/devices/users/2", token)
defer resp.Body.Close()
if resp.StatusCode != http.StatusForbidden {
t.Errorf("expected status %d, got %d, body: %s", http.StatusForbidden, resp.StatusCode, body)
}
}
func TestDeviceHandler_GetAllDevices_AdminOnly(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
t.Setenv("BOOTSTRAP_SECRET", "handler-bootstrap-secret")
adminToken := bootstrapAdmin(server.URL, "handler-bootstrap-secret", "deviceadmin2", "deviceadmin2@test.com", "AdminPass123!")
if adminToken == "" {
t.Fatal("bootstrap admin should return access token")
}
resp, body := doGet(server.URL+"/api/v1/admin/devices", adminToken)
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatalf("expected status %d, got %d, body: %s", http.StatusOK, resp.StatusCode, body)
}
}
func TestDeviceHandler_GetAllDevices_NonAdminForbidden(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
registerUser(server.URL, "deviceuser3", "deviceuser3@test.com", "UserPass123!")
token := getToken(server.URL, "deviceuser3", "UserPass123!")
resp, body := doGet(server.URL+"/api/v1/admin/devices", token)
defer resp.Body.Close()
if resp.StatusCode != http.StatusForbidden {
t.Errorf("expected status %d, got %d, body: %s", http.StatusForbidden, resp.StatusCode, body)
}
}
func TestDeviceHandler_TrustDeviceByDeviceID(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
registerUser(server.URL, "devicetrustiduser", "devicetrustid@test.com", "UserPass123!")
token := getToken(server.URL, "devicetrustiduser", "UserPass123!")
// Create device with specific device_id
resp, body := doPost(server.URL+"/api/v1/devices", token, map[string]interface{}{
"name": "Trust By ID Device",
"device_id": "my-unique-device-id",
"device_type": 1,
})
defer resp.Body.Close()
if resp.StatusCode != http.StatusCreated {
t.Fatalf("expected create status %d, got %d, body: %s", http.StatusCreated, resp.StatusCode, body)
}
// Trust by device ID
trustResp, trustBody := doPost(server.URL+"/api/v1/devices/by-device-id/my-unique-device-id/trust", token, map[string]interface{}{
"trust_duration": "24h",
})
defer trustResp.Body.Close()
if trustResp.StatusCode != http.StatusOK {
t.Fatalf("expected status %d, got %d, body: %s", http.StatusOK, trustResp.StatusCode, trustBody)
}
}
func TestDeviceHandler_TrustDeviceByDeviceID_EmptyID(t *testing.T) {
server, cleanup := setupHandlerTestServer(t)
defer cleanup()
registerUser(server.URL, "devicetrustidbad", "devicetrustidbad@test.com", "UserPass123!")
token := getToken(server.URL, "devicetrustidbad", "UserPass123!")
// The route uses ":deviceId" path param, so empty ID would be a different route or 404
// Actually the route is /by-device-id/:deviceId/trust, so empty deviceId is not matched
// Let's test with a device ID that doesn't exist
resp, body := doPost(server.URL+"/api/v1/devices/by-device-id/nonexistent/trust", token, map[string]interface{}{
"trust_duration": "24h",
})
defer resp.Body.Close()
// Service returns error for non-existent device
if resp.StatusCode != http.StatusNotFound && resp.StatusCode != http.StatusInternalServerError {
t.Errorf("expected status 404 or 500 for non-existent device, got %d, body: %s", resp.StatusCode, body)
}
}