- 다른 공사 파일의 순공사비 위 내역 전체를 하위 자료와 함께 이 공사 순공사비 위에 합침 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
129 lines
5.0 KiB
Python
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": "추가할 공사에 ",
|
|
}
|