"""STMATE 재현 앱 — 그림 코드(I · COSTN IFORM · SIZE 꼴) 지킴 시험 · 쓰기는 임시 폴더. 근거: 지식DB REA 분석/37 §4 — STmate.Cpm TwM_Boxa 0x514df0(넣기) · 0x514bc0~0x514d30(저장: NAME 빈칸이면 파일 이름 · SIZE = UpperCase(그림.ClassName) · IFORM = 그림 스트림) · 0x4dac10(새 코드 이름 「새로운 그림코드」) """ import base64 import io import sys from pathlib import Path import pytest ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(ROOT / "tools/stmate-test")) import api_img as A # noqa: E402 import server # noqa: E402 from dbf_memo import SixMemo # noqa: E402 from dbf_rw import DbfTable, find_rows # noqa: E402 from img_code import NEW_NAME, get_image, kind_of_file, list_images, put_image # noqa: E402 from stc_read import Source # noqa: E402 STC = next((ROOT / "tools/stmate-test/work_dbf").glob("*.STC"), None) def _bmp(w=3, h=2) -> bytes: from PIL import Image out = io.BytesIO() Image.new("RGB", (w, h), (200, 30, 40)).save(out, "BMP") return out.getvalue() @pytest.fixture(scope="module") def files(): if STC is None: pytest.skip("표본 STC 없음") return Source(str(STC)).files def test_kind_by_extension(): assert [kind_of_file(n) for n in ("a.JPG", "b.jpeg", "c.bmp", "d.ico", "e.emf", "f.WMF")] == [ "TJPEGIMAGE", "TJPEGIMAGE", "TBITMAP", "TICON", "TMETAFILE", "TMETAFILE", ] with pytest.raises(ValueError): kind_of_file("x.png") def test_new_code_put_and_read_back(files): data = _bmp() out, rep = put_image(files, "i00009", data, "다리 사진.bmp") assert rep == {"code": "I00009", "new": True, "kind": "TBITMAP", "bytes": len(data), "name": NEW_NAME} f2 = dict(files) | out got, kind, mime = get_image(f2, "I00009") assert got == data and kind == "TBITMAP" and mime == "image/bmp" assert any(r["CODE"] == "I00009" and r["bytes"] == len(data) for r in list_images(f2)) # 다른 레코드·옛 메모 바이트는 그대로(무손실) a, b = DbfTable(files["COSTN.DBF"]), DbfTable(out["COSTN.DBF"]) assert b.recs[: len(a.recs)] == a.recs and len(b.recs) == len(a.recs) + 1 old = files.get("COSTN.SMT", b"") assert out["COSTN.SMT"][16 : len(old)] == old[16:] def test_existing_code_bitmap_to_jpeg_and_name_rule(files): f1 = dict(files) | put_image(files, "I00009", _bmp(), "x.bmp")[0] t = DbfTable(f1["COSTN.DBF"]) i = find_rows(t, "CODE")["I00009"] t.set(i, "NAME", "") # 이름이 비면 파일 이름(확장자 뺌) f1["COSTN.DBF"] = t.to_bytes() out, rep = put_image(f1, "I00009", _bmp(8, 8), "교량 정면.BMP", to_jpeg=True) assert rep["kind"] == "TJPEGIMAGE" and rep["name"] == "교량 정면" and not rep["new"] data, kind, _ = get_image(dict(f1) | out, "I00009") assert data[:3] == b"\xff\xd8\xff" and kind == "TJPEGIMAGE" with pytest.raises(ValueError): put_image(files, "M00001", data, "a.jpg") # I 코드만 with pytest.raises(ValueError): put_image(files, "I00010", data, "a.jpg", to_jpeg=True) # 변환은 비트맵만 def test_sniff_when_size_blank(files): out, _ = put_image(files, "I00011", _bmp(), "a.bmp") t = DbfTable(out["COSTN.DBF"]) t.set(find_rows(t, "CODE")["I00011"], "SIZE", "") f2 = dict(files) | out | {"COSTN.DBF": t.to_bytes()} assert get_image(f2, "I00011")[1] == "TBITMAP" assert SixMemo(out["COSTN.SMT"]).read(t.raw(find_rows(t, "CODE")["I00011"], "IFORM"))[:2] == b"BM" def test_api_put_writes_only_costn(files, tmp_path, monkeypatch): monkeypatch.setattr(server, "WORK_DBF_ROOT", tmp_path) b64 = "data:image/bmp;base64," + base64.b64encode(_bmp()).decode() r = A.img_put(A.ImgPutReq(source_path=str(STC), code="I00012", data_b64=b64, filename="p.bmp")) assert Path(r["saved_dir"]).parent == tmp_path and r["report"]["kind"] == "TBITMAP" new = Source(r["saved_stc"]).files changed = sorted(k for k in files if new.get(k) != files[k]) assert set(changed) <= {"COSTN.DBF", "COSTN.SMT"} res = A.img_get(A.ImgReq(source_path=r["saved_stc"], code="I00012")) assert res.headers["X-Img-Kind"] == "TBITMAP" and res.body[:2] == b"BM" assert any(x["CODE"] == "I00012" for x in A.img_list(A.ImgReq(source_path=r["saved_dir"]))["rows"])