diff --git a/tests/m5a_live_db_harness.py b/tests/m5a_live_db_harness.py index 49aa292..14b3176 100644 --- a/tests/m5a_live_db_harness.py +++ b/tests/m5a_live_db_harness.py @@ -76,22 +76,11 @@ CREATE TABLE `pbl_evidence` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci """ -# 沙箱侧适配(**不是被测逻辑**,仅补 DDL 缺口,collector 源码一行未改): -# models/pbl_evidence.json 的 id 是 str(32) NOT NULL 且无 default,而 collector -# _upsert_evidence 的 INSERT 列集合来自 record(不含 id),写完才 SELECT 回读 id -# ——即它假定 id 由 DB 侧生成。MariaDB STRICT_TRANS_TABLES 下这会直接 1364 -# "Field 'id' doesn't have a default value'". 真实部署 DDL 必须由 build.sh/xls2ddl 生成带默认值 -# 的 id 列或应用层 getID 赋值;本 harness 用 BEFORE INSERT trigger 在**沙箱库**内 -# 等价补上该生成能力,并在日志显式声明为发现项(见 DISC.1 / IDM.10)。 -TRG_PBL_EVIDENCE_ID = """ -CREATE TRIGGER `trg_pbl_evidence_id` BEFORE INSERT ON `pbl_evidence` -FOR EACH ROW -BEGIN - IF NEW.`id` IS NULL OR NEW.`id` = '' THEN - SET NEW.`id` = REPLACE(UUID(), '-', ''); - END IF; -END -""" +# DISC.1 闭环(M5a-closure3 本轮删除):原先此处定义沙箱侧 `BEFORE INSERT` trigger +# 等价补 id 生成能力,用来掩盖「collector 的 INSERT 不带 id」。现已整体删除——harness +# 不再建任何 trigger,pbl_evidence.id 必须由应用层 pbl_evidence.pk.gen_pk 生成 +# (collector.py:235 record 构造处;api.py:102/278 两条插入路径),DDL 与 models 保持 +# VARCHAR(32) NOT NULL、无 AUTO_INCREMENT、无 DEFAULT。 DDL_PBL_RUNTIME_EVENT = """ CREATE TABLE `pbl_runtime_event` ( @@ -331,11 +320,12 @@ def phase_idempotent(collector, box): print('\n--- 阶段 2:同 source_event_id + evidence_type 连续采集两次(真库)---') box.ddl(DDL_PBL_RUNTIME_EVENT) box.ddl(DDL_PBL_EVIDENCE) - box.ddl(TRG_PBL_EVIDENCE_ID) - print('[DISC.1] 沙箱适配声明:pbl_evidence.id 在 models 定义里 NOT NULL 无 default,' - '而 collector 的 INSERT 不带 id(写完才回读)——沙箱库用 BEFORE INSERT ' - 'trigger 等价补 id 生成能力,被测源码零改动;真实部署需由 DDL 默认值或' - '应用层 getID 落定(已作为发现项上报,不在本 harness 修复范围)。') + print('[DISC.1-已闭环] 本沙箱库**不建任何 trigger**(BEFORE INSERT 适配已删除):' + 'pbl_evidence.id 由应用层 gen_pk 在 INSERT 前显式生成(单一落点 collector.py:235),' + '建表 DDL 与 models/pbl_evidence.json 一致——VARCHAR(32) NOT NULL、' + '无 AUTO_INCREMENT、无 DEFAULT。若主键生成未真实生效,本阶段 INSERT 会以 1364' + ' Field "id" does not have a default value 失败,harness 立刻暴露,' + '不存在 DB 侧兜底的伪 PASS。') print('建表后 SHOW TABLES: %s' % [list(r.values())[0] for r in box.raw('SHOW TABLES')]) print('uk_ev_dedup 索引定义(SHOW INDEX 原样回显):') @@ -431,12 +421,27 @@ def phase_idempotent(collector, box): id_rows = box.raw('SELECT id, tenant_id, source_event_id, evidence_type ' 'FROM `pbl_evidence` ORDER BY source_event_id, evidence_type') ids = [str(r.get('id') or '') for r in id_rows] - print('证据行主键回显(id 由沙箱 trigger 生成,非空且互异): %s' % ids) - check('IDM.10 落库行 id 非空且互不相同(沙箱 id 生成适配生效,非静默吞错)', + print('证据行主键回显(id 由应用层 gen_pk 生成,非空/≤32/前缀 ev/互异): %s' % ids) + _pkmod = sys.modules.get('pbl_evidence.pk') or importlib.import_module('pbl_evidence.pk') + print('gen_pk 生成器来源: %s | PK_MAX_LEN=%s | collector 侧已导入 gen_pk: %s' + % (_pkmod.pk_source(), _pkmod.PK_MAX_LEN, hasattr(collector, 'gen_pk'))) + check('IDM.10 落库行 id 非空且互不相同(应用层 gen_pk 生效,非 DB 侧兜底)', len(ids) == 2 and all(ids) and len(set(ids)) == 2, 'ids=%s' % ids) + check('IDM.11 id 形态为 gen_pk 产物(长度<=PK_MAX_LEN=32 且前缀 ev,非 trigger 的 ev+32hex=34)', + all(0 < len(i) <= 32 and i.startswith('ev') for i in ids), + 'lens=%s prefixes=%s' % ([len(i) for i in ids], [i[:2] for i in ids])) + check('IDM.12 沙箱库内不存在任何 trigger(BEFORE INSERT 适配已删除)', + _no_trigger(box), 'SHOW TRIGGERS 原样回显见上一行') return s2, rows2 +def _no_trigger(box): + """证明 harness 真实链路:沙箱库内 trigger 数量必须为 0。""" + rows = box.raw('SHOW TRIGGERS') + print('SHOW TRIGGERS 原样回显(%d 条): %s' % (len(rows), rows)) + return len(rows) == 0 + + def _dedup_matches(rows): from hashlib import md5 for r in rows: