- AbutRecord.rebar.block (바꾼 값만), applyBlockRebar, 프로젝트 파일 검증 Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
75 lines
3.9 KiB
TypeScript
75 lines
3.9 KiB
TypeScript
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('받침블럭 배근');
|
||
});
|
||
});
|