All four progress write paths now delegate to ProgressService.SaveProgress: - MediaHandler: UpdateMediaReadingProgress uses ProgressService for web saves with richer request body (reading_mode, zoom_level, scroll). GET now uses GetUniversalProgress query that JOINs media_items for format_group, total_characters, chapter_count. - KOReaderHandler: updateProgressForBook delegates to ProgressService. Fixed device ID bug (was using userID, now uses deviceID). Removed duplicate UpdateDeviceLastSync with zero UUID. Added pgtype helper functions (textPtrToPgText, intPtrToPgInt4, int64PtrToPgInt8). - KoboHandler: all four progress write points (Markup ReadingSync, Markup last-read-place, AnalyticsGettests, SyncFromServer) delegate to ProgressService. Fixed empty epubcfi string now correctly set to Valid: false. SyncFromServer preserves last_sync_source=bookhoard and Broadcast: false. - QueueProcessor: syncProgress delegates to ProgressService. - main.go: creates ProgressService after ConnectionManager, injects via SetProgressService() on all handlers and queue processor. Handler tests cover pgtype conversion helpers (textPtrToPgText, etc.) and device icon mapping.
153 lines
3.5 KiB
Go
153 lines
3.5 KiB
Go
package handlers
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestGetDeviceIcon_Kobo(t *testing.T) {
|
|
result := getDeviceIcon("kobo")
|
|
assert.Equal(t, "📚", result)
|
|
}
|
|
|
|
func TestGetDeviceIcon_KOReader(t *testing.T) {
|
|
result := getDeviceIcon("koreader")
|
|
assert.Equal(t, "📖", result)
|
|
}
|
|
|
|
func TestGetDeviceIcon_Kindle(t *testing.T) {
|
|
result := getDeviceIcon("kindle")
|
|
assert.Equal(t, "📱", result)
|
|
}
|
|
|
|
func TestGetDeviceIcon_Unknown(t *testing.T) {
|
|
result := getDeviceIcon("unknown")
|
|
assert.Equal(t, "📚", result)
|
|
|
|
result = getDeviceIcon("")
|
|
assert.Equal(t, "📚", result)
|
|
}
|
|
|
|
func TestTextPtrToPgText(t *testing.T) {
|
|
t.Run("nil returns invalid", func(t *testing.T) {
|
|
result := textPtrToPgText(nil)
|
|
assert.False(t, result.Valid)
|
|
})
|
|
t.Run("non-nil returns valid", func(t *testing.T) {
|
|
s := "epubcfi(/6/4/2:10)"
|
|
result := textPtrToPgText(&s)
|
|
assert.True(t, result.Valid)
|
|
assert.Equal(t, s, result.String)
|
|
})
|
|
t.Run("empty string returns valid", func(t *testing.T) {
|
|
s := ""
|
|
result := textPtrToPgText(&s)
|
|
assert.True(t, result.Valid)
|
|
assert.Equal(t, "", result.String)
|
|
})
|
|
}
|
|
|
|
func TestIntPtrToPgInt4(t *testing.T) {
|
|
t.Run("nil returns invalid", func(t *testing.T) {
|
|
result := intPtrToPgInt4(nil)
|
|
assert.False(t, result.Valid)
|
|
})
|
|
t.Run("non-nil returns valid", func(t *testing.T) {
|
|
v := 5
|
|
result := intPtrToPgInt4(&v)
|
|
assert.True(t, result.Valid)
|
|
assert.Equal(t, int32(5), result.Int32)
|
|
})
|
|
}
|
|
|
|
func TestInt64PtrToPgInt8(t *testing.T) {
|
|
t.Run("nil returns invalid", func(t *testing.T) {
|
|
result := int64PtrToPgInt8(nil)
|
|
assert.False(t, result.Valid)
|
|
})
|
|
t.Run("non-nil returns valid", func(t *testing.T) {
|
|
v := int64(10000)
|
|
result := int64PtrToPgInt8(&v)
|
|
assert.True(t, result.Valid)
|
|
assert.Equal(t, int64(10000), result.Int64)
|
|
})
|
|
}
|
|
|
|
func TestFloat64PtrHelpers(t *testing.T) {
|
|
t.Run("pgtype float64 valid", func(t *testing.T) {
|
|
v := pgtype.Float8{Float64: 0.5, Valid: true}
|
|
result := float64PtrVal(v)
|
|
assert.NotNil(t, result)
|
|
assert.InDelta(t, 0.5, *result, 0.001)
|
|
})
|
|
t.Run("pgtype float64 invalid", func(t *testing.T) {
|
|
v := pgtype.Float8{Valid: false}
|
|
result := float64PtrVal(v)
|
|
assert.Nil(t, result)
|
|
})
|
|
t.Run("pgtype text valid", func(t *testing.T) {
|
|
v := pgtype.Text{String: "hello", Valid: true}
|
|
result := textPtrVal(v)
|
|
assert.NotNil(t, result)
|
|
assert.Equal(t, "hello", *result)
|
|
})
|
|
t.Run("pgtype text invalid", func(t *testing.T) {
|
|
v := pgtype.Text{Valid: false}
|
|
result := textPtrVal(v)
|
|
assert.Nil(t, result)
|
|
})
|
|
t.Run("pgtype int4 valid", func(t *testing.T) {
|
|
v := pgtype.Int4{Int32: 42, Valid: true}
|
|
result := int32PtrVal(v)
|
|
assert.NotNil(t, result)
|
|
assert.Equal(t, 42, *result)
|
|
})
|
|
t.Run("pgtype int4 invalid", func(t *testing.T) {
|
|
v := pgtype.Int4{Valid: false}
|
|
result := int32PtrVal(v)
|
|
assert.Nil(t, result)
|
|
})
|
|
t.Run("pgtype int8 valid", func(t *testing.T) {
|
|
v := pgtype.Int8{Int64: 10000, Valid: true}
|
|
result := int64PtrVal(v)
|
|
assert.NotNil(t, result)
|
|
assert.Equal(t, int64(10000), *result)
|
|
})
|
|
t.Run("pgtype int8 invalid", func(t *testing.T) {
|
|
v := pgtype.Int8{Valid: false}
|
|
result := int64PtrVal(v)
|
|
assert.Nil(t, result)
|
|
})
|
|
}
|
|
|
|
func float64PtrVal(v pgtype.Float8) *float64 {
|
|
if v.Valid {
|
|
return &v.Float64
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func textPtrVal(v pgtype.Text) *string {
|
|
if v.Valid {
|
|
return &v.String
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func int32PtrVal(v pgtype.Int4) *int {
|
|
if v.Valid {
|
|
val := int(v.Int32)
|
|
return &val
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func int64PtrVal(v pgtype.Int8) *int64 {
|
|
if v.Valid {
|
|
return &v.Int64
|
|
}
|
|
return nil
|
|
}
|