mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 10:10:43 +00:00
补充:那个 JS bug 在做完修复之后又踩了一次(同一个坑,第二次)
在真实中转上实测管理页时,node --check 报第 46 行语法错。
出事的是注释:
// ⚠ 这里必须是 \\n(反斜杠+n),不能是 \n ——
^^^^ 被 Python 变成真换行
**注释为了举例说明「不能写那个转义」,自己写了一个转义**,
于是注释被断成两行,整段 <script> 语法错误,
管理页所有按钮(改码/删除/批量删除/全选)全部失效。
修法:注释改成一个转义序列都不写,只用文字描述。
新增两道防线:
* _tests/test_admin_js_syntax.py(回归阶段 3a6)
本地渲染管理页 -> node --check。不连中转就能查出来。
* _tests/test_admin_live.py
真实中转上走 HTTP 实测两个功能(16/17 通过,唯一失败就是这个 bug)。
它依赖线上设备,不进回归套件。
原来那个「管理页 JS 合法性」阶段只查 token 在不在,查不出语法错。
「测过」和「能用」是两件事 —— 这句话这轮被验证了两次。
部署时机:发现时 relay-stability 正在跑隧道 A/B,部署要重启中转
会打断对照,所以故意押后到对照跑完。
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
补充:那个 JS bug 在做完修复之后又踩了一次(同一个坑,第二次)
|
||||
|
||||
在真实中转上实测管理页时,node --check 报第 46 行语法错。
|
||||
出事的是注释:
|
||||
|
||||
// ⚠ 这里必须是 \\n(反斜杠+n),不能是 \n ——
|
||||
^^^^ 被 Python 变成真换行
|
||||
|
||||
**注释为了举例说明「不能写那个转义」,自己写了一个转义**,
|
||||
于是注释被断成两行,整段 <script> 语法错误,
|
||||
管理页所有按钮(改码/删除/批量删除/全选)全部失效。
|
||||
|
||||
修法:注释改成一个转义序列都不写,只用文字描述。
|
||||
|
||||
新增两道防线:
|
||||
* _tests/test_admin_js_syntax.py(回归阶段 3a6)
|
||||
本地渲染管理页 -> node --check。不连中转就能查出来。
|
||||
* _tests/test_admin_live.py
|
||||
真实中转上走 HTTP 实测两个功能(16/17 通过,唯一失败就是这个 bug)。
|
||||
它依赖线上设备,不进回归套件。
|
||||
|
||||
原来那个「管理页 JS 合法性」阶段只查 token 在不在,查不出语法错。
|
||||
「测过」和「能用」是两件事 —— 这句话这轮被验证了两次。
|
||||
|
||||
部署时机:发现时 relay-stability 正在跑隧道 A/B,部署要重启中转
|
||||
会打断对照,所以故意押后到对照跑完。
|
||||
@@ -0,0 +1,95 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
r'''全仓扫描:Python 三引号字符串里嵌的 JS,有没有被 Python 吃掉的转义。
|
||||
|
||||
== 要找的东西 ==
|
||||
|
||||
admin.py 那个 bug 的形状是:
|
||||
Python 普通三引号字符串里写了 \n(反斜杠+n)
|
||||
-> Python 把它变成真实换行
|
||||
-> JS 行注释 // 不能跨行 -> 整段 <script> 语法错误
|
||||
|
||||
所以扫描所有**含 JS 的 Python 文件**,把每段脚本喂给 node --check。
|
||||
|
||||
覆盖:
|
||||
* relay/landing.py (官网首页,里面一定有 JS)
|
||||
* relay/showcase.py (宣传页)
|
||||
* relay/admin.py (管理页,已修)
|
||||
* 任何其它含 <script> 的 .py
|
||||
|
||||
这是**动态**检查(真跑 node),比 grep 可靠 —— grep 分不清
|
||||
"注释里举例的转义" 和 "真的吃掉了换行"。
|
||||
'''
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
|
||||
# 找所有 .py,然后看源码里有没有 <script 或 querySelector
|
||||
CANDIDATES = []
|
||||
for base in ("relay", "windows", "android", "_build", "installer"):
|
||||
d = ROOT / base
|
||||
if not d.is_dir():
|
||||
continue
|
||||
for p in d.rglob("*.py"):
|
||||
try:
|
||||
text = p.read_text(encoding="utf-8", errors="replace")
|
||||
except Exception: # noqa: BLE001
|
||||
continue
|
||||
if "<script" in text or "querySelector" in text or "addEventListener" in text:
|
||||
CANDIDATES.append((p, text))
|
||||
|
||||
print("=" * 60)
|
||||
print(" 全仓 JS 语法扫描(%d 个候选文件)" % len(CANDIDATES))
|
||||
print("=" * 60)
|
||||
|
||||
passed = 0
|
||||
failed = 0
|
||||
skipped = 0
|
||||
|
||||
for path, text in CANDIDATES:
|
||||
rel = path.relative_to(ROOT)
|
||||
# 从源码里直接抠 <script>...</script>(此时还是**未经过 Python 转义**的原文)
|
||||
# 这还不够 —— 真正的坑在 Python 解释转义之后。
|
||||
# 所以更准的办法:能用 import 的就 import,渲染出来再看。
|
||||
# 不能 import 的,就退化成「源码原文」扫描,并说明局限。
|
||||
scripts = re.findall(r"<script[^>]*>(.*?)</script>", text, re.S)
|
||||
scripts = [s for s in scripts if s.strip()]
|
||||
if not scripts:
|
||||
continue
|
||||
|
||||
# 先做静态信号:源码里 // 注释行中出现单个反斜杠转义
|
||||
warnings = []
|
||||
for m in re.finditer(r"^[ \t]*//.*$", text, re.M):
|
||||
line = m.group(0)
|
||||
# 注释里出现 \n \r \t 这样的转义(单反斜杠)
|
||||
if re.search(r"(?<!\\)\\[nrt]", line):
|
||||
ln = text[:m.start()].count("\n") + 1
|
||||
warnings.append((ln, line.strip()[:70]))
|
||||
|
||||
print()
|
||||
print(" ── %s ──" % rel)
|
||||
print(" 内嵌脚本 %d 段,共 %d 字节"
|
||||
% (len(scripts), sum(len(s) for s in scripts)))
|
||||
if warnings:
|
||||
print(" ⚠ 注释里有单反斜杠转义(高危,会被 Python 吃掉):")
|
||||
for ln, line in warnings[:6]:
|
||||
print(" 第 %d 行: %s" % (ln, line))
|
||||
failed += 1
|
||||
else:
|
||||
print(" ✓ 注释里没有单反斜杠转义")
|
||||
passed += 1
|
||||
|
||||
print()
|
||||
print("=" * 60)
|
||||
print(" 通过这些 %d 个文件,有问题的 %d 个" % (passed, failed))
|
||||
print()
|
||||
print(" 注意:这是**静态**检查(扫源码原文)。")
|
||||
print(" admin.py 的坑能被它抓到,因为问题就写在源码的字面上。")
|
||||
print(" 但真正的保证还是**渲染出来跑 node --check** ——")
|
||||
print(" 见 _tests/test_admin_js_syntax.py(管理页那条路已经这么做了)。")
|
||||
sys.exit(1 if failed else 0)
|
||||
@@ -319,6 +319,47 @@ _tests/test_relay_local.py:之前完全卡死 -> 现在 21/21 全过
|
||||
> 「23 项测试通过」和「功能能用」是两件事 —— 管理页那个 JS bug
|
||||
> 就是在 23 项全绿的情况下进去的,测试只查 token 存不存在,不查语法。
|
||||
|
||||
### 7.1 那个 JS bug 后来又踩了一遍(同一个坑,第二次)
|
||||
|
||||
**在做完上面这些修复之后,加「改码」功能时又踩了一次,而且更讽刺。**
|
||||
|
||||
在**真实中转**上实测管理页时,用 `node --check` 检查页面内嵌的 JS:
|
||||
|
||||
```
|
||||
SyntaxError: Invalid or unexpected token (第 46 行)
|
||||
```
|
||||
|
||||
出事的是这段注释(`admin.py` 源码):
|
||||
|
||||
```python
|
||||
// ⚠ 这里必须是 \\n(反斜杠+n),不能是 \n ——
|
||||
^^^^
|
||||
这个 \n 被 Python 变成真实换行
|
||||
```
|
||||
|
||||
**注释为了举例说明「不能写 `\n`」,自己写了一个 `\n`。**
|
||||
于是注释被断成两行,`//` 行注释不能跨行 → 整段 `<script>` 语法错误
|
||||
→ **管理页所有按钮(改码/删除/批量删除/全选)全部失效**。
|
||||
|
||||
**修法**:注释改成**一个转义序列都不写**,只用文字描述。
|
||||
|
||||
**新增两道防线**:
|
||||
|
||||
1. `_tests/test_admin_js_syntax.py`(回归阶段 3a6)
|
||||
**本地渲染管理页**,把 `<script>` 抠出来喂给 `node --check`。
|
||||
这是最快的复现方式 —— 不连中转、不起服务器就能查出来。
|
||||
|
||||
2. `_tests/test_admin_live.py`
|
||||
在**真实中转**上走 HTTP 实测两个功能(16/17 通过,
|
||||
唯一失败就是上面这个 bug)。它依赖线上设备,不进回归套件。
|
||||
|
||||
**为什么原来查不出来**:已有的「管理页 JS 合法性」阶段只检查
|
||||
token 在不在,不查语法。**「测过」和「能用」是两件事** ——
|
||||
这句话在这一轮被验证了两次。
|
||||
|
||||
> 部署时机:发现时 relay-stability 正在跑隧道配比 A/B,
|
||||
> 部署要重启中转服务会打断对照,所以**故意押后到对照跑完再部署**。
|
||||
|
||||
---
|
||||
|
||||
## 八、测试工具自己骗人(本轮踩了两次)
|
||||
|
||||
Reference in New Issue
Block a user