2025-12-19 08:24:32 +01:00
|
|
|
package pdfrender
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"testing"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestPreparePaginatedPageData_Page1Stats(t *testing.T) {
|
|
|
|
|
// Create test view model with many skills to test pagination
|
|
|
|
|
viewModel := &CharacterSheetViewModel{
|
|
|
|
|
Skills: make([]SkillViewModel, 50), // 50 skills should exceed column capacities
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fill with test data
|
|
|
|
|
for i := range viewModel.Skills {
|
|
|
|
|
viewModel.Skills[i] = SkillViewModel{
|
|
|
|
|
Name: "Test Skill",
|
|
|
|
|
Value: 10,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-21 22:07:46 +01:00
|
|
|
pageData, err := PreparePaginatedPageData(viewModel, "page_1.html", 1, "2024-01-01")
|
2025-12-19 08:24:32 +01:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("PreparePaginatedPageData failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Verify columns are populated
|
|
|
|
|
if len(pageData.SkillsColumn1) == 0 {
|
|
|
|
|
t.Error("SkillsColumn1 is empty")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(pageData.SkillsColumn2) == 0 {
|
|
|
|
|
t.Error("SkillsColumn2 is empty")
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-19 22:32:43 +01:00
|
|
|
// Get capacities from template
|
|
|
|
|
templateSet := DefaultA4QuerTemplateSet()
|
|
|
|
|
var page1Template *TemplateWithMeta
|
|
|
|
|
for i := range templateSet.Templates {
|
2025-12-21 22:07:46 +01:00
|
|
|
if templateSet.Templates[i].Metadata.Name == "page_1.html" {
|
2025-12-19 22:32:43 +01:00
|
|
|
page1Template = &templateSet.Templates[i]
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
var col1MaxItems, col2MaxItems int
|
|
|
|
|
for i := range page1Template.Metadata.Blocks {
|
|
|
|
|
if page1Template.Metadata.Blocks[i].Name == "skills_column1" {
|
|
|
|
|
col1MaxItems = page1Template.Metadata.Blocks[i].MaxItems
|
|
|
|
|
} else if page1Template.Metadata.Blocks[i].Name == "skills_column2" {
|
|
|
|
|
col2MaxItems = page1Template.Metadata.Blocks[i].MaxItems
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check capacities (from template)
|
|
|
|
|
if len(pageData.SkillsColumn1) > col1MaxItems {
|
|
|
|
|
t.Errorf("SkillsColumn1 exceeds capacity: got %d, max %d (from template)", len(pageData.SkillsColumn1), col1MaxItems)
|
2025-12-19 08:24:32 +01:00
|
|
|
}
|
|
|
|
|
|
2025-12-19 22:32:43 +01:00
|
|
|
if len(pageData.SkillsColumn2) > col2MaxItems {
|
|
|
|
|
t.Errorf("SkillsColumn2 exceeds capacity: got %d, max %d (from template)", len(pageData.SkillsColumn2), col2MaxItems)
|
2025-12-19 08:24:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Verify skills are split correctly
|
|
|
|
|
totalPaginated := len(pageData.SkillsColumn1) + len(pageData.SkillsColumn2)
|
2025-12-19 22:32:43 +01:00
|
|
|
expectedTotal := col1MaxItems + col2MaxItems
|
2025-12-19 08:24:32 +01:00
|
|
|
if totalPaginated > expectedTotal {
|
2025-12-19 22:32:43 +01:00
|
|
|
t.Errorf("Total paginated skills exceeds capacity: got %d, max %d (from template)", totalPaginated, expectedTotal)
|
2025-12-19 08:24:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
t.Logf("Column1: %d skills, Column2: %d skills (total: %d)",
|
|
|
|
|
len(pageData.SkillsColumn1), len(pageData.SkillsColumn2), totalPaginated)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSplitSkillsForColumns(t *testing.T) {
|
2025-12-19 22:32:43 +01:00
|
|
|
// Get actual column capacities from template
|
|
|
|
|
templateSet := DefaultA4QuerTemplateSet()
|
|
|
|
|
var page1Template *TemplateWithMeta
|
|
|
|
|
for i := range templateSet.Templates {
|
2025-12-21 22:07:46 +01:00
|
|
|
if templateSet.Templates[i].Metadata.Name == "page_1.html" {
|
2025-12-19 22:32:43 +01:00
|
|
|
page1Template = &templateSet.Templates[i]
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
var col1MaxItems, col2MaxItems int
|
|
|
|
|
for i := range page1Template.Metadata.Blocks {
|
|
|
|
|
if page1Template.Metadata.Blocks[i].Name == "skills_column1" {
|
|
|
|
|
col1MaxItems = page1Template.Metadata.Blocks[i].MaxItems
|
|
|
|
|
} else if page1Template.Metadata.Blocks[i].Name == "skills_column2" {
|
|
|
|
|
col2MaxItems = page1Template.Metadata.Blocks[i].MaxItems
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-19 08:24:32 +01:00
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
skills int
|
|
|
|
|
col1Max int
|
|
|
|
|
col2Max int
|
|
|
|
|
wantCol1 int
|
|
|
|
|
wantCol2 int
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "few skills - only column 1",
|
2025-12-20 14:37:56 +01:00
|
|
|
skills: 3, // Less than col1Max
|
2025-12-19 22:32:43 +01:00
|
|
|
col1Max: col1MaxItems,
|
|
|
|
|
col2Max: col2MaxItems,
|
2025-12-20 14:37:56 +01:00
|
|
|
wantCol1: 3,
|
2025-12-19 08:24:32 +01:00
|
|
|
wantCol2: 0,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "exactly column 1 capacity",
|
2025-12-19 22:32:43 +01:00
|
|
|
skills: col1MaxItems,
|
|
|
|
|
col1Max: col1MaxItems,
|
|
|
|
|
col2Max: col2MaxItems,
|
|
|
|
|
wantCol1: col1MaxItems,
|
2025-12-19 08:24:32 +01:00
|
|
|
wantCol2: 0,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "overflow to column 2",
|
2025-12-20 14:37:56 +01:00
|
|
|
skills: col1MaxItems + 3, // More than col1, but less than col1+col2
|
2025-12-19 22:32:43 +01:00
|
|
|
col1Max: col1MaxItems,
|
|
|
|
|
col2Max: col2MaxItems,
|
|
|
|
|
wantCol1: col1MaxItems,
|
2025-12-20 14:37:56 +01:00
|
|
|
wantCol2: 3,
|
2025-12-19 08:24:32 +01:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "both columns full",
|
2025-12-19 22:32:43 +01:00
|
|
|
skills: col1MaxItems + col2MaxItems,
|
|
|
|
|
col1Max: col1MaxItems,
|
|
|
|
|
col2Max: col2MaxItems,
|
|
|
|
|
wantCol1: col1MaxItems,
|
|
|
|
|
wantCol2: col2MaxItems,
|
2025-12-19 08:24:32 +01:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "more than both columns - truncate",
|
2025-12-19 22:32:43 +01:00
|
|
|
skills: col1MaxItems + col2MaxItems + 12,
|
|
|
|
|
col1Max: col1MaxItems,
|
|
|
|
|
col2Max: col2MaxItems,
|
|
|
|
|
wantCol1: col1MaxItems,
|
|
|
|
|
wantCol2: col2MaxItems,
|
2025-12-19 08:24:32 +01:00
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
// Create test skills
|
|
|
|
|
skills := make([]SkillViewModel, tt.skills)
|
|
|
|
|
for i := range skills {
|
|
|
|
|
skills[i] = SkillViewModel{
|
|
|
|
|
Name: "Test Skill",
|
|
|
|
|
Value: 10,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
col1, col2 := SplitSkillsForColumns(skills, tt.col1Max, tt.col2Max)
|
|
|
|
|
|
|
|
|
|
if len(col1) != tt.wantCol1 {
|
|
|
|
|
t.Errorf("Column1: got %d skills, want %d", len(col1), tt.wantCol1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(col2) != tt.wantCol2 {
|
|
|
|
|
t.Errorf("Column2: got %d skills, want %d", len(col2), tt.wantCol2)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestPreparePaginatedPageData_Page2Play(t *testing.T) {
|
|
|
|
|
// Create 40 weapons to test capacity limiting
|
|
|
|
|
viewModel := &CharacterSheetViewModel{
|
|
|
|
|
Weapons: make([]WeaponViewModel, 40),
|
|
|
|
|
}
|
|
|
|
|
for i := range viewModel.Weapons {
|
|
|
|
|
viewModel.Weapons[i] = WeaponViewModel{
|
|
|
|
|
Name: "Test Weapon",
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-21 22:07:46 +01:00
|
|
|
pageData, err := PreparePaginatedPageData(viewModel, "page_2.html", 2, "2024-01-01")
|
2025-12-19 08:24:32 +01:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("PreparePaginatedPageData failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Page 2 should have weapons limited to 30
|
|
|
|
|
if len(pageData.Weapons) > 30 {
|
|
|
|
|
t.Errorf("Weapons exceed capacity: got %d, max 30", len(pageData.Weapons))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
t.Logf("Page2: %d weapons", len(pageData.Weapons))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestPreparePaginatedPageData_Page3Spell(t *testing.T) {
|
2025-12-19 10:47:05 +01:00
|
|
|
// Get capacities from template
|
|
|
|
|
templateSet := DefaultA4QuerTemplateSet()
|
2025-12-22 23:20:49 +01:00
|
|
|
leftCap := GetBlockCapacity(&templateSet, "page_3.html", "spells_column1")
|
|
|
|
|
rightCap := GetBlockCapacity(&templateSet, "page_3.html", "spells_column2")
|
2025-12-21 22:07:46 +01:00
|
|
|
magicItemsCap := GetBlockCapacity(&templateSet, "page_3.html", "magic_items")
|
2025-12-19 10:47:05 +01:00
|
|
|
|
|
|
|
|
// Create test data exceeding capacities
|
2025-12-19 08:24:32 +01:00
|
|
|
viewModel := &CharacterSheetViewModel{
|
2025-12-19 10:47:05 +01:00
|
|
|
Spells: make([]SpellViewModel, 50),
|
|
|
|
|
MagicItems: make([]MagicItemViewModel, 20),
|
2025-12-19 08:24:32 +01:00
|
|
|
}
|
|
|
|
|
for i := range viewModel.Spells {
|
|
|
|
|
viewModel.Spells[i] = SpellViewModel{
|
|
|
|
|
Name: "Test Spell",
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for i := range viewModel.MagicItems {
|
|
|
|
|
viewModel.MagicItems[i] = MagicItemViewModel{
|
|
|
|
|
Name: "Test Item",
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-21 22:07:46 +01:00
|
|
|
pageData, err := PreparePaginatedPageData(viewModel, "page_3.html", 3, "2024-01-01")
|
2025-12-19 08:24:32 +01:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("PreparePaginatedPageData failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-19 10:47:05 +01:00
|
|
|
// Verify capacities match template
|
|
|
|
|
if len(pageData.SpellsLeft) != leftCap {
|
|
|
|
|
t.Errorf("SpellsLeft should be filled to %d (from template), got %d", leftCap, len(pageData.SpellsLeft))
|
2025-12-19 08:30:45 +01:00
|
|
|
}
|
|
|
|
|
|
2025-12-19 10:47:05 +01:00
|
|
|
if len(pageData.SpellsRight) != rightCap {
|
|
|
|
|
t.Errorf("SpellsRight should be filled to %d (from template), got %d", rightCap, len(pageData.SpellsRight))
|
2025-12-19 08:24:32 +01:00
|
|
|
}
|
|
|
|
|
|
2025-12-19 10:47:05 +01:00
|
|
|
if len(pageData.MagicItems) != magicItemsCap {
|
|
|
|
|
t.Errorf("MagicItems should be filled to %d (from template), got %d", magicItemsCap, len(pageData.MagicItems))
|
2025-12-19 08:24:32 +01:00
|
|
|
}
|
|
|
|
|
|
2025-12-19 10:47:05 +01:00
|
|
|
t.Logf("Page3: left=%d, right=%d (total=%d), magic_items=%d (from template)",
|
|
|
|
|
len(pageData.SpellsLeft), len(pageData.SpellsRight),
|
|
|
|
|
len(pageData.SpellsLeft)+len(pageData.SpellsRight), len(pageData.MagicItems))
|
2025-12-19 08:24:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestPreparePaginatedPageData_Page4Equipment(t *testing.T) {
|
2025-12-20 14:37:56 +01:00
|
|
|
// Page 4 equipment page needs ALL equipment (not limited by capacity)
|
|
|
|
|
// because the template has complex container logic that requires the full equipment list
|
2025-12-19 10:47:05 +01:00
|
|
|
|
2025-12-20 14:37:56 +01:00
|
|
|
// Create test data
|
2025-12-19 08:24:32 +01:00
|
|
|
viewModel := &CharacterSheetViewModel{
|
2025-12-19 10:47:05 +01:00
|
|
|
Equipment: make([]EquipmentViewModel, 50),
|
2025-12-19 08:24:32 +01:00
|
|
|
}
|
|
|
|
|
for i := range viewModel.Equipment {
|
|
|
|
|
viewModel.Equipment[i] = EquipmentViewModel{
|
|
|
|
|
Name: "Test Equipment",
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-21 22:07:46 +01:00
|
|
|
pageData, err := PreparePaginatedPageData(viewModel, "page_4.html", 4, "2024-01-01")
|
2025-12-19 08:24:32 +01:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("PreparePaginatedPageData failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-20 14:37:56 +01:00
|
|
|
// Verify all equipment is included (not truncated)
|
|
|
|
|
if len(pageData.Equipment) != 50 {
|
|
|
|
|
t.Errorf("Equipment should include all items (50), got %d", len(pageData.Equipment))
|
2025-12-19 08:24:32 +01:00
|
|
|
}
|
|
|
|
|
|
2025-12-20 14:37:56 +01:00
|
|
|
t.Logf("Page4: %d equipment items (all items included for container rendering)", len(pageData.Equipment))
|
2025-12-19 08:24:32 +01:00
|
|
|
}
|