修管理页测试的引号敏感 + 补 netPanel id

重做管理页时把 HTML 从 class=pick(无引号)改成了
class="pick"(标准写法),但测试里写死了无引号形式,
于是 5 个断言集体误报失败。

HTML 两种写法都合法,测试不该关心这个 ——
给两个测试加了 _has(),带不带引号都认。

另外给新管理页的体检卡补回 id=netPanel(语义合理,
也顺手让测试钩子还在)。

同时删掉 _build/_missing_tail.py(恢复事故时的临时文件,
静态检查把它当成真模块查,报了 8 个 undefined name)。

验证:管理页网络体检 15/15、中转批量删除 24/24、
全局 32 个阶段全部通过。
This commit is contained in:
xmz7891
2026-10-07 09:45:10 +08:00
parent 1f05084f51
commit 160dc3765e
7 changed files with 174 additions and 66 deletions
+17
View File
@@ -0,0 +1,17 @@
修管理页测试的引号敏感 + 补 netPanel id
重做管理页时把 HTML 从 class=pick(无引号)改成了
class="pick"(标准写法),但测试里写死了无引号形式,
于是 5 个断言集体误报失败。
HTML 两种写法都合法,测试不该关心这个 ——
给两个测试加了 _has(),带不带引号都认。
另外给新管理页的体检卡补回 id=netPanel(语义合理,
也顺手让测试钩子还在)。
同时删掉 _build/_missing_tail.py(恢复事故时的临时文件,
静态检查把它当成真模块查,报了 8 个 undefined name)。
验证:管理页网络体检 15/15、中转批量删除 24/24、
全局 32 个阶段全部通过。
-60
View File
@@ -1,60 +0,0 @@
def _esc(text) -> str:
return (str(text).replace("&", "&amp;").replace("<", "&lt;")
.replace(">", "&gt;").replace('"', "&quot;"))
def _kb(value: int) -> str:
if value < 1024:
return f"{value} B"
if value < 1024 * 1024:
return f"{value / 1024:.1f} KB"
return f"{value / 1024 / 1024:.1f} MB"
def _uptime(seconds: int) -> str:
days, rest = divmod(int(seconds), 86400)
hours, rest = divmod(rest, 3600)
minutes = rest // 60
if days:
return f"{days} 天 {hours} 小时"
if hours:
return f"{hours} 小时 {minutes} 分"
return f"{minutes} 分"
# ======================================================================
# 启动
# ======================================================================
class Server(ThreadingHTTPServer):
daemon_threads = True
allow_reuse_address = True
request_queue_size = 64
def main() -> int:
DATA_DIR.mkdir(parents=True, exist_ok=True)
try:
server = Server((BIND, PORT), Handler)
except OSError as exc:
print(f"监听 {BIND}:{PORT} 失败:{exc}")
return 1
log(f"中转服务器 v{VERSION} 启动,监听 {BIND}:{PORT}")
log(f"长轮询 {POLL_SECONDS:.0f} 秒 · 队列上限 "
f"{MAX_QUEUE_BYTES // 1024} KB · 数据目录 {DATA_DIR}")
key = admin_key()
log(f"管理页:http://<本机>:{PORT}/?key={key}")
log(f"(钥匙也在 {DATA_DIR / 'admin_key.txt'})")
try:
server.serve_forever()
except KeyboardInterrupt:
log("收到中断,退出")
finally:
server.server_close()
return 0
if __name__ == "__main__":
sys.exit(main())
+116
View File
@@ -0,0 +1,116 @@
# -*- coding: utf-8 -*-
r'''让管理页测试对"属性有没有引号"不敏感。
背景:
旧版管理页的 HTML 是 `class=pick`(无引号),
重做时改成了标准的 `class="pick"`(带引号)。
测试里写死了 `"class=pick" in html`,于是集体误报失败。
HTML 两种写法都合法,测试不该关心这个。
所以给测试加个 _has():两种写法都认。
'''
from __future__ import annotations
import ast
import re
import sys
from pathlib import Path
TESTS = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote\_tests")
# 要改的测试文件和里面的断言
TARGETS = {
"test_admin_batch_delete.py": [
('check("有复选框(input.pick)", "class=pick" in html)',
'check("有复选框(input.pick)", _has(html, "class=pick"))'),
('check("有全选(pickAll)", "id=pickAll" in html)',
'check("有全选(pickAll)", _has(html, "id=pickAll"))'),
('check("有「删除选中」按钮", "id=delPicked" in html)',
'check("有「删除选中」按钮", _has(html, "id=delPicked"))'),
],
"test_admin_netcheck.py": [
('check("有网络体检面板", \'id=netPanel\' in html, "")',
'check("有网络体检面板", _has(html, "id=netPanel"), "")'),
('check("有「测一下」按钮", \'id=netBtn\' in html, "")',
'check("有「测一下」按钮", _has(html, "id=netBtn"), "")'),
],
}
# 插到 check() 定义附近
HELPER = '''
def _has(html: str, token: str) -> bool:
"""判断 html 里有没有这个属性,**带不带引号都算**。
旧版页面写的是 class=pick(无引号),
重做后改成了标准的 class="pick"(带引号)。
测试不该因为引号差异就报失败 —— HTML 两种都合法。
"""
if token in html:
return True
# class=pick -> class="pick" / id=netBtn -> id="netBtn"
match = re.match(r"^([A-Za-z-]+)=(.+)$", token)
if not match:
return False
attr, value = match.group(1), match.group(2)
return ('%s="%s"' % (attr, value)) in html
'''
def ensure_import_re(text: str) -> str:
if re.search(r"^import re$", text, re.M):
return text
# 插到第一个 import 之后
match = re.search(r"^(import \w+|from \w+ import .+)$", text, re.M)
if match:
return (text[:match.end()] + "\nimport re" + text[match.end():])
return "import re\n" + text
def main() -> int:
total = 0
for name, replacements in TARGETS.items():
path = TESTS / name
if not path.is_file():
print(" [跳过] %s 不存在" % name)
continue
text = path.read_text(encoding="utf-8")
if "_has(" in text and "def _has" in text:
print(" %s: 已经是新版" % name)
continue
changed = 0
for old, new in replacements:
if old in text:
text = text.replace(old, new, 1)
changed += 1
if changed == 0:
print(" %s: 没找到要改的断言" % name)
continue
text = ensure_import_re(text)
# 把 _has 插到 "def check(" 前面
marker = "def check("
index = text.find(marker)
if index < 0:
print(" [失败] %s 找不到 check()" % name)
continue
text = text[:index] + HELPER.strip() + "\n\n\n" + text[index:]
path.write_text(text, encoding="utf-8", newline="\n")
try:
ast.parse(text)
print(" %s: 改了 %d 处 + 加了 _has(),语法 OK" % (name, changed))
total += changed
except SyntaxError as exc:
print(" [失败] %s 行 %s: %s" % (name, exc.lineno, exc.msg))
print()
print(" 共改 %d 处" % total)
return 0
if __name__ == "__main__":
sys.exit(main())
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

