import { describe, expect, it } from 'vitest'; import { buildPier } from '../src/core/geometry'; import { loadP1 } from './helpers'; import { adjustRow, defaultRowCounts, layoutText, maxCount, resolveRowCounts, rowLayout, totalPiles, type PileGeometry } from '../src/core/piles'; // Footing 10400 × 8150, pile end 700, minimum centre spacing 2250 (spec §4.6, §3). const G: PileGeometry = { footingLength: 10.4, footingWidth: 8.15, end: 0.7, minSpacing: 2.25 }; describe('말뚝 열별 본수 (spec §4.6, plan C4)', () => { it('defaults give 4 열 × 5 본 = 20 for the P1 footing, and the 1단계 text is kept', () => { expect(defaultRowCounts(G)).toEqual([5, 5, 5, 5]); expect(totalPiles(defaultRowCounts(G))).toBe(20); expect(layoutText(defaultRowCounts(G), G)).toBe('4@2250 × 3@2250 = 20본'); }); it('a 열 with 6 본 gives 5 gaps at 1800 mm, as in the plan (warning, not refused)', () => { const counts = adjustRow(defaultRowCounts(G), 0, 1); expect(counts[0]).toBe(6); const layout = rowLayout(counts, G); expect(Math.round(layout[0].spacing * 1000)).toBe(1800); expect(layout[0].belowMinimum).toBe(true); expect(layout[1].belowMinimum).toBe(false); expect(layoutText(counts, G)).toBe('열1 5@1800 · 열2 4@2250 · 열3 4@2250 · 열4 4@2250 = 21본'); }); it('the total follows the counts: − and + change it by one per 열', () => { expect(totalPiles(adjustRow(defaultRowCounts(G), 2, -1))).toBe(19); expect(totalPiles(adjustRow(defaultRowCounts(G), 2, 1))).toBe(21); }); it('counts are kept within the limits when adjusting', () => { expect(adjustRow([2, 2, 2, 2], 0, -1)[0]).toBe(2); expect(adjustRow([30, 2, 2, 2], 0, 1)[0]).toBe(30); }); it('refuses a wrong number of 열 and a count that is not a whole number in range', () => { expect(() => resolveRowCounts([5, 5, 5], G)).toThrow('4개여야'); expect(() => resolveRowCounts([5, 5, 1, 5], G)).toThrow('2–30'); expect(() => resolveRowCounts([5, 5, 5.5, 5], G)).toThrow('정수'); expect(resolveRowCounts([6, 5, 5, 5], G)).toEqual([6, 5, 5, 5]); }); it('the default count matches the spec formula floor((L − 1400) / 2250) + 1', () => { expect(maxCount(10.4, G)).toBe(5); expect(maxCount(17.6, G)).toBe(8); }); }); describe('열별 본수 in the model (spec §4.6)', () => { it('buildPier uses the per-row counts: 6 + 5 + 5 + 5 = 21 piles, and the text follows', () => { const p1 = loadP1(); const m = buildPier({ ...p1, pileRows: [6, 5, 5, 5] }); expect(m.piles).toHaveLength(21); expect(m.derived.pileLayout).toBe('열1 5@1800 · 열2 4@2250 · 열3 4@2250 · 열4 4@2250 = 21본'); }); it('absent 열별 본수 gives the 1단계 result: 20 piles', () => { expect(buildPier(loadP1()).piles).toHaveLength(20); }); it('a wrong number of 열 is refused by buildPier', () => { expect(() => buildPier({ ...loadP1(), pileRows: [5, 5, 5] })).toThrow('4개여야'); }); });