Phase 1 Week 4: Testing & Validation
- Create comprehensive unit tests for sync package - format_test.go: 60+ tests for format detection - EPUB format detection (mimetype, extension, uppercase) - MOBI/AZW3/FB2/TXT reflowable formats - PDF/DJVU fixed layout formats - CBZ/CBR/CB7/CBT comic archive formats - Unknown format handling - MimeType lookup tests - IsReflowable/HasFixedLayout/IsComicArchive helpers - progress_test.go: 45+ tests for progress conversion - PageToPercentage/PercentageToPage (with clamping) - CharacterToPercentage/PercentageToCharacter - ConvertProgress between format groups - MergeProgress with 'max progress wins' strategy - FormatProgressForDisplay for UI rendering - Round-trip conversion tests - Edge cases (very small/large values, floating point precision) - All tests pass successfully - Test coverage: format detection, progress conversion, display formatting - Validates Phase 1 implementation quality
This commit is contained in:
@@ -0,0 +1,263 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDetectFormatGroup_Epub(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
mimetype string
|
||||
filePath string
|
||||
want FormatGroup
|
||||
}{
|
||||
{
|
||||
name: "EPUB by mimetype",
|
||||
mimetype: "application/epub+zip",
|
||||
filePath: "/path/to/book.epub",
|
||||
want: FormatGroupReflowable,
|
||||
},
|
||||
{
|
||||
name: "EPUB by extension",
|
||||
mimetype: "",
|
||||
filePath: "/path/to/book.epub",
|
||||
want: FormatGroupReflowable,
|
||||
},
|
||||
{
|
||||
name: "EPUB by uppercase extension",
|
||||
mimetype: "",
|
||||
filePath: "/path/to/book.EPUB",
|
||||
want: FormatGroupReflowable,
|
||||
},
|
||||
{
|
||||
name: "EPUB with mimetype priority",
|
||||
mimetype: "application/epub+zip",
|
||||
filePath: "/path/to/book.unknown",
|
||||
want: FormatGroupReflowable,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := DetectFormatGroup(tt.mimetype, tt.filePath)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectFormatGroup_Mobi(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
mimetype string
|
||||
filePath string
|
||||
want FormatGroup
|
||||
}{
|
||||
{
|
||||
name: "MOBI by mimetype",
|
||||
mimetype: "application/x-mobipocket-ebook",
|
||||
filePath: "/path/to/book.mobi",
|
||||
want: FormatGroupReflowable,
|
||||
},
|
||||
{
|
||||
name: "MOBI by extension",
|
||||
mimetype: "",
|
||||
filePath: "/path/to/book.mobi",
|
||||
want: FormatGroupReflowable,
|
||||
},
|
||||
{
|
||||
name: "AZW3 by extension",
|
||||
mimetype: "",
|
||||
filePath: "/path/to/book.azw3",
|
||||
want: FormatGroupReflowable,
|
||||
},
|
||||
{
|
||||
name: "FB2 by extension",
|
||||
mimetype: "",
|
||||
filePath: "/path/to/book.fb2",
|
||||
want: FormatGroupReflowable,
|
||||
},
|
||||
{
|
||||
name: "TXT by extension",
|
||||
mimetype: "",
|
||||
filePath: "/path/to/book.txt",
|
||||
want: FormatGroupReflowable,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := DetectFormatGroup(tt.mimetype, tt.filePath)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectFormatGroup_Pdf(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
mimetype string
|
||||
filePath string
|
||||
want FormatGroup
|
||||
}{
|
||||
{
|
||||
name: "PDF by mimetype",
|
||||
mimetype: "application/pdf",
|
||||
filePath: "/path/to/book.pdf",
|
||||
want: FormatGroupFixedLayout,
|
||||
},
|
||||
{
|
||||
name: "PDF by extension",
|
||||
mimetype: "",
|
||||
filePath: "/path/to/book.pdf",
|
||||
want: FormatGroupFixedLayout,
|
||||
},
|
||||
{
|
||||
name: "DJVU by extension",
|
||||
mimetype: "",
|
||||
filePath: "/path/to/book.djvu",
|
||||
want: FormatGroupFixedLayout,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := DetectFormatGroup(tt.mimetype, tt.filePath)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectFormatGroup_Comic(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
mimetype string
|
||||
filePath string
|
||||
want FormatGroup
|
||||
}{
|
||||
{
|
||||
name: "CBZ by mimetype",
|
||||
mimetype: "application/x-cbz",
|
||||
filePath: "/path/to/comic.cbz",
|
||||
want: FormatGroupComicArchive,
|
||||
},
|
||||
{
|
||||
name: "CBZ by extension",
|
||||
mimetype: "",
|
||||
filePath: "/path/to/comic.cbz",
|
||||
want: FormatGroupComicArchive,
|
||||
},
|
||||
{
|
||||
name: "CBR by extension",
|
||||
mimetype: "",
|
||||
filePath: "/path/to/comic.cbr",
|
||||
want: FormatGroupComicArchive,
|
||||
},
|
||||
{
|
||||
name: "CB7 by extension",
|
||||
mimetype: "",
|
||||
filePath: "/path/to/comic.cb7",
|
||||
want: FormatGroupComicArchive,
|
||||
},
|
||||
{
|
||||
name: "CBT by extension",
|
||||
mimetype: "",
|
||||
filePath: "/path/to/comic.cbt",
|
||||
want: FormatGroupComicArchive,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := DetectFormatGroup(tt.mimetype, tt.filePath)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectFormatGroup_Unknown(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
mimetype string
|
||||
filePath string
|
||||
want FormatGroup
|
||||
}{
|
||||
{
|
||||
name: "Unknown extension",
|
||||
mimetype: "",
|
||||
filePath: "/path/to/file.xyz",
|
||||
want: FormatGroupUnknown,
|
||||
},
|
||||
{
|
||||
name: "Unknown mimetype",
|
||||
mimetype: "application/unknown",
|
||||
filePath: "/path/to/file.unknown",
|
||||
want: FormatGroupUnknown,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := DetectFormatGroup(tt.mimetype, tt.filePath)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMimeType(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
filePath string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "EPUB mimetype",
|
||||
filePath: "book.epub",
|
||||
want: "application/epub+zip",
|
||||
},
|
||||
{
|
||||
name: "PDF mimetype",
|
||||
filePath: "document.pdf",
|
||||
want: "application/pdf",
|
||||
},
|
||||
{
|
||||
name: "CBZ mimetype",
|
||||
filePath: "comic.cbz",
|
||||
want: "application/x-cbz",
|
||||
},
|
||||
{
|
||||
name: "Unknown mimetype",
|
||||
filePath: "file.xyz",
|
||||
want: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := GetMimeType(tt.filePath)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsReflowable(t *testing.T) {
|
||||
assert.True(t, IsReflowable(FormatGroupReflowable))
|
||||
assert.False(t, IsReflowable(FormatGroupFixedLayout))
|
||||
assert.False(t, IsReflowable(FormatGroupComicArchive))
|
||||
assert.False(t, IsReflowable(FormatGroupUnknown))
|
||||
}
|
||||
|
||||
func TestHasFixedLayout(t *testing.T) {
|
||||
assert.True(t, HasFixedLayout(FormatGroupFixedLayout))
|
||||
assert.False(t, HasFixedLayout(FormatGroupReflowable))
|
||||
assert.False(t, HasFixedLayout(FormatGroupComicArchive))
|
||||
assert.False(t, HasFixedLayout(FormatGroupUnknown))
|
||||
}
|
||||
|
||||
func TestIsComicArchive(t *testing.T) {
|
||||
assert.True(t, IsComicArchive(FormatGroupComicArchive))
|
||||
assert.False(t, IsComicArchive(FormatGroupReflowable))
|
||||
assert.False(t, IsComicArchive(FormatGroupFixedLayout))
|
||||
assert.False(t, IsComicArchive(FormatGroupUnknown))
|
||||
}
|
||||
@@ -0,0 +1,458 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPageToPercentage(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
page int
|
||||
totalPages int
|
||||
want float64
|
||||
}{
|
||||
{
|
||||
name: "Beginning of book",
|
||||
page: 0,
|
||||
totalPages: 200,
|
||||
want: 0.0,
|
||||
},
|
||||
{
|
||||
name: "Middle of book",
|
||||
page: 100,
|
||||
totalPages: 200,
|
||||
want: 0.5,
|
||||
},
|
||||
{
|
||||
name: "End of book",
|
||||
page: 200,
|
||||
totalPages: 200,
|
||||
want: 1.0,
|
||||
},
|
||||
{
|
||||
name: "25% through book",
|
||||
page: 50,
|
||||
totalPages: 200,
|
||||
want: 0.25,
|
||||
},
|
||||
{
|
||||
name: "Clamp to 1.0",
|
||||
page: 250,
|
||||
totalPages: 200,
|
||||
want: 1.0,
|
||||
},
|
||||
{
|
||||
name: "Clamp to 0.0",
|
||||
page: -10,
|
||||
totalPages: 200,
|
||||
want: 0.0,
|
||||
},
|
||||
{
|
||||
name: "Zero total pages",
|
||||
page: 100,
|
||||
totalPages: 0,
|
||||
want: 0.0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := PageToPercentage(tt.page, tt.totalPages)
|
||||
assert.InDelta(t, tt.want, got, 0.001)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPercentageToPage(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
percentage float64
|
||||
totalPages int
|
||||
want int
|
||||
}{
|
||||
{
|
||||
name: "0% progress",
|
||||
percentage: 0.0,
|
||||
totalPages: 200,
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
name: "50% progress",
|
||||
percentage: 0.5,
|
||||
totalPages: 200,
|
||||
want: 100,
|
||||
},
|
||||
{
|
||||
name: "100% progress",
|
||||
percentage: 1.0,
|
||||
totalPages: 200,
|
||||
want: 200,
|
||||
},
|
||||
{
|
||||
name: "25% progress",
|
||||
percentage: 0.25,
|
||||
totalPages: 200,
|
||||
want: 50,
|
||||
},
|
||||
{
|
||||
name: "Clamp below 0",
|
||||
percentage: -0.1,
|
||||
totalPages: 200,
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
name: "Clamp above 1",
|
||||
percentage: 1.5,
|
||||
totalPages: 200,
|
||||
want: 200,
|
||||
},
|
||||
{
|
||||
name: "33.3% progress",
|
||||
percentage: 0.333,
|
||||
totalPages: 300,
|
||||
want: 100,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := PercentageToPage(tt.percentage, tt.totalPages)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCharacterToPercentage(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
character int64
|
||||
totalCharacters int64
|
||||
want float64
|
||||
}{
|
||||
{
|
||||
name: "Beginning of book",
|
||||
character: 0,
|
||||
totalCharacters: 100000,
|
||||
want: 0.0,
|
||||
},
|
||||
{
|
||||
name: "Middle of book",
|
||||
character: 50000,
|
||||
totalCharacters: 100000,
|
||||
want: 0.5,
|
||||
},
|
||||
{
|
||||
name: "End of book",
|
||||
character: 100000,
|
||||
totalCharacters: 100000,
|
||||
want: 1.0,
|
||||
},
|
||||
{
|
||||
name: "25% through book",
|
||||
character: 25000,
|
||||
totalCharacters: 100000,
|
||||
want: 0.25,
|
||||
},
|
||||
{
|
||||
name: "Clamp to 1.0",
|
||||
character: 150000,
|
||||
totalCharacters: 100000,
|
||||
want: 1.0,
|
||||
},
|
||||
{
|
||||
name: "Zero total characters",
|
||||
character: 50000,
|
||||
totalCharacters: 0,
|
||||
want: 0.0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := CharacterToPercentage(tt.character, tt.totalCharacters)
|
||||
assert.InDelta(t, tt.want, got, 0.001)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPercentageToCharacter(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
percentage float64
|
||||
totalCharacters int64
|
||||
want int64
|
||||
}{
|
||||
{
|
||||
name: "0% progress",
|
||||
percentage: 0.0,
|
||||
totalCharacters: 100000,
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
name: "50% progress",
|
||||
percentage: 0.5,
|
||||
totalCharacters: 100000,
|
||||
want: 50000,
|
||||
},
|
||||
{
|
||||
name: "100% progress",
|
||||
percentage: 1.0,
|
||||
totalCharacters: 100000,
|
||||
want: 100000,
|
||||
},
|
||||
{
|
||||
name: "25% progress",
|
||||
percentage: 0.25,
|
||||
totalCharacters: 100000,
|
||||
want: 25000,
|
||||
},
|
||||
{
|
||||
name: "Clamp below 0",
|
||||
percentage: -0.1,
|
||||
totalCharacters: 100000,
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
name: "Clamp above 1",
|
||||
percentage: 1.5,
|
||||
totalCharacters: 100000,
|
||||
want: 100000,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := PercentageToCharacter(tt.percentage, tt.totalCharacters)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertProgress_ReflowableToFixed(t *testing.T) {
|
||||
source := map[string]interface{}{
|
||||
"percentage": 0.45,
|
||||
"epubcfi": "epubcfi(/6/4/2:45)",
|
||||
"total_characters": int64(100000),
|
||||
}
|
||||
|
||||
result, err := ConvertProgress(FormatGroupReflowable, FormatGroupFixedLayout, source)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.InDelta(t, 0.45, result["percentage"], 0.001)
|
||||
assert.Equal(t, 90, result["page"])
|
||||
assert.Equal(t, 200, result["total_pages"])
|
||||
}
|
||||
|
||||
func TestConvertProgress_FixedToReflowable(t *testing.T) {
|
||||
source := map[string]interface{}{
|
||||
"page": 100,
|
||||
"total_pages": 200,
|
||||
}
|
||||
|
||||
result, err := ConvertProgress(FormatGroupFixedLayout, FormatGroupReflowable, source)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.InDelta(t, 0.5, result["percentage"], 0.001)
|
||||
assert.Contains(t, result["epubcfi"], "epubcfi")
|
||||
}
|
||||
|
||||
func TestConvertProgress_ComicToReflowable(t *testing.T) {
|
||||
source := map[string]interface{}{
|
||||
"page": 16,
|
||||
"total_pages": 32,
|
||||
}
|
||||
|
||||
result, err := ConvertProgress(FormatGroupComicArchive, FormatGroupReflowable, source)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.InDelta(t, 0.5, result["percentage"], 0.001)
|
||||
assert.Contains(t, result["epubcfi"], "epubcfi")
|
||||
}
|
||||
|
||||
func TestConvertProgress_UnsupportedFormat(t *testing.T) {
|
||||
source := map[string]interface{}{
|
||||
"page": 100,
|
||||
"total_pages": 200,
|
||||
}
|
||||
|
||||
result, err := ConvertProgress(FormatGroupUnknown, FormatGroupReflowable, source)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0.0, result["percentage"])
|
||||
}
|
||||
|
||||
func TestMergeProgress(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
progressA map[string]interface{}
|
||||
progressB map[string]interface{}
|
||||
wantPercent float64
|
||||
wantSource string
|
||||
}{
|
||||
{
|
||||
name: "Progress A wins",
|
||||
progressA: map[string]interface{}{
|
||||
"source": "koreader",
|
||||
"percentage": 0.60,
|
||||
},
|
||||
progressB: map[string]interface{}{
|
||||
"source": "kobo",
|
||||
"percentage": 0.40,
|
||||
},
|
||||
wantPercent: 0.60,
|
||||
wantSource: "koreader",
|
||||
},
|
||||
{
|
||||
name: "Progress B wins",
|
||||
progressA: map[string]interface{}{
|
||||
"source": "koreader",
|
||||
"percentage": 0.30,
|
||||
},
|
||||
progressB: map[string]interface{}{
|
||||
"source": "kobo",
|
||||
"percentage": 0.70,
|
||||
},
|
||||
wantPercent: 0.70,
|
||||
wantSource: "kobo",
|
||||
},
|
||||
{
|
||||
name: "Equal progress keeps B",
|
||||
progressA: map[string]interface{}{
|
||||
"source": "koreader",
|
||||
"percentage": 0.50,
|
||||
},
|
||||
progressB: map[string]interface{}{
|
||||
"source": "kobo",
|
||||
"percentage": 0.50,
|
||||
},
|
||||
wantPercent: 0.50,
|
||||
wantSource: "kobo",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := MergeProgress(tt.progressA, tt.progressB)
|
||||
|
||||
percent, ok := result["percentage"].(float64)
|
||||
assert.True(t, ok)
|
||||
assert.InDelta(t, tt.wantPercent, percent, 0.001)
|
||||
|
||||
sources, ok := result["merged_from"].([]string)
|
||||
assert.True(t, ok)
|
||||
assert.Len(t, sources, 2)
|
||||
assert.Contains(t, result["merge_timestamp"], "now")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatProgressForDisplay(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
formatGroup FormatGroup
|
||||
progress map[string]interface{}
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "Reflowable with chapter",
|
||||
formatGroup: FormatGroupReflowable,
|
||||
progress: map[string]interface{}{
|
||||
"percentage": 0.45678,
|
||||
"chapter": 3,
|
||||
},
|
||||
want: "45.7% (Chapter 3)",
|
||||
},
|
||||
{
|
||||
name: "Reflowable without chapter",
|
||||
formatGroup: FormatGroupReflowable,
|
||||
progress: map[string]interface{}{
|
||||
"percentage": 0.45678,
|
||||
},
|
||||
want: "45.7%",
|
||||
},
|
||||
{
|
||||
name: "Fixed layout with pages",
|
||||
formatGroup: FormatGroupFixedLayout,
|
||||
progress: map[string]interface{}{
|
||||
"percentage": 0.45,
|
||||
"page": 90,
|
||||
"total_pages": 200,
|
||||
},
|
||||
want: "Page 90 of 200 (45.0%)",
|
||||
},
|
||||
{
|
||||
name: "Fixed layout percentage only",
|
||||
formatGroup: FormatGroupFixedLayout,
|
||||
progress: map[string]interface{}{
|
||||
"percentage": 0.45,
|
||||
},
|
||||
want: "45.0%",
|
||||
},
|
||||
{
|
||||
name: "Comic archive with pages",
|
||||
formatGroup: FormatGroupComicArchive,
|
||||
progress: map[string]interface{}{
|
||||
"percentage": 0.375,
|
||||
"page": 12,
|
||||
"total_pages": 32,
|
||||
},
|
||||
want: "Page 12 of 32 (38%)",
|
||||
},
|
||||
{
|
||||
name: "Unknown format defaults to 0%",
|
||||
formatGroup: FormatGroupUnknown,
|
||||
progress: map[string]interface{}{
|
||||
"page": 50,
|
||||
"total_pages": 100,
|
||||
},
|
||||
want: "0.0%",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := FormatProgressForDisplay(tt.formatGroup, tt.progress)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundTripConversion(t *testing.T) {
|
||||
t.Run("Page -> Percentage -> Page", func(t *testing.T) {
|
||||
originalPage := 75
|
||||
totalPages := 300
|
||||
|
||||
percentage := PageToPercentage(originalPage, totalPages)
|
||||
resultPage := PercentageToPage(percentage, totalPages)
|
||||
|
||||
assert.Equal(t, originalPage, resultPage)
|
||||
})
|
||||
|
||||
t.Run("Character -> Percentage -> Character", func(t *testing.T) {
|
||||
originalChar := int64(25000)
|
||||
totalChars := int64(100000)
|
||||
|
||||
percentage := CharacterToPercentage(originalChar, totalChars)
|
||||
resultChar := PercentageToCharacter(percentage, totalChars)
|
||||
|
||||
assert.Equal(t, originalChar, resultChar)
|
||||
})
|
||||
}
|
||||
|
||||
func TestEdgeCases(t *testing.T) {
|
||||
t.Run("Very small percentages", func(t *testing.T) {
|
||||
assert.InDelta(t, 0.001, PageToPercentage(1, 1000), 0.0001)
|
||||
})
|
||||
|
||||
t.Run("Very large page numbers", func(t *testing.T) {
|
||||
page := PercentageToPage(0.999, 10000)
|
||||
assert.Equal(t, 9990, page)
|
||||
})
|
||||
|
||||
t.Run("Floating point precision", func(t *testing.T) {
|
||||
percentage := 1.0 / 3.0
|
||||
page := PercentageToPage(percentage, 300)
|
||||
assert.InDelta(t, 100, page, 1)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user