+21 -3
View File
@@ -10,6 +10,7 @@
6. 管理页上有复选框、全选、删除选中按钮
"""
from __future__ import annotations
import re
import http.cookiejar
import json
@@ -34,6 +35,23 @@ passed = 0
failed = 0
def _has(html: str, token: str) -> bool:
"""判断 html 里有没有这个属性,**带不带引号都算**。
旧版页面写的是 class=pick(无引号),
重做后改成了标准的 class="pick"(带引号)。
测试不该因为引号差异就报失败 —— HTML 两种都合法。
"""
if token in html:
return True
# class=pick -> class="pick" / id=netBtn -> id="netBtn"
match = re.match(r"^([A-Za-z-]+)=(.+)$", token)
if not match:
return False
attr, value = match.group(1), match.group(2)
return ('%s="%s"' % (attr, value)) in html
def check(name, ok, detail=""):
global passed, failed
if ok:
@@ -120,9 +138,9 @@ def main() -> int:
print("一、管理页上有批量删除的控件")
html = opener.open(urllib.request.Request(BASE + "/zhongzhuanzhan", headers=_UA),
timeout=15).read().decode("utf-8")
check("有复选框(input.pick)", "class=pick" in html)
check("有全选(pickAll)", "id=pickAll" in html)
check("有「删除选中」按钮", "id=delPicked" in html)
check("有复选框(input.pick)", _has(html, "class=pick"))
check("有全选(pickAll)", _has(html, "id=pickAll"))
check("有「删除选中」按钮", _has(html, "id=delPicked"))
check("调的是 delete_many 接口", "/v1/admin/delete_many" in html)
check("复选框的 value 是 deviceId", 'value="pc-1"' in html, "")
+19 -2
View File
@@ -25,6 +25,23 @@ passed = 0
failed = 0
def _has(html: str, token: str) -> bool:
"""判断 html 里有没有这个属性,**带不带引号都算**。
旧版页面写的是 class=pick(无引号),
重做后改成了标准的 class="pick"(带引号)。
测试不该因为引号差异就报失败 —— HTML 两种都合法。
"""
if token in html:
return True
# class=pick -> class="pick" / id=netBtn -> id="netBtn"
match = re.match(r"^([A-Za-z-]+)=(.+)$", token)
if not match:
return False
attr, value = match.group(1), match.group(2)
return ('%s="%s"' % (attr, value)) in html
def check(name, ok, detail=""):
global passed, failed
if ok:
@@ -127,8 +144,8 @@ def main() -> int:
print("三、管理页上有这张卡片")
html = opener.open(urllib.request.Request(BASE + "/zhongzhuanzhan", headers=UA),
timeout=10).read().decode("utf-8")
check("有网络体检面板", 'id=netPanel' in html, "")
check("有「测一下」按钮", 'id=netBtn' in html, "")
check("有网络体检面板", _has(html, "id=netPanel"), "")
check("有「测一下」按钮", _has(html, "id=netBtn"), "")
check("JS 里调 netcheck 接口", "/v1/admin/netcheck" in html, "")
check("会显示隧道连接数", "tunnelConnections" in html, "")
+1 -1
View File
@@ -601,7 +601,7 @@ def render_admin(snap: dict, public: str, admin_path: str, codes: int,
</div>
<div class="tools">
<div class="tool">
<div class="tool" id="netPanel">
<h4>网络体检</h4>
<div class="d">看看这台机器是走局域网还是走隧道。</div>
<div class="out" id="netBody">还没测过。</div>