#!/usr/bin/env python3 # -*- coding: utf-8 -*- """入口页资源版本号注入(破浏览器缓存)的纯逻辑测试。 从 drouter-web.py 抽出 _ASSET_RE / _inject_asset_version 两个片段, 在隔离命名空间里执行,不导入整个后端(那会连数据库、读证书)。 """ import ast import os import re import sys import tempfile SRC = os.path.join(os.path.dirname(__file__), '..', 'backend', 'drouter-web.py') code = open(SRC, encoding='utf-8').read() tree = ast.parse(code) want = {'_ASSET_RE', '_inject_asset_version'} picked = [] for node in tree.body: if isinstance(node, ast.Assign): for t in node.targets: if isinstance(t, ast.Name) and t.id in want: picked.append(node) break elif isinstance(node, ast.FunctionDef) and node.name in want: picked.append(node) found = set() for node in picked: if isinstance(node, ast.Assign): for t in node.targets: if isinstance(t, ast.Name): found.add(t.id) else: found.add(node.name) missing = want - found assert not missing, '未能从源码抽取:%s' % ', '.join(sorted(missing)) # 建一个假 WEB_DIR,里面放两个已知 mtime 的文件 tmp = tempfile.mkdtemp() for name in ('app.js', 'app.css'): p = os.path.join(tmp, name) open(p, 'w', encoding='utf-8').write('/* %s */' % name) os.utime(p, (1_700_000_000, 1_700_000_000)) ns = {'os': os, 're': re, 'WEB_DIR': tmp} exec(compile(ast.Module(body=picked, type_ignores=[]), SRC, 'exec'), ns) inject = ns['_inject_asset_version'] HTML = ('\n' '\n' '\n' '\n' '\n') out = inject(HTML.encode('utf-8')).decode('utf-8') fails = [] def ck(name, cond, extra=''): print(' %-42s %s %s' % (name, 'OK ' if cond else 'FAIL', extra)) if not cond: fails.append(name) print('资源版本号注入:') ck('app.css 追加了 ?v=', '/app.css?v=1700000000' in out) ck('app.js 追加了 ?v=', '/app.js?v=1700000000' in out) ck('已带查询串的 theme.css 不被改动', '/theme.css?v=123' in out and '/theme.css?v=123?v=' not in out) ck('非 js/css 的 logo.svg 不动', '/logo.svg' in out and '/logo.svg?v=' not in out) ck('不会重复追加(幂等)', inject(out.encode('utf-8')).decode('utf-8').count('/app.js?v=') == 1) ck('输出仍是合法 utf-8 且保留 doctype', out.startswith('')) ck('单引号属性也能处理', "/app.css?v=" in inject(b"").decode('utf-8')) print('\n结果:%d 项,失败 %d 项' % (7, len(fails))) raise SystemExit(1 if fails else 0)