import json import struct import subprocess import zipfile import pytest from test_est_native_export import ROOT, fixture def inspect(path): script = """const fs=require('fs'),m=require('./tools/est-test/inspect-accom.cjs'); try { process.stdout.write(JSON.stringify(m.inspectAccom(fs.readFileSync(process.argv[1])))); } catch(e) { process.stdout.write(JSON.stringify({error:e.message})); }""" return json.loads(subprocess.check_output(["node", "-e", script, str(path)], cwd=ROOT)) def test_accom_reads_synthetic_table_and_rejects_missing_or_bad_signature(tmp_path): base = tmp_path / "base.estx" fixture(base) with zipfile.ZipFile(base) as archive: data = bytearray(archive.read("EstMainDat.dat")) # Fixture GU is a double field; rename it to the observed Accom quantity field. offset = 512 + 768 * 4 data[offset + 2] = len("TotalGN") data[offset + 3 : offset + 3 + len("TotalGN")] = b"TotalGN" path = tmp_path / "accom.estx" with zipfile.ZipFile(path, "w") as archive: archive.writestr("AccomMainTable.Dat", data) result = inspect(path) assert result["rows"][0]["Code"] == "A" assert result["rows"][0]["TotalGN"] == 100 assert "TotalGN" in result["fields"] script = """const fs=require('fs'),{server}=require('./tools/est-test/server.cjs'); server.listen(0,'127.0.0.1',async()=>{try{ const r=await fetch(`http://127.0.0.1:${server.address().port}/api/inspect-accom`, {method:'POST',body:fs.readFileSync(process.argv[1])}); const p=await r.json();process.stdout.write(JSON.stringify({status:r.status,rows:p.rows})); }finally{server.close()}});""" api = json.loads(subprocess.check_output(["node", "-e", script, str(path)], cwd=ROOT)) assert api["status"] == 200 assert api["rows"][0]["TotalGN"] == 100 assert "없습니다" in inspect(base)["error"] data[24] ^= 1 with zipfile.ZipFile(path, "w") as archive: archive.writestr("AccomMainTable.Dat", data) assert "서명" in inspect(path)["error"] def test_accom_view_reads_file_without_changing_project(tmp_path): playwright = pytest.importorskip("playwright.sync_api") base = tmp_path / "base.estx" fixture(base) with zipfile.ZipFile(base) as archive: data = bytearray(archive.read("EstMainDat.dat")) offset = 512 + 768 * 4 data[offset + 2] = len("TotalGN") data[offset + 3 : offset + 3 + len("TotalGN")] = b"TotalGN" stride = struct.unpack_from(" Boolean(EstApp.state.project)") page.evaluate("EstApp.state.view='progress'; EstApp.render()") before = page.evaluate("JSON.stringify(EstApp.state.project)") page.locator("[data-accom-inspect]").set_input_files(str(path)) page.locator("#dialog-content").get_by_text("TotalGN").wait_for(timeout=5000) assert page.locator("#dialog-content").get_by_text("100", exact=True).count() == 200 page.locator('[data-accom-page="1"]').click() assert page.locator("#dialog-content").get_by_text("100", exact=True).count() == 1 assert page.evaluate("JSON.stringify(EstApp.state.project)") == before page.evaluate("""() => { document.getElementById('dialog').close(); const original=window.fetch; window.fetch=(url,options)=>url==='/api/inspect-accom' ? new Promise(resolve=>window.releaseAccom=()=>resolve(original(url,options))) : original(url,options); }""") page.locator("[data-accom-inspect]").set_input_files(str(path)) page.wait_for_function("() => typeof window.releaseAccom === 'function'") page.evaluate("EstApp.showDialog('편집 중', '')") with page.expect_event("dialog") as dialog_event: page.evaluate("window.releaseAccom()") dialog_event.value.accept() assert page.locator("#dialog-title").inner_text() == "편집 중" assert page.locator("#keep-value").input_value() == "보존" browser.close()