Skip to content

bugfix: 审计修复与全量测试覆盖 (v1.1.1 后续) - #17

Open
PythonSmall-Q wants to merge 7 commits into
devfrom
bugfix/audit-fixes-and-coverage
Open

PythonSmall-Q wants to merge 7 commits into
devfrom
bugfix/audit-fixes-and-coverage

Conversation

@PythonSmall-Q

Copy link
Copy Markdown
Member

概述

对 xmoj.tech 全页面逐项审计后的修复与接线批次(基于 v1.1.1),共 3 个提交:

🐛 Bug 修复(实跑验证)

  • parsers.ts 标题清洗误删数字开头标题(「24小时降水量」→「小时降水量」)
  • parsers.ts JUDGE_RESULTS 补 code 3(运行并评判),修复评测中自动轮询不启动
  • parsers.ts userRating 不再误抓判题机名(saturn)与编译参数(-O2)
  • rank.ts 排名 AC 颜色按 G 通道判定,修复 #33ff33/#73ff73/#93ff93/#aaffaa 及一血 #aaaaff 全部漏标(OI 榜此前 0/12 正确)
  • parsers.ts 倒计时保持网站源顺序(不再按天数重排)
  • sources.ts 剥离 getsource.php 尾部 /****** 元信息注释块(源码查看/对比/导出 AC 均受益)

🔌 半成品接线(后端早已就绪的部分)

  • 设置页「清除缓存」入口(IPC 链路原本全通)
  • 题目页「查看标程」(fetchStdCode 链路原本全通,清掉死代码 ref)
  • 比赛内提交携带 cid/pid(SubmitRequest/buildSubmitForm 原本全通),提交页显示比赛标识、手动换题自动脱离上下文
  • 状态页「代码长度」列(parser 原本已解析)
  • 更新检查对话框接真实结果(版本比较+更新条目+发布页跳转)
  • 导出 AC 代码标题改用全量题库(此前 1386 题仅 6 题有真标题)
  • 比赛创建者显示 + 修复服务端 createdBy 字段名不匹配
  • 通知已读上报(ReadBBSMention/ReadMailMention)——重启不再反复弹同一通知
  • 私信通知深跳到具体会话(新增 mail-target store)
  • 短消息:会话列表最新消息预览、单条未读徽标

🧪 测试(716 条新断言 + npm run test:offline 一键套件)

  • parser-deep / api-mapping / lib-coverage / ipc-contract(三端 IPC 一致性)/ stores
  • bugfix-regressions 改为 CI 安全(live-dump 守卫跳过)

验证

  • npm run typecheck 0 错误
  • npm run test:offline 全部通过(716+ 断言)

备注

  • 真实账号发帖/回帖(Turnstile)与比赛内实际提交建议合并后人工点验一次
  • 建议合并后发布 v1.1.2

- Added a submit button for contest problems in Contests.vue.
- Updated problem submission handling to support contest context in Submit.vue.
- Introduced a dialog to view standard solutions in ProblemView.vue.
- Enhanced mail functionality with target user management in Mail.vue and a new mail-target store.
- Implemented cache clearing functionality in Settings.vue.
- Added code length display in Status.vue for submission records.
- Improved error handling and user feedback in various components.
- Updated API services to include new mail and BBS mention read functions.
- Added regression tests for various features to ensure stability.
Cut the HUSTOJ /******...****** footer block (extending to EOF) from getsource.php responses so source view, compare and AC export no longer carry the metadata tail. Only a line-initial banner running to EOF is stripped; inline or string-literal occurrences are untouched.
- tests/parser-deep.ts: title cleaning, JUDGE_RESULTS incl. code 3 -> pending, status row matrix, rank AC color palette, countdown order, fetchSource stripping (134 assertions)
- tests/api-mapping.ts: createdBy->creator mapping, buildSubmitForm cid/pid priority, parseLanguages (73)
- tests/lib-coverage.ts: markdown extensions, discussion permissions, rating colors, format/poller/languages, update check states (110)
- tests/ipc-contract.ts: three-end IPC contract consistency across types.ts/main.ts/preload/api.ts (291)
- tests/stores.ts: navigation contest context, mail-target, panel, dialog, preferences (108)
- bugfix-regressions made CI-safe (live-dump guarded with existsSync skips)
- register npm scripts incl. test:offline chain
@claude

This comment was marked as resolved.

Comment thread src/stores/navigation.ts Resolved
Comment thread tests/loader-stores.mjs Resolved
Comment thread src/pages/Contests.vue Resolved
Comment thread electron/rank.ts Resolved
Comment thread tests/ipc-contract.ts Resolved
Comment thread src/pages/ProblemView.vue Resolved
- navigation: clear activeContest on navigate()/viewProblem() to stop contest context leaking into unrelated submissions
- Contests: derive contestPid from problems.indexOf instead of letter math
- rank: tighten AC color rule to green-dominant (excludes #ffffff/#ffff00/#00ffff), keep first-blood #aaaaff
- judge-api: fetchContestDetail guards a missing contest object
- update: treat prerelease versions as older than the same stable; default update dialog focus to 关闭
- Mail: reveal pending thread after loadThreads so scroll target exists
- ProblemView: lazy-load standard solution on button click instead of prefetching per problem
- tests: strip comments in new suites; loader-stores resolves @/ alias; add regressions for contest clearing, color palette, missing-contest guard, prerelease compare
@claude

This comment was marked as resolved.

Comment thread src/pages/ProblemView.vue Resolved
Comment thread electron/sources.ts Resolved
Comment thread electron/rank.ts Resolved
- ProblemView: actually remove the standard-solution prefetch in load(); openStd now always retries (guard is only stdLoading) and loadStd resets std/stdError before each attempt
- sources: tempered banner regex so a mid-file /****** section comment can no longer swallow code up to the real footer banner; add regression for section-comment + footer banner coexistence
- rank: explicit parentheses around the AC color condition
- parsers: replace lowercase-regex judger heuristic with an explicit judger-name whitelist (saturn/hustoj/jupiter/mars/judge), case-insensitive
@claude

This comment was marked as resolved.

Comment thread electron/parsers.ts Resolved
Comment thread src/pages/ProblemView.vue Resolved
- parsers: restore 8-space indentation of the userRating block (edit artifact)
- ProblemView: openStd reuses a loaded standard solution and only refetches after a failure or when empty
@claude

claude Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Claude finished @PythonSmall-Q's task in 3m 24s —— View job


本轮复核(针对 443fcd4)

  • 核对上一轮遗留的 2 项 nit
  • 复查 8323dd4..443fcd4 是否引入回归
  • 仓库规则核对(无注释 / 无 webview / 主进程 HTTP / CACHE_VERSION)
  • 实际运行 npm run typecheck 与 npm run test:offline(本环境无 node_modules,npm ci 与 node 均需授权,见文末)

上一轮 nit 核对

上一轮意见 结论
electron/parsers.ts:643 缩进被误改为 16 空格 ✅ 已恢复为 8 空格,与 630–655 行上下文一致
ProblemView.openStd() 每次打开都重新请求标程 ✅ 守卫改为 !stdLoading.value && !std.value && problem.value

对第二项我按状态机逐条推演过,行为正确:

  • 已成功加载后再次打开对话框直接复用 std,不再多发一次 IPC/网络往返;
  • 切题时 watch(activeProblemId) → load(),其中 stdRequest += 1、stdLoading = false、std/stdError 置空,因此切题后仍会重新加载;
  • load() 递增 stdRequest 后,在途的 loadStd 会在 finally 里因 current !== stdRequest 跳过,不会把新题目的 loading 态误关;
  • 加载失败时 std 仍为 null,下次打开会重新请求(重试路径保留)。

新发现

本轮改动仅两行,未发现回归或阻塞问题。另有 1 条非阻塞 nit 已留 inline(std 为空时对话框没有空态,建议补 v-else 兜底文案)。

仓库规则核对

  • 无注释:443fcd4 未新增注释;src/、electron/、tests/ 全量此前已核对 ✅
  • 无 <webview> / window.open / target="_blank" / shell.openExternal:本轮未涉及;全 PR 复核未命中新增调用 ✅
  • xmoj.tech 读取走主进程:本轮无渲染进程请求;更新检查等仍走 window.xmoj.app.fetchJson IPC ✅
  • CACHE_VERSION:本轮未改变缓存 payload 形状,无需 bump ✅

验证限制

本环境没有 node_modules,npm ci 与 node 执行均需授权且未获通过,因此 npm run typecheck 与 npm run test:offline 仍未实际运行,以上结论均为静态阅读。建议以 CI 结果为准。

结论

上一轮 2 项 nit 均已落实,未发现新缺陷。从我的角度可以合并。

Comment thread src/pages/ProblemView.vue Resolved
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
@claude

claude Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Claude finished @PythonSmall-Q's task in 8m 12s —— View job


本轮复核(针对 22edeef)

  • 核对最新提交 22edeef 的改动(ProblemView.vue 标程空态)
  • 复核上一轮遗留 nit 的落实情况
  • 复查 443fcd4..22edeef 是否引入回归
  • 仓库规则核对(无注释 / 无 <webview> 等 / 主进程 HTTP / CACHE_VERSION)
  • 实际运行 npm run typecheck 与 npm run test:offline(本环境无 node_modules,node / npm 执行均需授权且未获通过,见文末「验证限制」)

最新提交核对

src/pages/ProblemView.vue 本次只加了一行:

<p v-else class="text-sm text-muted-foreground">该题暂无标程</p>
  • 它是 stdLoading / stdError / std 之后的最终 v-else,正好补上「Success: true 但 StdCode 为空串」时的空白态;
  • openStd() 打开对话框时同步把 stdLoading 置为 true,所以不会先闪一下空态;
  • 加载失败或返回空串时 std 仍为 null,下次打开会因 !std.value 重新请求;成功加载后则复用 std,重试/空态两条路径都成立,状态机无回归。

上一轮 nit

上一轮意见 结论
electron/parsers.ts:650 userRating 块缩进 16 空格 ✅ 已恢复 8 空格
ProblemView.openStd() 成功加载后重复请求标程 ✅ 守卫为 !stdLoading && !std
std 为空时对话框没有空态 ✅ 本次提交补上

新发现(均非阻塞)

  1. CACHE_VERSION 未随 payload 形状变化 bump(建议处理,已留 inline 评论) —— 本 PR 在 electron/judge-api.ts 的 fetchContestList / fetchContestDetail 中把条目由 createdBy 归一化为 creator,而 contest:list:* / contest:detail:* 都在 electron/cache.ts 的 PERSISTED_PREFIXES 内、会落盘到 page-cache.json。升级用户的旧缓存条目只有 createdBy、没有 creator,形状与新版产出一致性被破坏。实际不会崩溃(Contests.vue 是 cacheOnly → fresh 自愈),但按仓库约定应把 CACHE_VERSION 从 3 提到 4;顺带还能让旧 parser 缓存的题库标题(problemset:v2:all)与倒计时顺序(home:snapshot)立即失效——ExportAcCode.vue 只有 fetchAllProblems(true)、没有 fresh 兜底,否则「数字开头标题被截断」的修复对已缓存用户不会马上生效。
    Fix this →
  2. electron/rank.ts:60 style 提取偏窄(nit) —— background(?:-color)?\s*:\s*([^;]+) 会把整段声明值(可能含 !important、url(...) 等附加 token)当成颜色,而 isAcceptedRankColor 只认纯 6 位 hex / rgb(a);一旦站方输出 background-color: #33ff33 !important 就会漏标 AC。目前观测到的线上样式不含这些 token,所以不影响现状;若想更稳,可只抓第一个颜色 token:/background(?:-color)?\s*:\s*(#[0-9a-f]{6}|rgba?\([^)]*\))/i。

仓库规则核对

  • 无注释:22edeef 未新增注释;对 src/、electron/、tests/、scripts/ 全量新增行再次扫描,0 命中 ✅
  • 无 <webview> / window.open / target="_blank" / shell.openExternal:新增行扫描 0 命中 ✅。补充说明:更新提示走的是既有的 window.xmoj.app.openLink IPC,主进程内部对「发布页」类链接会调用 shell.openExternal,rich-links / Downloads 已在用同一套入口;按字面口径没有新增禁用 API,如维护者口径更严格可再确认这一点
  • xmoj.tech 读取走主进程:新增行没有渲染进程 fetch;更新检查仍走 window.xmoj.app.fetchJson(主进程 HTTP 客户端)✅
  • CACHE_VERSION:⚠️ 见上,建议 bump 到 4

验证限制

本环境没有 node_modules,npm ci 与 node --experimental-strip-types …(含 npm run typecheck / npm run test:offline)均被权限拦截、无法执行;以上结论均来自静态阅读,最终请以 CI 结果为准。

结论

最新提交正确收尾了上一轮唯一的 nit,未发现新缺陷。除 CACHE_VERSION 与 rank.ts 颜色提取两处建议外没有阻塞项,从我的角度可以合并。


Comment thread electron/judge-api.ts
return { ok: false, status: result.status, data: null, error: result.error };
}
const list = (result.data.list ?? []).map((contest) => {
const { createdBy, ...rest } = contest;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

建议同步 bump CACHE_VERSION(非阻塞)。

本 PR 在 fetchContestList / fetchContestDetail 里把条目的 createdBy 归一化为 creator 并剥离 createdBy。这两类结果由 electron/main.ts 以 contest:list:* / contest:detail:* 写入 PageCache,而这两个前缀都在 electron/cache.ts 的 PERSISTED_PREFIXES 中,会持久化到 page-cache.json:升级用户的旧缓存条目只有 createdBy、没有 creator,与新版产出的 payload 形状不再一致。

实际影响有限:Contests.vue 的 load() / openContest() 都是 cacheOnly → fresh,同一次打开就会被新数据覆盖,不会崩。但按仓库「payload 形状变化要 bump CACHE_VERSION」的约定,建议把它从 3 提升到 4。顺带还能让旧 parser 写入的题库标题(problemset:v2:all)与倒计时顺序(home:snapshot)立即失效——否则 ExportAcCode.vue 只调用 fetchAllProblems(true)、没有 fresh 兜底,旧的「数字开头标题被截断」会继续留在导出文件名里。

@zsTree0830

Copy link
Copy Markdown
Member
image

@PythonSmall-Q 你这个做的更新检查为什么是 XMOJ Script 的

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants