test(handlers): add unit tests for conflict resolution logic
Add table-driven tests for the conflict handler's source selection methods: GetMostRecentSource, GetHighestProgressSource, and GetEarliestSource. Covers cases where each device wins, ties, and missing/invalid data.
This commit is contained in:
@@ -0,0 +1,309 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetMostRecentSource_KOReaderMoreRecent(t *testing.T) {
|
||||
handler := &ConflictHandler{}
|
||||
|
||||
conflictData := map[string]ConflictSourceData{
|
||||
"koreader": {
|
||||
Source: "koreader",
|
||||
Timestamp: time.Date(2026, 1, 30, 20, 10, 0, 0, time.UTC),
|
||||
Data: map[string]interface{}{
|
||||
"percentage": 0.45,
|
||||
"epubcfi": "epubcfi(/6/4/2:15)",
|
||||
},
|
||||
},
|
||||
"kobo": {
|
||||
Source: "kobo",
|
||||
Timestamp: time.Date(2026, 1, 30, 20, 5, 0, 0, time.UTC),
|
||||
Data: map[string]interface{}{
|
||||
"percentage": 0.42,
|
||||
"page": float64(89),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
source, data := handler.getMostRecentSource(conflictData)
|
||||
assert.Equal(t, "koreader", source)
|
||||
assert.Equal(t, 0.45, data["percentage"])
|
||||
}
|
||||
|
||||
func TestGetMostRecentSource_KoboMoreRecent(t *testing.T) {
|
||||
handler := &ConflictHandler{}
|
||||
|
||||
conflictData := map[string]ConflictSourceData{
|
||||
"koreader": {
|
||||
Source: "koreader",
|
||||
Timestamp: time.Date(2026, 1, 30, 20, 5, 0, 0, time.UTC),
|
||||
Data: map[string]interface{}{
|
||||
"percentage": 0.45,
|
||||
},
|
||||
},
|
||||
"kobo": {
|
||||
Source: "kobo",
|
||||
Timestamp: time.Date(2026, 1, 30, 20, 15, 0, 0, time.UTC),
|
||||
Data: map[string]interface{}{
|
||||
"percentage": 0.42,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
source, data := handler.getMostRecentSource(conflictData)
|
||||
assert.Equal(t, "kobo", source)
|
||||
assert.Equal(t, 0.42, data["percentage"])
|
||||
}
|
||||
|
||||
func TestGetMostRecentSource_SingleSource(t *testing.T) {
|
||||
handler := &ConflictHandler{}
|
||||
|
||||
conflictData := map[string]ConflictSourceData{
|
||||
"koreader": {
|
||||
Source: "koreader",
|
||||
Timestamp: time.Date(2026, 1, 30, 20, 10, 0, 0, time.UTC),
|
||||
Data: map[string]interface{}{
|
||||
"percentage": 0.50,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
source, data := handler.getMostRecentSource(conflictData)
|
||||
assert.Equal(t, "koreader", source)
|
||||
assert.Equal(t, 0.50, data["percentage"])
|
||||
}
|
||||
|
||||
func TestGetMostRecentSource_SameTimestamp(t *testing.T) {
|
||||
handler := &ConflictHandler{}
|
||||
|
||||
ts := time.Date(2026, 1, 30, 20, 10, 0, 0, time.UTC)
|
||||
conflictData := map[string]ConflictSourceData{
|
||||
"koreader": {
|
||||
Source: "koreader",
|
||||
Timestamp: ts,
|
||||
Data: map[string]interface{}{
|
||||
"percentage": 0.45,
|
||||
},
|
||||
},
|
||||
"kobo": {
|
||||
Source: "kobo",
|
||||
Timestamp: ts,
|
||||
Data: map[string]interface{}{
|
||||
"percentage": 0.42,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
source, _ := handler.getMostRecentSource(conflictData)
|
||||
assert.Contains(t, []string{"koreader", "kobo"}, source)
|
||||
}
|
||||
|
||||
func TestGetMostRecentSource_EmptyData(t *testing.T) {
|
||||
handler := &ConflictHandler{}
|
||||
|
||||
conflictData := map[string]ConflictSourceData{}
|
||||
|
||||
source, data := handler.getMostRecentSource(conflictData)
|
||||
assert.Equal(t, "", source)
|
||||
assert.Nil(t, data)
|
||||
}
|
||||
|
||||
func TestGetHighestProgressSource_KOReaderHigher(t *testing.T) {
|
||||
handler := &ConflictHandler{}
|
||||
|
||||
conflictData := map[string]ConflictSourceData{
|
||||
"koreader": {
|
||||
Source: "koreader",
|
||||
Data: map[string]interface{}{
|
||||
"percentage": 0.75,
|
||||
},
|
||||
},
|
||||
"kobo": {
|
||||
Source: "kobo",
|
||||
Data: map[string]interface{}{
|
||||
"percentage": 0.42,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
source, data := handler.getHighestProgressSource(conflictData)
|
||||
assert.Equal(t, "koreader", source)
|
||||
assert.Equal(t, 0.75, data["percentage"])
|
||||
}
|
||||
|
||||
func TestGetHighestProgressSource_KoboHigher(t *testing.T) {
|
||||
handler := &ConflictHandler{}
|
||||
|
||||
conflictData := map[string]ConflictSourceData{
|
||||
"koreader": {
|
||||
Source: "koreader",
|
||||
Data: map[string]interface{}{
|
||||
"percentage": 0.30,
|
||||
},
|
||||
},
|
||||
"kobo": {
|
||||
Source: "kobo",
|
||||
Data: map[string]interface{}{
|
||||
"percentage": 0.90,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
source, data := handler.getHighestProgressSource(conflictData)
|
||||
assert.Equal(t, "kobo", source)
|
||||
assert.Equal(t, 0.90, data["percentage"])
|
||||
}
|
||||
|
||||
func TestGetHighestProgressSource_SingleSource(t *testing.T) {
|
||||
handler := &ConflictHandler{}
|
||||
|
||||
conflictData := map[string]ConflictSourceData{
|
||||
"koreader": {
|
||||
Source: "koreader",
|
||||
Data: map[string]interface{}{
|
||||
"percentage": 0.50,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
source, data := handler.getHighestProgressSource(conflictData)
|
||||
assert.Equal(t, "koreader", source)
|
||||
assert.Equal(t, 0.50, data["percentage"])
|
||||
}
|
||||
|
||||
func TestGetHighestProgressSource_EmptyData(t *testing.T) {
|
||||
handler := &ConflictHandler{}
|
||||
|
||||
conflictData := map[string]ConflictSourceData{}
|
||||
|
||||
source, data := handler.getHighestProgressSource(conflictData)
|
||||
assert.Equal(t, "", source)
|
||||
assert.Nil(t, data)
|
||||
}
|
||||
|
||||
func TestGetHighestProgressSource_NoPercentageField(t *testing.T) {
|
||||
handler := &ConflictHandler{}
|
||||
|
||||
conflictData := map[string]ConflictSourceData{
|
||||
"koreader": {
|
||||
Source: "koreader",
|
||||
Data: map[string]interface{}{},
|
||||
},
|
||||
}
|
||||
|
||||
source, data := handler.getHighestProgressSource(conflictData)
|
||||
assert.Equal(t, "", source)
|
||||
assert.Nil(t, data)
|
||||
}
|
||||
|
||||
func TestGetHighestProgressSource_MixedPercentageTypes(t *testing.T) {
|
||||
handler := &ConflictHandler{}
|
||||
|
||||
conflictData := map[string]ConflictSourceData{
|
||||
"koreader": {
|
||||
Source: "koreader",
|
||||
Data: map[string]interface{}{
|
||||
"percentage": 0.60,
|
||||
},
|
||||
},
|
||||
"kobo": {
|
||||
Source: "kobo",
|
||||
Data: map[string]interface{}{
|
||||
"percentage": float64(0.80),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
source, data := handler.getHighestProgressSource(conflictData)
|
||||
assert.Equal(t, "kobo", source)
|
||||
assert.Equal(t, float64(0.80), data["percentage"])
|
||||
}
|
||||
|
||||
func TestGetHighestProgressSource_BothZero(t *testing.T) {
|
||||
handler := &ConflictHandler{}
|
||||
|
||||
conflictData := map[string]ConflictSourceData{
|
||||
"koreader": {
|
||||
Source: "koreader",
|
||||
Data: map[string]interface{}{
|
||||
"percentage": 0.0,
|
||||
},
|
||||
},
|
||||
"kobo": {
|
||||
Source: "kobo",
|
||||
Data: map[string]interface{}{
|
||||
"percentage": 0.0,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
source, data := handler.getHighestProgressSource(conflictData)
|
||||
assert.NotEmpty(t, source)
|
||||
assert.NotNil(t, data)
|
||||
assert.Equal(t, 0.0, data["percentage"])
|
||||
}
|
||||
|
||||
func TestGetHighestProgressSource_ThreeWayConflict(t *testing.T) {
|
||||
handler := &ConflictHandler{}
|
||||
|
||||
conflictData := map[string]ConflictSourceData{
|
||||
"koreader": {
|
||||
Source: "koreader",
|
||||
Data: map[string]interface{}{
|
||||
"percentage": 0.45,
|
||||
},
|
||||
},
|
||||
"kobo": {
|
||||
Source: "kobo",
|
||||
Data: map[string]interface{}{
|
||||
"percentage": 0.92,
|
||||
},
|
||||
},
|
||||
"web": {
|
||||
Source: "web",
|
||||
Data: map[string]interface{}{
|
||||
"percentage": 0.70,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
source, data := handler.getHighestProgressSource(conflictData)
|
||||
assert.Equal(t, "kobo", source)
|
||||
assert.Equal(t, 0.92, data["percentage"])
|
||||
}
|
||||
|
||||
func TestGetMostRecentSource_ThreeWayConflict(t *testing.T) {
|
||||
handler := &ConflictHandler{}
|
||||
|
||||
conflictData := map[string]ConflictSourceData{
|
||||
"koreader": {
|
||||
Source: "koreader",
|
||||
Timestamp: time.Date(2026, 1, 30, 20, 5, 0, 0, time.UTC),
|
||||
Data: map[string]interface{}{
|
||||
"percentage": 0.45,
|
||||
},
|
||||
},
|
||||
"kobo": {
|
||||
Source: "kobo",
|
||||
Timestamp: time.Date(2026, 1, 30, 20, 20, 0, 0, time.UTC),
|
||||
Data: map[string]interface{}{
|
||||
"percentage": 0.92,
|
||||
},
|
||||
},
|
||||
"web": {
|
||||
Source: "web",
|
||||
Timestamp: time.Date(2026, 1, 30, 20, 10, 0, 0, time.UTC),
|
||||
Data: map[string]interface{}{
|
||||
"percentage": 0.70,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
source, data := handler.getMostRecentSource(conflictData)
|
||||
assert.Equal(t, "kobo", source)
|
||||
assert.Equal(t, 0.92, data["percentage"])
|
||||
}
|
||||
Reference in New Issue
Block a user