import json import struct import subprocess import zipfile import pytest from pathlib import Path ROOT = Path(__file__).resolve().parents[2] def test_quantity_document_edits_keep_styles_and_bookmarks_and_block_cross_style_edits(): script = r"""const q=require('./tools/est-test/native-quantity.cjs'),w=require('./tools/est-test/export-quantity.cjs'); const doc='
폭= 6 m
'; const old=q.fromWpt('A','문서',doc,[],{}),edited=structuredClone(old); edited.rows[0].LineData=edited.rows[0].LineData.replace('6','12'); const next=w.editDocument(doc,old,edited),again=q.fromWpt('A','문서',next,[],{}); const bad=structuredClone(old);bad.rows[0].LineData='완전히 다른 줄'; let rejected=false;try{w.editDocument(doc,old,bad)}catch{rejected=true} process.stdout.write(JSON.stringify({same:JSON.stringify(again)===JSON.stringify(edited),tags:next.includes('k.endsWith('.idx')||k.startsWith('cad')).every(([k,v])=>v.equals(b.get(k))); process.stdout.write(JSON.stringify({line:q.quantitySheets[0].rows[0].LineData,others:JSON.stringify(q.quantitySheets.slice(1))===JSON.stringify(p.quantitySheets.slice(1)),unchanged}));""" result = json.loads( subprocess.check_output(["node", "-e", script, str(sample)], cwd=ROOT).decode("utf-8") ) assert "시험 표준" in result["line"] assert result["others"] and result["unchanged"] def test_estx_options_and_expanded_blob_preserve_unrelated_data_when_sample_exists(): sample = Path("C:/Dasansoft/Est Plus 2/Data/ProjectData/sample.estx") if not sample.exists(): pytest.skip("installed EST sample unavailable") script = """const fs=require('fs'),m=require('./tools/est-test/import-estx.cjs'),e=require('./tools/est-test/export-estx.cjs'); const bytes=fs.readFileSync(process.argv[1]),p=m.importEstx(bytes),before=structuredClone(p); p.name='길이가 달라진 새 공사명';p.options.AutoCalc=!p.options.AutoCalc; p.options.CostValueTrunc=!p.options.CostValueTrunc;p.options.MidCostDiget=3; const row=p.tables.EstMainDat.find(r=>Object.values(r).some(v=>v&&v.binary)); const field=Object.keys(row).find(k=>row[k]&&row[k].binary); const content=Buffer.alloc(12000, 65);row[field]={binary:content.toString('base64')}; const out=e.exportEstx(p),q=m.importEstx(out),a=m.unzip(bytes),b=m.unzip(out); const unchanged=[...a].filter(([k])=>k.endsWith('.idx')||k.startsWith('cad')).every(([k,v])=>v.equals(b.get(k))); const other=p.tables.EstMainDat.every((r,i)=>Object.keys(r).every(k=>JSON.stringify(r[k])===JSON.stringify(q.tables.EstMainDat[i][k]))); const options=before.originalOptions.every(x=>{const y=q.originalOptions.find(z=>z.className===x.className); return Object.entries(x.properties).every(([k,v])=>['ProjectName','CalcAuto','CostValueTrunc','MidCostDiget'].includes(k)||JSON.stringify(v)===JSON.stringify(y.properties[k]));}); process.stdout.write(JSON.stringify({unchanged,other,options,name:q.name,auto:q.options.AutoCalc,cost:q.options.CostValueTrunc,digits:q.options.MidCostDiget,expected:p.options,content:Buffer.from(q.tables.EstMainDat.find(r=>r.PkID===row.PkID)[field].binary,'base64').equals(content)}));""" result = json.loads( subprocess.check_output(["node", "-e", script, str(sample)], cwd=ROOT).decode("utf-8") ) assert result["unchanged"] and result["other"] and result["options"] and result["content"] assert result["name"] == "길이가 달라진 새 공사명" assert result["auto"] == result["expected"]["AutoCalc"] assert result["cost"] == result["expected"]["CostValueTrunc"] assert result["digits"] == 3 def test_recalculation_and_native_export_keep_original_title_identities_when_sample_exists(): sample = Path("C:/Dasansoft/Est Plus 2/Data/ProjectData/sample.estx") if not sample.exists(): pytest.skip("installed EST sample unavailable") script = """const fs=require('fs'),m=require('./tools/est-test/import-estx.cjs'),e=require('./tools/est-test/export-estx.cjs'),s=require('./tools/est-test/server.cjs'); const bytes=fs.readFileSync(process.argv[1]),p=m.importEstx(bytes),q=s.calculate(p); const ids=['HeadTitleDat','CostTitleDat','MachTitleDat'].every(key=>p.tables[key].every((r,i)=>r.PkID===q.tables[key][i].PkID)); const row=q.tables.EstMainDat.find(r=>r.GN&&r.TC);row.GN+=1; const edited=s.calculate(q),out=e.exportEstx(edited),again=m.importEstx(out),original=m.unzip(bytes),files=m.unzip(out); const indexes=[...original].filter(([name])=>name.endsWith('.idx')).every(([name,value])=>value.equals(files.get(name))); process.stdout.write(JSON.stringify({ids,indexes,gn:again.tables.EstMainDat.find(r=>r.PkID===row.PkID).GN,expected:row.GN}));""" result = json.loads( subprocess.check_output(["node", "-e", script, str(sample)], cwd=ROOT).decode("utf-8") ) assert result["ids"] and result["indexes"] assert result["gn"] == result["expected"] def fixture(path): fields = [ ("PkID", 2, 4), ("Code", 1, 16), ("Name", 1, 32), ("GN", 7, 8), ("GU", 7, 8), ("TC", 7, 8), ] stride = 25 + sum(width + 1 for _, _, width in fields) data = bytearray(512 + 768 * len(fields) + stride) data[8:24] = bytes.fromhex("068abe8e592364cb403d71d2e3bc64d0") struct.pack_into("