Files
bookhoard/internal/sync/queue_test.go
T
john-okeefe 5b9f21a592 Final cleanup: Update remaining comments and variable names
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.
2026-02-01 16:24:58 -05:00

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
}