Files
Pier_v1/tests/abut-rebar-block.test.ts
T
ypshimandClaude Haiku 5.5 5856cfb423 feat(abut): 받침블럭 배근 입력 저장
- AbutRecord.rebar.block (바꾼 값만), applyBlockRebar, 프로젝트 파일 검증

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-11-05 09:37:12 +09:00

75 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it } from 'vitest';
import { abutBlockRebar, abutBarDepths } from '../src/core/abut/rebar/block';
// 교대 받침블럭 (교대 기획서 §10 · 받침블럭철근.md §6.6): 블럭 5개, 1200 × 1200, 높이 125 (패드 50 포함, 콘크리트 75).
const blocks = [1, 2, 3, 4, 5].map((no, i) => ({ no, s: 1 + i * 2, t: 0.2, rotDeg: 0, seatEl: 44.367, blockHeightMm: 125 }));
const bottom = 40.895;
const A1 = abutBlockRebar(blocks, bottom);
const bar = (r: typeof A1, m: 'S1' | 'S2') => r.bars.find((b) => b.mark === m)!;
describe('교대 받침블럭 배근 A1 (AD6, 교대 기획서 §10)', () => {
it('블럭 콘크리트 75 (블럭 125 − 패드 50), 깊이: S2 50 · S1 62.7 (H13 두 개)', () => {
expect(A1.concreteH).toBe(75);
expect(abutBarDepths(A1.settings)).toEqual({ S2: 50, S1: 50 + 6.35 + 6.35 });
});
it('본수: 블럭마다 8본 (7@125, 끝 162.5), 5개 → S1 40 · S2 40', () => {
expect(bar(A1, 'S1').count).toBe(40);
expect(bar(A1, 'S2').count).toBe(40);
expect(A1.paths.filter((p) => p.mark === 'S1' && p.block === 1).length).toBe(8);
// 블럭 1 (s = 1000 mm): S2 본은 블럭 모서리에서 162.5, 125 간격 → 첫 본 s = 1000 − 600 + 162.5 = 562.5.
const s2 = A1.paths.filter((p) => p.mark === 'S2' && p.block === 1).map((p) => p.pts[0][0]).sort((a, b) => a - b);
expect(s2[0]).toBeCloseTo(562.5, 6);
expect(s2[1] - s2[0]).toBeCloseTo(125, 6);
expect(s2.length).toBe(8);
});
it('길이 2500 (1100 직선 + 다리 700 × 2), 묻힘: S2 675 · S1 688', () => {
expect([bar(A1, 'S1').length, bar(A1, 'S2').length]).toEqual([2500, 2500]);
expect(Math.round(A1.embedment.S2)).toBe(675);
expect(Math.round(A1.embedment.S1)).toBe(688);
});
it('간섭 (Q-S4, 경고만): S1 다리가 W2 층을 지남 → RB-AK01 (W), 다른 오류 없음', () => {
const codes = A1.issues.map((i) => `${i.code}:${i.level}`);
expect(codes).toContain('RB-AK01:W');
expect(A1.issues.filter((i) => i.level === 'E')).toEqual([]);
});
it('간격 수정 → RB-AK03 "수정됨"', () => {
const r = abutBlockRebar(blocks, bottom, { spacingS1: 150 });
expect(r.issues.map((i) => i.code)).toContain('RB-AK03');
expect(bar(r, 'S1').modified).toBe(true);
expect(bar(r, 'S2').modified).toBe(false);
});
it('회전 (방위각): 블럭 축이 돌면 경로 점도 같이 돈다 (교대축 방향 길이 보존)', () => {
const r = abutBlockRebar([{ ...blocks[0], rotDeg: 30 }], bottom);
const p = r.paths.find((q) => q.mark === 'S1')!;
const len = Math.hypot(p.pts[2][0] - p.pts[1][0], p.pts[2][1] - p.pts[1][1]);
expect(len).toBeCloseTo(1100, 6);
});
});
import { applyBlockRebar } from '../src/core/abut/edit';
import { checkStoredAbutments } from '../src/core/project';
import { parseAbutments } from '../src/core/abut/parse';
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import * as XLSX from 'xlsx';
describe('③ 받침블럭 배근 입력 저장', () => {
const rec = parseAbutments(XLSX.read(readFileSync(fileURLToPath(new URL('../../../RAG/운곡교P1입력.xlsx', import.meta.url))), { type: 'buffer' })).abutments[0];
it('stores only the changed values; undefined links back; refuses bad values', () => {
const a = applyBlockRebar(rec, { spacingS1: 150, legLength: 800 });
expect(a.rebar?.block).toEqual({ spacingS1: 150, legLength: 800 });
expect(applyBlockRebar(a, { spacingS1: undefined }).rebar?.block).toEqual({ legLength: 800 });
expect(() => applyBlockRebar(rec, { legLength: 0 })).toThrow();
});
it('the project file keeps the 받침블럭 input and refuses a non-number', () => {
expect(() => checkStoredAbutments([applyBlockRebar(rec, { coverTop: 60 })])).not.toThrow();
expect(() => checkStoredAbutments([{ ...rec, rebar: { block: { coverTop: 'x' } } as never }])).toThrow('받침블럭 배근');
});
});