import { readFileSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import * as XLSX from 'xlsx'; import { describe, expect, it } from 'vitest'; import { buildAbut } from '../src/core/abut/geometry'; import { parseAbutments, toAbutInput } from '../src/core/abut/parse'; const RAG = fileURLToPath(new URL('../../../RAG/운곡교P1입력.xlsx', import.meta.url)); const load = (buf: Buffer) => XLSX.read(buf, { type: 'buffer' }); describe('교대 입력 읽기 (운곡교P1입력.xlsx, AA 입력)', () => { const { abutments, issues } = parseAbutments(load(readFileSync(RAG))); const a1 = abutments.find((a) => a.name === 'A1')!; it('reads A1 without issues', () => { expect(issues).toEqual([]); expect(abutments.map((a) => a.name)).toEqual(['A1']); expect(a1.type).toBe('A1'); expect(a1.seatEl).toBeCloseTo(44.367, 3); expect(a1.foundation).toBe('말뚝기초'); expect(a1.pileLengthMm).toBe(21600); expect(a1.skewDeg).toBeCloseTo(95 + 2 / 60 + 52 / 3600, 6); expect(a1.slopeI).toBeCloseTo(-0.0191, 6); }); it('IP1·IP2·IP3 use E = y_m, N = x_m (2026-11-03 규약)', () => { expect(a1.ip1.e).toBeCloseTo(179131.917023, 6); expect(a1.ip1.n).toBeCloseTo(259815.91973, 6); expect(a1.ip3.e).toBeCloseTo(179119.891533, 6); }); it('reads the five bearings with their own 교좌면 EL and the 받침높이 as written (275 mm)', () => { expect(a1.bearings.map((b) => b.no)).toEqual([1, 2, 3, 4, 5]); expect(a1.bearings.map((b) => b.seatEl)).toEqual([44.367, 44.438, 44.509, 44.58, 44.651]); expect(a1.bearingHeightMm).toBe(275); }); it('the parsed A1 gives the AA1 and AA2 values (length 12.197 m, wing 255.32°, slope +1.91 %)', () => { const m = buildAbut(toAbutInput(a1)); expect(m.length).toBeCloseTo(12.197, 3); expect(m.layoutMatchesType).toBe(true); expect(m.wing!.lineDeg).toBeCloseTo(255.32, 1); expect(m.wing!.slopePct).toBeCloseTo(1.91, 2); }); }); describe('교대 입력 오류 표시', () => { const rows = (type: string, skew: string) => [ ['교대명', '형식', '교좌면\nEL_m', '기초 하단\nEL_m', '지반고\nEL_m', '기초 형식', '말뚝길이\n_mm', 'skew', '종단경사'], ['A1', type, 44.367, 40.895, 33.984, '말뚝기초', 21600, skew, -0.0191], [], ['교대명', 'IP점', 'x_m', 'y_m'], ['A1', 'IP1', 259815.91973, 179131.917023], ['', 'IP2', 259815.91805, 179131.907126], ['', 'IP3', 259817.959709, 179119.891533], [], ['교대명', '받침번호', 'x_m', 'y_m', 'z_m_EL', '방위각', '위치', '교좌면_EL', '받침높이'], ['A1', 1, 259818.5035, 179121.1404, 44.492, "75°56'55.9\"", '전열', 44.367, 275], ['', 2, 259818.0835, 179123.6148, 44.563, "75°57'14.1\"", '전열', 44.438, 275], ]; const book = (r: unknown[][]) => { const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, XLSX.utils.aoa_to_sheet(r), '교대'); return wb; }; it('a wrong 형식 (A3) is an error and the abutment is left out', () => { const r = parseAbutments(book(rows('A3', "95°02'52\""))); expect(r.issues.map((i) => i.code)).toContain('AP02'); expect(r.abutments).toEqual([]); }); it('an unreadable skew is an error', () => { const r = parseAbutments(book(rows('A1', 'abc'))); expect(r.issues.map((i) => i.code)).toContain('AP03'); }); it('a clean sheet with the same layout reads with no issues', () => { const r = parseAbutments(book(rows('A1', "95°02'52\""))); expect(r.issues).toEqual([]); expect(r.abutments[0].bearings).toHaveLength(2); }); });