import { readFileSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import * as XLSX from 'xlsx'; import { describe, expect, it } from 'vitest'; import { applyAbutEdit, applyBearingEdit, restoreAbutDefault, setWingForm, applyWingDim, copyWingDims } from '../src/core/abut/edit'; import { parseAbutments } from '../src/core/abut/parse'; const RAG = fileURLToPath(new URL('../../../RAG/운곡교P1입력.xlsx', import.meta.url)); const rec = parseAbutments(XLSX.read(readFileSync(RAG), { type: 'buffer' })).abutments[0]; describe('교대 입력 편집 (교대 편집 카드)', () => { it('a valid change returns a new record and leaves the old one unchanged', () => { const next = applyAbutEdit(rec, 'groundEl', 34.5); expect(next.groundEl).toBe(34.5); expect(rec.groundEl).toBeCloseTo(33.984, 3); }); it('a non-number, a pile length of 0 or a skew out of range are refused with the field name', () => { expect(() => applyAbutEdit(rec, 'seatEl', NaN)).toThrow('교좌면 EL'); expect(() => applyAbutEdit(rec, 'pileLengthMm', 0)).toThrow('말뚝길이'); expect(() => applyAbutEdit(rec, 'skewDeg', 200)).toThrow('skew'); }); it('the 교좌면 EL must stay above the 기초 하단 EL', () => { expect(() => applyAbutEdit(rec, 'seatEl', rec.foundationBottomEl)).toThrow('기초 하단 EL보다 높아야'); }); it('a wing form change sets one side and keeps the other (좌 · 우 따로)', () => { expect(setWingForm(rec, 'left', 'short').wings).toEqual({ left: 'short', right: 'general' }); expect(rec.wings).toBeUndefined(); }); it('wing dims are per side; 0 thickness and a negative haunch are refused', () => { expect(applyWingDim(rec, 'left', 'lengthMm', 6000).wingDims).toEqual({ left: { lengthMm: 6000 } }); expect(() => applyWingDim(rec, 'left', 'thicknessMm', 0)).toThrow('두께'); expect(() => applyWingDim(rec, 'left', 'haunchMm', -1)).toThrow('헌치'); }); it('반대쪽에도 적용: copies 두께 · 끝 높이 · 헌치 once, and the 길이 only when both forms match', () => { let r = applyWingDim(applyWingDim(rec, 'right', 'lengthMm', 6000), 'right', 'thicknessMm', 500); expect(copyWingDims(r, 'right').wingDims?.left).toEqual({ lengthMm: 6000, thicknessMm: 500 }); r = setWingForm(r, 'left', 'short'); expect(copyWingDims(r, 'right').wingDims?.left).toEqual({ thicknessMm: 500 }); // 형식이 달라 길이는 복사하지 않음 expect(() => copyWingDims(setWingForm(r, 'left', 'none'), 'right')).toThrow('반대쪽'); // one copy, no link: a later change on the right does not move the left const copied = copyWingDims(applyWingDim(rec, 'right', 'haunchMm', 200), 'right'); expect(applyWingDim(copied, 'right', 'haunchMm', 250).wingDims?.left).toEqual({ haunchMm: 200 }); }); }); describe('교대 기본값 복원과 받침 편집', () => { it('restores the imported defaults after several changes (a copy, not the same object)', () => { const changed = setWingForm(applyAbutEdit(rec, 'groundEl', 34.5), 'left', 'none'); const back = restoreAbutDefault(rec); expect(back).toEqual(rec); expect(back).not.toBe(rec); expect(changed.groundEl).toBe(34.5); }); it('a bearing z change keeps the others; a 교좌면 override sets that bearing only', () => { const z = applyBearingEdit(rec, 3, 'z', 44.7); expect(z.bearings.find((b) => b.no === 3)!.z).toBe(44.7); expect(z.bearings.find((b) => b.no === 1)!.z).toBe(rec.bearings[0].z); const seat = applyBearingEdit(rec, 2, 'seatEl', 44.5); expect(seat.bearings.find((b) => b.no === 2)!.seatEl).toBe(44.5); const auto = applyBearingEdit(seat, 2, 'seatEl', undefined); expect(auto.bearings.find((b) => b.no === 2)!.seatEl).toBeUndefined(); }); it('refuses an unknown bearing, a non-number z, and a 교좌면 at or below the 기초 하단', () => { expect(() => applyBearingEdit(rec, 99, 'z', 44)).toThrow('받침 99번'); expect(() => applyBearingEdit(rec, 1, 'z', undefined)).toThrow('z는 숫자'); expect(() => applyBearingEdit(rec, 1, 'seatEl', rec.foundationBottomEl)).toThrow('기초 하단'); }); });