import { describe, expect, test } from 'bun:test' import { CeilingNode, SlabNode, WallNode } from '../schema' import { planAutoCeilingsForLevel, planAutoSlabsForLevel } from './space-detection' const square: Array<[number, number]> = [ [0, 0], [4, 0], [4, 3], [0, 3], ] function roomPolygon() { return square.map(([x, y]) => ({ x, y })) } function squareWalls(height = 2.5) { return [ WallNode.parse({ start: [0, 0], end: [4, 0], height }), WallNode.parse({ start: [4, 0], end: [4, 3], height }), WallNode.parse({ start: [4, 3], end: [0, 3], height }), WallNode.parse({ start: [0, 3], end: [0, 0], height }), ] } function slab(elevation: number) { return SlabNode.parse({ polygon: square, elevation, autoFromWalls: true, }) } describe('planAutoCeilingsForLevel', () => { test('creates auto ceilings at the top of the room walls', () => { const created = planAutoCeilingsForLevel([roomPolygon()], [], { walls: squareWalls(), slabs: [slab(0.05)], }).create[0] expect(created?.height).toBeCloseTo(2.55) }) test('updates existing auto ceiling height when the slab elevation changes', () => { const ceiling = CeilingNode.parse({ polygon: square, height: 2.55, autoFromWalls: true, }) const plan = planAutoCeilingsForLevel([roomPolygon()], [ceiling], { walls: squareWalls(), slabs: [slab(0.4)], }) expect(plan.update).toHaveLength(1) expect(plan.update[0]?.id).toBe(ceiling.id) expect(plan.update[0]?.data.polygon).toBeUndefined() expect(plan.update[0]?.data.height).toBeCloseTo(2.9) }) test('updates existing auto ceiling height when wall height changes', () => { const ceiling = CeilingNode.parse({ polygon: square, height: 2.55, autoFromWalls: true, }) const plan = planAutoCeilingsForLevel([roomPolygon()], [ceiling], { walls: squareWalls(3), slabs: [slab(0.05)], }) expect(plan.update).toHaveLength(1) expect(plan.update[0]?.data.height).toBeCloseTo(3.05) }) test('does not replace a manual ceiling with an auto ceiling', () => { const manualCeiling = CeilingNode.parse({ polygon: square, height: 2.5, autoFromWalls: false, }) const plan = planAutoCeilingsForLevel([roomPolygon()], [manualCeiling], { walls: squareWalls(), slabs: [slab(0.4)], }) expect(plan.create).toHaveLength(0) expect(plan.update).toHaveLength(0) }) }) describe('planAutoSlabsForLevel', () => { test('matches two identical rooms to their own existing auto-slabs without churn', () => { // Two rooms with identical polygon signatures previously collided in a // signature-keyed Map, so one detected room never matched an existing slab // and churned (delete + recreate) on every pass. const slabA = slab(0.05) const slabB = slab(0.05) const plan = planAutoSlabsForLevel([roomPolygon(), roomPolygon()], [slabA, slabB]) expect(plan.create).toHaveLength(0) expect(plan.delete).toHaveLength(0) expect(plan.update).toHaveLength(0) }) test('deletes an extra auto-slab when only one identical room is detected', () => { const plan = planAutoSlabsForLevel([roomPolygon()], [slab(0.05), slab(0.05)]) expect(plan.create).toHaveLength(0) expect(plan.delete).toHaveLength(1) }) })