pr-check-static

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

PR Check — Static Analysis (B1–B21)

PR检查——静态分析(B1–B21)

Overview

概述

Checks for runtime bugs, silent logic errors, unused code, and test correctness — things that compile fine but break at runtime or make tests unreliable.

检查运行时bug、静默逻辑错误、未使用代码以及测试正确性——这些问题在编译时不会报错,但会在运行时崩溃或导致测试不可靠。

Required Workflow

必备工作流

Step 1: Understand the diff

步骤1:理解代码差异

Run
git diff main...HEAD
or read the list of modified files to understand the scope.
运行
git diff main...HEAD
或查看修改文件列表,了解变更范围。

Step 2: Apply the Checklist

步骤2:应用检查清单

Work through each category. Flag every issue with file path and line number.

逐一检查每个分类。标记每个问题的文件路径和行号。

Checklist

检查清单

B1. Module Initialisation Timing

B1. 模块初始化时机

  • Are any
    const FOO = process.env.BAR
    declarations at module top-level (outside a class)?
  • Env vars must be read via
    ConfigService
    at runtime, not
    process.env
    at import time.
  • Pattern to flag:
    const FOO = process.env.BAR
    at top of file (not inside a class method or factory).
  • 是否有
    const FOO = process.env.BAR
    这样的声明位于模块顶层(类外部)?
  • 环境变量必须在运行时通过
    ConfigService
    读取,而非在导入时通过
    process.env
    读取。
  • 需要标记的模式:文件顶层的
    const FOO = process.env.BAR
    (不在类方法或工厂函数内部)。

B2. Silent Logic Bugs

B2. 静默逻辑错误

  • ||
    chains between non-empty string constants always resolve to the first truthy operand — use
    as const
    array or union type instead.
  • Optional fields (
    field?: T
    ) used in comparisons like
    field <= 0
    silently return
    false
    when
    undefined
    .
  • Check if reordering a
    ||
    chain changed which value is evaluated first.
  • 非空字符串常量之间的
    ||
    链式判断始终会解析为第一个真值操作数——请改用
    as const
    数组或联合类型。
  • 可选字段(
    field?: T
    )用于
    field <= 0
    这类比较时,当值为
    undefined
    会静默返回
    false
  • 检查
    ||
    链式判断的顺序调整是否改变了优先求值的对象。

B3. Unused Code

B3. 未使用代码

  • Unused imports — especially after refactors.
  • Variables assigned an initial value that is always overwritten before first read — declare without initial value.
  • 未使用的导入——尤其是重构之后的。
  • 变量被赋予初始值,但在首次读取前总是被覆盖——请声明时不要加初始值。

B4. Test Assertion No-Ops

B4. 测试断言无操作

  • Scan all test files for
    .toHaveBeenCalled
    without
    ()
    .
  • Same for
    .toBeDefined
    ,
    .toBeTruthy
    ,
    .toBeFalsy
    ,
    .toBeNull
    — all require
    ()
    .
  • 扫描所有测试文件,查找不带
    ()
    .toHaveBeenCalled
  • .toBeDefined
    .toBeTruthy
    .toBeFalsy
    .toBeNull
    同理——都必须带
    ()

B5. Flaky Tests from Random Data

B5. 随机数据导致的不稳定测试

  • Tests where
    faker
    generates a value that could randomly collide with a hardcoded value in the mock or the system under test.
  • Use explicit hardcoded values for boundary conditions.
  • 测试中
    faker
    生成的值可能与模拟数据或被测系统中的硬编码值随机冲突。
  • 边界条件请使用明确的硬编码值。

B6. Redundant Type Definitions

B6. 冗余类型定义

  • New interfaces structurally identical to existing types — search before defining.
  • 新定义的接口与现有类型在结构上完全一致——定义前请先搜索。

B7. Wrong Test Title

B7. 错误的测试标题

  • Test description says "X fails" but the mock actually simulates "Y fails".
  • Read the test name and then read the
    mockRejectedValue
    /
    mockResolvedValue
    calls — they must match.
  • 测试描述写的是“X失败”,但模拟实际是“Y失败”。
  • 阅读测试名称,再查看
    mockRejectedValue
    /
    mockResolvedValue
    调用——两者必须匹配。

B8. Missing Critical Assertions

B8. 缺失关键断言

  • Happy-path tests: are all critical side effects asserted (state changes, service calls, DB writes)?
  • Guard/skip tests: are downstream methods asserted
    .not.toHaveBeenCalled()
    ?
  • 正常流程测试:是否所有关键副作用都有断言(状态变更、服务调用、数据库写入)?
  • 守卫/跳过测试:是否断言下游方法
    .not.toHaveBeenCalled()

B9. URL Path Construction

B9. URL路径构造

  • Same path string used for both signature generation and the actual HTTP call?
  • Base URL missing a required path prefix (e.g.,
    /api
    )?
  • 签名生成和实际HTTP调用是否使用了相同的路径字符串?
  • 基础URL是否缺少必要的路径前缀(例如
    /api
    )?

B10. Status Mapper Completeness

B10. 状态映射器完整性

  • Does at least one status value map to each terminal internal state (
    'finalised'
    ,
    'failed'
    )?
  • When a mapper returns
    null
    or an unknown status, is that logged + Sentry notified?
  • 每个终端内部状态(
    'finalised'
    'failed'
    )是否至少有一个状态值与之映射?
  • 当映射器返回
    null
    或未知状态时,是否有日志记录并通知Sentry?

B11. API Response Envelope Type Accuracy

