Files
bookhoard/internal/handlers/devices_test.go
T
john-okeefe 289284522b test: add test reliability plan and device test coverage
- Add TEST_RELIABILITY_PLAN.md documenting test strategy
- Add devices_test.go with device handler tests
- Add device_auth_test.go with device authentication middleware tests
2026-02-13 16:37:54 -05:00

203 lines
5.6 KiB
Go

package handlers
import (
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
func TestDeviceInfo_DeviceTypeSyncURLs(t *testing.T) {
baseURL := "http://localhost:8080"
tests := []struct {
name string
deviceType string
authToken string
expectedURLs map[string]string
expectedHasURLs bool
}{
{
name: "Kobo device",
deviceType: "kobo",
authToken: "dev_abc123",
expectedURLs: map[string]string{
"sync_url": "http://localhost:8080/api/sync/kobo/dev_abc123",
"markup": "http://localhost:8080/api/sync/kobo/dev_abc123/markup",
"bookmark": "http://localhost:8080/api/sync/kobo/dev_abc123/bookmark",
"init": "http://localhost:8080/api/sync/kobo/dev_abc123/v1/initialization",
},
expectedHasURLs: true,
},
{
name: "KOReader device",
deviceType: "koreader",
authToken: "dev_xyz789",
expectedURLs: map[string]string{
"progress": "http://localhost:8080/api/sync/koreader/progress",
"metadata": "http://localhost:8080/api/sync/koreader/metadata",
"bookmarks": "http://localhost:8080/api/sync/koreader/bookmarks",
},
expectedHasURLs: true,
},
{
name: "Unknown device type",
deviceType: "unknown",
authToken: "dev_test",
expectedURLs: map[string]string{},
expectedHasURLs: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Build sync URLs as done in RegenerateDeviceToken
syncURLs := map[string]string{}
switch tt.deviceType {
case "kobo":
syncURLs["sync_url"] = baseURL + "/api/sync/kobo/" + tt.authToken
syncURLs["markup"] = baseURL + "/api/sync/kobo/" + tt.authToken + "/markup"
syncURLs["bookmark"] = baseURL + "/api/sync/kobo/" + tt.authToken + "/bookmark"
syncURLs["init"] = baseURL + "/api/sync/kobo/" + tt.authToken + "/v1/initialization"
case "koreader":
syncURLs["progress"] = baseURL + "/api/sync/koreader/progress"
syncURLs["metadata"] = baseURL + "/api/sync/koreader/metadata"
syncURLs["bookmarks"] = baseURL + "/api/sync/koreader/bookmarks"
}
if tt.expectedHasURLs {
assert.Len(t, syncURLs, len(tt.expectedURLs))
for key, expectedURL := range tt.expectedURLs {
actualURL, ok := syncURLs[key]
assert.True(t, ok, "URL key %s should exist", key)
assert.Equal(t, expectedURL, actualURL)
}
} else {
assert.Empty(t, syncURLs)
}
})
}
}
func TestGenerateDeviceToken(t *testing.T) {
// Test that generateDeviceToken produces valid tokens
tokens := make(map[string]bool)
// Generate multiple tokens and verify they're unique
for i := 0; i < 100; i++ {
token, err := generateDeviceToken()
assert.NoError(t, err, "Should generate token without error")
assert.NotEmpty(t, token, "Token should not be empty")
// Verify token starts with "dev_"
assert.True(t, len(token) > 4, "Token should be longer than prefix")
assert.Contains(t, token, "dev_", "Token should start with dev_ prefix")
// Verify tokens are unique
assert.False(t, tokens[token], "Token should be unique")
tokens[token] = true
}
// Verify we got 100 unique tokens
assert.Len(t, tokens, 100, "All generated tokens should be unique")
}
func TestDeviceInfo_SyncEnabledValidation(t *testing.T) {
tests := []struct {
name string
syncEnabled bool
syncEnabledValid bool
expectedFinalValue bool
}{
{
name: "Sync enabled and valid",
syncEnabled: true,
syncEnabledValid: true,
expectedFinalValue: true,
},
{
name: "Sync disabled but valid",
syncEnabled: false,
syncEnabledValid: true,
expectedFinalValue: false,
},
{
name: "Sync enabled but not valid",
syncEnabled: true,
syncEnabledValid: false,
expectedFinalValue: false,
},
{
name: "Sync disabled and not valid",
syncEnabled: false,
syncEnabledValid: false,
expectedFinalValue: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Simulate the logic in RegenerateDeviceToken
finalValue := tt.syncEnabled && tt.syncEnabledValid
assert.Equal(t, tt.expectedFinalValue, finalValue)
})
}
}
func TestDeviceInfo_IDParsing(t *testing.T) {
// Test UUID parsing logic from device ID
deviceID := uuid.New()
deviceIDBytes := [16]byte(deviceID)
// Verify we can convert back
parsedUUID := uuid.UUID(deviceIDBytes)
assert.Equal(t, deviceID, parsedUUID, "UUID should be preserved through byte array conversion")
// Test that we can get the string representation
deviceIDStr := deviceID.String()
assert.NotEmpty(t, deviceIDStr, "UUID string should not be empty")
// Test that parsing the string gives us the same UUID
parsedFromStr, err := uuid.Parse(deviceIDStr)
assert.NoError(t, err, "Should parse UUID string without error")
assert.Equal(t, deviceID, parsedFromStr, "Parsed UUID should match original")
}
func TestDeviceUpdateRequest_Validation(t *testing.T) {
tests := []struct {
name string
syncFrequency int32
expectedValid bool
}{
{
name: "Valid sync frequency",
syncFrequency: 5,
expectedValid: true,
},
{
name: "Zero sync frequency",
syncFrequency: 0,
expectedValid: true,
},
{
name: "High sync frequency",
syncFrequency: 1440, // 1 day
expectedValid: true,
},
{
name: "Negative sync frequency",
syncFrequency: -1,
expectedValid: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Simulate validation logic
isValid := tt.syncFrequency >= 0
assert.Equal(t, tt.expectedValid, isValid)
})
}
}