Ctrl K
ComunidadGuías…

TEST_CASES.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# FlareDrive 完整功能测试用例

> 2026-08-29 制定,覆盖 16 个模块共 ~150 条用例。优先级:**P0** 每轮必跑(冒烟 + 核心路径),
> **P1** 功能轮必跑,**P2** 发布前跑。执行环境与历史结果见 [TESTING.md](./TESTING.md)。
> 通用约束:所有破坏性操作只在自己创建的测试目录内进行。

## 执行环境

- 本地:`npm run build && npx wrangler pages dev build`(:8788),`.dev.vars` 提供 `WEBDAV_USERNAME/WEBDAV_PASSWORD`,R2 数据在 `.wrangler/state/`(删除即重置)。
- 素材:小文本/中文文本、PNG/JPG(含透明 PNG)、SVG、MP4、MP3、PDF、ZIP、>100MB 大文件、0 字节文件、特殊字符文件名(`空 格 & # ? 中文 、引号"'.txt`、超长名 255 字节)、`.hidden` 隐藏文件、嵌套目录 a/b/c/d。
- 工具:浏览器(亮/暗两套)+ curl(开放接口/WebDAV)。

---

## 1. 认证与会话(TC-AUTH)

| 编号 | 用例 | 步骤 | 预期 | 优先级 |
|------|------|------|------|--------|
| AUTH-01 | 正确凭据登录 | 输入正确用户名密码,点登录 | 进入文件页,凭据写入 localStorage `flaredrive.auth` | P0 |
| AUTH-02 | 错误密码拒绝 | 输入错误密码登录 | 提示「用户名或密码错误」,停留登录框 | P0 |
| AUTH-03 | 空凭据校验 | 用户名或密码留空登录 | 无法提交或提示必填 | P1 |
| AUTH-04 | 会话恢复 | 登录后刷新页面 | 保持登录,无需重输 | P0 |
| AUTH-05 | 凭据失效弹登录 | localStorage 写入错误凭据后刷新 | 任意请求 401 → 清除凭据并弹登录框 | P1 |
| AUTH-06 | 退出登录 | 账户菜单 → 退出 | 清除凭据,弹登录框,文件区清空 | P0 |
| AUTH-07 | 未启用 WebDAV | 移除 .dev.vars 重启后访问 | 接口 403「WebDAV protocol is not enabled」 | P2 |

## 2. 浏览与导航(TC-NAV)

| 编号 | 用例 | 步骤 | 预期 | 优先级 |
|------|------|------|------|--------|
| NAV-01 | 根目录列表 | 登录进入首页 | 显示根目录全部文件/文件夹,统计胶囊数量正确 | P0 |
| NAV-02 | 进入目录 | 单击文件夹 | 面包屑追加,hash 路由变为 `#/<dir>/`,列表刷新 | P0 |
| NAV-03 | 面包屑跳转 | 点击面包屑中间节点 | 跳到该层级 | P0 |
| NAV-04 | 返回上级 | 点返回按钮 / 面包屑「全部文件」 | 回到上级目录 | P0 |
| NAV-05 | 浏览器前进后退 | 进入目录后点浏览器后退/前进 | 路由与列表同步,不报错 | P1 |
| NAV-06 | 复制路径 | 点复制路径按钮 | 剪贴板为 `/a/b/` 形式路径,toast 成功 | P1 |
| NAV-07 | 直接打开深层路由 | 地址栏输入 `#/a/b/c/` 回车 | 直接进入该目录;不存在的目录显示空状态 | P1 |
| NAV-08 | 空目录空状态 | 进入空目录 | 显示「这里还没有文件」+ 上传/新建引导 | P1 |
| NAV-09 | 最近打开 | 打开文件/进入目录后点「最近」 | 最近记录按时间倒序,点击可跳转 | P2 |

## 3. 上传(TC-UP)