B11. API响应信封类型准确性

  • TypeScript type for each API response must match the actual envelope key from the API docs.
  • Don't assume
    { data: [] }
    — verify:
    { orders: [] }
    ,
    { order: {} }
    ,
    { shipments: [] }
    , etc.
  • 每个API响应的TypeScript类型必须与API文档中的实际信封键匹配。
  • 不要默认是
    { data: [] }
    ——请验证:
    { orders: [] }
    { order: {} }
    { shipments: [] }
    等。

B12. Webhook Controller Must Not Swallow All Exceptions

B12. Webhook控制器不得吞噬所有异常

  • Blanket try-catch returning
    200 OK
    means failed webhooks are permanently lost.
  • Unexpected errors should propagate or return 4xx/5xx so the provider retries.
  • 用try-catch直接返回
    200 OK
    会导致失败的Webhook永久丢失。
  • 意外错误应向上传播或返回4xx/5xx状态码,以便提供者重试。

B13. No-Op Stubs Must Have No Side Effects

B13. 无操作存根不得有副作用

  • A deferred/stub method must not still call
    updateLastSync
    , fire notifications, or write DB records.
  • A true no-op: only a comment or
    return
    .
  • 延迟/存根方法不得仍调用
    updateLastSync
    、发送通知或写入数据库记录。
  • 真正的无操作:仅包含注释或
    return
    语句。

B14. Duplicate Error Handling in Strategy and Caller

B14. 策略与调用者中重复的错误处理

  • If a method catches, logs, Sentries, and rethrows — the caller must NOT catch and log the same error again.
  • Double-catch = duplicate audit log entries + duplicate Sentry alerts.
  • 如果某个方法捕获、记录、上报Sentry并重新抛出错误——调用者不得再次捕获并记录相同的错误。
  • 双重捕获会导致重复的审计日志条目和重复的Sentry告警。

B15. Enum Constants in Comparisons

B15. 比较中使用枚举常量

  • When a TypeScript enum exists for event types or status strings, use it — don't compare against string literals.
  • 当存在事件类型或状态字符串的TypeScript枚举时,请使用枚举——不要直接与字符串字面量比较。

B16. Dead Type Checks in Catch Blocks

B16. Catch块中的无效类型检查

  • instanceof AxiosError
    in a DB/repository catch block will never be true — it's dead code.
  • Match the expected error type to what the wrapped operation actually throws.
  • 在数据库/仓库的catch块中使用
    instanceof AxiosError
    永远不会为true——这是无效代码。
  • 请将预期错误类型与被包装操作实际抛出的错误类型匹配。

B17. Database Transactions for Multi-Step Writes

B17. 多步骤写入的数据库事务

  • Multiple sequential saves (2+) across entities must be wrapped in a TypeORM transaction.
  • Without a transaction, partial failure leaves orphaned records.
  • 跨实体的多个连续保存操作(2个及以上)必须用TypeORM事务包裹。
  • 没有事务的话,部分失败会留下孤立记录。

B18. Race Condition / TOCTOU on State Guards

B18. 状态守卫中的竞态条件/TOCTOU

  • Check-then-act is not atomic — two concurrent requests can both pass the check before either updates state.
  • Fix: atomic conditional update —
    UPDATE table SET flag = false WHERE id = $1 AND flag = true
    .
  • Or: wrap in a transaction with
    SELECT FOR UPDATE
    .
  • 先检查后操作不是原子性的——两个并发请求可能都通过检查,然后才更新状态。
  • 修复方法:原子条件更新——
    UPDATE table SET flag = false WHERE id = $1 AND flag = true
  • 或者:用
    SELECT FOR UPDATE
    包裹在事务中。

B19. Missing Null Check on
findOne()

B19.
findOne()
缺失空值检查

  • findOne()
    returns
    null
    when no record matches — accessing without guard causes silent data corruption.
  • Pattern:
    if (!record) throw new NotFoundException('record not found');
  • findOne()
    在无匹配记录时返回
    null
    ——未加守卫直接访问会导致静默数据损坏。
  • 模式:
    if (!record) throw new NotFoundException('record not found');

B20. Credentials in Documentation Files

B20. 文档文件中的凭证

  • Real API tokens in
    .md
    files or API test transcripts are committed to git history permanently.
  • Use clearly fake placeholders:
    YOUR_BEARER_TOKEN_HERE
    ,
    <api-token>
    .
  • Applies to staging credentials too.
  • .md
    文件或API测试记录中的真实API令牌会被永久提交到git历史中。
  • 请使用明确的假占位符:
    YOUR_BEARER_TOKEN_HERE
    <api-token>
  • Staging环境的凭证也适用此规则。

B21. Input Normalization Consistency

B21. 输入归一化一致性

  • New flows must normalize user-provided strings (email, username) the same way as existing flows.
  • Check how the login/existing flow handles the same field —
    trim().toLowerCase()
    ,
    @Transform
    , SQL
    LOWER(TRIM(...))
    .

  • 新流程必须与现有流程以相同方式归一化用户提供的字符串(邮箱、用户名)。
  • 检查登录/现有流程如何处理相同字段——
    trim().toLowerCase()
    @Transform
    、SQL
    LOWER(TRIM(...))

Step 3: Report Findings

步骤3:报告发现

For each issue found:
[B1] path/to/file.ts:3
Severity: High
Issue: reads process.env at module-load time before ConfigModule bootstraps.
Fix: inject ConfigService, read inside method.
对于每个发现的问题:
[B1] path/to/file.ts:3
Severity: High
Issue: reads process.env at module-load time before ConfigModule bootstraps.
Fix: inject ConfigService, read inside method.

Step 4: Summary

步骤4:总结

  • Total issues by severity (High / Medium / Low)
  • Files most affected
  • Verdict: ready / needs fixes
  • 按严重程度分类的问题总数(高/中/低)
  • 受影响最严重的文件
  • 结论:可合并/需要修复