[🤖] Add Unit-Tests
Build Docker and Deploy / Run goext test-suite (push) Successful in 1m34s

This commit is contained in:
2026-04-27 10:46:08 +02:00
parent dad0e3240d
commit 02d6894ec6
116 changed files with 18795 additions and 1 deletions
+304
View File
@@ -0,0 +1,304 @@
package excelext
import (
"bytes"
"errors"
"testing"
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
"github.com/xuri/excelize/v2"
)
type testRow struct {
Name string
Age int
Score float64
}
func openBytes(t *testing.T, data []byte) *excelize.File {
t.Helper()
f, err := excelize.OpenReader(bytes.NewReader(data))
if err != nil {
t.Fatalf("failed to open xlsx bytes: %v", err)
}
return f
}
func cellValue(t *testing.T, f *excelize.File, sheet, axis string) string {
t.Helper()
v, err := f.GetCellValue(sheet, axis)
if err != nil {
t.Fatalf("GetCellValue(%s, %s) failed: %v", sheet, axis, err)
}
return v
}
func TestNewExcelMapper(t *testing.T) {
em, err := NewExcelMapper[testRow]()
tst.AssertNoErr(t, err)
if em == nil {
t.Fatal("expected non-nil mapper")
}
tst.AssertEqual(t, em.SkipColumnHeader, false)
tst.AssertEqual(t, len(em.colDefinitions), 0)
tst.AssertEqual(t, len(em.wsHeader), 0)
tst.AssertEqual(t, len(em.colFilter), 0)
if em.StyleDate != nil || em.StyleHeader != nil {
t.Errorf("expected styles to be nil before init")
}
}
func TestInitNewFileAndStyles(t *testing.T) {
em, _ := NewExcelMapper[testRow]()
f, err := em.InitNewFile("Sheet-Foo")
tst.AssertNoErr(t, err)
if f == nil {
t.Fatal("expected non-nil file")
}
sheets := f.GetSheetList()
tst.AssertEqual(t, len(sheets), 1)
tst.AssertEqual(t, sheets[0], "Sheet-Foo")
if em.StyleDate == nil || em.StyleDatetime == nil || em.StyleEUR == nil ||
em.StylePercentage == nil || em.StyleHeader == nil || em.StyleWSHeader == nil {
t.Errorf("expected all styles to be initialized")
}
}
func TestAddColumn(t *testing.T) {
em, _ := NewExcelMapper[testRow]()
em.AddColumn("Name", nil, nil, func(r testRow) any { return r.Name })
em.AddColumn("Age", nil, langext.Ptr(12.0), func(r testRow) any { return r.Age })
tst.AssertEqual(t, len(em.colDefinitions), 2)
tst.AssertEqual(t, em.colDefinitions[0].header, "Name")
tst.AssertEqual(t, em.colDefinitions[1].header, "Age")
if em.colDefinitions[1].width == nil || *em.colDefinitions[1].width != 12.0 {
t.Errorf("expected width 12.0")
}
val, err := em.colDefinitions[0].fn(testRow{Name: "Alice"})
tst.AssertNoErr(t, err)
tst.AssertEqual(t, val.(string), "Alice")
}
func TestAddColumnErr(t *testing.T) {
em, _ := NewExcelMapper[testRow]()
sentinel := errors.New("boom")
em.AddColumnErr("X", nil, nil, func(r testRow) (any, error) {
if r.Age < 0 {
return nil, sentinel
}
return r.Age, nil
})
tst.AssertEqual(t, len(em.colDefinitions), 1)
v, err := em.colDefinitions[0].fn(testRow{Age: 5})
tst.AssertNoErr(t, err)
tst.AssertEqual(t, v.(int), 5)
_, err = em.colDefinitions[0].fn(testRow{Age: -1})
if !errors.Is(err, sentinel) {
t.Errorf("expected sentinel error, got %v", err)
}
}
func TestAddWorksheetHeader(t *testing.T) {
em, _ := NewExcelMapper[testRow]()
em.AddWorksheetHeader("Title 1", nil)
em.AddWorksheetHeader("Title 2", langext.Ptr(7))
tst.AssertEqual(t, len(em.wsHeader), 2)
tst.AssertEqual(t, em.wsHeader[0].V1, "Title 1")
tst.AssertEqual(t, em.wsHeader[1].V1, "Title 2")
if em.wsHeader[1].V2 == nil || *em.wsHeader[1].V2 != 7 {
t.Errorf("expected style ptr 7")
}
if em.wsHeader[0].V2 != nil {
t.Errorf("expected nil style for first header")
}
}
func TestAddFilter(t *testing.T) {
em, _ := NewExcelMapper[testRow]()
em.AddFilter(func(v testRow) bool { return v.Age >= 18 })
em.AddFilter(func(v testRow) bool { return v.Score > 0 })
tst.AssertEqual(t, len(em.colFilter), 2)
}
func TestBuildBasic(t *testing.T) {
em, _ := NewExcelMapper[testRow]()
em.AddColumn("Name", nil, nil, func(r testRow) any { return r.Name })
em.AddColumn("Age", nil, nil, func(r testRow) any { return r.Age })
rows := []testRow{
{Name: "Alice", Age: 30},
{Name: "Bob", Age: 25},
}
data, err := em.Build("Sheet1", rows)
tst.AssertNoErr(t, err)
if len(data) == 0 {
t.Fatal("expected non-empty xlsx output")
}
f := openBytes(t, data)
defer f.Close()
tst.AssertEqual(t, cellValue(t, f, "Sheet1", "A1"), "Name")
tst.AssertEqual(t, cellValue(t, f, "Sheet1", "B1"), "Age")
tst.AssertEqual(t, cellValue(t, f, "Sheet1", "A2"), "Alice")
tst.AssertEqual(t, cellValue(t, f, "Sheet1", "B2"), "30")
tst.AssertEqual(t, cellValue(t, f, "Sheet1", "A3"), "Bob")
tst.AssertEqual(t, cellValue(t, f, "Sheet1", "B3"), "25")
}
func TestBuildSkipColumnHeader(t *testing.T) {
em, _ := NewExcelMapper[testRow]()
em.SkipColumnHeader = true
em.AddColumn("Name", nil, nil, func(r testRow) any { return r.Name })
rows := []testRow{{Name: "Alice"}, {Name: "Bob"}}
data, err := em.Build("Data", rows)
tst.AssertNoErr(t, err)
f := openBytes(t, data)
defer f.Close()
tst.AssertEqual(t, cellValue(t, f, "Data", "A1"), "Alice")
tst.AssertEqual(t, cellValue(t, f, "Data", "A2"), "Bob")
}
func TestBuildWithFilter(t *testing.T) {
em, _ := NewExcelMapper[testRow]()
em.AddColumn("Name", nil, nil, func(r testRow) any { return r.Name })
em.AddFilter(func(v testRow) bool { return v.Age >= 18 })
rows := []testRow{
{Name: "Alice", Age: 30},
{Name: "Charlie", Age: 12},
{Name: "Bob", Age: 25},
}
data, err := em.Build("S", rows)
tst.AssertNoErr(t, err)
f := openBytes(t, data)
defer f.Close()
tst.AssertEqual(t, cellValue(t, f, "S", "A1"), "Name")
tst.AssertEqual(t, cellValue(t, f, "S", "A2"), "Alice")
tst.AssertEqual(t, cellValue(t, f, "S", "A3"), "Bob")
tst.AssertEqual(t, cellValue(t, f, "S", "A4"), "")
}
func TestBuildWithWorksheetHeader(t *testing.T) {
em, _ := NewExcelMapper[testRow]()
em.AddWorksheetHeader("My Big Title", nil)
em.AddColumn("Name", nil, nil, func(r testRow) any { return r.Name })
em.AddColumn("Age", nil, nil, func(r testRow) any { return r.Age })
rows := []testRow{{Name: "Alice", Age: 30}}
data, err := em.Build("S", rows)
tst.AssertNoErr(t, err)
f := openBytes(t, data)
defer f.Close()
tst.AssertEqual(t, cellValue(t, f, "S", "A1"), "My Big Title")
tst.AssertEqual(t, cellValue(t, f, "S", "A3"), "Name")
tst.AssertEqual(t, cellValue(t, f, "S", "B3"), "Age")
tst.AssertEqual(t, cellValue(t, f, "S", "A4"), "Alice")
tst.AssertEqual(t, cellValue(t, f, "S", "B4"), "30")
}
func TestBuildHandlesNilPointer(t *testing.T) {
type ptrRow struct {
Name *string
}
em, _ := NewExcelMapper[ptrRow]()
em.AddColumn("Name", nil, nil, func(r ptrRow) any { return r.Name })
name := "Alice"
rows := []ptrRow{
{Name: &name},
{Name: nil},
}
data, err := em.Build("S", rows)
tst.AssertNoErr(t, err)
f := openBytes(t, data)
defer f.Close()
tst.AssertEqual(t, cellValue(t, f, "S", "A2"), "Alice")
tst.AssertEqual(t, cellValue(t, f, "S", "A3"), "")
}
func TestBuildPropagatesColumnError(t *testing.T) {
em, _ := NewExcelMapper[testRow]()
sentinel := errors.New("col fail")
em.AddColumnErr("Bad", nil, nil, func(r testRow) (any, error) {
return nil, sentinel
})
_, err := em.Build("S", []testRow{{Name: "X"}})
if err == nil {
t.Fatal("expected error from column fn to propagate")
}
}
func TestBuildEmptyData(t *testing.T) {
em, _ := NewExcelMapper[testRow]()
em.AddColumn("Name", nil, nil, func(r testRow) any { return r.Name })
data, err := em.Build("S", []testRow{})
tst.AssertNoErr(t, err)
f := openBytes(t, data)
defer f.Close()
tst.AssertEqual(t, cellValue(t, f, "S", "A1"), "Name")
tst.AssertEqual(t, cellValue(t, f, "S", "A2"), "")
}
func TestBuildSingleSheetWithExistingFile(t *testing.T) {
em, _ := NewExcelMapper[testRow]()
em.AddColumn("Name", nil, nil, func(r testRow) any { return r.Name })
f, err := em.InitNewFile("S1")
tst.AssertNoErr(t, err)
_, err = f.NewSheet("S2")
tst.AssertNoErr(t, err)
err = em.BuildSingleSheet(f, "S2", []testRow{{Name: "Bob"}})
tst.AssertNoErr(t, err)
tst.AssertEqual(t, cellValue(t, f, "S2", "A1"), "Name")
tst.AssertEqual(t, cellValue(t, f, "S2", "A2"), "Bob")
}
func TestBuildWithColumnWidthAndStyle(t *testing.T) {
em, _ := NewExcelMapper[testRow]()
f, err := em.InitNewFile("S")
tst.AssertNoErr(t, err)
em.AddColumn("Name", em.StyleHeader, langext.Ptr(20.5), func(r testRow) any { return r.Name })
err = em.BuildSingleSheet(f, "S", []testRow{{Name: "Alice"}})
tst.AssertNoErr(t, err)
w, err := f.GetColWidth("S", "A")
tst.AssertNoErr(t, err)
if w < 20.0 || w > 21.0 {
t.Errorf("expected column width near 20.5, got %v", w)
}
}
+61
View File
@@ -0,0 +1,61 @@
package excelext
import (
"testing"
"time"
"git.blackforestbytes.com/BlackForestBytes/goext/rfctime"
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
)
func TestCellAddress(t *testing.T) {
tst.AssertEqual(t, c(1, 0), "A1")
tst.AssertEqual(t, c(1, 1), "B1")
tst.AssertEqual(t, c(2, 0), "A2")
tst.AssertEqual(t, c(10, 25), "Z10")
tst.AssertEqual(t, c(1, 26), "AA1")
tst.AssertEqual(t, c(99, 27), "AB99")
tst.AssertEqual(t, c(100, 51), "AZ100")
tst.AssertEqual(t, c(1, 52), "BA1")
}
func TestExcelizeOptTimeNil(t *testing.T) {
got := excelizeOptTime(nil)
if got != "" {
t.Errorf("expected empty string for nil time, got %v", got)
}
}
func TestExcelizeOptTimeValue(t *testing.T) {
now := time.Date(2024, 5, 17, 13, 45, 30, 0, time.UTC)
rt := rfctime.RFC3339NanoTime(now)
got := excelizeOptTime(&rt)
gt, ok := got.(time.Time)
if !ok {
t.Fatalf("expected time.Time, got %T", got)
}
if !gt.Equal(now) {
t.Errorf("expected %v, got %v", now, gt)
}
}
func TestExcelizeOptDateNil(t *testing.T) {
got := excelizeOptDate(nil)
if got != "" {
t.Errorf("expected empty string for nil date, got %v", got)
}
}
func TestExcelizeOptDateValue(t *testing.T) {
d := rfctime.NewDate(time.Date(2025, 11, 3, 0, 0, 0, 0, time.UTC))
got := excelizeOptDate(&d)
gt, ok := got.(time.Time)
if !ok {
t.Fatalf("expected time.Time, got %T", got)
}
if gt.Year() != 2025 || gt.Month() != 11 || gt.Day() != 3 {
t.Errorf("unexpected date returned: %v", gt)
}
}