Defining list length in Template

Test don't care about list lenght
This commit is contained in:
2025-12-19 22:32:43 +01:00
parent 165f55f891
commit 92f9e4c27b
9 changed files with 543 additions and 163 deletions
+3 -3
View File
@@ -21,15 +21,15 @@ type EqWaffe struct {
BamortCharTrait
Magisch
Beschreibung string `json:"beschreibung"`
Abwb int `json:"abwb"`
Anb int `json:"anb"`
Abwb int `json:"abwb"` // Abwehrbonus
Anb int `json:"anb"` // Angriffsbonus
Anzahl int `json:"anzahl"`
BeinhaltetIn string `json:"beinhaltet_in"`
ContainedIn uint `json:"contained_in"`
ContainerType string `json:"container_type"`
Gewicht float64 `json:"gewicht"`
NameFuerSpezialisierung string `json:"nameFuerSpezialisierung"`
Schb int `json:"schb"`
Schb int `json:"schb"` // Schadensbonus
Wert float64 `json:"wert"`
}
+23 -3
View File
@@ -81,10 +81,30 @@ func TestTemplateWithEmptyRows(t *testing.T) {
t.Fatalf("Failed to render template: %v", err)
}
// Get expected skill capacity from template
templateSet := DefaultA4QuerTemplateSet()
var page1Template *TemplateWithMeta
for i := range templateSet.Templates {
if templateSet.Templates[i].Metadata.Name == "page1_stats.html" {
page1Template = &templateSet.Templates[i]
break
}
}
if page1Template == nil {
t.Fatal("page1_stats.html template not found")
}
var col1Capacity int
for i := range page1Template.Metadata.Blocks {
if page1Template.Metadata.Blocks[i].Name == "skills_column1" {
col1Capacity = page1Template.Metadata.Blocks[i].MaxItems
break
}
}
// Count the number of <tr> tags in skills table
// Should have 29 rows (2 filled + 27 empty)
// Should have col1Capacity rows (2 filled + remaining empty)
trCount := strings.Count(html, "<tr><td>")
if trCount < 29 {
t.Errorf("Expected at least 29 skill rows in HTML, got %d", trCount)
if trCount < col1Capacity {
t.Errorf("Expected at least %d skill rows in HTML (from template), got %d", col1Capacity, trCount)
}
}
+70 -22
View File
@@ -150,17 +150,19 @@ func TestIntegration_TemplateMetadata(t *testing.T) {
t.Fatalf("Failed to load templates: %v", err)
}
// Load template set to get expected values from actual template files
templateSet := DefaultA4QuerTemplateSet()
// Act & Assert - Check each template has metadata
testCases := []struct {
template string
expectedBlock string
expectedMax int
}{
{"page1_stats.html", "skills_column1", 29},
{"page2_play.html", "skills_learned", 17}, // From template: MAX: 17
{"page3_spell.html", "spells_left", 15}, // From template: MAX: 15
{"page3_spell.html", "spells_right", 10}, // From template: MAX: 10
{"page4_equip.html", "equipment_worn", 10},
{"page1_stats.html", "skills_column1"},
{"page2_play.html", "skills_learned"},
{"page3_spell.html", "spells_left"},
{"page3_spell.html", "spells_right"},
{"page4_equip.html", "equipment_worn"},
}
for _, tc := range testCases {
@@ -176,9 +178,23 @@ func TestIntegration_TemplateMetadata(t *testing.T) {
continue
}
if block.MaxItems != tc.expectedMax {
t.Errorf("Template %s block %s: expected max %d, got %d",
tc.template, tc.expectedBlock, tc.expectedMax, block.MaxItems)
// Get expected value from template set
var expectedMax int
for i := range templateSet.Templates {
if templateSet.Templates[i].Metadata.Name == tc.template {
for j := range templateSet.Templates[i].Metadata.Blocks {
if templateSet.Templates[i].Metadata.Blocks[j].Name == tc.expectedBlock {
expectedMax = templateSet.Templates[i].Metadata.Blocks[j].MaxItems
break
}
}
break
}
}
if block.MaxItems != expectedMax {
t.Errorf("Template %s block %s: expected max %d (from template), got %d",
tc.template, tc.expectedBlock, expectedMax, block.MaxItems)
}
}
}
@@ -265,14 +281,28 @@ func TestIntegration_PaginationWithPDF(t *testing.T) {
t.Logf("Successfully generated page 1 PDF with %d skills, size: %d bytes", len(pageData.Skills), len(pdfBytes))
// Verify second page has remaining skills (94 total - 58 from page 1 = 36 remaining)
// But with 29+29 capacity, it will be 29+13 = 42 on page 2
// Verify second page has remaining skills
// Get expected capacity from template (reuse templateSet from above)
var page1Template *TemplateWithMeta
for i := range templateSet.Templates {
if templateSet.Templates[i].Metadata.Name == "page1_stats.html" {
page1Template = &templateSet.Templates[i]
break
}
}
col1Block := GetBlockByName(page1Template.Metadata.Blocks, "skills_column1")
col2Block := GetBlockByName(page1Template.Metadata.Blocks, "skills_column2")
expectedPage1Capacity := col1Block.MaxItems + col2Block.MaxItems
col1Page2 := pages[1].Data["skills_column1"].([]SkillViewModel)
col2Page2 := pages[1].Data["skills_column2"].([]SkillViewModel)
totalPage2 := len(col1Page2) + len(col2Page2)
if totalPage2 != 42 { // 100 total - 58 from page 1 = 42 remaining
t.Errorf("Expected 42 skills on page 2, got %d", totalPage2)
expectedPage2 := 100 - expectedPage1Capacity // 100 total - capacity from page 1
if totalPage2 != expectedPage2 {
t.Errorf("Expected %d skills on page 2 (100 total - %d from page 1), got %d", expectedPage2, expectedPage1Capacity, totalPage2)
}
t.Logf("Page 2 would have %d skills distributed across columns", totalPage2)
@@ -280,7 +310,21 @@ func TestIntegration_PaginationWithPDF(t *testing.T) {
// TestIntegration_MultiPageSpellList tests spell pagination across multiple pages
func TestIntegration_MultiPageSpellList(t *testing.T) {
// Arrange - Create 30 spells (will need 2 pages with 24 capacity each)
// Get expected capacity from template
templateSet := DefaultA4QuerTemplateSet()
var page3Template *TemplateWithMeta
for i := range templateSet.Templates {
if templateSet.Templates[i].Metadata.Name == "page3_spell.html" {
page3Template = &templateSet.Templates[i]
break
}
}
spellsLeftBlock := GetBlockByName(page3Template.Metadata.Blocks, "spells_left")
spellsRightBlock := GetBlockByName(page3Template.Metadata.Blocks, "spells_right")
expectedSpellCapacity := spellsLeftBlock.MaxItems + spellsRightBlock.MaxItems
// Arrange - Create 30 spells
spells := make([]SpellViewModel, 30)
for i := 0; i < 30; i++ {
spells[i] = SpellViewModel{
@@ -293,7 +337,6 @@ func TestIntegration_MultiPageSpellList(t *testing.T) {
}
// Create paginator
templateSet := DefaultA4QuerTemplateSet()
paginator := NewPaginator(templateSet)
// Paginate spells
@@ -302,21 +345,26 @@ func TestIntegration_MultiPageSpellList(t *testing.T) {
t.Fatalf("Failed to paginate spells: %v", err)
}
// With 25 capacity (15+10), 30 spells should need 2 pages
// Calculate expected pages
expectedPages := (30 + expectedSpellCapacity - 1) / expectedSpellCapacity // Ceiling division
// Verify distribution
// With 15+10 capacity, 30 spells should need 2 pages
if len(pages) != 2 {
t.Fatalf("Expected 2 pages for 30 spells, got %d", len(pages))
if len(pages) != expectedPages {
t.Fatalf("Expected %d pages for 30 spells (capacity %d), got %d", expectedPages, expectedSpellCapacity, len(pages))
}
// Page 1: 15 (left) + 10 (right) = 25 spells
// Page 1 should have min(30, capacity) spells
leftPage1 := pages[0].Data["spells_left"].([]SpellViewModel)
rightPage1 := pages[0].Data["spells_right"].([]SpellViewModel)
totalPage1 := len(leftPage1) + len(rightPage1)
if totalPage1 != 25 {
t.Errorf("Expected 25 spells on page 1 (15+10), got %d", totalPage1)
expectedPage1 := expectedSpellCapacity
if 30 < expectedSpellCapacity {
expectedPage1 = 30
}
if totalPage1 != expectedPage1 {
t.Errorf("Expected %d spells on page 1 (capacity %d+%d), got %d", expectedPage1, spellsLeftBlock.MaxItems, spellsRightBlock.MaxItems, totalPage1)
}
t.Logf("Successfully distributed 30 spells: Page 1 has %d (left %d, right %d)", totalPage1, len(leftPage1), len(rightPage1))
+63 -27
View File
@@ -32,20 +32,38 @@ func TestPreparePaginatedPageData_Page1Stats(t *testing.T) {
t.Error("SkillsColumn2 is empty")
}
// Check capacities (MAX: 29 each)
if len(pageData.SkillsColumn1) > 29 {
t.Errorf("SkillsColumn1 exceeds capacity: got %d, max 29", len(pageData.SkillsColumn1))
// Get capacities from template
templateSet := DefaultA4QuerTemplateSet()
var page1Template *TemplateWithMeta
for i := range templateSet.Templates {
if templateSet.Templates[i].Metadata.Name == "page1_stats.html" {
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
}
}
if len(pageData.SkillsColumn2) > 29 {
t.Errorf("SkillsColumn2 exceeds capacity: got %d, max 29", len(pageData.SkillsColumn2))
// Check capacities (from template)
if len(pageData.SkillsColumn1) > col1MaxItems {
t.Errorf("SkillsColumn1 exceeds capacity: got %d, max %d (from template)", len(pageData.SkillsColumn1), col1MaxItems)
}
if len(pageData.SkillsColumn2) > col2MaxItems {
t.Errorf("SkillsColumn2 exceeds capacity: got %d, max %d (from template)", len(pageData.SkillsColumn2), col2MaxItems)
}
// Verify skills are split correctly
totalPaginated := len(pageData.SkillsColumn1) + len(pageData.SkillsColumn2)
expectedTotal := 58 // 29 + 29
expectedTotal := col1MaxItems + col2MaxItems
if totalPaginated > expectedTotal {
t.Errorf("Total paginated skills exceeds capacity: got %d, max %d", totalPaginated, expectedTotal)
t.Errorf("Total paginated skills exceeds capacity: got %d, max %d (from template)", totalPaginated, expectedTotal)
}
t.Logf("Column1: %d skills, Column2: %d skills (total: %d)",
@@ -53,6 +71,24 @@ func TestPreparePaginatedPageData_Page1Stats(t *testing.T) {
}
func TestSplitSkillsForColumns(t *testing.T) {
// Get actual column capacities from template
templateSet := DefaultA4QuerTemplateSet()
var page1Template *TemplateWithMeta
for i := range templateSet.Templates {
if templateSet.Templates[i].Metadata.Name == "page1_stats.html" {
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
}
}
tests := []struct {
name string
skills int
@@ -64,42 +100,42 @@ func TestSplitSkillsForColumns(t *testing.T) {
{
name: "few skills - only column 1",
skills: 10,
col1Max: 29,
col2Max: 29,
col1Max: col1MaxItems,
col2Max: col2MaxItems,
wantCol1: 10,
wantCol2: 0,
},
{
name: "exactly column 1 capacity",
skills: 29,
col1Max: 29,
col2Max: 29,
wantCol1: 29,
skills: col1MaxItems,
col1Max: col1MaxItems,
col2Max: col2MaxItems,
wantCol1: col1MaxItems,
wantCol2: 0,
},
{
name: "overflow to column 2",
skills: 40,
col1Max: 29,
col2Max: 29,
wantCol1: 29,
skills: col1MaxItems + 11,
col1Max: col1MaxItems,
col2Max: col2MaxItems,
wantCol1: col1MaxItems,
wantCol2: 11,
},
{
name: "both columns full",
skills: 58,
col1Max: 29,
col2Max: 29,
wantCol1: 29,
wantCol2: 29,
skills: col1MaxItems + col2MaxItems,
col1Max: col1MaxItems,
col2Max: col2MaxItems,
wantCol1: col1MaxItems,
wantCol2: col2MaxItems,
},
{
name: "more than both columns - truncate",
skills: 70,
col1Max: 29,
col2Max: 29,
wantCol1: 29,
wantCol2: 29,
skills: col1MaxItems + col2MaxItems + 12,
col1Max: col1MaxItems,
col2Max: col2MaxItems,
wantCol1: col1MaxItems,
wantCol2: col2MaxItems,
},
}
+216 -74
View File
@@ -1,6 +1,7 @@
package pdfrender
import (
"fmt"
"testing"
)
@@ -83,7 +84,7 @@ func TestPaginateSkills_SinglePage(t *testing.T) {
t.Errorf("Expected page number 1, got %d", page.PageNumber)
}
// Column 1 should have all 10 skills (max 32)
// Column 1 should have all 10 skills
col1Data, ok := page.Data["skills_column1"].([]SkillViewModel)
if !ok {
t.Fatal("skills_column1 data not found or wrong type")
@@ -107,7 +108,17 @@ func TestPaginateSkills_MultiColumn(t *testing.T) {
templateSet := DefaultA4QuerTemplateSet()
paginator := NewPaginator(templateSet)
// Create 40 skills - should fill first column (32) and spill to second (8)
// Get expected capacity from template
var page1Template *TemplateWithMeta
for i := range templateSet.Templates {
if templateSet.Templates[i].Metadata.Name == "page1_stats.html" {
page1Template = &templateSet.Templates[i]
break
}
}
col1Block := GetBlockByName(page1Template.Metadata.Blocks, "skills_column1")
// Create 40 skills - should fill first column and spill to second
skills := make([]SkillViewModel, 40)
for i := 0; i < 40; i++ {
skills[i] = SkillViewModel{Name: "Skill" + string(rune(i))}
@@ -127,16 +138,17 @@ func TestPaginateSkills_MultiColumn(t *testing.T) {
page := pages[0]
// Column 1 should have 29 skills
// Column 1 should have max capacity skills from template
col1Data := page.Data["skills_column1"].([]SkillViewModel)
if len(col1Data) != 29 {
t.Errorf("Expected 29 skills in column 1, got %d", len(col1Data))
if len(col1Data) != col1Block.MaxItems {
t.Errorf("Expected %d skills in column 1 (from template), got %d", col1Block.MaxItems, len(col1Data))
}
// Column 2 should have 11 skills (40 total - 29 in col1)
// Column 2 should have remaining skills
col2Data := page.Data["skills_column2"].([]SkillViewModel)
if len(col2Data) != 11 {
t.Errorf("Expected 11 skills in column 2, got %d", len(col2Data))
expectedCol2 := 40 - col1Block.MaxItems
if len(col2Data) != expectedCol2 {
t.Errorf("Expected %d skills in column 2 (40 total - %d in col1), got %d", expectedCol2, col1Block.MaxItems, len(col2Data))
}
}
@@ -163,26 +175,46 @@ func TestPaginateSkills_MultiPage(t *testing.T) {
t.Fatalf("Expected 2 pages, got %d", len(pages))
}
// Page 1 should have 58 skills (29 + 29)
// Get column capacities from template (reuse templateSet from above)
var page1Template *TemplateWithMeta
for i := range templateSet.Templates {
if templateSet.Templates[i].Metadata.Name == "page1_stats.html" {
page1Template = &templateSet.Templates[i]
break
}
}
var col1Capacity, col2Capacity int
for i := range page1Template.Metadata.Blocks {
if page1Template.Metadata.Blocks[i].Name == "skills_column1" {
col1Capacity = page1Template.Metadata.Blocks[i].MaxItems
} else if page1Template.Metadata.Blocks[i].Name == "skills_column2" {
col2Capacity = page1Template.Metadata.Blocks[i].MaxItems
}
}
// Page 1 should have full capacity (col1 + col2)
page1 := pages[0]
col1Page1 := page1.Data["skills_column1"].([]SkillViewModel)
col2Page1 := page1.Data["skills_column2"].([]SkillViewModel)
if len(col1Page1) != 29 {
t.Errorf("Page 1 col1: expected 29 skills, got %d", len(col1Page1))
if len(col1Page1) != col1Capacity {
t.Errorf("Page 1 col1: expected %d skills (template capacity), got %d", col1Capacity, len(col1Page1))
}
if len(col2Page1) != 29 {
t.Errorf("Page 1 col2: expected 29 skills, got %d", len(col2Page1))
if len(col2Page1) != col2Capacity {
t.Errorf("Page 1 col2: expected %d skills (template capacity), got %d", col2Capacity, len(col2Page1))
}
// Page 2 should have 42 skills (29 + 13) - 100 total - 58 from page 1 = 42 remaining
// Page 2 should have remaining skills
page2 := pages[1]
col1Page2 := page2.Data["skills_column1"].([]SkillViewModel)
col2Page2 := page2.Data["skills_column2"].([]SkillViewModel)
if len(col1Page2) != 29 {
t.Errorf("Page 2 col1: expected 29 skills, got %d", len(col1Page2))
page1Total := col1Capacity + col2Capacity
remainingSkills := 100 - page1Total
if len(col1Page2) != col1Capacity {
t.Errorf("Page 2 col1: expected %d skills (template capacity), got %d", col1Capacity, len(col1Page2))
}
if len(col2Page2) != 13 {
t.Errorf("Page 2 col2: expected 13 skills, got %d", len(col2Page2))
expectedCol2 := remainingSkills - col1Capacity
if len(col2Page2) != expectedCol2 {
t.Errorf("Page 2 col2: expected %d skills (remaining), got %d", expectedCol2, len(col2Page2))
}
}
@@ -191,36 +223,62 @@ func TestPaginateSpells_TwoColumns(t *testing.T) {
templateSet := DefaultA4QuerTemplateSet()
paginator := NewPaginator(templateSet)
// Create 15 spells - should fit in first column (12) with 3 in second column (12)
spells := make([]SpellViewModel, 15)
for i := 0; i < 15; i++ {
spells[i] = SpellViewModel{Name: "Spell" + string(rune('A'+i))}
// Get spell column capacities from template
var page3Template *TemplateWithMeta
for i := range templateSet.Templates {
if templateSet.Templates[i].Metadata.Name == "page3_spell.html" {
page3Template = &templateSet.Templates[i]
break
}
}
// Act
pages, err := paginator.PaginateSpells(spells, "page3_spell.html")
// Assert
if err != nil {
t.Fatalf("Expected no error, got %v", err)
var leftCapacity, rightCapacity int
for i := range page3Template.Metadata.Blocks {
if page3Template.Metadata.Blocks[i].Name == "spells_left" {
leftCapacity = page3Template.Metadata.Blocks[i].MaxItems
} else if page3Template.Metadata.Blocks[i].Name == "spells_right" {
rightCapacity = page3Template.Metadata.Blocks[i].MaxItems
}
}
totalCapacity := leftCapacity + rightCapacity
if len(pages) != 1 {
t.Fatalf("Expected 1 page, got %d", len(pages))
}
// Create spells that fit within total capacity
testCount := totalCapacity
if testCount > 0 {
spells := make([]SpellViewModel, testCount)
for i := 0; i < testCount; i++ {
spells[i] = SpellViewModel{Name: "Spell" + string(rune('A'+i))}
}
page := pages[0]
// Act
pages, err := paginator.PaginateSpells(spells, "page3_spell.html")
// Left column should have 15 spells (all fit in 20 capacity)
leftData := page.Data["spells_left"].([]SpellViewModel)
if len(leftData) != 15 {
t.Errorf("Expected 15 spells in left column, got %d", len(leftData))
}
// Assert
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
// Right column should be empty (15 spells all fit in left)
rightData := page.Data["spells_right"].([]SpellViewModel)
if len(rightData) != 0 {
t.Errorf("Expected 0 spells in right column, got %d", len(rightData))
if len(pages) != 1 {
t.Fatalf("Expected 1 page (capacity %d from template), got %d", totalCapacity, len(pages))
}
page := pages[0]
// Left column should be filled first
leftData := page.Data["spells_left"].([]SpellViewModel)
expectedLeft := leftCapacity
if testCount < leftCapacity {
expectedLeft = testCount
}
if len(leftData) != expectedLeft {
t.Errorf("Expected %d spells in left column (template capacity), got %d", expectedLeft, len(leftData))
}
// Right column gets remainder
rightData := page.Data["spells_right"].([]SpellViewModel)
expectedRight := testCount - expectedLeft
if len(rightData) != expectedRight {
t.Errorf("Expected %d spells in right column (remaining), got %d", expectedRight, len(rightData))
}
}
}
@@ -229,7 +287,24 @@ func TestPaginateSpells_MultiPage(t *testing.T) {
templateSet := DefaultA4QuerTemplateSet()
paginator := NewPaginator(templateSet)
// Create 30 spells - should fit on 1 page (30 capacity = 20 left + 10 right)
// Get spell column capacities from template
var page3Template *TemplateWithMeta
for i := range templateSet.Templates {
if templateSet.Templates[i].Metadata.Name == "page3_spell.html" {
page3Template = &templateSet.Templates[i]
break
}
}
var leftCapacity, rightCapacity int
for i := range page3Template.Metadata.Blocks {
if page3Template.Metadata.Blocks[i].Name == "spells_left" {
leftCapacity = page3Template.Metadata.Blocks[i].MaxItems
} else if page3Template.Metadata.Blocks[i].Name == "spells_right" {
rightCapacity = page3Template.Metadata.Blocks[i].MaxItems
}
}
// Create 30 spells - should span multiple pages based on template capacity
spells := make([]SpellViewModel, 30)
for i := 0; i < 30; i++ {
spells[i] = SpellViewModel{Name: "Spell" + string(rune(i))}
@@ -243,20 +318,22 @@ func TestPaginateSpells_MultiPage(t *testing.T) {
t.Fatalf("Expected no error, got %v", err)
}
// With capacity of 15+10=25 (from template), 30 spells require 2 pages
if len(pages) != 2 {
t.Fatalf("Expected 2 pages, got %d", len(pages))
// With capacity from template, 30 spells require 2 pages
totalCapacity := leftCapacity + rightCapacity
expectedPages := (30 + totalCapacity - 1) / totalCapacity // ceiling division
if len(pages) != expectedPages {
t.Fatalf("Expected %d pages (capacity %d from template), got %d", expectedPages, totalCapacity, len(pages))
}
// Page 1 should have 25 spells (15 left + 10 right)
// Page 1 should have full capacity (left + right)
page1 := pages[0]
leftPage1 := page1.Data["spells_left"].([]SpellViewModel)
rightPage1 := page1.Data["spells_right"].([]SpellViewModel)
if len(leftPage1) != 15 {
t.Errorf("Page 1 left: expected 15 spells (template capacity), got %d", len(leftPage1))
if len(leftPage1) != leftCapacity {
t.Errorf("Page 1 left: expected %d spells (template capacity), got %d", leftCapacity, len(leftPage1))
}
if len(rightPage1) != 10 {
t.Errorf("Page 1 right: expected 10 spells (template capacity), got %d", len(rightPage1))
if len(rightPage1) != rightCapacity {
t.Errorf("Page 1 right: expected %d spells (template capacity), got %d", rightCapacity, len(rightPage1))
}
}
@@ -294,7 +371,30 @@ func TestPaginateWeapons_MultiPage(t *testing.T) {
templateSet := DefaultA4QuerTemplateSet()
paginator := NewPaginator(templateSet)
// Create 50 weapons - should span 3 pages (22 capacity per page from template)
// Get weapon capacity from template
var page2Template *TemplateWithMeta
for i := range templateSet.Templates {
if templateSet.Templates[i].Metadata.Name == "page2_play.html" {
page2Template = &templateSet.Templates[i]
break
}
}
if page2Template == nil {
t.Fatal("page2_play.html template not found")
}
var weaponsBlock *BlockMetadata
for i := range page2Template.Metadata.Blocks {
if page2Template.Metadata.Blocks[i].Name == "weapons_main" {
weaponsBlock = &page2Template.Metadata.Blocks[i]
break
}
}
if weaponsBlock == nil {
t.Fatal("weapons_main block not found")
}
weaponCapacity := weaponsBlock.MaxItems
// Create 50 weapons - should span 3 pages based on template capacity
weapons := make([]WeaponViewModel, 50)
for i := 0; i < 50; i++ {
weapons[i] = WeaponViewModel{Name: "Weapon" + string(rune(i))}
@@ -308,26 +408,24 @@ func TestPaginateWeapons_MultiPage(t *testing.T) {
t.Fatalf("Expected no error, got %v", err)
}
if len(pages) != 3 {
t.Fatalf("Expected 3 pages (22+22+6 from template capacity), got %d", len(pages))
expectedPages := (50 + weaponCapacity - 1) / weaponCapacity // ceiling division
if len(pages) != expectedPages {
t.Fatalf("Expected %d pages (%d capacity from template), got %d", expectedPages, weaponCapacity, len(pages))
}
// Page 1 should have 22 weapons
page1Weapons := pages[0].Data["weapons_main"].([]WeaponViewModel)
if len(page1Weapons) != 22 {
t.Errorf("Page 1: expected 22 weapons (template capacity), got %d", len(page1Weapons))
// First pages should have weaponCapacity weapons each
for i := 0; i < expectedPages-1; i++ {
pageWeapons := pages[i].Data["weapons_main"].([]WeaponViewModel)
if len(pageWeapons) != weaponCapacity {
t.Errorf("Page %d: expected %d weapons (template capacity), got %d", i+1, weaponCapacity, len(pageWeapons))
}
}
// Page 2 should have 22 weapons
page2Weapons := pages[1].Data["weapons_main"].([]WeaponViewModel)
if len(page2Weapons) != 22 {
t.Errorf("Page 2: expected 22 weapons (template capacity), got %d", len(page2Weapons))
}
// Page 3 should have 6 weapons (remaining)
page3Weapons := pages[2].Data["weapons_main"].([]WeaponViewModel)
if len(page3Weapons) != 6 {
t.Errorf("Page 3: expected 6 weapons (remaining), got %d", len(page3Weapons))
// Last page should have remaining weapons
remainingWeapons := 50 - ((expectedPages - 1) * weaponCapacity)
lastPageWeapons := pages[expectedPages-1].Data["weapons_main"].([]WeaponViewModel)
if len(lastPageWeapons) != remainingWeapons {
t.Errorf("Last page: expected %d weapons (remaining), got %d", remainingWeapons, len(lastPageWeapons))
}
}
@@ -336,6 +434,50 @@ func TestCalculatePagesNeeded(t *testing.T) {
templateSet := DefaultA4QuerTemplateSet()
paginator := NewPaginator(templateSet)
// Get capacities from templates
var page1Template, page2Template, page3Template *TemplateWithMeta
for i := range templateSet.Templates {
switch templateSet.Templates[i].Metadata.Name {
case "page1_stats.html":
page1Template = &templateSet.Templates[i]
case "page2_play.html":
page2Template = &templateSet.Templates[i]
case "page3_spell.html":
page3Template = &templateSet.Templates[i]
}
}
// Get skill capacity (col1 + col2)
var skillCol1, skillCol2 int
for i := range page1Template.Metadata.Blocks {
if page1Template.Metadata.Blocks[i].Name == "skills_column1" {
skillCol1 = page1Template.Metadata.Blocks[i].MaxItems
} else if page1Template.Metadata.Blocks[i].Name == "skills_column2" {
skillCol2 = page1Template.Metadata.Blocks[i].MaxItems
}
}
skillCapacity := skillCol1 + skillCol2
// Get weapon capacity
var weaponCapacity int
for i := range page2Template.Metadata.Blocks {
if page2Template.Metadata.Blocks[i].Name == "weapons_main" {
weaponCapacity = page2Template.Metadata.Blocks[i].MaxItems
break
}
}
// Get spell capacity (col1 + col2)
var spellCol1, spellCol2 int
for i := range page3Template.Metadata.Blocks {
if page3Template.Metadata.Blocks[i].Name == "spells_left" {
spellCol1 = page3Template.Metadata.Blocks[i].MaxItems
} else if page3Template.Metadata.Blocks[i].Name == "spells_right" {
spellCol2 = page3Template.Metadata.Blocks[i].MaxItems
}
}
spellCapacity := spellCol1 + spellCol2
testCases := []struct {
name string
templateName string
@@ -344,15 +486,15 @@ func TestCalculatePagesNeeded(t *testing.T) {
expectedPages int
}{
{"10 skills on page1", "page1_stats.html", "skills", 10, 1},
{"58 skills on page1", "page1_stats.html", "skills", 58, 1}, // 29+29 = 58 fits on 1 page
{"59 skills on page1", "page1_stats.html", "skills", 59, 2}, // 59 requires 2 pages
{fmt.Sprintf("%d skills on page1", skillCapacity), "page1_stats.html", "skills", skillCapacity, 1},
{fmt.Sprintf("%d skills on page1", skillCapacity+1), "page1_stats.html", "skills", skillCapacity + 1, 2},
{"100 skills on page1", "page1_stats.html", "skills", 100, 2},
{"10 weapons on page2", "page2_play.html", "weapons", 10, 1},
{"22 weapons on page2", "page2_play.html", "weapons", 22, 1}, // MAX:22 from template
{"23 weapons on page2", "page2_play.html", "weapons", 23, 2}, // exceeds capacity
{fmt.Sprintf("%d weapons on page2", weaponCapacity), "page2_play.html", "weapons", weaponCapacity, 1},
{fmt.Sprintf("%d weapons on page2", weaponCapacity+1), "page2_play.html", "weapons", weaponCapacity + 1, 2},
{"10 spells on page3", "page3_spell.html", "spells", 10, 1},
{"25 spells on page3", "page3_spell.html", "spells", 25, 1}, // 15+10 = 25 fits on 1 page (from template)
{"26 spells on page3", "page3_spell.html", "spells", 26, 2}, // 26 requires 2 pages
{fmt.Sprintf("%d spells on page3", spellCapacity), "page3_spell.html", "spells", spellCapacity, 1},
{fmt.Sprintf("%d spells on page3", spellCapacity+1), "page3_spell.html", "spells", spellCapacity + 1, 2},
}
for _, tc := range testCases {
@@ -1,6 +1,7 @@
package pdfrender
import (
"os"
"testing"
)
@@ -34,7 +35,25 @@ func TestLoadTemplateSetFromFiles(t *testing.T) {
t.Error("Expected blocks in page1 metadata")
}
// Verify skills_column1 block
// Verify skills_column1 block - read expected value directly from template file
templateContent, err := os.ReadFile("../templates/Default_A4_Quer/page1_stats.html")
if err != nil {
t.Fatalf("Failed to read template file: %v", err)
}
expectedBlocks := ParseTemplateMetadata(string(templateContent))
var expectedSkillsCol1 *BlockMetadata
for i := range expectedBlocks {
if expectedBlocks[i].Name == "skills_column1" {
expectedSkillsCol1 = &expectedBlocks[i]
break
}
}
if expectedSkillsCol1 == nil {
t.Fatal("skills_column1 block not found in template file")
}
var skillsCol1 *BlockMetadata
for i := range page1.Metadata.Blocks {
if page1.Metadata.Blocks[i].Name == "skills_column1" {
@@ -47,8 +66,8 @@ func TestLoadTemplateSetFromFiles(t *testing.T) {
t.Error("skills_column1 block not found")
} else {
// Should match the MAX value in the template comment
if skillsCol1.MaxItems != 29 {
t.Errorf("Expected skills_column1 MaxItems 29 (from template), got %d", skillsCol1.MaxItems)
if skillsCol1.MaxItems != expectedSkillsCol1.MaxItems {
t.Errorf("Expected skills_column1 MaxItems %d (from template), got %d", expectedSkillsCol1.MaxItems, skillsCol1.MaxItems)
}
if skillsCol1.ListType != "skills" {
t.Errorf("Expected ListType 'skills', got '%s'", skillsCol1.ListType)
@@ -65,7 +84,25 @@ func TestDefaultA4QuerTemplateSet_LoadsFromFiles(t *testing.T) {
}
// Verify metadata comes from template files, not hardcoded
// Check page3_spell.html spells_left should be 20 (from template)
// Read expected value directly from template file
templateContent, err := os.ReadFile("../templates/Default_A4_Quer/page3_spell.html")
if err != nil {
t.Fatalf("Failed to read template file: %v", err)
}
expectedBlocks := ParseTemplateMetadata(string(templateContent))
var expectedSpellsLeft *BlockMetadata
for i := range expectedBlocks {
if expectedBlocks[i].Name == "spells_left" {
expectedSpellsLeft = &expectedBlocks[i]
break
}
}
if expectedSpellsLeft == nil {
t.Fatal("spells_left block not found in template file")
}
var page3 *TemplateWithMeta
for i := range templateSet.Templates {
if templateSet.Templates[i].Metadata.Name == "page3_spell.html" {
@@ -89,9 +126,9 @@ func TestDefaultA4QuerTemplateSet_LoadsFromFiles(t *testing.T) {
if spellsLeft == nil {
t.Error("spells_left block not found")
} else {
// Should be 15 from the template file (<!-- BLOCK: spells_left, TYPE: spells, MAX: 15 -->)
if spellsLeft.MaxItems != 15 {
t.Errorf("Expected spells_left MaxItems 15 (from template file), got %d", spellsLeft.MaxItems)
// Should match the value from the template file
if spellsLeft.MaxItems != expectedSpellsLeft.MaxItems {
t.Errorf("Expected spells_left MaxItems %d (from template file), got %d", expectedSpellsLeft.MaxItems, spellsLeft.MaxItems)
}
}
}
+29 -6
View File
@@ -82,23 +82,46 @@ func TestGetTemplateMetadata(t *testing.T) {
t.Fatal("Expected metadata blocks, got none")
}
// Check for spells_left block (template says MAX: 15)
// Read actual template to get expected MAX values
templateSet := DefaultA4QuerTemplateSet()
var page3Template *TemplateWithMeta
for i := range templateSet.Templates {
if templateSet.Templates[i].Metadata.Name == "page3_spell.html" {
page3Template = &templateSet.Templates[i]
break
}
}
if page3Template == nil {
t.Fatal("page3_spell.html template not found")
}
// Get expected values from template
var expectedLeftMax, expectedRightMax int
for i := range page3Template.Metadata.Blocks {
if page3Template.Metadata.Blocks[i].Name == "spells_left" {
expectedLeftMax = page3Template.Metadata.Blocks[i].MaxItems
} else if page3Template.Metadata.Blocks[i].Name == "spells_right" {
expectedRightMax = page3Template.Metadata.Blocks[i].MaxItems
}
}
// Check for spells_left block
leftBlock := GetBlockByName(metadata, "spells_left")
if leftBlock == nil {
t.Error("Expected to find 'spells_left' block")
} else {
if leftBlock.MaxItems != 15 {
t.Errorf("Expected spells_left max 15 (from template), got %d", leftBlock.MaxItems)
if leftBlock.MaxItems != expectedLeftMax {
t.Errorf("Expected spells_left max %d (from template), got %d", expectedLeftMax, leftBlock.MaxItems)
}
}
// Check for spells_right block (template says MAX: 10)
// Check for spells_right block
rightBlock := GetBlockByName(metadata, "spells_right")
if rightBlock == nil {
t.Error("Expected to find 'spells_right' block")
} else {
if rightBlock.MaxItems != 10 {
t.Errorf("Expected spells_right max 10 (from template), got %d", rightBlock.MaxItems)
if rightBlock.MaxItems != expectedRightMax {
t.Errorf("Expected spells_right max %d (from template), got %d", expectedRightMax, rightBlock.MaxItems)
}
}
}
+47 -5
View File
@@ -1,5 +1,47 @@
* weapons_main list currently uses Waffenfertigkeiten (weapon skills).
NOTE: Equipment.Weapons (EqWaffe) contains physical weapons with metadata like Abwb/Schb.
Waffenfertigkeiten already provides EW (Fertigkeitswert) which is the skill value needed for the character sheet.
Current implementation is correct - weapons_main shows weapon skills with their EW values.
* The implementation is NOT correct, because the eapons_main shows weapon skills. But it should show the weapons list from the equipment
## COMPLETED
* ✅ Page 2 Weapons Table now shows the following information:
- For each Weapon a character has:
- Its name
- ✅ the Fertigkeitswert (EW): Waffenfertigkeit.Fertigkeitswert + Character.AngriffBonus + Weapon.Anb (if equipped)
- ❌ TODO: the Schaden (Damage) including character's SchadenBonus and weapon's Schadensbonus
- ❌ TODO: If it is a ranged weapon, the ranges for near, medium and far
Implementation notes:
- ✅ AngriffBonus and SchadenBonus are now calculated in DerivedValueSet from character attributes
- ✅ mapWeapons() function now:
- Calculates EW = Waffenfertigkeit.Fertigkeitswert + Character.AngriffBonus + Weapon.Anb
- Matches Waffenfertigkeiten with equipped Waffen by name
- Adds weapon attack bonus (Anb) if weapon is equipped
- ✅ Added test TestMapWeapons_WithEWCalculation to verify correct EW calculation
- ✅ Used TDD approach: wrote failing test first, then implemented solution
* ✅ Template MaxItems expectations are now dynamic:
- Tests now read MaxItems values directly from template HTML comments
- Updated TestLoadTemplateSetFromFiles to parse templates dynamically
- Updated TestDefaultA4QuerTemplateSet_LoadsFromFiles to use dynamic expectations
- Updated TestPaginationUsesTemplateMetadata to read from template files
- Tests will automatically adapt when template capacities change
## TODO (Remaining)
* Page 2 Weapons - Damage calculation:
- Calculate and display weapon damage including:
- Base weapon damage (from models.Weapon)
- Character's SchadenBonus
- Weapon's Schadensbonus (Schb from EqWaffe)
- Format: e.g., "1W6+3" where +3 = SchadenBonus + Schb
* Page 2 Weapons - Ranged weapon ranges:
- For ranged weapons (Bogen, Armbrust, Wurfwaffe), show:
- Range for "Nah" (near)
- Range for "Mittel" (medium)
- Range for "Fern" (far)
- This data should come from models.Weapon.Range or similar field
.
* add 3 field to gsm_weapons that holds the ranges near, middle, far all values are measured in meters so integer seems to be a good datatype. If at least 1 of 3 values is > 0 the weapon is treated as a ranged weapon
* add a field to gsm_weapons that holds the damage the weapon creates Damage is notated as 2W6+3
* EqWaffe already contains the values for the bunus values we need Attack (Anb), Damage (Schb) und Defence (Abwb)
* currently the template fetched for rendering is set to Default_A4_Quer
+48 -16
View File
@@ -2,11 +2,23 @@ package pdfrender
import (
"bamort/models"
"os"
"testing"
)
// TestPaginationUsesTemplateMetadata verifies tests use actual template MAX values
func TestPaginationUsesTemplateMetadata(t *testing.T) {
// Read expected values directly from template file
templateContent, err := os.ReadFile("../templates/Default_A4_Quer/page2_play.html")
if err != nil {
t.Fatalf("Failed to read template file: %v", err)
}
expectedBlocks := ParseTemplateMetadata(string(templateContent))
expectedSkillsLearned := GetBlockByName(expectedBlocks, "skills_learned")
expectedSkillsLanguages := GetBlockByName(expectedBlocks, "skills_languages")
expectedWeaponsMain := GetBlockByName(expectedBlocks, "weapons_main")
// Load template set from actual files
templateSet := DefaultA4QuerTemplateSet()
@@ -28,28 +40,39 @@ func TestPaginationUsesTemplateMetadata(t *testing.T) {
if skillsLearned == nil {
t.Fatal("skills_learned block not found")
}
if skillsLearned.MaxItems != 17 {
t.Errorf("skills_learned: expected MAX 17 from template, got %d", skillsLearned.MaxItems)
if expectedSkillsLearned != nil && skillsLearned.MaxItems != expectedSkillsLearned.MaxItems {
t.Errorf("skills_learned: expected MAX %d from template, got %d", expectedSkillsLearned.MaxItems, skillsLearned.MaxItems)
}
skillsLanguages := GetBlockByName(page2.Metadata.Blocks, "skills_languages")
if skillsLanguages == nil {
t.Fatal("skills_languages block not found")
}
if skillsLanguages.MaxItems != 4 {
t.Errorf("skills_languages: expected MAX 4 from template, got %d", skillsLanguages.MaxItems)
if expectedSkillsLanguages != nil && skillsLanguages.MaxItems != expectedSkillsLanguages.MaxItems {
t.Errorf("skills_languages: expected MAX %d from template, got %d", expectedSkillsLanguages.MaxItems, skillsLanguages.MaxItems)
}
weaponsMain := GetBlockByName(page2.Metadata.Blocks, "weapons_main")
if weaponsMain == nil {
t.Fatal("weapons_main block not found")
}
if weaponsMain.MaxItems != 22 {
t.Errorf("weapons_main: expected MAX 22 from template, got %d", weaponsMain.MaxItems)
if expectedWeaponsMain != nil && weaponsMain.MaxItems != expectedWeaponsMain.MaxItems {
t.Errorf("weapons_main: expected MAX %d from template, got %d", expectedWeaponsMain.MaxItems, weaponsMain.MaxItems)
}
}
func TestPage2PaginationWithCorrectCapacities(t *testing.T) {
// Read expected values directly from template file
templateContent, err := os.ReadFile("../templates/Default_A4_Quer/page2_play.html")
if err != nil {
t.Fatalf("Failed to read template file: %v", err)
}
expectedBlocks := ParseTemplateMetadata(string(templateContent))
expectedSkillsLearned := GetBlockByName(expectedBlocks, "skills_learned")
expectedSkillsLanguages := GetBlockByName(expectedBlocks, "skills_languages")
expectedWeaponsMain := GetBlockByName(expectedBlocks, "weapons_main")
// Create test data
viewModel := &CharacterSheetViewModel{
Skills: []SkillViewModel{
@@ -69,21 +92,30 @@ func TestPage2PaginationWithCorrectCapacities(t *testing.T) {
t.Fatalf("Failed to prepare page data: %v", err)
}
// Verify capacities match template (18, 5, 24)
if len(pageData.SkillsLearned) != 17 {
t.Errorf("SkillsLearned should be filled to 17, got %d", len(pageData.SkillsLearned))
// Verify capacities match template values
if expectedSkillsLearned != nil && len(pageData.SkillsLearned) != expectedSkillsLearned.MaxItems {
t.Errorf("SkillsLearned should be filled to %d (from template), got %d", expectedSkillsLearned.MaxItems, len(pageData.SkillsLearned))
}
if len(pageData.SkillsLanguage) != 4 {
t.Errorf("SkillsLanguage should be filled to 4, got %d", len(pageData.SkillsLanguage))
if expectedSkillsLanguages != nil && len(pageData.SkillsLanguage) != expectedSkillsLanguages.MaxItems {
t.Errorf("SkillsLanguage should be filled to %d (from template), got %d", expectedSkillsLanguages.MaxItems, len(pageData.SkillsLanguage))
}
if len(pageData.Weapons) != 22 {
t.Errorf("Weapons should be filled to 22, got %d", len(pageData.Weapons))
if expectedWeaponsMain != nil && len(pageData.Weapons) != expectedWeaponsMain.MaxItems {
t.Errorf("Weapons should be filled to %d (from template), got %d", expectedWeaponsMain.MaxItems, len(pageData.Weapons))
}
}
func TestPage3MagicItemsCapacity(t *testing.T) {
// Read expected values directly from template file
templateContent, err := os.ReadFile("../templates/Default_A4_Quer/page3_spell.html")
if err != nil {
t.Fatalf("Failed to read template file: %v", err)
}
expectedBlocks := ParseTemplateMetadata(string(templateContent))
expectedMagicItems := GetBlockByName(expectedBlocks, "magic_items")
// Create test data with magic items
viewModel := &CharacterSheetViewModel{
MagicItems: []MagicItemViewModel{
@@ -100,9 +132,9 @@ func TestPage3MagicItemsCapacity(t *testing.T) {
t.Fatalf("Failed to prepare page data: %v", err)
}
// Template says MAX: 8 for magic_items
if len(pageData.MagicItems) != 5 {
t.Errorf("MagicItems should be filled to 5, got %d", len(pageData.MagicItems))
// Verify capacity matches template
if expectedMagicItems != nil && len(pageData.MagicItems) != expectedMagicItems.MaxItems {
t.Errorf("MagicItems should be filled to %d (from template), got %d", expectedMagicItems.MaxItems, len(pageData.MagicItems))
}
}