141 lines
8.6 KiB
Python
141 lines
8.6 KiB
Python
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='<cs nr=1/><div><c nr=1/>폭= <begin Tag=2 wpsty=[[Name:"r000001";]]/>6<end Tag=2/> m</div>';
|
|
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('<begin Tag=2')&&next.includes('<end Tag=2'),rejected}));"""
|
|
result = json.loads(subprocess.check_output(["node", "-e", script], cwd=ROOT).decode("utf-8"))
|
|
assert result == {"same": True, "tags": True, "rejected": True}
|
|
|
|
|
|
def test_estx_quantity_edit_preserves_other_documents_and_indexes_when_sample_exists():
|
|
sample = Path("C:/Dasansoft/Est Plus 2/Data/ProjectData/main.estx")
|
|
if not sample.exists():
|
|
pytest.skip("installed EST main 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),first=p.quantitySheets[0];
|
|
first.rows[0].LineData=first.rows[0].LineData.replace('표준','시험 표준');
|
|
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)));
|
|
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("<IHH", data, 41, 1, stride, len(fields))
|
|
start = 512 + 768 * len(fields)
|
|
offset = 25
|
|
for i, (name, kind, width) in enumerate(fields):
|
|
at = 512 + i * 768
|
|
data[at + 2] = len(name)
|
|
data[at + 3 : at + 3 + len(name)] = name.encode("ascii")
|
|
data[at + 164] = kind
|
|
struct.pack_into("<H", data, at + 169, width)
|
|
struct.pack_into("<I", data, at + 172, offset)
|
|
data[start + offset] = 1
|
|
payload = start + offset + 1
|
|
if name == "PkID":
|
|
struct.pack_into("<i", data, payload, 1)
|
|
elif name in ["Code", "Name"]:
|
|
text = ("A" if name == "Code" else "원본").encode("cp949")
|
|
data[payload : payload + len(text)] = text
|
|
else:
|
|
struct.pack_into("<d", data, payload, {"GN": 2, "GU": 100, "TC": 200}[name])
|
|
offset += width + 1
|
|
with zipfile.ZipFile(path, "w", compression=zipfile.ZIP_DEFLATED) as archive:
|
|
archive.writestr("EstMainDat.dat", data)
|
|
archive.writestr("EstMainDat.idx", b"opaque index must remain unchanged")
|
|
archive.writestr("CadMainDat.blb", b"original drawing payload")
|
|
|
|
|
|
def test_estx_patches_values_and_preserves_indexes_drawings_and_rejects_structural_changes(
|
|
tmp_path,
|
|
):
|
|
source = tmp_path / "native.estx"
|
|
fixture(source)
|
|
script = """const fs=require('fs'),m=require('./tools/est-test/import-estx.cjs'),e=require('./tools/est-test/export-estx.cjs');
|
|
const b=fs.readFileSync(process.argv[1]),p=m.importEstx(b),same=e.exportEstx(p).equals(b);
|
|
p.tables.EstMainDat[0].GN=3;p.tables.EstMainDat[0].TC=300;p.tables.EstMainDat[0].Name='변경';
|
|
const next=e.exportEstx(p),q=m.importEstx(next),old=m.unzip(b),now=m.unzip(next);
|
|
let blocked=false;p.tables.EstMainDat[0].Code='B';try{e.exportEstx(p)}catch{blocked=true}
|
|
process.stdout.write(JSON.stringify({same,row:q.tables.EstMainDat[0],blocked,index:old.get('estmaindat.idx').equals(now.get('estmaindat.idx')),drawing:old.get('cadmaindat.blb').equals(now.get('cadmaindat.blb'))}));"""
|
|
result = json.loads(
|
|
subprocess.check_output(["node", "-e", script, str(source)], cwd=ROOT).decode("utf-8")
|
|
)
|
|
assert result["same"] and result["blocked"] and result["index"] and result["drawing"]
|
|
assert result["row"]["Name"] == "변경"
|
|
assert result["row"]["GN"] == 3 and result["row"]["TC"] == 300
|