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) }) } } func TestValidateDeviceToken(t *testing.T) { hashedToken := []byte("$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi") plainToken := "password" result := ValidateDeviceToken(string(hashedToken), plainToken) assert.True(t, result, "Valid token should return true") } func TestValidateDeviceToken_Invalid(t *testing.T) { hashedToken := []byte("$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi") plainToken := "wrongpassword" result := ValidateDeviceToken(string(hashedToken), plainToken) assert.False(t, result, "Invalid token should return false") } func TestValidateDeviceToken_Empty(t *testing.T) { result := ValidateDeviceToken("", "anypassword") assert.False(t, result, "Empty hash should return false") }