Simplify device update test by using new(false) and new(int32(10)) instead of declaring named sync variables and taking their addresses.
536 lines
20 KiB
Go
536 lines
20 KiB
Go
package main
|
|
|
|
import (
|
|
"bookhoard/internal/database"
|
|
"bookhoard/internal/handlers"
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestDeviceRegistrationFlow(t *testing.T) {
|
|
setup := setupTestServer(t)
|
|
|
|
// Step 1: Initiate device registration
|
|
regRequest := map[string]interface{}{
|
|
"device_name": "Test Kindle Paperwhite",
|
|
"device_type": "koreader",
|
|
"device_identifier": "kindle-test-hw-id-12345",
|
|
}
|
|
regBody, _ := json.Marshal(regRequest)
|
|
|
|
req := httptest.NewRequest("POST", "/api/devices/register", bytes.NewReader(regBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
rec := httptest.NewRecorder()
|
|
setup.Server.Config.Handler.ServeHTTP(rec, req)
|
|
|
|
assert.Equal(t, http.StatusCreated, rec.Code, "Should initiate device registration")
|
|
|
|
var regResponse map[string]interface{}
|
|
json.Unmarshal(rec.Body.Bytes(), ®Response)
|
|
|
|
registrationID, ok := regResponse["registration_id"].(string)
|
|
assert.True(t, ok, "Should have registration_id")
|
|
assert.NotEmpty(t, registrationID, "Registration ID should not be empty")
|
|
|
|
authURL, ok := regResponse["auth_url"].(string)
|
|
assert.True(t, ok, "Should have auth_url")
|
|
assert.NotEmpty(t, authURL, "Auth URL should not be empty")
|
|
|
|
// Step 2: Check registration status (should be pending initially)
|
|
statusRequest := map[string]interface{}{
|
|
"registration_id": registrationID,
|
|
}
|
|
statusBody, _ := json.Marshal(statusRequest)
|
|
|
|
req = httptest.NewRequest("POST", "/api/devices/register/status", bytes.NewReader(statusBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
rec = httptest.NewRecorder()
|
|
setup.Server.Config.Handler.ServeHTTP(rec, req)
|
|
|
|
assert.Equal(t, http.StatusOK, rec.Code, "Should check registration status")
|
|
|
|
var statusResponse map[string]interface{}
|
|
json.Unmarshal(rec.Body.Bytes(), &statusResponse)
|
|
|
|
status, ok := statusResponse["status"].(string)
|
|
assert.True(t, ok, "Should have status")
|
|
assert.Equal(t, "pending", status, "Should be pending initially")
|
|
|
|
// Step 3: Login as user to approve device
|
|
loginRequest := map[string]interface{}{
|
|
"login": "testuser@tests.bookhoard.internal",
|
|
"password": "Test@Pass123!",
|
|
}
|
|
loginBody, _ := json.Marshal(loginRequest)
|
|
|
|
req = httptest.NewRequest("POST", "/api/auth/login", bytes.NewReader(loginBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
rec = httptest.NewRecorder()
|
|
setup.Server.Config.Handler.ServeHTTP(rec, req)
|
|
|
|
assert.Equal(t, http.StatusOK, rec.Code, "Should login successfully")
|
|
|
|
var loginResponse map[string]interface{}
|
|
json.Unmarshal(rec.Body.Bytes(), &loginResponse)
|
|
|
|
token, ok := loginResponse["access_token"].(string)
|
|
assert.True(t, ok, "Should have access_token")
|
|
|
|
// Step 4: Approve the device
|
|
req = httptest.NewRequest("GET", fmt.Sprintf("/api/devices/approve/%s", registrationID), nil)
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
rec = httptest.NewRecorder()
|
|
setup.Server.Config.Handler.ServeHTTP(rec, req)
|
|
|
|
assert.Equal(t, http.StatusOK, rec.Code, "Should approve device")
|
|
|
|
// Step 5: Check registration status again (should be approved now)
|
|
req = httptest.NewRequest("POST", "/api/devices/register/status", bytes.NewReader(statusBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
rec = httptest.NewRecorder()
|
|
setup.Server.Config.Handler.ServeHTTP(rec, req)
|
|
|
|
assert.Equal(t, http.StatusOK, rec.Code, "Should check registration status after approval")
|
|
|
|
var approvedStatus map[string]interface{}
|
|
json.Unmarshal(rec.Body.Bytes(), &approvedStatus)
|
|
|
|
status, ok = approvedStatus["status"].(string)
|
|
assert.True(t, ok, "Should have status")
|
|
assert.Equal(t, "approved", status, "Should be approved after user approval")
|
|
|
|
authToken, ok := approvedStatus["auth_token"].(string)
|
|
assert.True(t, ok, "Should have auth_token after approval")
|
|
assert.NotEmpty(t, authToken, "Auth token should not be empty")
|
|
}
|
|
|
|
func TestListDevices(t *testing.T) {
|
|
setup := setupDeviceTest(t)
|
|
defer setup.Server.Close()
|
|
|
|
// Create a device using the setup helper
|
|
_ = setup.CreateDevice(t, "Test Device", "koreader", "test-device-123")
|
|
|
|
// List devices
|
|
req := httptest.NewRequest("GET", "/api/devices", nil)
|
|
req.Header.Set("Authorization", "Bearer "+setup.UserToken)
|
|
rec := httptest.NewRecorder()
|
|
setup.Server.Config.Handler.ServeHTTP(rec, req)
|
|
|
|
assert.Equal(t, http.StatusOK, rec.Code, "Should list devices")
|
|
|
|
var response handlers.DeviceListResponse
|
|
err := json.Unmarshal(rec.Body.Bytes(), &response)
|
|
require.NoError(t, err, "Should unmarshal device list response")
|
|
assert.GreaterOrEqual(t, len(response.Devices), 1, "Should have at least one device")
|
|
|
|
firstDevice := response.Devices[0]
|
|
assert.Equal(t, "Test Device", firstDevice.DeviceName, "Should match created device name")
|
|
}
|
|
|
|
func TestUpdateDevice(t *testing.T) {
|
|
setup := setupDeviceTest(t)
|
|
defer setup.Server.Close()
|
|
|
|
// Create a device using the setup helper
|
|
device := setup.CreateDevice(t, "Test Device", "koreader", "test-device-123")
|
|
|
|
// Update device
|
|
updateRequest := handlers.DeviceUpdateRequest{
|
|
DeviceName: "Updated Device Name",
|
|
SyncEnabled: new(false),
|
|
SyncFrequencyMinutes: new(int32(10)),
|
|
}
|
|
updateBody, _ := json.Marshal(updateRequest)
|
|
|
|
req := httptest.NewRequest("PUT", fmt.Sprintf("/api/devices/%s", device.ID.String()), bytes.NewReader(updateBody))
|
|
req.Header.Set("Authorization", "Bearer "+setup.UserToken)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
rec := httptest.NewRecorder()
|
|
setup.Server.Config.Handler.ServeHTTP(rec, req)
|
|
|
|
assert.Equal(t, http.StatusOK, rec.Code, "Should update device")
|
|
|
|
var response map[string]interface{}
|
|
json.Unmarshal(rec.Body.Bytes(), &response)
|
|
|
|
assert.True(t, response["device_updated"].(bool), "Should confirm device updated")
|
|
|
|
// NEW: Verify database state
|
|
updatedDevice := response["device"].(map[string]interface{})
|
|
assert.Equal(t, "Updated Device Name", updatedDevice["device_name"], "Should have updated name")
|
|
|
|
// Verify in database
|
|
pgDeviceID := pgtype.UUID{Bytes: [16]byte(device.ID), Valid: true}
|
|
dbDevice, err := setup.DB.GetDevice(context.Background(), pgDeviceID)
|
|
require.NoError(t, err, "Should retrieve updated device")
|
|
assert.Equal(t, "Updated Device Name", dbDevice.DeviceName, "DB should have updated name")
|
|
assert.Equal(t, false, dbDevice.SyncEnabled.Bool, "DB should show sync disabled")
|
|
assert.Equal(t, int32(10), dbDevice.SyncFrequencyMinutes.Int32, "DB should have updated frequency")
|
|
}
|
|
|
|
func TestDeleteDevice(t *testing.T) {
|
|
setup := setupDeviceTest(t)
|
|
defer setup.Server.Close()
|
|
|
|
// Create a device using the setup helper
|
|
device := setup.CreateDevice(t, "Test Device", "koreader", "test-device-123")
|
|
|
|
// Delete device
|
|
req := httptest.NewRequest("DELETE", fmt.Sprintf("/api/devices/%s", device.ID.String()), nil)
|
|
req.Header.Set("Authorization", "Bearer "+setup.UserToken)
|
|
rec := httptest.NewRecorder()
|
|
setup.Server.Config.Handler.ServeHTTP(rec, req)
|
|
|
|
assert.Equal(t, http.StatusNoContent, rec.Code, "Should delete device")
|
|
|
|
// Verify device is deleted
|
|
pgDeviceID := pgtype.UUID{Bytes: [16]byte(device.ID), Valid: true}
|
|
_, err := setup.DB.GetDevice(context.Background(), pgDeviceID)
|
|
assert.Error(t, err, "Device should be deleted")
|
|
}
|
|
|
|
func TestDeviceAuthentication(t *testing.T) {
|
|
setup := setupTestServer(t)
|
|
|
|
// Create a device directly in the database
|
|
userID := getTestUserID(t, setup.DB)
|
|
|
|
deviceToken := fmt.Sprintf("dev_%s", uuid.New().String())
|
|
_, err := setup.DB.CreateDevice(context.Background(), database.CreateDeviceParams{
|
|
UserID: pgtype.UUID{Bytes: [16]byte(userID), Valid: true},
|
|
DeviceName: "Test Device",
|
|
DeviceType: "koreader",
|
|
DeviceIdentifier: "test-device-123",
|
|
AuthToken: deviceToken,
|
|
SyncEnabled: pgtype.Bool{Bool: true, Valid: true},
|
|
AutoSync: pgtype.Bool{Bool: true, Valid: true},
|
|
SyncFrequencyMinutes: pgtype.Int4{Int32: 5, Valid: true},
|
|
DeviceMetadata: []byte("{}"),
|
|
})
|
|
assert.NoError(t, err, "Should create device")
|
|
|
|
// Test device authentication
|
|
req := httptest.NewRequest("GET", "/api/devices", nil)
|
|
req.Header.Set("Authorization", "Bearer "+deviceToken)
|
|
rec := httptest.NewRecorder()
|
|
setup.Server.Config.Handler.ServeHTTP(rec, req)
|
|
|
|
// This should fail because device auth middleware is not applied to /api/devices
|
|
// Device auth is for sync endpoints only
|
|
assert.Equal(t, http.StatusUnauthorized, rec.Code, "Should require user auth for device management")
|
|
}
|
|
|
|
func TestListPendingRegistrations(t *testing.T) {
|
|
setup := setupTestServer(t)
|
|
|
|
req := httptest.NewRequest("GET", "/api/devices/pending", nil)
|
|
req.Header.Set("Authorization", "Bearer "+setup.Token)
|
|
rec := httptest.NewRecorder()
|
|
setup.Server.Config.Handler.ServeHTTP(rec, req)
|
|
|
|
assert.Equal(t, http.StatusOK, rec.Code, "Should list pending registrations")
|
|
|
|
var response map[string]interface{}
|
|
json.Unmarshal(rec.Body.Bytes(), &response)
|
|
|
|
pending, ok := response["pending_registrations"].([]interface{})
|
|
assert.True(t, ok, "Should have pending_registrations array")
|
|
assert.NotNil(t, pending, "Pending registrations should not be nil")
|
|
}
|
|
|
|
func TestApproveDeviceRegistration(t *testing.T) {
|
|
setup := setupTestServer(t)
|
|
|
|
regRequest := map[string]interface{}{
|
|
"device_name": "Test Device for Approval",
|
|
"device_type": "koreader",
|
|
"device_identifier": "test-approval-12345",
|
|
}
|
|
regBody, _ := json.Marshal(regRequest)
|
|
|
|
req := httptest.NewRequest("POST", "/api/devices/register", bytes.NewReader(regBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
rec := httptest.NewRecorder()
|
|
setup.Server.Config.Handler.ServeHTTP(rec, req)
|
|
|
|
assert.Equal(t, http.StatusCreated, rec.Code, "Should initiate device registration")
|
|
|
|
var regResponse map[string]interface{}
|
|
json.Unmarshal(rec.Body.Bytes(), ®Response)
|
|
|
|
registrationID, ok := regResponse["registration_id"].(string)
|
|
assert.True(t, ok, "Should have registration_id")
|
|
|
|
req = httptest.NewRequest("GET", fmt.Sprintf("/api/devices/approve/%s", registrationID), nil)
|
|
req.Header.Set("Authorization", "Bearer "+setup.Token)
|
|
rec = httptest.NewRecorder()
|
|
setup.Server.Config.Handler.ServeHTTP(rec, req)
|
|
|
|
assert.Equal(t, http.StatusOK, rec.Code, "Should approve device registration")
|
|
|
|
var approveResponse map[string]interface{}
|
|
json.Unmarshal(rec.Body.Bytes(), &approveResponse)
|
|
|
|
assert.True(t, approveResponse["approved"].(bool), "Should confirm approval")
|
|
}
|
|
|
|
func TestRejectDeviceRegistration(t *testing.T) {
|
|
setup := setupTestServer(t)
|
|
|
|
regRequest := map[string]interface{}{
|
|
"device_name": "Test Device for Rejection",
|
|
"device_type": "koreader",
|
|
"device_identifier": "test-rejection-12345",
|
|
}
|
|
regBody, _ := json.Marshal(regRequest)
|
|
|
|
req := httptest.NewRequest("POST", "/api/devices/register", bytes.NewReader(regBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
rec := httptest.NewRecorder()
|
|
setup.Server.Config.Handler.ServeHTTP(rec, req)
|
|
|
|
assert.Equal(t, http.StatusCreated, rec.Code, "Should initiate device registration")
|
|
|
|
var regResponse map[string]interface{}
|
|
json.Unmarshal(rec.Body.Bytes(), ®Response)
|
|
|
|
registrationID, ok := regResponse["registration_id"].(string)
|
|
assert.True(t, ok, "Should have registration_id")
|
|
|
|
req = httptest.NewRequest("POST", fmt.Sprintf("/api/devices/reject/%s", registrationID), nil)
|
|
req.Header.Set("Authorization", "Bearer "+setup.Token)
|
|
rec = httptest.NewRecorder()
|
|
setup.Server.Config.Handler.ServeHTTP(rec, req)
|
|
|
|
assert.Equal(t, http.StatusOK, rec.Code, "Should reject device registration")
|
|
|
|
var rejectResponse map[string]interface{}
|
|
json.Unmarshal(rec.Body.Bytes(), &rejectResponse)
|
|
|
|
assert.Equal(t, "device registration rejected", rejectResponse["message"], "Should confirm rejection message")
|
|
}
|
|
|
|
func TestRegenerateDeviceToken_Success(t *testing.T) {
|
|
setup := setupDeviceTest(t)
|
|
defer setup.Server.Close()
|
|
|
|
// Create a device
|
|
device := setup.CreateDevice(t, "Test Device", "koreader", "test-device-123")
|
|
oldToken := device.AuthToken
|
|
|
|
// Regenerate token
|
|
req := httptest.NewRequest("PUT", fmt.Sprintf("/api/devices/%s/regenerate-token", device.ID.String()), nil)
|
|
req.Header.Set("Authorization", "Bearer "+setup.UserToken)
|
|
rec := httptest.NewRecorder()
|
|
setup.Server.Config.Handler.ServeHTTP(rec, req)
|
|
|
|
assert.Equal(t, http.StatusOK, rec.Code, "Should regenerate token")
|
|
|
|
var response map[string]interface{}
|
|
json.Unmarshal(rec.Body.Bytes(), &response)
|
|
|
|
assert.True(t, response["message"].(string) != "", "Should have success message")
|
|
|
|
newToken, ok := response["auth_token"].(string)
|
|
assert.True(t, ok, "Should have auth_token")
|
|
assert.NotEmpty(t, newToken, "New token should not be empty")
|
|
assert.NotEqual(t, oldToken, newToken, "New token should be different from old token")
|
|
|
|
// Verify device info is returned
|
|
deviceInfo, ok := response["device"].(map[string]interface{})
|
|
assert.True(t, ok, "Should have device info")
|
|
assert.Equal(t, "Test Device", deviceInfo["device_name"], "Should return device name")
|
|
|
|
// Verify sync URLs are returned
|
|
syncURLs, ok := response["sync_urls"].(map[string]interface{})
|
|
assert.True(t, ok, "Should have sync_urls")
|
|
assert.Contains(t, syncURLs, "progress", "Should have progress URL")
|
|
assert.Contains(t, syncURLs, "metadata", "Should have metadata URL")
|
|
}
|
|
|
|
func TestRegenerateDeviceToken_OldTokenInvalidated(t *testing.T) {
|
|
setup := setupDeviceTest(t)
|
|
defer setup.Server.Close()
|
|
|
|
// Create a device
|
|
device := setup.CreateDevice(t, "Test Device", "koreader", "test-device-invalidated")
|
|
|
|
// Test old token works initially
|
|
req1 := httptest.NewRequest("POST", "/api/sync/koreader/progress", nil)
|
|
req1.Header.Set("Authorization", "Bearer "+device.AuthToken)
|
|
req1.Header.Set("Content-Type", "application/json")
|
|
rec1 := httptest.NewRecorder()
|
|
setup.Server.Config.Handler.ServeHTTP(rec1, req1)
|
|
// May fail for other reasons (no data), but should not be unauthorized
|
|
assert.NotEqual(t, http.StatusUnauthorized, rec1.Code, "Old token should work initially")
|
|
|
|
// Regenerate token
|
|
req2 := httptest.NewRequest("PUT", fmt.Sprintf("/api/devices/%s/regenerate-token", device.ID.String()), nil)
|
|
req2.Header.Set("Authorization", "Bearer "+setup.UserToken)
|
|
rec2 := httptest.NewRecorder()
|
|
setup.Server.Config.Handler.ServeHTTP(rec2, req2)
|
|
assert.Equal(t, http.StatusOK, rec2.Code, "Should regenerate token")
|
|
|
|
var response map[string]interface{}
|
|
json.Unmarshal(rec2.Body.Bytes(), &response)
|
|
newToken := response["auth_token"].(string)
|
|
|
|
// Test old token no longer works
|
|
req3 := httptest.NewRequest("POST", "/api/sync/koreader/progress", nil)
|
|
req3.Header.Set("Authorization", "Bearer "+device.AuthToken)
|
|
req3.Header.Set("Content-Type", "application/json")
|
|
rec3 := httptest.NewRecorder()
|
|
setup.Server.Config.Handler.ServeHTTP(rec3, req3)
|
|
assert.Equal(t, http.StatusUnauthorized, rec3.Code, "Old token should be invalid after regeneration")
|
|
|
|
// Test new token works
|
|
req4 := httptest.NewRequest("POST", "/api/sync/koreader/progress", nil)
|
|
req4.Header.Set("Authorization", "Bearer "+newToken)
|
|
req4.Header.Set("Content-Type", "application/json")
|
|
rec4 := httptest.NewRecorder()
|
|
setup.Server.Config.Handler.ServeHTTP(rec4, req4)
|
|
// May fail for other reasons, but should not be unauthorized
|
|
assert.NotEqual(t, http.StatusUnauthorized, rec4.Code, "New token should work")
|
|
}
|
|
|
|
func TestRegenerateDeviceToken_Unauthorized(t *testing.T) {
|
|
setup := setupDeviceTest(t)
|
|
defer setup.Server.Close()
|
|
|
|
device := setup.CreateDevice(t, "Test Device", "koreader", "test-device-unauth")
|
|
|
|
// Try to regenerate without JWT token
|
|
req := httptest.NewRequest("PUT", fmt.Sprintf("/api/devices/%s/regenerate-token", device.ID.String()), nil)
|
|
rec := httptest.NewRecorder()
|
|
setup.Server.Config.Handler.ServeHTTP(rec, req)
|
|
|
|
assert.Equal(t, http.StatusUnauthorized, rec.Code, "Should require authentication")
|
|
|
|
var response map[string]interface{}
|
|
json.Unmarshal(rec.Body.Bytes(), &response)
|
|
assert.Contains(t, response, "error", "Should return error message")
|
|
}
|
|
|
|
func TestRegenerateDeviceToken_Forbidden(t *testing.T) {
|
|
setup := setupDeviceTest(t)
|
|
defer setup.Server.Close()
|
|
|
|
// Create device for user 1
|
|
device1 := setup.CreateDevice(t, "User1 Device", "koreader", "user1-device")
|
|
|
|
// Create a second user with different credentials
|
|
ctx := context.Background()
|
|
passwordHash := "$2a$10$JjAtK7PPa1WexQC3AUGe8OXLeuseZ/haN1Mz7emMo6CfOvMiTVXWq"
|
|
_, err := setup.DB.CreateUser(ctx, database.CreateUserParams{
|
|
Email: "differentuser@example.com",
|
|
Username: "differentuser",
|
|
PasswordHash: passwordHash,
|
|
FirstName: pgtype.Text{String: "Different", Valid: true},
|
|
LastName: pgtype.Text{String: "User", Valid: true},
|
|
Role: "user",
|
|
})
|
|
assert.NoError(t, err)
|
|
|
|
// Clean up the second user after the test
|
|
defer func() {
|
|
user2, err := setup.DB.GetUserByEmail(ctx, "differentuser@example.com")
|
|
if err == nil {
|
|
setup.DB.DeleteUser(ctx, user2.ID)
|
|
}
|
|
}()
|
|
|
|
// Login as user 2
|
|
loginRequest := map[string]interface{}{
|
|
"login": "differentuser@example.com",
|
|
"password": "Test@Pass123!",
|
|
}
|
|
loginBody, _ := json.Marshal(loginRequest)
|
|
loginReq := httptest.NewRequest("POST", "/api/auth/login", bytes.NewReader(loginBody))
|
|
loginReq.Header.Set("Content-Type", "application/json")
|
|
loginRec := httptest.NewRecorder()
|
|
setup.Server.Config.Handler.ServeHTTP(loginRec, loginReq)
|
|
|
|
var loginResponse map[string]interface{}
|
|
json.Unmarshal(loginRec.Body.Bytes(), &loginResponse)
|
|
user2Token := loginResponse["access_token"].(string)
|
|
|
|
// Try to regenerate user 1's device with user 2's token
|
|
req := httptest.NewRequest("PUT", fmt.Sprintf("/api/devices/%s/regenerate-token", device1.ID.String()), nil)
|
|
req.Header.Set("Authorization", "Bearer "+user2Token)
|
|
rec := httptest.NewRecorder()
|
|
setup.Server.Config.Handler.ServeHTTP(rec, req)
|
|
|
|
assert.Equal(t, http.StatusForbidden, rec.Code, "Should forbid access to other user's device")
|
|
|
|
var response map[string]interface{}
|
|
json.Unmarshal(rec.Body.Bytes(), &response)
|
|
assert.Contains(t, response, "error", "Should return error message")
|
|
}
|
|
|
|
func TestRegenerateDeviceToken_NotFound(t *testing.T) {
|
|
setup := setupDeviceTest(t)
|
|
defer setup.Server.Close()
|
|
|
|
fakeDeviceID, _ := uuid.NewUUID()
|
|
|
|
// Try to regenerate non-existent device
|
|
req := httptest.NewRequest("PUT", fmt.Sprintf("/api/devices/%s/regenerate-token", fakeDeviceID.String()), nil)
|
|
req.Header.Set("Authorization", "Bearer "+setup.UserToken)
|
|
rec := httptest.NewRecorder()
|
|
setup.Server.Config.Handler.ServeHTTP(rec, req)
|
|
|
|
assert.Equal(t, http.StatusNotFound, rec.Code, "Should return not found")
|
|
|
|
var response map[string]interface{}
|
|
json.Unmarshal(rec.Body.Bytes(), &response)
|
|
assert.Contains(t, response, "error", "Should return error message")
|
|
}
|
|
|
|
func TestRegenerateDeviceToken_KoboDevice(t *testing.T) {
|
|
setup := setupDeviceTest(t)
|
|
defer setup.Server.Close()
|
|
|
|
// Create a Kobo device
|
|
koboDevice := setup.CreateDevice(t, "Test Kobo", "kobo", "test-kobo-regen")
|
|
oldToken := koboDevice.AuthToken
|
|
|
|
// Regenerate token
|
|
req := httptest.NewRequest("PUT", fmt.Sprintf("/api/devices/%s/regenerate-token", koboDevice.ID.String()), nil)
|
|
req.Header.Set("Authorization", "Bearer "+setup.UserToken)
|
|
rec := httptest.NewRecorder()
|
|
setup.Server.Config.Handler.ServeHTTP(rec, req)
|
|
|
|
assert.Equal(t, http.StatusOK, rec.Code, "Should regenerate token")
|
|
|
|
var response map[string]interface{}
|
|
json.Unmarshal(rec.Body.Bytes(), &response)
|
|
|
|
newToken := response["auth_token"].(string)
|
|
assert.NotEqual(t, oldToken, newToken, "New token should be different")
|
|
|
|
// Verify Kobo sync URLs are returned
|
|
syncURLs, ok := response["sync_urls"].(map[string]interface{})
|
|
assert.True(t, ok, "Should have sync_urls")
|
|
assert.Contains(t, syncURLs, "sync_url", "Should have sync_url")
|
|
assert.Contains(t, syncURLs, "markup", "Should have markup URL")
|
|
assert.Contains(t, syncURLs, "bookmark", "Should have bookmark URL")
|
|
assert.Contains(t, syncURLs, "init", "Should have init URL")
|
|
|
|
// Verify URLs contain new token
|
|
syncURL := syncURLs["sync_url"].(string)
|
|
assert.Contains(t, syncURL, newToken, "Sync URL should contain new token")
|
|
assert.NotContains(t, syncURL, oldToken, "Sync URL should not contain old token")
|
|
}
|