| 编号 | 用例 | 步骤 | 预期 | 优先级 |
|------|------|------|------|--------|
| UP-01 | 文件选择器上传 | 「上传文件」选 1 个小文件 | 队列出现→完成→列表出现,toast「已上传」 | P0 |
| UP-02 | 多文件上传 | 同时选 5 个文件 | 并发 2 顺序完成,全部出现在列表 | P0 |
| UP-03 | 拖拽上传 | 拖文件到窗口 | 全窗口虚线遮罩「松手上传到当前目录」,松手上传到当前目录 | P0 |
| UP-04 | 拖拽文件夹 | 拖入含子目录的文件夹 | 保持目录结构上传(子目录自动创建) | P1 |
| UP-05 | 粘贴图片上传 | 截图后 Ctrl/Cmd+V | 自动命名(时间戳)上传 | P1 |
| UP-06 | 记事本建文件 | 记事本写内容保存 | 以输入文件名上传为文本文件 | P1 |
| UP-07 | 重名自动改名 | 上传与现有文件同名的文件 | 前端改名 `name (2).ext`,不覆盖原文件 | P0 |
| UP-08 | 上传进度与暂停 | 传大文件时暂停/继续 | 进度条暂停不回退,继续后续传完成 | P1 |
| UP-09 | 失败重试与取消 | 断网上传后恢复 | 失败任务可手动重试成功;取消则移除队列 | P1 |
| UP-10 | >100MB 分块上传 | 网页上传 150MB 文件 | 自动分块(100MB/块),走 multipart 三段式,完成后可预览/下载 | P1 |
| UP-11 | 队列徽标 | 上传中看顶栏 | 上传图标显示进行中数量 Badge | P2 |
| UP-12 | 0 字节文件 | 上传 0 字节文件 | 成功,列表显示 0 B,可下载 | P1 |
| UP-13 | 类型过滤上传入口 | 仅看「图片」筛选时上传 | 上传不受筛选影响,完成后按筛选决定是否可见 | P2 |

## 4. 下载与预览(TC-PV)

| 编号 | 用例 | 步骤 | 预期 | 优先级 |
|------|------|------|------|--------|
| PV-01 | 图片预览 | 单击 PNG | 预览弹层显示图片,标题含文件名与大小,底部操作齐全 | P0 |
| PV-02 | 文本预览 | 单击 .txt | 显示内容与行号,JSON 自动格式化,含行号栏 | P0 |
| PV-03 | 文本 2MB 上限 | 预览 >2MB 文本文件 | 提示过大不渲染,提供下载 | P1 |
| PV-04 | PDF 预览 | 单击 .pdf | 内嵌 pdf.js 渲染 | P0 |
| PV-05 | 视频/音频预览 | 单击 MP4 / MP3 | 原生播放器可播放 | P1 |
| PV-06 | 不可预览类型 | 单击 .zip | 不弹预览,直接触发下载 | P0 |
| PV-07 | 预览切换 | 预览中点上一个/下一个、左右方向键 | 仅在同级可预览兄弟文件间切换,首尾按钮禁用 | P1 |
| PV-08 | 预览内操作 | 预览中下载/重命名/删除 | 操作成功且列表同步 | P1 |
| PV-09 | 下载文件 | 列表 ⋯ 菜单 → 下载 | 文件内容与上传一致(校验哈希) | P0 |
| PV-10 | 打包下载 | 多选含文件夹 → 下载 | 得到 zip,含目录结构与空目录占位 | P1 |
| PV-11 | 缩略图认证 | 私有模式上传图片后刷新 | 缩略图经认证正常显示,不裂图 | P0 |
| PV-12 | SVG 不内联 | 预览 .svg 文件 | 不作为图片内联(安全),按下载/文本处理 | P1 |
| PV-13 | 图片缩放平移 | 预览图片滚轮缩放、拖拽平移、双击复位 | 缩放 25%-400%,操作栏百分比可点复位,双击回到适应窗口 | P1 |
| PV-14 | 旋转自适应 | 旋转 90°/270° 竖图/横图 | 图片整体保持在容器内不溢出,百分比按旋转后计算 | P1 |
| PV-15 | 透明图棋盘格 | 上传透明 PNG,看缩略图与预览 | 缩略图与预览均有棋盘格衬底,亮暗主题各自适配 | P2 |
| PV-16 | 语言切换全面性 | 切到 English 走查所有面板 | 多选工具栏/排序菜单/预览动作栏/回收站/记事本/邀请面板无中文残留,类型筛选 Chip 与右键菜单即时跟随 | P1 |

## 5. 文件操作(TC-FILE)

