"""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]