Changes: - Update comments: "Bookmann UUID" → "Bookhoard UUID" - Rename sidecar struct field: Bookmann → Bookhoard - Update type names: SidecarBookmannConfig → SidecarBookhoardConfig - Fix test database name in queue_test.go - Fix uppercase env var examples in KOBO_SETUP.md Internal Go variable names (BookmannUuid, bookmannUUID) left unchanged as they're implementation details that don't affect functionality. Part of project rename to Bookhoard.
138 lines
4.2 KiB
Go
138 lines
4.2 KiB
Go
package sync
|
|
|
|
import (
|
|
"bookhoard/internal/database"
|
|
"context"
|
|
"encoding/json"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestSyncQueueProcessor_EnqueueProgress(t *testing.T) {
|
|
ctx := context.Background()
|
|
db := setupTestDB(t)
|
|
defer teardownTestDB(t, db)
|
|
|
|
processor := NewSyncQueueProcessor(db)
|
|
|
|
userID := pgtype.UUID{Bytes: uuid.New(), Valid: true}
|
|
deviceID := pgtype.UUID{Bytes: uuid.New(), Valid: true}
|
|
mediaItemID := pgtype.UUID{Bytes: uuid.New(), Valid: true}
|
|
|
|
percentage := 0.45
|
|
chapter := 3
|
|
update := &ProgressUpdate{
|
|
DeviceID: deviceID,
|
|
MediaItemID: mediaItemID,
|
|
UserID: userID,
|
|
Percentage: percentage,
|
|
Chapter: &chapter,
|
|
Source: "koreader",
|
|
SyncMode: "immediate",
|
|
}
|
|
|
|
err := processor.EnqueueProgress(update)
|
|
require.NoError(t, err, "should enqueue progress update")
|
|
|
|
items, err := db.ListPendingSyncQueueItems(ctx, database.ListPendingSyncQueueItemsParams{
|
|
DeviceID: deviceID,
|
|
Limit: 10,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Len(t, items, 1, "should have one queue item")
|
|
|
|
item := items[0]
|
|
assert.Equal(t, "progress", item.SyncType)
|
|
assert.Equal(t, SyncStatusPending, item.Status.String)
|
|
assert.Equal(t, int32(PriorityPageTurn), item.Priority.Int32)
|
|
|
|
var syncData map[string]interface{}
|
|
err = json.Unmarshal(item.SyncData, &syncData)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, percentage, syncData["percentage"])
|
|
assert.Equal(t, "koreader", syncData["source"])
|
|
}
|
|
|
|
func TestCalculateNextRetry(t *testing.T) {
|
|
processor := &SyncQueueProcessor{}
|
|
|
|
tests := []struct {
|
|
name string
|
|
attempts int32
|
|
minDelay time.Duration
|
|
maxDelay time.Duration
|
|
}{
|
|
{"Attempt 0", 0, 0, 1 * time.Second},
|
|
{"Attempt 1", 1, 59 * time.Second, 61 * time.Second},
|
|
{"Attempt 2", 2, 4*time.Minute + 59*time.Second, 5*time.Minute + 1*time.Second},
|
|
{"Attempt 3", 3, 14*time.Minute + 59*time.Second, 15*time.Minute + 1*time.Second},
|
|
{"Attempt 4", 4, 59*time.Minute + 59*time.Second, 60*time.Minute + 1*time.Second},
|
|
{"Attempt 5", 5, 23*time.Hour + 59*time.Minute, 24*time.Hour + 1*time.Minute},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
nextRetry := processor.calculateNextRetry(tt.attempts)
|
|
delay := nextRetry.Sub(time.Now())
|
|
|
|
assert.GreaterOrEqual(t, delay, tt.minDelay, "delay should be at least minDelay")
|
|
assert.LessOrEqual(t, delay, tt.maxDelay, "delay should be at most maxDelay")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSyncTypeConstants(t *testing.T) {
|
|
assert.Equal(t, "progress", SyncTypeProgress)
|
|
assert.Equal(t, "note", SyncTypeNote)
|
|
assert.Equal(t, "highlight", SyncTypeHighlight)
|
|
assert.Equal(t, "bookmark", SyncTypeBookmark)
|
|
}
|
|
|
|
func TestSyncStatusConstants(t *testing.T) {
|
|
assert.Equal(t, "pending", SyncStatusPending)
|
|
assert.Equal(t, "processing", SyncStatusProcessing)
|
|
assert.Equal(t, "completed", SyncStatusCompleted)
|
|
assert.Equal(t, "failed", SyncStatusFailed)
|
|
}
|
|
|
|
func TestPriorityConstants(t *testing.T) {
|
|
assert.Equal(t, int32(1), PriorityUserInitiated)
|
|
assert.Equal(t, int32(2), PriorityBookCompletion)
|
|
assert.Equal(t, int32(3), PriorityCriticalNote)
|
|
assert.Equal(t, int32(5), PriorityPageTurn)
|
|
assert.Equal(t, int32(7), PriorityCheckpoint)
|
|
assert.Equal(t, int32(10), PriorityBackgroundSync)
|
|
}
|
|
|
|
func setupTestDB(t *testing.T) *database.Queries {
|
|
ctx := context.Background()
|
|
|
|
dbURL := "postgresql://postgres:postgres@localhost:5432/bookhoard?sslmode=disable"
|
|
dbPool, err := pgxpool.New(ctx, dbURL)
|
|
require.NoError(t, err, "Failed to connect to test database")
|
|
|
|
db := database.New(dbPool)
|
|
|
|
t.Cleanup(func() {
|
|
_, _ = dbPool.Exec(ctx, "DELETE FROM sync_queue WHERE true")
|
|
_, _ = dbPool.Exec(ctx, "DELETE FROM reading_progress WHERE true")
|
|
_, _ = dbPool.Exec(ctx, "DELETE FROM media_items WHERE title LIKE 'Test %'")
|
|
_, _ = dbPool.Exec(ctx, "DELETE FROM libraries WHERE name LIKE 'Test %'")
|
|
_, _ = dbPool.Exec(ctx, "DELETE FROM devices WHERE device_name LIKE 'Test %'")
|
|
_, _ = dbPool.Exec(ctx, "DELETE FROM users WHERE email LIKE 'test%'")
|
|
dbPool.Close()
|
|
})
|
|
|
|
return db
|
|
}
|
|
|
|
func teardownTestDB(t *testing.T, db *database.Queries) {
|
|
// Cleanup is handled in setupTestDB via t.Cleanup
|
|
}
|