| 编号 | 用例 | 步骤 | 预期 | 优先级 |
|------|------|------|------|--------|
| FILE-01 | 重命名文件 | F2 或菜单重命名 | 新名出现在列表,内容不变 | P0 |
| FILE-02 | 重命名目录 | 菜单重命名文件夹 | 目录连同子内容整体更名 | P0 |
| FILE-03 | 重名冲突 | 重命名为已存在的名字 | 报错提示,原名不变 | P1 |
| FILE-04 | 复制粘贴 | 复制 → 另一目录粘贴 | 两处都有,原文件保留 | P0 |
| FILE-05 | 剪切粘贴 | 剪切 → 另一目录粘贴 | 原处消失,新处出现(多选工具栏「粘贴 1 项」可见) | P0 |
| FILE-06 | 拖入文件夹移动 | 拖文件到文件夹卡片 | 移动到目标文件夹 | P1 |
| FILE-07 | 多选操作 | 多选 → 批量删除/移动 | 全部目标生效,计数正确 | P1 |
| FILE-08 | 右键菜单完整性 | 右键文件/文件夹 | 菜单含 打开/下载/重命名/移动/分享/复制/剪切/删除,文件夹无预览相关项 | P1 |
| FILE-09 | 特殊字符重命名 | 重命名为含空格/中文/`&#?` 的名字 | 操作成功,列表/下载/分享均正常 | P1 |
| FILE-10 | 非法名拒绝 | 重命名为 `..` 或空名 | 前端校验拒绝,不发请求 | P1 |
| FILE-11 | 新建文件夹 | 工具栏新建 | toast + 列表出现,统计更新 | P0 |
| FILE-12 | 同名新建 | 新建与现有同名的文件夹 | 报错提示已存在 | P1 |
| FILE-13 | 深层嵌套 | 建 a/b/c/d 四层并逐层进入 | 每层列表与面包屑正确 | P2 |
| FILE-14 | 隐藏文件开关 | 开/关「显示隐藏文件」 | `.hidden` 与系统垃圾文件随之显示/隐藏 | P2 |

## 6. 回收站(TC-TRASH)

| 编号 | 用例 | 步骤 | 预期 | 优先级 |
|------|------|------|------|--------|
| TRASH-01 | 软删除 | 删除文件并确认 | 原位消失;回收站显示原名/原路径/时间/大小 | P0 |
| TRASH-02 | 还原 | 回收站勾选 → 恢复 | 原路径重新出现,回收站清空该条 | P0 |
| TRASH-03 | 还原冲突 | 原路径已被同名新文件占用 | 返回 conflict 状态并明确提示,不覆盖 | P1 |
| TRASH-04 | 彻底删除 | 勾选 → 彻底删除 | 条目消失且不可再还原 | P1 |
| TRASH-05 | 清空回收站 | 点清空并确认 | 全部条目消失 | P1 |
| TRASH-06 | 空回收站状态 | 清空后查看 | 空状态插画与引导正确 | P2 |
| TRASH-07 | 目录软删除 | 删除整个文件夹 | 回收站记录目录,还原后子树完整 | P1 |
| TRASH-08 | 批量还原 | 多选还原 | 全部回到各自原路径 | P1 |

## 7. 搜索(TC-SEARCH)

| 编号 | 用例 | 步骤 | 预期 | 优先级 |
|------|------|------|------|--------|
| SRCH-01 | 当前目录过滤 | 搜索框输入当前目录内文件名片段 | 即时过滤(300ms 防抖),仅本层 | P0 |
| SRCH-02 | 当前目录无结果 | 搜索只存在于子目录的词 | 本范围 0 结果 | P0 |
| SRCH-03 | 全盘搜索 | 切「全盘搜索」同词 | 命中子目录文件,含路径信息 | P0 |
| SRCH-04 | 全盘加载更多 | 构造 >100 条命中 | 「加载更多」分页追加,无重复 | P1 |
| SRCH-05 | 快捷键 | `/` 或 Ctrl+K 聚焦搜索 | 输入框聚焦并全选;Esc 清空并失焦 | P1 |
| SRCH-06 | 特殊字符 | 搜索 `%\'"` 等字符 | 不报错,合理空结果 | P1 |
| SRCH-07 | 搜索不进入目录 | 搜索结果点击文件夹项 | 跳转进入该目录 | P2 |

