Files
Aislo/resources/tester/test_est_transfer.py
T
eomsangdonandClaude Opus 5.5 dc365e2140 feat(est-test): 프로젝트 추가로 다른 공사 내역 합치기
- 다른 공사 파일의 순공사비 위 내역 전체를 하위 자료와 함께 이 공사 순공사비 위에 합침

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 17:12:15 +09:00

129 lines
5.0 KiB
Python

"""EST 테스트 앱 연결 자료와 함께 복사·붙여넣기(원본 TFRMOTHERLOAD)."""
import json
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
FILES = [
"native-math.js",
"native-formula.js",
"native-machine.js",
"native-rows.js",
"native-text.js",
"native-quantity.cjs",
"native-wonga.js",
"native-engine.js",
"native-row-match.js",
"native-escalation.js",
"native-progress.js",
"native-business.js",
"native-review.js",
"native-resources.js",
"native-schedule.js",
"native-model.js",
"native-verify.js",
"native-transfer.js",
]
def run_js(code: str):
runner = f"""
const fs = require('fs'), vm = require('vm');
const ctx = vm.createContext({{ structuredClone, console }});
for (const f of {json.dumps(FILES)})
vm.runInContext(fs.readFileSync('tools/est-test/' + f, 'utf8'), ctx, {{ filename: f }});
ctx.source = JSON.parse(fs.readFileSync('tools/est-test/sample.json', 'utf8'));
process.stdout.write(JSON.stringify(vm.runInContext({json.dumps(code)}, ctx)));
"""
out = subprocess.check_output(["node", "-e", runner], cwd=ROOT)
return json.loads(out.decode("utf-8"))
PICK = """
const calc = (p) => EstNative.calculate(EstModel.validate(p));
const src = calc(source);
const linked = src.tables.EstMainDat.filter((r) => r.Code && !Number(r.Level));
const picked = [5, 103, 104].flatMap((k) => linked.filter((r) => Number(r.LinkIndex) === k).slice(0, 2));
const deps = EstTransfer.collect(src, picked);
"""
def test_copy_into_empty_project_keeps_prices_and_links():
result = run_js(
"(() => {"
+ PICK
+ """
const target = EstNative.empty();
target.options = structuredClone(src.options);
const merged = EstTransfer.merge(target, structuredClone(deps));
EstTransfer.paste(target, picked, 0);
const done = calc(target);
const pasted = done.tables.EstMainDat.slice(0, picked.length);
return {
kinds: [...new Set(picked.map((r) => Number(r.LinkIndex)))].sort((a, b) => a - b),
groups: Object.keys(deps.groups).sort(),
nested: (deps.flat.LaborcDat || []).length > 0,
missing: done.calculation.errors.filter((e) => e.includes('연결 자료 없음') || e.includes('하위 자료 없음')),
same: pasted.every((r, i) => Math.abs(Number(r.TC) - Number(picked[i].TC)) < 1),
added: merged.added.length > 0, kept: merged.kept.length, criteria: merged.criteria,
unique: new Set(done.tables.HeadMainDat.map((r) => r.PkID)).size === done.tables.HeadMainDat.length,
};
})()"""
)
assert result["kinds"] == [5, 103, 104]
assert result["groups"] == ["CostMainDat", "HeadMainDat", "MachMainDat"]
assert result["nested"] is True
assert result["missing"] == []
assert result["same"] is True
assert result["added"] is True
assert result["kept"] == 0
assert result["criteria"] is True
assert result["unique"] is True
def test_same_code_in_target_is_kept_not_overwritten():
result = run_js(
"(() => {"
+ PICK
+ """
const target = structuredClone(source);
const labor = target.tables.LaborcDat[0];
labor.Cost1 = Number(labor.Cost1) * 2; labor.TC = Number(labor.TC) * 2;
const before = target.tables.HeadMainDat.length;
target.tables.MachGijunDat[0].TC = 12345;
const merged = EstTransfer.merge(target, structuredClone(deps));
return { added: merged.added.length, kept: merged.kept.length > 0,
criteria: merged.criteria === false && target.tables.MachGijunDat[0].TC === 12345,
heads: target.tables.HeadMainDat.length === before,
labor: target.tables.LaborcDat[0].TC === labor.TC };
})()"""
)
assert result == {"added": 0, "kept": True, "criteria": True, "heads": True, "labor": True}
def test_project_add_merges_rows_above_net_cost():
result = run_js(
"""(() => {
const calc = (p) => EstNative.calculate(EstModel.validate(p));
const target = calc(structuredClone(source)), other = calc(structuredClone(source));
const net = (p) => p.tables.EstMainDat.find((r) => Number(r.LinkIndex) === 54);
const before = Number(net(target).TC), count = target.tables.EstMainDat.length;
const boundary = target.tables.EstMainDat.findIndex((r) => Number(r.LinkIndex) === 54);
const out = EstTransfer.addProject(target, other);
const done = calc(target);
let empty = '';
try { EstTransfer.addProject(target, { tables: { EstMainDat: [{ LinkIndex: 54 }] } }); } catch (e) { empty = e.message; }
return { rows: out.rows === boundary, added: out.added.length, grew: done.tables.EstMainDat.length - count === boundary,
doubled: Math.abs(Number(net(done).TC) - 2 * before) < 2, errors: done.calculation.errors.length, empty: empty.slice(0, 8) };
})()"""
)
assert result == {
"rows": True,
"added": 0,
"grew": True,
"doubled": True,
"errors": 0,
"empty": "추가할 공사에 ",
}