
CASE V1 :: 目标地址校验：只允许带主机名的 http/https
  ok   接受 https://docs.example.test/guide?id=7
  ok   接受 http://example.test/a
  ok   拒绝 javascript:alert(1) (scheme:javascript)
  ok   拒绝 data:text/html,hi (scheme:data)
  ok   拒绝 //evil.example.test/x (scheme:)
  ok   拒绝 /relative/path (scheme:)
  ok   拒绝 ftp://files.example.test/a (scheme:ftp)
  ok   拒绝 https://user-0042@login.example.test/ (userinfo)
  ok   拒绝 https://example.test/a\r\nSet-Cookie:x=1 (control-or-space)
  ok   拒绝 https:///nohost (host)

CASE A1 :: 16 个线程并发创建同一别名 promo-q4，目标各不相同
  ok   成功创建数 (expected=1, actual=1)
  ok   收到 AliasTaken 的调用数 (expected=15, actual=15)
  ok   库中目标等于唯一成功者提交的目标 (expected=https://shop.example.test/q4?variant=3, actual=https://shop.example.test/q4?variant=3)

CASE NC1 :: 负向对照：先查后写 + ON CONFLICT DO UPDATE（覆盖式 upsert）
  note 报告成功的调用数 16，其目标已被覆盖的调用数 15
  ok   negative control exposes: 多个创建者都被告知成功，其中至少一个的目标已被他人覆盖

CASE A2 :: 随机码冲突：插入失败后换码重试；连续冲突达到上限则报错
  ok   最终使用的码 (expected=Zxy98765, actual=Zxy98765)
  ok   冲突重试次数 (expected=2, actual=2)
  ok   已有行未被覆盖 (expected=https://example.test/existing, actual=https://example.test/existing)
  ok   连续 5 次冲突后报错: code space exhausted after 5 collisions

CASE A3 :: 16 线程 × 50 次随机码创建
  ok   返回码数 (expected=800, actual=800)
  ok   不同码数 (expected=800, actual=800)
  ok   库中行数 (expected=800, actual=800)

CASE R1 :: 重定向、未知码、过期、方法限制与响应头
  ok   活跃短链状态码 (expected=302, actual=302)
  ok   Location (expected=https://docs.example.test/guide?id=7, actual=https://docs.example.test/guide?id=7)
  ok   302 的 Cache-Control (expected=no-store, actual=no-store)
  ok   未知码状态码 (expected=404, actual=404)
  ok   404 的 Cache-Control (expected=no-store, actual=no-store)
  ok   过期前访问 (expected=302, actual=302)
  ok   过期后访问（条目仍在缓存 TTL 内） (expected=410, actual=410)
  ok   POST 状态码 (expected=405, actual=405)
  ok   405 带 Allow 头（RFC 9110 §15.5.6） (expected=GET, HEAD, actual=GET, HEAD)

CASE R2 :: 撤销生效窗口，真实 HTTP 测量：轮询开启（poll=100ms, TTL=2000ms）
  ok   撤销前访问（同时装入缓存） (expected=302, actual=302)
  ok   撤销后版本 (expected=2, actual=2)
  ok   撤销最终生效（返回 410）
  note 撤销提交后仍返回 302 的请求数 1，首次 410 响应距提交 29 ms，相邻两次请求发起间隔最大 14 ms（本机观察值）
  ok   撤销后每个 302 依据的缓存条目年龄都小于 TTL（最大 14 ms）
  ok   观察窗口不超过 TTL + 采样间隔 + 100ms 余量
  note 轮询淘汰次数 1，回源加载次数 2
  ok   之后的访问保持 410 (expected=410, actual=410)
  ok   重复撤销不再产生新版本 (expected=-1, actual=-1)

CASE R3 :: 撤销生效窗口，真实 HTTP 测量：轮询关闭（只剩 TTL=1500ms 上界）
  ok   撤销前访问（同时装入缓存） (expected=302, actual=302)
  ok   撤销后版本 (expected=2, actual=2)
  ok   撤销最终生效（返回 410）
  note 撤销提交后仍返回 302 的请求数 69，首次 410 响应距提交 1533 ms，相邻两次请求发起间隔最大 49 ms（本机观察值）
  ok   撤销后每个 302 依据的缓存条目年龄都小于 TTL（最大 1483 ms）
  ok   观察窗口不超过 TTL + 采样间隔 + 100ms 余量
  note 轮询淘汰次数 0，回源加载次数 2
  ok   之后的访问保持 410 (expected=410, actual=410)
  ok   重复撤销不再产生新版本 (expected=-1, actual=-1)

CASE R4 :: 回源读在撤销提交前、缓存填充在轮询淘汰后：版本守卫拒绝旧读
  ok   请求 A 已读到 ACTIVE 行并停在填充缓存之前
  ok   轮询线程已处理撤销变更（已见版本） (expected=2, actual=2)
  ok   请求 A 依据撤销前读到的行返回 302 (expected=302, actual=302)
  note 请求 A 完成后的下一次访问状态 410，回源加载次数 2，轮询淘汰次数 0
  ok   下一次访问回源并返回 410 (expected=410, actual=410)
  ok   旧读没有进入缓存，第二次访问重新回源 (expected=2, actual=2)

CASE NC3 :: 负向对照：去掉版本守卫，同一交错让旧条目留在缓存直到 TTL
  ok   请求 A 已读到 ACTIVE 行并停在填充缓存之前
  ok   轮询线程已处理撤销变更（已见版本） (expected=2, actual=2)
  ok   请求 A 依据撤销前读到的行返回 302 (expected=302, actual=302)
  note 请求 A 完成后的下一次访问状态 302，回源加载次数 1，轮询淘汰次数 0
  ok   negative control exposes: 轮询已处理撤销，旧条目仍被写入缓存并继续返回 302

CASE R5 :: 两个撤销交错提交：变更日志写入锁使序号顺序等于提交顺序
  ok   X 撤销前访问（装入缓存） (expected=302, actual=302)
  ok   Y 撤销前访问（装入缓存） (expected=302, actual=302)
  ok   撤销 A（X）已写入变更记录、尚未提交
  note A 未提交时 B 是否已提交: false，此时轮询已见 Y 版本 0
  ok   撤销 A 提交后的版本 (expected=2, actual=2)
  ok   撤销 B 提交后的版本 (expected=2, actual=2)
  note seq(X)=5 seq(Y)=6，提交 500ms 后轮询已见版本 X=2 Y=2，访问状态 X=410 Y=410
  ok   A 提交前 B 在写入锁上等待
  ok   序号顺序与提交顺序一致（A 先）
  ok   轮询见到 X 的撤销 (expected=2, actual=2)
  ok   X 返回 410 (expected=410, actual=410)
  ok   Y 返回 410 (expected=410, actual=410)

CASE NC4 :: 负向对照：不取写入锁，小序号晚提交，轮询游标越过它
  ok   X 撤销前访问（装入缓存） (expected=302, actual=302)
  ok   Y 撤销前访问（装入缓存） (expected=302, actual=302)
  ok   撤销 A（X）已写入变更记录、尚未提交
  note A 未提交时 B 是否已提交: true，此时轮询已见 Y 版本 2
  ok   撤销 A 提交后的版本 (expected=2, actual=2)
  ok   撤销 B 提交后的版本 (expected=2, actual=2)
  note seq(X)=7 seq(Y)=8，提交 500ms 后轮询已见版本 X=0 Y=2，访问状态 X=302 Y=410
  ok   前提成立：小序号的 A 晚于 B 提交
  ok   negative control exposes: 轮询游标越过晚提交的小序号，X 的撤销被漏掉，缓存继续返回 302

CASE S1 :: 统计正常：20 次点击全部入库
  ok   302 次数 (expected=20, actual=20)
  ok   丢弃数 (expected=0, actual=0)
  ok   统计库点击数 (expected=20, actual=20)

CASE S2 :: 统计库连接失败：重定向不受影响，队列满后丢弃并计数，恢复后补写
  ok   统计库不可达期间 302 次数 (expected=30, actual=30)
  ok   写入器记录失败批次 7，最后错误: 08001 Connection to localhost:1 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.
  ok   有界队列满后丢弃 21 次
  ok   入队数 + 丢弃数 = 302 次数 (expected=30, actual=30)
  ok   恢复后统计库点击数 = 入队数 (expected=9, actual=9)

CASE S3 :: 统计表被 ACCESS EXCLUSIVE 锁住：重定向仍返回 302
  ok   锁持有期间 302 次数（客户端超时 1000ms） (expected=20, actual=20)
  ok   写入器因锁等待失败 2 次，最后错误: 55P03 ERROR: canceling statement due to lock timeout
  ok   释放锁后点击数 = 入队数 (expected=20, actual=20)

CASE NC2 :: 负向对照：统计同步写在重定向路径上，统计表被锁时重定向超时
  note 3 次请求中 302=0，客户端超时=3
  ok   negative control exposes: 统计表锁直接阻塞重定向

CASE S4 :: 统计重放：提交成功但确认丢失，批次重写不重复计数
  note 批次尝试 3，失败 1，插入行 10，主键去重行 1
  ok   确认丢失的批次被重试
  ok   重试批次中有行被 click_id 去重
  ok   实际插入行数 (expected=10, actual=10)
  ok   统计库点击数 (expected=10, actual=10)

SUMMARY passed=86 failed=0