## 8. 排序 / 筛选 / 视图(TC-VIEW)

| 编号 | 用例 | 步骤 | 预期 | 优先级 |
|------|------|------|------|--------|
| VIEW-01 | 名称升降序 | 排序菜单切换 | 目录恒在前;文件按 localeCompare(numeric)排序 | P0 |
| VIEW-02 | 大小/日期排序 | 切换字段 | 数值/时间正确升降 | P1 |
| VIEW-03 | 排序持久化 | 切换后刷新 | localStorage `flaredrive.sort` 保持 | P1 |
| VIEW-04 | 类型筛选 | 依次点 图片/视频/文档/其他 | 仅对应类型;文件夹始终显示 | P0 |
| VIEW-05 | 筛选与排序叠加 | 筛选后再排序 | 两者同时生效 | P1 |
| VIEW-06 | 网格/列表切换 | 点切换视图 | 布局切换,列表有粘性表头;持久化 | P0 |
| VIEW-07 | 密度切换 | 标准/紧凑 | 卡片尺寸与间距变化;持久化 | P2 |
| VIEW-08 | 统计胶囊 | 对比实际内容 | 「N 个文件夹 · N 个文件 · 共 X」与真实一致 | P1 |
| VIEW-09 | 骨架屏 | 弱网进入目录 | 先显示骨架再出内容,无布局跳动 | P2 |

## 9. 分享(TC-SHARE)

| 编号 | 用例 | 步骤 | 预期 | 优先级 |
|------|------|------|------|--------|
| SHARE-01 | 无提取码分享 | 创建分享(留空提取码) | 生成 `/share/<token>` 链接;新标签直接看到内容 | P0 |
| SHARE-02 | 带提取码分享 | 设置提取码创建 | 打开链接先出提取码表单 | P0 |
| SHARE-03 | 错误提取码 | 输错码提交 | 「提取码不正确」,不出内容 | P0 |
| SHARE-04 | 正确提取码 | 输对提交 | 内容内联展示(图片)/可下载 | P0 |
| SHARE-05 | 有效期选项 | 创建时选 1 天/7 天/30 天 | 面板显示对应有效期 | P1 |
| SHARE-06 | 过期访问 | 伪造过期时间戳访问 | HTTP 410 过期页 | P1 |
| SHARE-07 | 撤销分享 | 分享面板撤销 | 列表移除;原链接再访问 404/410 | P0 |
| SHARE-08 | 分享目录拒绝 | 对文件夹创建分享 | 400 提示仅支持文件 | P1 |
| SHARE-09 | 分享 Range | `curl -H "Range: bytes=0-10"` 访问分享直链 | 206 分段返回 | P2 |

## 10. API 密钥管理(TC-KEY)

| 编号 | 用例 | 步骤 | 预期 | 优先级 |
|------|------|------|------|--------|
| KEY-01 | GUI 创建 | API 面板创建密钥 | 明文仅显示一次 + 一次性警告;列表出现前缀 | P0 |
| KEY-02 | 自定义密钥 | 用自定义 key(8-256 位) | 创建成功可用;过短/含空白被拒 | P1 |
| KEY-03 | 有效期 | 创建 1 天有效期 | `expiresAt` 正确;到期后调用返回 401「已过期」 | P1 |
| KEY-04 | 作废 | 点作废 | 列表移除;旧 key 调用 401 | P0 |
| KEY-05 | 哈希存储 | 检查 R2 内部 `apikeys/*.json` | 仅 `keyHash`(SHA-256),无明文 | P1 |
| KEY-06 | 最近使用 | 用 key 调接口后面板查看 | lastUsedAt 更新 | P2 |
| KEY-07 | 双头鉴权 | 分别用 `Bearer` 与 `X-Api-Key` | 两种方式都通过 | P1 |

## 11. 开放接口(TC-API,curl)

