The TestKoboInitialization test was calling getTestUserID() explicitly on line 25, but loginTestUser() already calls this internally. This caused the test user to be deleted and recreated after login, leading to inconsistent state and HTTP 500 errors when creating libraries. After removing the redundant call: - TestKoboInitialization now passes - All Kobo sync tests pass successfully
274 lines
8.2 KiB
Go
274 lines
8.2 KiB
Go
package main
|
|
|
|
import (
|
|
"bookhoard/internal/handlers"
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestKoboInitialization(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("Skipping integration test in short mode")
|
|
}
|
|
|
|
setup := setupTestServer(t)
|
|
|
|
token := loginTestUser(t, setup.Server, setup.DB)
|
|
_ = createTestMediaItemID(t, setup.Server, token)
|
|
|
|
log.Printf("[DEBUG] Kobo test setup: creating device and media")
|
|
|
|
t.Run("successful initialization", func(t *testing.T) {
|
|
// Create Kobo device using TestDeviceSetup for proper authentication
|
|
deviceSetup := setupDeviceTest(t)
|
|
koboDevice := deviceSetup.CreateDevice(t, "Test Kobo", "kobo", "kobo-clara-test")
|
|
|
|
req, _ := http.NewRequest("GET", setup.Server.URL+"/api/sync/kobo/v1/initialization", nil)
|
|
req.Header.Set("Authorization", "Bearer "+koboDevice.AuthToken)
|
|
req.Header.Set("x-kobo-device", fmt.Sprintf(`{"DeviceId":"%s","Model":"Kobo Clara","SerialNumber":"%s"}`,
|
|
koboDevice.ID.String(), koboDevice.Identifier))
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err)
|
|
defer resp.Body.Close()
|
|
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
})
|
|
}
|
|
|
|
func TestKoboLibrarySync(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("Skipping integration test in short mode")
|
|
}
|
|
|
|
setup := setupTestServer(t)
|
|
|
|
token := loginTestUser(t, setup.Server, setup.DB)
|
|
_ = createTestMediaItemID(t, setup.Server, token)
|
|
|
|
log.Printf("[DEBUG] Kobo test setup: creating device and media")
|
|
|
|
t.Run("successful library sync", func(t *testing.T) {
|
|
deviceSetup := setupDeviceTest(t)
|
|
koboDevice := deviceSetup.CreateDevice(t, "Test Kobo", "kobo", "kobo-clara-test")
|
|
|
|
req, _ := http.NewRequest("GET", setup.Server.URL+"/api/sync/kobo/v1/initialization", nil)
|
|
req.Header.Set("Authorization", "Bearer "+koboDevice.AuthToken)
|
|
req.Header.Set("x-kobo-device", fmt.Sprintf(`{"DeviceId":"%s","Model":"Kobo Clara","SerialNumber":"%s"}`,
|
|
koboDevice.ID.String(), koboDevice.Identifier))
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err)
|
|
defer resp.Body.Close()
|
|
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
})
|
|
}
|
|
|
|
func TestKoboMarkupSync(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("Skipping integration test in short mode")
|
|
}
|
|
|
|
setup := setupTestServer(t)
|
|
|
|
token := loginTestUser(t, setup.Server, setup.DB)
|
|
mediaItemID := createTestMediaItemID(t, setup.Server, token)
|
|
|
|
log.Printf("[DEBUG] Kobo test setup: creating device and media")
|
|
|
|
t.Run("successful markup sync with annotations and bookmarks", func(t *testing.T) {
|
|
deviceSetup := setupDeviceTest(t)
|
|
koboDevice := deviceSetup.CreateDevice(t, "Test Kobo", "kobo", "kobo-clara-markup")
|
|
|
|
reqBody := map[string]interface{}{
|
|
"ReadingSync": []map[string]interface{}{
|
|
{
|
|
"ContentId": mediaItemID,
|
|
"PercentRead": 45.6,
|
|
"EntitlementId": "ent-123",
|
|
"RemainingTimeMinutes": 120,
|
|
"LastModified": "2026-01-30T20:00:00Z",
|
|
},
|
|
},
|
|
"BookmarkSync": []map[string]interface{}{
|
|
{
|
|
"BookmarkId": "bookmark-1",
|
|
"ContentId": mediaItemID,
|
|
"BookmarkText": "This is highlighted text",
|
|
"BookmarkType": "annotation",
|
|
"BookmarkTitle": "Chapter 3",
|
|
},
|
|
{
|
|
"BookmarkId": "bookmark-2",
|
|
"ContentId": mediaItemID,
|
|
"BookmarkText": "This is my note about book",
|
|
"BookmarkType": "bookmark",
|
|
},
|
|
{
|
|
"BookmarkId": "epubcfi(/6/4[chap1]!/4/2/1:0)",
|
|
"ContentId": mediaItemID,
|
|
"BookmarkType": "last-read-place",
|
|
"Hidden": true,
|
|
"Chapter": 1,
|
|
},
|
|
},
|
|
}
|
|
|
|
body, _ := json.Marshal(reqBody)
|
|
req, _ := http.NewRequest("POST", setup.Server.URL+"/api/sync/kobo/markup", bytes.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("Authorization", "Bearer "+koboDevice.AuthToken)
|
|
req.Header.Set("x-kobo-device", fmt.Sprintf(`{"DeviceId":"%s","Model":"Kobo Clara","SerialNumber":"%s"}`,
|
|
koboDevice.ID.String(), koboDevice.Identifier))
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err)
|
|
defer resp.Body.Close()
|
|
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
var result map[string]interface{}
|
|
json.NewDecoder(resp.Body).Decode(&result)
|
|
assert.Contains(t, result, "Status")
|
|
})
|
|
}
|
|
|
|
func TestKoboBookmarkSync(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("Skipping integration test in short mode")
|
|
}
|
|
|
|
setup := setupTestServer(t)
|
|
|
|
token := loginTestUser(t, setup.Server, setup.DB)
|
|
mediaItemID := createTestMediaItemID(t, setup.Server, token)
|
|
|
|
log.Printf("[DEBUG] Kobo test setup: creating device and media")
|
|
|
|
t.Run("successful bookmark sync with last-read-place", func(t *testing.T) {
|
|
deviceSetup := setupDeviceTest(t)
|
|
koboDevice := deviceSetup.CreateDevice(t, "Test Kobo", "kobo", "kobo-clara-bookmark")
|
|
|
|
reqBody := map[string]interface{}{
|
|
"BookmarkSync": []map[string]interface{}{
|
|
{
|
|
"BookmarkId": "bookmark-3",
|
|
"ContentId": mediaItemID,
|
|
"BookmarkText": "Important note about book",
|
|
"BookmarkType": "bookmark",
|
|
"DateCreated": "2026-01-30T19:55:00Z",
|
|
},
|
|
{
|
|
"BookmarkId": "epubcfi(/6/4[chap1]!/4/2/1:156)",
|
|
"ContentId": mediaItemID,
|
|
"BookmarkType": "last-read-place",
|
|
"Hidden": true,
|
|
"Chapter": 1,
|
|
"DateCreated": "2026-01-30T19:55:00Z",
|
|
},
|
|
},
|
|
}
|
|
|
|
body, _ := json.Marshal(reqBody)
|
|
req, _ := http.NewRequest("POST", setup.Server.URL+"/api/sync/kobo/bookmark", bytes.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("Authorization", "Bearer "+koboDevice.AuthToken)
|
|
req.Header.Set("x-kobo-device", fmt.Sprintf(`{"DeviceId":"%s","Model":"Kobo Clara","SerialNumber":"%s"}`,
|
|
koboDevice.ID.String(), koboDevice.Identifier))
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err)
|
|
defer resp.Body.Close()
|
|
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
var result map[string]interface{}
|
|
json.NewDecoder(resp.Body).Decode(&result)
|
|
assert.Contains(t, result, "Status")
|
|
})
|
|
}
|
|
|
|
func TestKoboAnalyticsGettests(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("Skipping integration test in short mode")
|
|
}
|
|
|
|
setup := setupTestServer(t)
|
|
|
|
token := loginTestUser(t, setup.Server, setup.DB)
|
|
mediaItemID := createTestMediaItemID(t, setup.Server, token)
|
|
|
|
log.Printf("[DEBUG] Kobo test setup: creating device and media")
|
|
|
|
t.Run("successful analytics tests", func(t *testing.T) {
|
|
deviceSetup := setupDeviceTest(t)
|
|
koboDevice := deviceSetup.CreateDevice(t, "Test Kobo", "kobo", "kobo-clara-analytics")
|
|
|
|
reqBody := map[string]interface{}{
|
|
"meta": map[string]string{
|
|
"name": "Kobo Analytics Tests",
|
|
},
|
|
"ContentId": mediaItemID,
|
|
"ReadingEvent": "Reading",
|
|
"RemainingTimeMin": 180,
|
|
"PercentRead": 67.8,
|
|
}
|
|
|
|
body, _ := json.Marshal(reqBody)
|
|
req, _ := http.NewRequest("POST", setup.Server.URL+"/api/sync/kobo/v1/analytics/gettests", bytes.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("Authorization", "Bearer "+koboDevice.AuthToken)
|
|
req.Header.Set("x-kobo-device", fmt.Sprintf(`{"DeviceId":"%s","Model":"Kobo Clara","SerialNumber":"%s"}`,
|
|
koboDevice.ID.String(), koboDevice.Identifier))
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err)
|
|
defer resp.Body.Close()
|
|
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
var result map[string]interface{}
|
|
json.NewDecoder(resp.Body).Decode(&result)
|
|
assert.Contains(t, result, "Status")
|
|
})
|
|
}
|
|
|
|
func TestKoboDeviceHeaderParsing(t *testing.T) {
|
|
t.Run("valid device header", func(t *testing.T) {
|
|
reqBody := map[string]interface{}{
|
|
"DeviceId": "kobo-clara-test",
|
|
"Model": "Kobo Clara",
|
|
"SerialNumber": "N123456789",
|
|
"Firmware": "4.38.23555",
|
|
}
|
|
|
|
jsonData, _ := json.Marshal(reqBody)
|
|
|
|
var device handlers.KoboDeviceInfo
|
|
err := json.Unmarshal(jsonData, &device)
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "kobo-clara-test", device.DeviceID)
|
|
assert.Equal(t, "Kobo Clara", device.Model)
|
|
assert.Equal(t, "N123456789", device.SerialNumber)
|
|
assert.Equal(t, "4.38.23555", device.Firmware)
|
|
})
|
|
}
|
|
|
|
func closeTestServer(t *testing.T, ts interface{}, db interface{}) {
|
|
if ts, ok := ts.(*httptest.Server); ok {
|
|
ts.Close()
|
|
}
|
|
}
|