feat(ground): accept ground_model.json written before the schema number

- A file with no `schema` key is read as schema 1, but every pier must have a
  `ground` object, so a renamed field is refused instead of loading as empty.
- `schema` null or any other number is still refused.
- The project file keeps the normalised model and checks it with its own
  check (checkStoredGroundModel), so a saved model reopens.
- Temporary: remove the no-schema branch once examples/out is regenerated.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
ypshimandClaude Haiku 5.5 committed 2026-10-23 17:41:24 +09:00
1 parent c3606011b5
commit be76425c8f
4 files changed
+453 -6

No files matched your search

+31
View File
@@ -1,3 +1,4 @@
import { readFileSync } from 'node:fs';
import { describe, expect, it } from 'vitest';
import { groundElFor, parseGroundModel } from '../src/core/ground-model';
import { changedKeys, overrideKeys, revertDraft, type PierValues } from '../src/core/changes';
@@ -42,6 +43,29 @@ describe('지반 모델 읽기 (ground_model.json, schema 1)', () => {
});
});
describe('번호 없는 옛 지반 모델 (B′: 임시 경로)', () => {
// examples/out/ground_model.json as written before the schema number existed (P1–P6, same values as the report).
const LEGACY = readFileSync(new URL('./fixtures/ground-model-legacy.json', import.meta.url), 'utf-8');
it('reads the unversioned file and reports it as schema 1', () => {
const m = parseGroundModel(LEGACY);
expect(m.schema).toBe(1);
expect(m.piers.map((p) => p.id)).toEqual(['P1', 'P2', 'P3', 'P4', 'P5', 'P6']);
expect(groundElFor(m, 'P1')).toBe(29.355);
expect(m.piers[0].excelEl).toBe(28.931);
});
it('refuses a pier that has no ground object (a renamed field must not load as empty values)', () => {
const data = JSON.parse(LEGACY);
delete data.piers[1].ground;
expect(() => parseGroundModel(JSON.stringify(data))).toThrow('P2: 지반 값(ground)이 없습니다');
});
it('refuses a null schema number instead of treating it as missing', () => {
expect(() => parseGroundModel(LEGACY.replace('{', '{"schema": null,'))).toThrow('형식 번호 null');
});
});
describe('지반고 적용 (사용자가 누를 때만, 변경으로 기록)', () => {
const base = loadP1();
const pier: PierValues = { defaults: base, input: base, draft: { ...base, groundEl: 29.355 } };
@@ -80,6 +104,13 @@ describe('프로젝트 파일의 지반 모델', () => {
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();
});