| 编号 | 用例 | 请求 | 预期 | 优先级 |
|------|------|------|------|--------|
| API-01 | 上传 multipart | `POST /api/upload?path=d/` -F file | 201 `{key,name,size,path,overwritten:false}` | P0 |
| API-02 | 上传原始体 | `X-File-Name` + `--data-binary` | 201,内容一致 | P1 |
| API-03 | 重名改名 | 重复上传同名 | `name (2).ext`,原文件不动 | P1 |
| API-04 | 覆盖上传 | `?overwrite=1` | `overwritten:true`,内容被替换 | P1 |
| API-05 | 超限 413 | 上传 >100MB | 413 中文提示 | P1 |
| API-06 | 列表 | `GET /api/list?path=d/` | 文件含 size/uploaded/updated/etag;目录 isDir/size 0;目录在前 | P0 |
| API-07 | 列表边界 | path 为文件 / 不存在 / 空 | 400 提示用 download / 404 / 根目录列表 | P1 |
| API-08 | 下载 | `GET /api/download?path=` | 200 流式,Content-Disposition attachment,中文文件名 RFC5987 | P0 |
| API-09 | 下载边界 | 目录 / 不存在 / 内部前缀 | 400 / 404 / 400 | P1 |
| API-10 | 建目录 | `POST /api/mkdir {path}` | 201 `{created:true}`;父级自动补建 | P0 |
| API-11 | mkdir 幂等/冲突 | 重复建 / 同名文件存在 | 200 `{created:false}` / 409 | P0 |
| API-12 | mkdir 非法路径 | `..` 穿越与 `_$flaredrive$/` | 400 | P0 |
| API-13 | 重命名 | `POST /api/rename {from,to}` | 200 `{from,to}`;内容跟随 | P0 |
| API-14 | rename 冲突/缺失 | to 已存在 / from 不存在 / 目录 | 409(overwrite 除外)/ 404 / 400 | P1 |
| API-15 | 删除 | `DELETE /api/delete?path=` | 200 `{deleted:true}`;文件确实消失 | P0 |
| API-16 | 删除边界 | 目录 / 不存在 / 无 key | 400 / 404 / 401 | P1 |
| API-17 | 冲突备份 | `POST /api/backup?path=` | 200 `{from,to}`,to 为 `.conflict-<UTC戳>.ext`;重名自动 -2/-3 | P1 |
| API-18 | 无效密钥 | 伪造 key 调任意接口 | 401「无效的 API 密钥」 | P0 |
| API-19 | 鉴权缺失 | 不带 key | 401「缺少 API 密钥」 | P0 |
| API-20 | 会话接口 | Basic 调 `/api/keys` CRUD | GET/POST/DELETE 正常;明文仅 POST 响应一次 | P1 |
| API-21 | 会话接口拒 key | 用 API key 调 `/api/keys` | 401(管理接口仅 Basic) | P1 |
| API-22 | 打包归档 | `POST /api/archive {keys}` | zip 可解压、含目录结构与空目录 | P1 |

## 12. WebDAV(TC-DAV,curl)

| 编号 | 用例 | 请求 | 预期 | 优先级 |
|------|------|------|------|--------|
| DAV-01 | OPTIONS | 无凭据 `OPTIONS /webdav/` | 200 + DAV 头 "1, 2"(免认证) | P1 |
| DAV-02 | PROPFIND | Depth 0 / 1 / infinity | 207,层级正确 | P0 |
| DAV-03 | 认证失败 | 错误密码 PROPFIND | 401 | P0 |
| DAV-04 | MKCOL | 建目录(含多级父) | 201;重复建 405/409 | P1 |
| DAV-05 | PUT/GET | 上传并取回 | 201/200,内容与 Content-Type 一致;GET 支持 Range 206 | P0 |
| DAV-06 | PUT 覆盖 | If-Match / If-None-Match:* | 条件满足 201/412 | P2 |
| DAV-07 | 大文件 413 | PUT >100MB | 413 中文提示 | P1 |
| DAV-08 | COPY | 文件与目录 COPY(Depth) | 201;Overwrite:F 时目标存在 → 412 | P1 |
| DAV-09 | MOVE | 文件/目录 MOVE + Destination | 201;源消失 | P0 |
| DAV-10 | DELETE | 删文件/目录(递归) | 204;子树消失 | P0 |
| DAV-11 | LOCK/UNLOCK | 写锁与解锁 | 200,锁信息存 customMetadata;冲突 PUT 423 | P2 |
| DAV-12 | PROPPATCH | 设置 dead property | 207;PROPFIND 可读回 | P2 |
| DAV-13 | 内部前缀保护 | 对 `_$flaredrive$/` 操作 | 拒绝 | P0 |
| DAV-14 | 公共读 | `WEBDAV_PUBLIC_READ=1` 时无凭据 GET/PROPFIND | 允许;写仍 401 | P2 |

