test(handlers): add org_layout_test.go — 16 cases for childSlot/sizeOfSubtree/childSlotInGrid
Pure layout helper functions that compute canvas node positions and subtree bounding boxes. Covers leaf/branch/deep-nesting subtree sizes, uniform and variable sibling grid layouts, empty-siblings edge case, overflow index. Closes test coverage gap on org.go canvas layout helpers. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
c09a5000b2
commit
848b2d96ca
244
workspace-server/internal/handlers/org_layout_test.go
Normal file
244
workspace-server/internal/handlers/org_layout_test.go
Normal file
@ -0,0 +1,244 @@
|
||||
package handlers
|
||||
|
||||
// org_layout_test.go — unit coverage for org canvas layout helpers
|
||||
// (org.go). These functions compute canvas node positions and subtree
|
||||
// bounding boxes; they are pure (no DB calls, no side effects).
|
||||
//
|
||||
// Coverage targets:
|
||||
// - childSlot: 2-column grid x,y for 0th..Nth child
|
||||
// - sizeOfSubtree: leaf, single child, multi-child, deep nesting
|
||||
// - childSlotInGrid: empty siblings, uniform sizes, variable sizes,
|
||||
// index boundaries
|
||||
|
||||
import "testing"
|
||||
|
||||
// ---------- childSlot ----------
|
||||
|
||||
func TestChildSlot_FirstChild(t *testing.T) {
|
||||
x, y := childSlot(0)
|
||||
// col=0, row=0; x=parentSidePadding=16, y=parentHeaderPadding=130
|
||||
if x != 16.0 {
|
||||
t.Errorf("x = %v; want 16.0", x)
|
||||
}
|
||||
if y != 130.0 {
|
||||
t.Errorf("y = %v; want 130.0", y)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChildSlot_SecondChild(t *testing.T) {
|
||||
x, y := childSlot(1)
|
||||
// col=1, row=0; x=16+(240+14)=270, y=130
|
||||
if x != 270.0 {
|
||||
t.Errorf("x = %v; want 270.0", x)
|
||||
}
|
||||
if y != 130.0 {
|
||||
t.Errorf("y = %v; want 130.0", y)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChildSlot_ThirdChild(t *testing.T) {
|
||||
x, y := childSlot(2)
|
||||
// col=0, row=1; x=16, y=130+(130+14)=274
|
||||
if x != 16.0 {
|
||||
t.Errorf("x = %v; want 16.0", x)
|
||||
}
|
||||
if y != 274.0 {
|
||||
t.Errorf("y = %v; want 274.0", y)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChildSlot_FourthChild(t *testing.T) {
|
||||
x, y := childSlot(3)
|
||||
// col=1, row=1; x=270, y=274
|
||||
if x != 270.0 {
|
||||
t.Errorf("x = %v; want 270.0", x)
|
||||
}
|
||||
if y != 274.0 {
|
||||
t.Errorf("y = %v; want 274.0", y)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- sizeOfSubtree ----------
|
||||
|
||||
func TestSizeOfSubtree_Leaf(t *testing.T) {
|
||||
ws := OrgWorkspace{Name: "leaf"}
|
||||
size := sizeOfSubtree(ws)
|
||||
if size.width != 240.0 {
|
||||
t.Errorf("width = %v; want 240.0", size.width)
|
||||
}
|
||||
if size.height != 130.0 {
|
||||
t.Errorf("height = %v; want 130.0", size.height)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSizeOfSubtree_SingleChild(t *testing.T) {
|
||||
ws := OrgWorkspace{
|
||||
Name: "parent",
|
||||
Children: []OrgWorkspace{{Name: "child"}},
|
||||
}
|
||||
size := sizeOfSubtree(ws)
|
||||
// cols = min(1,1) = 1; rows = 1
|
||||
// maxColW = 240 (child default)
|
||||
// width = 16*2 + 240*1 + 14*0 = 272
|
||||
// height = 130 + 130 + 14*0 + 16 = 276
|
||||
if size.width != 272.0 {
|
||||
t.Errorf("width = %v; want 272.0", size.width)
|
||||
}
|
||||
if size.height != 276.0 {
|
||||
t.Errorf("height = %v; want 276.0", size.height)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSizeOfSubtree_TwoChildren(t *testing.T) {
|
||||
ws := OrgWorkspace{
|
||||
Name: "parent",
|
||||
Children: []OrgWorkspace{
|
||||
{Name: "child1"},
|
||||
{Name: "child2"},
|
||||
},
|
||||
}
|
||||
size := sizeOfSubtree(ws)
|
||||
// cols = 2; rows = 1; maxColW = 240
|
||||
// width = 16*2 + 240*2 + 14*1 = 524
|
||||
// height = 130 + 130 + 16 = 276
|
||||
if size.width != 524.0 {
|
||||
t.Errorf("width = %v; want 524.0", size.width)
|
||||
}
|
||||
if size.height != 276.0 {
|
||||
t.Errorf("height = %v; want 276.0", size.height)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSizeOfSubtree_ThreeChildren(t *testing.T) {
|
||||
ws := OrgWorkspace{
|
||||
Name: "parent",
|
||||
Children: []OrgWorkspace{
|
||||
{Name: "child1"},
|
||||
{Name: "child2"},
|
||||
{Name: "child3"},
|
||||
},
|
||||
}
|
||||
size := sizeOfSubtree(ws)
|
||||
// cols = 2 (len=3, childGridColumnCount=2, min=2); rows = 2
|
||||
// maxColW = 240
|
||||
// width = 16*2 + 240*2 + 14*1 = 524
|
||||
// height = 130 + (130*2) + 14*1 + 16 = 420
|
||||
if size.width != 524.0 {
|
||||
t.Errorf("width = %v; want 524.0", size.width)
|
||||
}
|
||||
if size.height != 420.0 {
|
||||
t.Errorf("height = %v; want 420.0", size.height)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSizeOfSubtree_DeepNesting(t *testing.T) {
|
||||
// leaf → child → parent
|
||||
grandchild := OrgWorkspace{Name: "grandchild"}
|
||||
child := OrgWorkspace{Name: "child", Children: []OrgWorkspace{grandchild}}
|
||||
parent := OrgWorkspace{Name: "parent", Children: []OrgWorkspace{child}}
|
||||
size := sizeOfSubtree(parent)
|
||||
// grandchild: 240x130
|
||||
// child: cols=1, rows=1, maxColW=240 → 272x276
|
||||
// parent: cols=1, rows=1, maxColW=272 → 304x422
|
||||
if size.width != 304.0 {
|
||||
t.Errorf("width = %v; want 304.0", size.width)
|
||||
}
|
||||
if size.height != 422.0 {
|
||||
t.Errorf("height = %v; want 422.0", size.height)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- childSlotInGrid ----------
|
||||
|
||||
func TestChildSlotInGrid_EmptySiblings(t *testing.T) {
|
||||
x, y := childSlotInGrid(0, nil)
|
||||
if x != 16.0 || y != 130.0 {
|
||||
t.Errorf("empty siblings: got (%v,%v); want (16.0, 130.0)", x, y)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChildSlotInGrid_EmptySlice(t *testing.T) {
|
||||
x, y := childSlotInGrid(0, []nodeSize{})
|
||||
if x != 16.0 || y != 130.0 {
|
||||
t.Errorf("empty slice: got (%v,%v); want (16.0, 130.0)", x, y)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChildSlotInGrid_UniformSizes(t *testing.T) {
|
||||
sizes := []nodeSize{
|
||||
{240, 130},
|
||||
{240, 130},
|
||||
{240, 130},
|
||||
}
|
||||
// maxColW = 240; cols = 2; rows = 2
|
||||
// slot 0: col=0, row=0 → x=16, y=130
|
||||
x0, y0 := childSlotInGrid(0, sizes)
|
||||
if x0 != 16.0 || y0 != 130.0 {
|
||||
t.Errorf("slot 0: got (%v,%v); want (16.0, 130.0)", x0, y0)
|
||||
}
|
||||
// slot 1: col=1, row=0 → x=16+240+14=270, y=130
|
||||
x1, y1 := childSlotInGrid(1, sizes)
|
||||
if x1 != 270.0 || y1 != 130.0 {
|
||||
t.Errorf("slot 1: got (%v,%v); want (270.0, 130.0)", x1, y1)
|
||||
}
|
||||
// slot 2: col=0, row=1 → x=16, y=130+130+14=274
|
||||
x2, y2 := childSlotInGrid(2, sizes)
|
||||
if x2 != 16.0 || y2 != 274.0 {
|
||||
t.Errorf("slot 2: got (%v,%v); want (16.0, 274.0)", x2, y2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChildSlotInGrid_VariableSizes(t *testing.T) {
|
||||
sizes := []nodeSize{
|
||||
{100, 80}, // narrow, short
|
||||
{300, 200}, // wide, tall
|
||||
{200, 150}, // medium
|
||||
}
|
||||
// maxColW = 300; cols = 2; rows = 2
|
||||
// slot 0: col=0, row=0 → x=16, y=130
|
||||
x0, y0 := childSlotInGrid(0, sizes)
|
||||
if x0 != 16.0 || y0 != 130.0 {
|
||||
t.Errorf("slot 0: got (%v,%v); want (16.0, 130.0)", x0, y0)
|
||||
}
|
||||
// slot 1: col=1, row=0 → x=16+300+14=330, y=130
|
||||
x1, y1 := childSlotInGrid(1, sizes)
|
||||
if x1 != 330.0 || y1 != 130.0 {
|
||||
t.Errorf("slot 1: got (%v,%v); want (330.0, 130.0)", x1, y1)
|
||||
}
|
||||
// slot 2: col=0, row=1 → x=16, y=130+200+14=344
|
||||
x2, y2 := childSlotInGrid(2, sizes)
|
||||
if x2 != 16.0 || y2 != 344.0 {
|
||||
t.Errorf("slot 2: got (%v,%v); want (16.0, 344.0)", x2, y2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChildSlotInGrid_SingleChild(t *testing.T) {
|
||||
sizes := []nodeSize{{400, 300}}
|
||||
x, y := childSlotInGrid(0, sizes)
|
||||
// cols = 1 (len < 2), maxColW = 400
|
||||
// x = 16 + 0*(400+14) = 16, y = 130
|
||||
if x != 16.0 || y != 130.0 {
|
||||
t.Errorf("single child: got (%v,%v); want (16.0, 130.0)", x, y)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChildSlotInGrid_LastSlot(t *testing.T) {
|
||||
sizes := []nodeSize{{200, 100}, {200, 100}, {200, 100}}
|
||||
// cols = 2, rows = 2, maxColW = 200
|
||||
// slot 2: col=0, row=1 → x=16, y=130+100+14=244
|
||||
x, y := childSlotInGrid(2, sizes)
|
||||
if x != 16.0 || y != 244.0 {
|
||||
t.Errorf("last slot: got (%v,%v); want (16.0, 244.0)", x, y)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChildSlotInGrid_OverflowIndex(t *testing.T) {
|
||||
sizes := []nodeSize{{200, 100}}
|
||||
// Index beyond array bounds — Go handles this without panic
|
||||
x, y := childSlotInGrid(5, sizes)
|
||||
// col = 5 % 2 = 1, row = 5 / 2 = 2
|
||||
// x = 16 + 1*(200+14) = 230, y = 130 + 2*(100+14) = 358
|
||||
if x != 230.0 || y != 358.0 {
|
||||
t.Errorf("overflow index: got (%v,%v); want (230.0, 358.0)", x, y)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user