Files
Aislo/resources/tester/test_est_wonga_tools.py
T
eomsangdonandClaude Opus 5.5 d88eff4886 feat(est-test): 기타경비 계산과 산업안전보건관리비 비교
- 원본 처리 근거대로 두 기초금액·두 대상액 식 중 작은 쪽 선택, 비교 줄 표시
- 샘플에 원본이 적어 둔 안전관리비 식을 그대로 재현

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 20:16:04 +09:00

90 lines
3.9 KiB
Python
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.
"""EST 테스트 앱 원가 보조 계산(원본 TfrmWongaDiget 기타경비 계산·산업안전보건관리비 비교)."""
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-wonga-tools.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)));
"""
return json.loads(subprocess.check_output(["node", "-e", runner], cwd=ROOT).decode("utf-8"))
def test_anjun_reproduces_original_formula_pair_in_sample():
result = run_js(
"""(() => {
const calc = (p) => EstNative.calculate(EstModel.validate(p));
const p = calc(structuredClone(source));
const plain = (t) => String(t).replace(/\s+/g, '');
const row = p.tables.Wonga.find((r) => plain(r.Name).includes('산업안전') && Number(r.CalcIndex) !== 2);
const original = [row.VSusic, p.tables.Wonga.find((r) => plain(r.Name).includes('산업안전') && Number(r.CalcIndex) === 2).VSusic];
const before = p.tables.Wonga.length;
const out = EstWongaTools.anjun(p, { youl: 3.15, base: 5349000, addBase: false, compare: true });
const after = calc(p);
return { base: EstWongaTools.anjunBase(p.tables.Wonga), original, chosen: out.chosen, other: out.other,
rows: p.tables.Wonga.length === before, errors: after.calculation.errors.length };
})()"""
)
assert result["base"] == "(A + 4)"
assert result["chosen"] == "(A + 4) × 0.0315 × 1.2"
assert result["other"] == "((A + 4) + J/1.1) × 0.0315"
assert [x.replace(" ", "") for x in result["original"]] == [
result["chosen"].replace(" ", ""),
result["other"].replace(" ", ""),
]
assert result["rows"] is True and result["errors"] == 0
def test_gita_picks_smaller_base_and_validates():
result = run_js(
"""(() => {
const p = { options: {}, tables: { Wonga: [
{ Gubun: 'A', Name: '직 접 공 사 비', Cost: 1000 }, { Gubun: 'B', Name: '직접노무비', Cost: 300 },
{ Gubun: 'C', Name: '간접노무비', Cost: 50 }, { Gubun: 'D', Name: '기 타 경 비', Youl: '5%' } ] } };
const out = EstWongaTools.gita(p, { gcRatio: 0.4, baseRatio: 0.6, youl: 5.2, compare: true });
const row = p.tables.Wonga[3], cmp = p.tables.Wonga[4];
const errors = [];
for (const bad of [{ gcRatio: 2, baseRatio: 0.5, youl: 1 }, { gcRatio: 0.5, baseRatio: 0.5, youl: 11 }])
try { EstWongaTools.gita(p, bad); } catch (e) { errors.push(e.message.slice(0, 4)); }
let none = ''; try { EstWongaTools.anjun({ options: {}, tables: { Wonga: [] } }, { youl: 1 }); } catch (e) { none = e.message; }
return { values: out.values, vs: row.VSusic, youl: row.Youl, cmp: [cmp.VSusic, cmp.CalcIndex], errors, none,
stored: [p.options.GitaGCRatio, p.options.GitaBaseRatio] };
})()"""
)
assert result["values"] == [750, 600]
assert result["vs"] == "(A × 0.6) × 0.052"
assert result["youl"] == "5.2%"
assert result["cmp"] == ["((A × 0.4) + B + C) × 0.052", 2]
assert result["errors"] == ["1 이하", "기타경비"]
assert result["none"] == "산업안전보건관리비 비목이 없습니다."
assert result["stored"] == [0.4, 0.6]