## 13. 主题与外观(TC-THEME)

| 编号 | 用例 | 步骤 | 预期 | 优先级 |
|------|------|------|------|--------|
| THEME-01 | 切换深色 | 外观菜单选深色 | 全局换肤,图标变月亮,对比度正常 | P0 |
| THEME-02 | 持久化 | 刷新页面 | 保持所选模式 | P0 |
| THEME-03 | 跟随系统 | 选跟随系统后切换 OS 外观 | 实时跟随亮/暗 | P1 |
| THEME-04 | 双模式巡检 | 亮/暗下走查 预览/对话框/右键菜单/上传队列 | 无白底残留、无不可读文本 | P0 |
| THEME-05 | 系统色差 | prefers-color-scheme: dark 且未选择时 | 首屏即为深色 | P2 |

## 14. 键盘导航(TC-KB)

| 编号 | 用例 | 步骤 | 预期 | 优先级 |
|------|------|------|------|--------|
| KB-01 | 方向键移动 | ↑↓←→ 逐项移动 | 焦点描边移动,自动 scrollIntoView | P0 |
| KB-02 | 焦点边界 | 在首项按 ↑ / 尾项按 ↓ | 停留不越界不报错 | P1 |
| KB-03 | Space 选中 | 焦点上按 Space | 复选框切换,多选工具栏出现 | P0 |
| KB-04 | Shift+方向 | Shift+↓ 连续扩展 | 逐项加入选择 | P1 |
| KB-05 | 全选 | Ctrl/Cmd+A | 全部选中;再按 Esc 全清 | P0 |
| KB-06 | Enter 打开 | 对文件/目录按 Enter | 文件开预览、目录进入 | P0 |
| KB-07 | F2 重命名 | 对文件按 F2 | 弹重命名对话框并预填 | P0 |
| KB-08 | Delete 删除 | 对选中项按 Delete | 弹「移入回收站」确认框 | P0 |
| KB-09 | Esc 清除 | 有选择/焦点时按 Esc | 清空选择与焦点 | P1 |
| KB-10 | 输入框隔离 | 焦点在搜索框按方向键/Space | 不触发文件导航 | P0 |
| KB-11 | 弹层隔离 | 预览/对话框打开时按 Delete | 不触发删除(hasOpenOverlay 拦截) | P0 |
| KB-12 | Backspace 返回上级 | 在子目录按 Backspace | 返回上一级目录;根目录时无操作;不弹删除确认 | P0 |
| KB-13 | 连续消息不覆盖 | 连续触发两个失败操作 | 两条 toast 依次展示(队列化),error 默认 8s | P1 |
| KB-14 | 失败重试入口 | 断网删除/移动/粘贴 | toast 带「重试」按钮,点击恢复成功;上传失败 toast 带「上传任务」入口 | P1 |

## 15. 响应式与移动端(TC-RWD)

| 编号 | 用例 | 步骤 | 预期 | 优先级 |
|------|------|------|------|--------|
| RWD-01 | 390px 布局 | 缩窄视口走查 | 底部导航出现,两列网格,顶栏收缩 | P0 |
| RWD-02 | 底部导航 | 点 文件/上传/新建 | 切换正确;上传打开文件选择 | P1 |
| RWD-03 | 列表列隐藏 | 列表视图 390px | 大小/日期列隐藏(sm/md 断点) | P1 |
| RWD-04 | 预览全屏 | 手机尺寸打开预览 | 全屏展示,关闭可达 | P1 |
| RWD-05 | 安全区 | iPhone 刘海屏模拟 | 底部导航避让 safe-area | P2 |
| RWD-06 | 触控目标 | 走查所有按钮 | ≥44px 或有足够间距 | P2 |

## 16. 边界与异常(TC-EDGE)

