Files
Pier_v1/tests/ground-model.test.ts
T
ypshimandClaude Haiku 5.5 0e70cfb969 refactor(ground): remove the unversioned-file fallback
- A file without a schema number is refused again; the example output in
  RAG/pierzainer_ground now has schema 2, so the temporary path is not needed.
- The legacy fixture is removed; tests cover the missing and null schema
  numbers and a pier without a ground object.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-23 18:09:50 +09:00

154 lines
6.4 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 { groundElFor, parseGroundModel } from '../src/core/ground-model';
import { changedKeys, overrideKeys, revertDraft, type PierValues } from '../src/core/changes';
import { parseProject, serializeProject, SCHEMA_VERSION, type ProjectFile } from '../src/core/project';
import { loadP1 } from './helpers';
/** The shape written by RAG/pierzainer_ground (schema 1). */
const SAMPLE = JSON.stringify({
schema: 1,
generated: '2026-10-23',
coord: '측량 X(북) · Y(동), m. 치수 mm',
piers: [
{ id: 'P1', ground: { center: 29.355, excel: 28.931, used: 29.355, source: '계산' }, footing: { cover: 0.972 } },
{ id: 'P2', ground: { center: null, excel: null, used: null, source: '없음' } },
],
});
describe('지반 모델 읽기 (ground_model.json, schema 1)', () => {
it('reads the pier-centre ground EL and the excel value', () => {
const m = parseGroundModel(SAMPLE);
expect(m.piers[0]).toEqual({ id: 'P1', centerEl: 29.355, excelEl: 28.931 });
expect(groundElFor(m, 'P1')).toBe(29.355);
});
it('a pier outside the terrain has no value, and an unknown pier has none either', () => {
const m = parseGroundModel(SAMPLE);
expect(groundElFor(m, 'P2')).toBeUndefined();
expect(groundElFor(m, 'P9')).toBeUndefined();
});
it('refuses another schema number instead of guessing the layout', () => {
expect(() => parseGroundModel(SAMPLE.replace('"schema":1', '"schema":3'))).toThrow('형식 번호 3');
});
it('refuses a file without a pier list or without pier names', () => {
expect(() => parseGroundModel('{"schema": 1}')).toThrow('교각 목록');
expect(() => parseGroundModel('{"schema": 1, "piers": [{"ground": {}}]}')).toThrow('이름(id)');
});
it('refuses text that is not JSON', () => {
expect(() => parseGroundModel('not json')).toThrow('JSON으로 읽히지 않습니다');
});
});
const LEGACY_SAMPLE = JSON.stringify({ schema: 1, piers: [{ id: 'P1', ground: { center: 29.355, excel: 28.931 } }] });
describe('지형 격자 (schema 2)', () => {
const WITH_GRID = JSON.stringify({
schema: 2,
generated: '2026-10-23',
piers: [{ id: 'P1', ground: { center: 29.355, excel: 28.931 } }],
terrain_grid: { origin: [259815.676, 179088.094], step: 5, nx: 2, ny: 2, z: [28, null, 29, 30] },
});
it('reads the terrain grid as terrainGrid', () => {
const m = parseGroundModel(WITH_GRID);
expect(m.schema).toBe(2);
expect(m.terrainGrid).toEqual({ origin: [259815.676, 179088.094], step: 5, nx: 2, ny: 2, z: [28, null, 29, 30] });
});
it('a schema 1 file has no terrain grid', () => {
expect(parseGroundModel(LEGACY_SAMPLE).terrainGrid).toBeUndefined();
});
it('refuses a grid whose value count is not nx × ny', () => {
expect(() => parseGroundModel(WITH_GRID.replace('"z":[28,null,29,30]', '"z":[28,29]'))).toThrow('nx × ny');
});
it('refuses a grid value that is not a number or null', () => {
expect(() => parseGroundModel(WITH_GRID.replace('"z":[28,null,29,30]', '"z":[28,"x",29,30]'))).toThrow('숫자가 아닌');
});
it('refuses an unknown schema number', () => {
expect(() => parseGroundModel(WITH_GRID.replace('"schema":2', '"schema":3'))).toThrow('형식 번호 3');
});
it('keeps the grid in a saved project', () => {
const m = parseGroundModel(WITH_GRID);
const text = serializeProject({
schemaVersion: SCHEMA_VERSION,
project: { name: 'x', bridge: '운곡교', client: '', author: '', crs: 'KATEC', memo: '' },
source: { fileName: 'x.xlsx', sheets: [] },
piers: [{ id: 'P1', input: loadP1() }],
history: [],
groundModel: m,
});
expect(parseProject(text).groundModel?.terrainGrid).toEqual(m.terrainGrid);
});
});
describe('형식 번호 검사', () => {
it('refuses a file with no schema number (the B′ fallback is removed)', () => {
expect(() => parseGroundModel(JSON.stringify({ piers: [{ id: 'P1', ground: { center: 29.355, excel: 28.931 } }] }))).toThrow('형식 번호 undefined');
});
it('refuses a null schema number', () => {
expect(() => parseGroundModel('{"schema": null, "piers": []}')).toThrow('형식 번호 null');
});
it('refuses a pier that has no ground object', () => {
expect(() => parseGroundModel(JSON.stringify({ schema: 1, piers: [{ id: 'P2' }] }))).toThrow('P2: 지반 값(ground)이 없습니다');
});
});
describe('지반고 적용 (사용자가 누를 때만, 변경으로 기록)', () => {
const base = loadP1();
const pier: PierValues = { defaults: base, input: base, draft: { ...base, groundEl: 29.355 } };
it('an applied value counts as an override and can be reverted to the excel value', () => {
expect(overrideKeys(pier)).toContain('groundEl');
expect(revertDraft(pier, 'groundEl').groundEl).toBe(base.groundEl);
});
it('the draft and the generated input are compared, so the change waits for regeneration', () => {
expect(changedKeys(pier.input, pier.draft)).toEqual(['groundEl']);
});
});
describe('프로젝트 파일의 지반 모델', () => {
function project(extra: Partial<ProjectFile>): ProjectFile {
const p1 = loadP1();
return {
schemaVersion: SCHEMA_VERSION,
project: { name: 'x', bridge: '운곡교', client: '', author: '', crs: 'KATEC', memo: '' },
source: { fileName: 'x.xlsx', sheets: [] },
piers: [{ id: 'P1', input: p1 }],
history: [],
...extra,
};
}
it('keeps the imported ground model across save and open', () => {
const m = parseGroundModel(SAMPLE);
const reopened = parseProject(serializeProject(project({ groundModel: m })));
expect(reopened.groundModel).toEqual(m);
});
it('refuses a damaged ground model inside the project file', () => {
const text = serializeProject(project({})).replace('"history": []', '"history": [], "groundModel": {"schema": 1}');
expect(() => parseProject(text)).toThrow('교각 목록');
});
it('refuses a stored model whose pier values are not numbers or null', () => {
const m = parseGroundModel(SAMPLE);
const bad = { ...m, piers: [{ id: 'P1', centerEl: '29.3', excelEl: null }] };
const text = serializeProject(project({})).replace('"history": []', `"history": [], "groundModel": ${JSON.stringify(bad)}`);
expect(() => parseProject(text)).toThrow('P1의 값이 맞지 않습니다');
});
it('opens an older file that has no ground model', () => {
expect(parseProject(serializeProject(project({}))).groundModel).toBeUndefined();
});
});