You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
83 lines
3.6 KiB
83 lines
3.6 KiB
# -*- coding: utf-8 -*-
|
|
"""r2 票 02 静态对账:扫描 e2e-incr.py 全部 api()/api_raw() 调用,对照 dump_api_out.txt 基准。
|
|
|
|
与票 01 scan_calls.py 同款正则;只扫增量脚本、落 calls-incr.json——票 01 的 calls-r2.json 为冻结产物不动。
|
|
产出(stdout + calls-incr.json):
|
|
- 增量调用 × 基准命中(零未命中 = PASS)
|
|
- 票 02 范围 10 端点逐一核对(calls-expected 清单全覆盖)
|
|
"""
|
|
import json
|
|
import re
|
|
import io
|
|
import sys
|
|
|
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
|
|
|
SCRIPT = r'e:\code\crm-backend-matt\.scratch\customer-e2e\e2e-incr.py'
|
|
DUMP = r'e:\code\crm-backend-matt\.scratch\customer-rework\dump_api_out.txt'
|
|
OUTJSON = r'e:\code\crm-backend-matt\.scratch\customer-e2e-r2\calls-incr.json'
|
|
|
|
# 票 02 范围(票 01 对账锁定:客户域未覆盖 19 中恰此 10 个)
|
|
EXPECTED = {
|
|
('POST', '/api/customer/agreement/create'),
|
|
('POST', '/api/customer/agreement/edit'),
|
|
('POST', '/api/customer/agreement/delete'),
|
|
('GET', '/api/customer/agreement/detail'),
|
|
('GET', '/api/customer/agreement/list'),
|
|
('GET', '/api/rule/customer/dedup'),
|
|
('POST', '/api/rule/customer/dedup/save'),
|
|
('GET', '/api/preference/view-form/get'),
|
|
('POST', '/api/preference/view-form/save'),
|
|
('POST', '/api/customer/company-lookup/batch-update'),
|
|
}
|
|
|
|
endpoints = set()
|
|
for line in open(DUMP, encoding='utf-8'):
|
|
m = re.match(r'^(GET|POST) (\S+)', line.strip())
|
|
if m:
|
|
endpoints.add((m.group(1), m.group(2)))
|
|
print(f'基准端点(dump_api_out.txt 全模块):{len(endpoints)}')
|
|
|
|
src = open(SCRIPT, encoding='utf-8').read()
|
|
|
|
# 预解析模块级路径常量(NAME = '/api/...')——dedup/view-form 调用走常量而非字面量
|
|
consts = dict(re.findall(r"^([A-Z_]+)\s*=\s*'(/api/[^']+)'", src, re.M))
|
|
|
|
|
|
def _resolve(path):
|
|
return consts.get(path, path)
|
|
|
|
|
|
calls = set()
|
|
for m in re.finditer(r"api(?:_raw)?\(\s*\w+,\s*'(GET|POST)',\s*(?:f?)'([^']+)'", src):
|
|
calls.add((m.group(1), _resolve(m.group(2)))) # 字面量路径(含 f-string 内无插值的形态)
|
|
for m in re.finditer(r"api(?:_raw)?\(\s*\w+,\s*'(GET|POST)',\s*([A-Z_]{3,})\b", src):
|
|
p = consts.get(m.group(2))
|
|
if p:
|
|
calls.add((m.group(1), p)) # 模块常量路径(DEDUP_G/VF_G 等)
|
|
print(f'路径常量:{consts}')
|
|
print(f'\n增量脚本调用端点:{len(calls)}')
|
|
|
|
unknown = sorted(c for c in calls if c not in endpoints)
|
|
print('\n== 命中情况 ==')
|
|
for m, p in sorted(calls):
|
|
tag = 'OK' if (m, p) in endpoints else '!!未命中'
|
|
print(f' [{tag}] {m} {p}')
|
|
|
|
missing_expected = sorted(EXPECTED - calls)
|
|
extra = sorted(calls - EXPECTED - endpoints)
|
|
print(f'\n== 票 02 范围核对 == 期望 10,实调 {len(calls & EXPECTED)},缺 {len(missing_expected)}')
|
|
for m, p in missing_expected:
|
|
print(f' - 缺 {m} {p}')
|
|
for m, p in sorted(calls - EXPECTED):
|
|
in_base = (m, p) in endpoints
|
|
print(f' · 范围外{"(基准内,复用读端点等)" if in_base else "!!"} {m} {p}')
|
|
|
|
json.dump({'incr_calls': sorted(f'{m} {p}' for m, p in calls),
|
|
'unknown': [f'{m} {p}' for m, p in unknown],
|
|
'expected_scope': sorted(f'{m} {p}' for m, p in EXPECTED),
|
|
'missing_expected': [f'{m} {p}' for m, p in missing_expected]},
|
|
open(OUTJSON, 'w', encoding='utf-8'), ensure_ascii=False, indent=1)
|
|
print(f'\n✔ 落盘 calls-incr.json')
|
|
print('静态对账结论:' + ('PASS(零未命中 + 10 端点全覆盖)' if not unknown and not missing_expected
|
|
else f'FAIL(未命中 {len(unknown)} / 范围缺 {len(missing_expected)})'))
|
|
|