| 编号 | 用例 | 步骤 | 预期 | 优先级 |
|------|------|------|------|--------|
| EDGE-01 | 特殊字符文件名 | 上传 `空 格 & # ? 引号"'.txt` | 上传/重命名/预览/下载/分享全链路正常 | P1 |
| EDGE-02 | 超长文件名 | 255 字节文件名 | 列表截断显示(title 提示全名),操作不报错 | P1 |
| EDGE-03 | 中文名下载 | 下载中文文件 | RFC5987 编码正确,本地文件名完整 | P1 |
| EDGE-04 | 0 字节与空目录 | 上传 0B 文件、建空目录并归档 | 均正常;zip 含空目录占位 | P2 |
| EDGE-05 | 大目录性能 | 单目录 1000 文件 | 列表可滚动不卡死;排序/筛选可用 | P2 |
| EDGE-06 | 断网操作 | 断网后列目录/上传 | 明确错误提示,恢复后重试成功 | P1 |
| EDGE-07 | 并发写同名 | 两标签同时上传同名文件 | 后到者改名或覆盖,无数据损坏 | P2 |
| EDGE-08 | XSS 安全 | 上传名为 `<img src=x onerror=alert(1)>.txt` 的文件 | 列表按文本渲染,脚本不执行;预览转义 | P0 |
| EDGE-09 | 路径穿越 | mkdir/move 使用 `..` | 400 拒绝 | P0 |
| EDGE-10 | 内部目录保护 | UI/API/DAV 访问 `_$flaredrive$/` | 全部拒绝或不可见 | P0 |

## 17. 静态站点(TC-SITES)

| 编号 | 用例 | 步骤 | 预期 | 优先级 |
|------|------|------|------|--------|
| SITES-01 | 空状态 | 打开「站点」区块(无站点) | 空状态插画与引导正确;SITES_HOST 未配置时显示警示横幅 | P1 |
| SITES-02 | 站点列表 | 上传文件到 `sites/<slug>/` 后刷新 | 卡片显示 slug、静态/SPA 徽标、URL、文件数与大小(懒加载) | P1 |
| SITES-03 | zip 部署 | 卡片「部署 zip」→ 选 zip → 确认 | 上传队列出现对应任务;完成后站点文件与 zip 内容一致 | P0 |
| SITES-04 | 清空后部署 | 勾选「部署前清空」再部署 | 旧文件消失、新文件完整;SPA 开关保持原值 | P1 |
| SITES-05 | 覆盖部署 | 不勾选清空再部署同名 zip | 同名文件被覆盖(不出现「(2)」副本) | P1 |
| SITES-06 | SPA 回退 | 开启 SPA 开关 → 访问站点无此路径 | 返回 index.html(200),history 路由可深链刷新 | P0 |
| SITES-07 | 自定义 404 | 关闭 SPA、存在 404.html → 访问缺失路径 | 404 状态 + 自定义页内容 | P1 |
| SITES-08 | 管理文件 | 卡片「管理文件」 | 跳转 `sites/<slug>/` 目录,可上传/重命名/删除 | P1 |
| SITES-09 | 打开站点与复制 | 点「打开站点」/复制按钮 | 新标签打开站点 URL;剪贴板为完整 URL | P2 |
| SITES-10 | 删除站点 | 「删除站点」→ 确认 | 文件与配置移除,列表不再显示;Host 访问 404 | P0 |
| SITES-11 | 大 zip 提示 | 部署 >200MB 或 >2000 文件的 zip | 明确提示改用 WebDAV/CLI,不入队 | P2 |
| SITES-12 | 未配置 SITES_HOST | wrangler.toml 不设 SITES_HOST | 横幅指引配置方法;「打开站点」禁用;列表仍可用 | P1 |

---

## 执行建议

1. **P0 冒烟**(约 30 条):AUTH-01/02/04/06、NAV-01/02/04、UP-01/03/07、PV-01/02/04/06/09/11、FILE-01/04/05/11、TRASH-01/02、SRCH-01/03、VIEW-01/04/06、SHARE-01/03/04/07、API-01/06/10/18、DAV-05/09、THEME-01/02/04、KB-01/03/05/06/10/11、EDGE-08/09/10。
2. 每轮功能改动后:跑受影响模块全组 + 全部 P0;发布前跑全量。
3. API/DAV 组可直接脚本化(bash 循环断言状态码),UI 组保持人工/浏览器自动化。