# -*- coding: utf-8 -*- """验证新增功能: 物料明细(入库/消耗总量 + 入库流水时间) 与 BOM 明细(规格型号)。 正确收尾: 用 _exe_util.stop_exe 杀整棵进程树, 避免残留进程霸占 8080。 """ import http.client import os import shutil import sqlite3 import sys import time sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from _exe_util import EXE, start_exe, stop_exe HOST, PORT = "127.0.0.1", 8080 DB = r"D:\2026-09-10-task-20\material_wms\dist\app.db" SEED = r"D:\2026-09-10-task-20\material_wms\instance\app.db" def seed_test_bom(): """往 dist 库插入一个测试 BOM(幂等), 引用已存在的物料 1/2/3。""" c = sqlite3.connect(DB) c.execute("DELETE FROM bom_item WHERE bom_id IN (SELECT id FROM bom WHERE code='TESTBOM01')") c.execute("DELETE FROM bom WHERE code='TESTBOM01'") c.execute("INSERT INTO bom(code,name,note) VALUES('TESTBOM01','测试产品','verify')") bid = c.execute("SELECT id FROM bom WHERE code='TESTBOM01'").fetchone()[0] for mid, q in [(1, 2.0), (2, 3.0), (3, 1.0)]: c.execute("INSERT INTO bom_item(bom_id,material_id,qty_per_unit) VALUES(?,?,?)", (bid, mid, q)) c.commit() c.close() return bid def req(c, method, path, cookie="", body=None, headers=None): h = {"Cookie": cookie} if headers: h.update(headers) if body is not None and "Content-Type" not in h: h["Content-Type"] = "application/x-www-form-urlencoded" c.request(method, path, body=body, headers=h) r = c.getresponse() data = r.read().decode("utf-8", "ignore") return r.status, data, r.getheader("Set-Cookie") or "" def wait_port(): for _ in range(60): try: http.client.HTTPConnection(HOST, PORT, timeout=2).connect() return True except Exception: time.sleep(1) return False def main(): if os.path.exists(SEED): shutil.copyfile(SEED, DB) bid = seed_test_bom() p = start_exe() try: assert wait_port(), "端口未就绪" c = http.client.HTTPConnection(HOST, PORT, timeout=15) _, _, ck = req(c, "GET", "/login") _, _, ck2 = req(c, "POST", "/login", body=b"username=admin&password=admin123", cookie=ck.split(";")[0]) cookie = (ck2.split(";")[0]) if ck2 else ck.split(";")[0] ok = True # 1) 物料列表含 入库总数量 / 消耗总数量 列 s, html, _ = req(c, "GET", "/materials", cookie=cookie) print(f"[materials] status={s}") assert s == 200, f"materials status={s}" for kw in ["入库总数量", "消耗总数量"]: has = kw in html print(f" [{'OK' if has else 'FAIL'}] 列表含列: {kw}") ok = ok and has # 2) 物料明细 material/1: 入库=100, 消耗=30, 含入库流水日期 s, html, _ = req(c, "GET", "/material/1", cookie=cookie) print(f"[material/1] status={s} len={len(html)}") assert s == 200, f"material_detail status={s}" for kw in ["入库总数量", "消耗总数量", "入库流水", "当前在库"]: has = kw in html print(f" [{'OK' if has else 'FAIL'}] 明细含: {kw}") ok = ok and has has_date = "2026-09-11" in html print(f" [{'OK' if has_date else 'FAIL'}] 入库流水含入库日期 2026-09-11") ok = ok and has_date has_in = "100.00" in html print(f" [{'OK' if has_in else 'FAIL'}] 入库总数量显示 100.00") ok = ok and has_in has_out = "30.00" in html print(f" [{'OK' if has_out else 'FAIL'}] 消耗总数量显示 30.00") ok = ok and has_out # 3) BOM 明细含规格型号 s, html, _ = req(c, "GET", f"/bom/{bid}", cookie=cookie) print(f"[bom/{bid}] status={s} len={len(html)}") assert s == 200, f"bom_detail status={s}" for kw in ["型号 / 规格", "单套用量", "组成物料"]: has = kw in html print(f" [{'OK' if has else 'FAIL'}] BOM明细含: {kw}") ok = ok and has for spec in ["X1500", "FH8626V100", "CFW32C7UL_BUA"]: has = spec in html print(f" [{'OK' if has else 'FAIL'}] BOM明细含物料规格型号 {spec}") ok = ok and has c.close() print("\n" + ("✅ 新功能端到端验证通过" if ok else "❌ 存在未通过项")) return 0 if ok else 1 finally: stop_exe(p) if __name__ == "__main__": sys.exit(main())