From c253ccd794b97b49e8e3b8166e3dfbea97312f6a Mon Sep 17 00:00:00 2001 From: chiguyong Date: Thu, 4 Jun 2026 14:06:44 +0800 Subject: [PATCH] chore: add coverage/test artifacts to .gitignore, add plan docs - Add .coverage, htmlcov/, test-results/, playwright-report/ to .gitignore - Add Plan 008 (production readiness) and Plan 009 (production hardening + test infra) --- .gitignore | 4 + ...008-chore-geo-production-readiness-plan.md | 418 +++++++++++++++++ ...eo-production-hardening-test-infra-plan.md | 438 ++++++++++++++++++ 3 files changed, 860 insertions(+) create mode 100644 docs/plans/2026-06-04-008-chore-geo-production-readiness-plan.md create mode 100644 docs/plans/2026-06-04-009-chore-geo-production-hardening-test-infra-plan.md diff --git a/.gitignore b/.gitignore index 5e55d20..d618d89 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,7 @@ login_resp.json test_login.js test_login2.js testfile2.txt +backend/.coverage +backend/htmlcov/ +frontend/test-results/ +frontend/playwright-report/ diff --git a/docs/plans/2026-06-04-008-chore-geo-production-readiness-plan.md b/docs/plans/2026-06-04-008-chore-geo-production-readiness-plan.md new file mode 100644 index 0000000..6d91831 --- /dev/null +++ b/docs/plans/2026-06-04-008-chore-geo-production-readiness-plan.md @@ -0,0 +1,418 @@ +--- +title: "chore: GEO Platform Production Readiness — Final Mile to Deployable" +type: chore +status: active +date: 2026-06-04 +--- + +# GEO Platform Production Readiness — Final Mile to Deployable + +## Summary + +补齐 GEO 平台从"代码完成"到"生产可用"的最后一公里:修复 Agent 注册缺口、Docker 生产镜像瘦身与健康检查、CI/CD 流水线修复、Sentry 监控接入、测试体系收尾(散落文件迁移+重复文件清理)、配置一致性修复。完成后平台达到可部署、可监控、可持续交付的状态。 + +## Problem Frame + +7 个迭代计划已完成,GEO 平台功能代码基本就绪(8 个 Agent、35 个前端页面、1521 个后端测试、19 个 E2E 测试)。但深度审查发现多个生产阻塞问题:生产 Docker 镜像包含 Playwright+Chromium(+500MB 攻击面)、生产 docker-compose 缺少健康检查、3 个 Agent 未注册导出、CI 流水线 PR 评论功能失效、前后端 Sentry 监控预留但未接入、11 个后端测试文件散落在根目录且 4 个与子目录重复。这些问题不解决,平台无法安全部署到生产环境。 + +--- + +## Requirements + +**Agent 注册完整性** + +R1. 8 个 Agent 全部在 `__init__.py` 中导出,Agent 框架可正确发现和调度所有 Agent + +**Docker 生产就绪** + +R2. 生产 Docker 镜像不包含 Playwright/Chromium 等测试工具,镜像体积合理 +R3. 生产 docker-compose 中 backend 和 frontend 均配置 healthcheck +R4. `docker-compose.prod.yml` 移除已弃用的 `version` 字段 + +**CI/CD 流水线修复** + +R5. PR Check 工作流中的步骤引用修复,PR 评论正确显示检查结果 +R6. CI 和 PR Check 工作流避免对同一 PR 双重触发 +R7. CI 中 "Type check" 步骤名称与实际行为一致 + +**Sentry 监控接入** + +R8. 后端集成 Sentry SDK,未捕获异常和性能追踪自动上报 +R9. 前端集成 Sentry SDK,运行时错误和 React 错误边界自动上报 + +**测试体系收尾** + +R10. 11 个根目录散落测试文件迁移到对应子目录 +R11. 4 个与子目录重复的根目录测试文件删除 +R12. 后端 pytest 配置添加覆盖率报告 + +**配置一致性** + +R13. `config.py` 中 `REDIS_URL` 默认值不含密码问题修复 +R14. `.env.production.example` 补充缺失变量(POSTGRES_USER、POSTGRES_DB 等) +R15. `next.config.mjs` 生产构建启用类型检查和 lint + +--- + +## Key Technical Decisions + +KTD1. **生产 Dockerfile 拆分为 runtime 和 e2e 两个阶段。** 当前 runtime 阶段包含 Playwright+Chromium,是 E2E 测试工具不应出现在生产镜像。新增 `e2e` 阶段继承 runtime 并安装 Playwright,CI E2E 测试使用 `e2e` 阶段,生产部署使用 `runtime` 阶段。预估镜像体积减少 500MB+。 + +KTD2. **Sentry DSN 通过环境变量注入,未配置时静默跳过。** 使用 `sentry_sdk.init(dsn=settings.SENTRY_DSN)` 模式,`SENTRY_DSN` 默认为空字符串,Sentry SDK 在 DSN 为空时自动禁用,不影响开发和测试环境。 + +KTD3. **散落测试文件迁移而非重写。** 11 个文件仅移动位置并更新 import 路径,4 个重复文件直接删除根目录版本(保留子目录中更完整的版本)。 + +KTD4. **CI 修复采用最小改动策略。** 修复 pr-check.yml 的步骤 ID 引用、添加 concurrency 分组、修正步骤名称,不重构整个 CI 流程。 + +--- + +## High-Level Technical Design + +```mermaid +flowchart TB + subgraph Phase1["Phase 1 — 部署阻塞修复"] + U1[U1. Agent 注册补全] --> U2[U2. Docker 生产镜像瘦身] + U2 --> U3[U3. Docker 生产健康检查+配置修复] + end + + subgraph Phase2["Phase 2 — 可观测性"] + U4[U4. Sentry 后端接入] + U5[U5. Sentry 前端接入] + end + + subgraph Phase3["Phase 3 — CI/CD 修复"] + U6[U6. CI 流水线修复] + end + + subgraph Phase4["Phase 4 — 测试收尾"] + U7[U7. 测试文件迁移+清理] + U8[U8. 覆盖率配置+配置一致性] + end + + U3 --> U4 + U3 --> U6 + U6 --> U7 +``` + +--- + +## Implementation Units + +### U1. Agent Registration Completion + +**Goal:** 补全 Agent 框架注册,确保 8 个 Agent 全部可被发现和调度 + +**Requirements:** R1 + +**Dependencies:** none + +**Files:** +- `backend/app/agent_framework/agents/__init__.py` — 添加 3 个 Agent 导出 +- `backend/app/agent_framework/agents/competitor_analyzer.py` — 验证导出接口 +- `backend/app/agent_framework/agents/monitor_agent.py` — 验证导出接口 +- `backend/app/agent_framework/agents/schema_advisor.py` — 验证导出接口 +- `backend/app/agent_framework/registry.py` — 验证自动发现逻辑 +- `backend/tests/test_agent_framework/` — 验证测试通过 + +**Approach:** 在 `__init__.py` 中添加 CompetitorAnalyzerAgent、MonitorAgent、SchemaAdvisorAgent 的 import 和 `__all__` 导出。验证 `registry.py` 的自动发现逻辑能正确注册这 3 个 Agent。运行现有 Agent 框架测试确认无回归。 + +**Patterns to follow:** 现有 5 个 Agent 的导出模式 + +**Test scenarios:** +- `from app.agent_framework.agents import CompetitorAnalyzerAgent` 不报错 +- `from app.agent_framework.agents import MonitorAgent` 不报错 +- `from app.agent_framework.agents import SchemaAdvisorAgent` 不报错 +- Agent registry 自动注册后 `list_agents()` 返回 8 个 Agent +- 现有 Agent 框架测试全部通过 + +**Verification:** `pytest tests/test_agent_framework/` 通过,`__all__` 包含 8 个 Agent 名称 + +--- + +### U2. Docker Production Image Slimming + +**Goal:** 生产 Docker 镜像移除 Playwright/Chromium,减少镜像体积和攻击面 + +**Requirements:** R2 + +**Dependencies:** U1 + +**Files:** +- `backend/Dockerfile` — 拆分 runtime 和 e2e 阶段 + +**Approach:** 将当前 `runtime` 阶段中的 Playwright 安装(`playwright install --with-deps chromium`)和 Chromium 系统依赖(libglib2.0、libnss3 等)移到新增的 `e2e` 阶段。`runtime` 阶段仅保留 Python 依赖 + gunicorn。CI E2E 测试的 docker-compose 使用 `target: e2e`,生产部署使用 `target: runtime`。 + +**Patterns to follow:** 现有 Dockerfile 多阶段构建模式 + +**Test scenarios:** +- `docker build --target runtime .` 构建成功,镜像不含 Playwright +- `docker build --target e2e .` 构建成功,镜像包含 Playwright +- runtime 镜像体积比 e2e 镜像小 500MB+ +- runtime 镜像中 `python -c "import playwright"` 报 ImportError +- e2e 镜像中 `python -c "import playwright"` 成功 + +**Verification:** `docker build --target runtime .` 成功,`docker run python -c "from app.main import app"` 成功 + +--- + +### U3. Docker Production Health Check and Configuration Fixes + +**Goal:** 生产 docker-compose 添加健康检查,修复配置一致性问题 + +**Requirements:** R3, R4, R13, R14, R15 + +**Dependencies:** U2 + +**Files:** +- `docker-compose.prod.yml` — 添加 healthcheck、移除 version 字段 +- `backend/app/config.py` — 修复 REDIS_URL 默认值、添加 SENTRY_DSN 字段 +- `.env.production.example` — 补充缺失变量 +- `frontend/next.config.mjs` — 生产构建启用类型检查 + +**Approach:** +1. `docker-compose.prod.yml`:为 backend 和 frontend 添加与 Dockerfile 一致的 healthcheck 配置;移除 `version: "3.9"` +2. `config.py`:将 `REDIS_URL` 默认值改为空字符串,添加启动时验证(如果 REDIS_URL 为空则警告);添加 `SENTRY_DSN: str = ""` 字段 +3. `.env.production.example`:补充 `POSTGRES_USER`、`POSTGRES_DB`、`SENTRY_DSN`、AI 平台密钥等变量 +4. `next.config.mjs`:通过环境变量控制 `ignoreBuildErrors` 和 `ignoreDuringBuilds`,生产构建(`NEXT_BUILD_ENV=production`)时设为 false + +**Patterns to follow:** 开发环境 docker-compose.yml 的 healthcheck 配置模式 + +**Test scenarios:** +- 生产 docker-compose backend healthcheck 通过 +- 生产 docker-compose frontend healthcheck 通过 +- `config.py` REDIS_URL 为空时启动后端发出警告但不崩溃 +- `.env.production.example` 包含 docker-compose.prod.yml 所需的全部变量 +- `next.config.mjs` 在 NEXT_BUILD_ENV=production 时启用类型检查 + +**Verification:** `docker compose -f docker-compose.prod.yml up` 启动成功,所有服务健康检查通过 + +--- + +### U4. Sentry Backend Integration + +**Goal:** 后端集成 Sentry SDK,未捕获异常和性能追踪自动上报 + +**Requirements:** R8 + +**Dependencies:** U3(需要 SENTRY_DSN 配置字段) + +**Files:** +- `backend/requirements.txt` — 添加 `sentry-sdk[fastapi]` +- `backend/app/config.py` — 已在 U3 添加 SENTRY_DSN +- `backend/app/main.py` — Sentry 初始化 +- `backend/app/middleware/metrics.py` — 替换 TODO 注释为实际 Sentry 集成 + +**Approach:** +1. 添加 `sentry-sdk[fastapi]` 依赖 +2. 在 `main.py` 的 FastAPI app 创建前调用 `sentry_sdk.init(dsn=settings.SENTRY_DSN, traces_sample_rate=0.1, environment=settings.ENVIRONMENT)` +3. 替换 `metrics.py` 中的两处 Sentry TODO 注释为实际集成代码 +4. DSN 为空时 Sentry SDK 自动禁用,不影响开发环境 + +**Patterns to follow:** Sentry FastAPI 集成官方文档模式 + +**Test scenarios:** +- SENTRY_DSN 为空时后端正常启动,无 Sentry 上报 +- SENTRY_DSN 配置后未捕获异常自动上报 Sentry +- FastAPI 请求性能追踪数据上报 Sentry +- 现有测试不受 Sentry 集成影响 + +**Verification:** 配置有效 SENTRY_DSN 后,触发异常可在 Sentry dashboard 看到 + +--- + +### U5. Sentry Frontend Integration + +**Goal:** 前端集成 Sentry SDK,运行时错误和 React 错误边界自动上报 + +**Requirements:** R9 + +**Dependencies:** U3(需要 SENTRY_DSN 配置字段) + +**Files:** +- `frontend/package.json` — 添加 `@sentry/nextjs` 依赖 +- `frontend/next.config.mjs` — Sentry webpack 配置 +- `frontend/sentry.client.config.ts` — 新建,客户端 Sentry 初始化 +- `frontend/sentry.server.config.ts` — 新建,服务端 Sentry 初始化 +- `frontend/components/ErrorBoundary.tsx` — 替换 TODO 注释为 Sentry 上报 + +**Approach:** +1. 安装 `@sentry/nextjs` +2. 创建 `sentry.client.config.ts` 和 `sentry.server.config.ts`,使用 `NEXT_PUBLIC_SENTRY_DSN` 环境变量初始化 +3. 在 `next.config.mjs` 中添加 Sentry webpack 插件配置(withSentryConfig wrapper) +4. 替换 `ErrorBoundary.tsx` 中的 `// TODO:SENTRY` 为 `Sentry.captureException(error)` +5. DSN 为空时 Sentry 自动禁用 + +**Patterns to follow:** Sentry Next.js SDK 官方集成模式 + +**Test scenarios:** +- NEXT_PUBLIC_SENTRY_DSN 为空时前端正常加载,无 Sentry 上报 +- NEXT_PUBLIC_SENTRY_DSN 配置后未捕获错误自动上报 +- ErrorBoundary 捕获的 React 错误上报 Sentry +- 前端构建不受 Sentry 集成影响 + +**Verification:** 配置有效 DSN 后,触发前端错误可在 Sentry dashboard 看到 + +--- + +### U6. CI Pipeline Fixes + +**Goal:** 修复 CI/CD 流水线中的已知问题 + +**Requirements:** R5, R6, R7 + +**Dependencies:** U2(Docker 镜像拆分后 CI 需要更新) + +**Files:** +- `.github/workflows/ci.yml` — 修正步骤名称、更新 Docker build target +- `.github/workflows/pr-check.yml` — 修复步骤 ID 引用 + +**Approach:** +1. `ci.yml`:将 "Type check" 步骤名称改为 "Lint (ruff)" 或添加真正的 mypy 步骤;E2E 测试的 docker-compose 使用 `target: e2e`;添加 `concurrency: group: ci-${{ github.ref }}` 避免重复运行 +2. `pr-check.yml`:为每个步骤添加 `id`(如 `id: backend-lint`),修复 PR 评论中的步骤引用 `${{ steps.backend-lint.outcome }}`;添加 `concurrency: group: pr-check-${{ github.ref }}` + +**Patterns to follow:** GitHub Actions 工作流最佳实践 + +**Test scenarios:** +- PR 提交时 pr-check.yml 正确运行并在 PR 中评论检查结果 +- PR 评论表格中各项显示 pass/fail 而非 N/A +- 同一 PR 不会同时触发 ci.yml 和 pr-check.yml 的重复运行 +- E2E 测试使用 e2e Docker 镜像(含 Playwright) + +**Verification:** 提交 PR 后检查 Actions tab 和 PR 评论 + +--- + +### U7. Test File Migration and Cleanup + +**Goal:** 迁移散落测试文件到标准目录,删除重复文件 + +**Requirements:** R10, R11 + +**Dependencies:** U6(CI 修复后再迁移,避免 CI 配置冲突) + +**Files:** +- `backend/tests/test_config.py` → `backend/tests/test_infrastructure/test_config.py` +- `backend/tests/test_database_exception_handling.py` → `backend/tests/test_infrastructure/test_database_exception_handling.py` +- `backend/tests/test_database_migration.py` → `backend/tests/test_infrastructure/test_database_migration.py` +- `backend/tests/test_performance.py` → `backend/tests/test_infrastructure/test_performance.py` +- `backend/tests/test_security.py` → `backend/tests/test_infrastructure/test_security.py` +- `backend/tests/test_platform_adapters.py` → `backend/tests/test_services/test_platform_adapters.py` +- `backend/tests/test_scheduler.py` → `backend/tests/test_infrastructure/test_scheduler.py` +- `backend/tests/test_html_generator.py` — 删除(与 `test_content_pipeline/test_html_generator.py` 重复) +- `backend/tests/test_rule_validator.py` — 删除(与 `test_content_pipeline/test_rule_validator.py` 重复) +- `backend/tests/test_sensitive_filter.py` — 删除(与 `test_content_pipeline/test_sensitive_filter.py` 重复) +- `backend/tests/test_seo_optimizer.py` — 删除(与 `test_content_pipeline/test_seo_optimizer.py` 重复) + +**Approach:** +1. 迁移前运行 `pytest` 记录基线通过率 +2. 逐个迁移文件,更新内部 import 路径(如有) +3. 删除 4 个重复的根目录文件 +4. 迁移后运行 `pytest` 确认通过率一致 +5. 更新 `pyproject.toml` 的 `testpaths` 配置(如有必要) + +**Execution note:** 迁移前先运行全部测试记录基线,迁移后验证所有测试仍通过 + +**Patterns to follow:** 现有 `test_infrastructure/`、`test_services/` 目录的测试组织模式 + +**Test scenarios:** +- 迁移后 `pytest` 全部通过,通过率与迁移前一致 +- 根目录无散落测试文件 +- 4 个重复文件已删除 +- CI 中 `pytest tests/` 发现并运行全部测试 + +**Verification:** `pytest tests/` 通过,`find tests/ -maxdepth 1 -name "test_*.py"` 返回空 + +--- + +### U8. Coverage Configuration and Config Consistency + +**Goal:** 配置后端测试覆盖率报告,确保配置文件一致性 + +**Requirements:** R12, R13, R14, R15 + +**Dependencies:** U7 + +**Files:** +- `backend/pyproject.toml` — 添加 pytest-cov 配置 +- `backend/requirements.txt` — 确认 pytest-cov 已存在 +- `backend/app/config.py` — U3 中已修复 REDIS_URL +- `.env.production.example` — U3 中已补充变量 +- `frontend/next.config.mjs` — U3 中已修复构建检查 + +**Approach:** +1. 在 `pyproject.toml` 的 `[tool.pytest.ini_options]` 中添加 `addopts = "--cov=app --cov-report=term-missing --cov-report=html"` +2. 添加 `[tool.coverage.run]` 配置 omit 列表(排除测试文件、migrations 等) +3. 验证 `pytest-cov` 已在 requirements.txt 中 +4. 确认 U3 中的配置修复(REDIS_URL、.env.production.example、next.config.mjs)已生效 + +**Patterns to follow:** pytest-cov 标准配置模式 + +**Test scenarios:** +- `pytest --cov=app` 生成覆盖率报告 +- 覆盖率报告排除 migrations 和测试文件 +- HTML 覆盖率报告可正常打开查看 +- CI 中覆盖率步骤正常工作 + +**Verification:** `pytest --cov=app` 成功生成覆盖率报告,显示各模块覆盖率百分比 + +--- + +## Scope Boundaries + +**In scope:** +- Agent 注册补全 +- Docker 生产镜像优化和健康检查 +- CI/CD 流水线修复 +- Sentry 监控接入 +- 测试文件迁移和清理 +- 配置一致性修复 + +**Deferred for later:** +- 真实微信/支付宝 SDK 商户密钥配置(需外部资质审批) +- Redis 连接池统一管理(当前多模块各自创建连接,功能正常但不够优雅) +- 前端组件测试和页面级单元测试(当前仅 13 个单元测试,覆盖面窄) +- Playwright 移动端设备测试项目 +- 性能/负载测试框架(Locust/k6)搭建 +- 视觉回归测试 +- 生产环境域名和 HTTPS 配置 +- 前端 `images.unoptimized` 评估 + +**Outside this plan:** +- 新功能开发 +- 代码重构(除测试文件迁移外) +- UI 打磨 + +--- + +## Risks & Dependencies + +- **Sentry DSN 需要 Sentry 项目创建。** 如果团队尚未创建 Sentry 项目,需要先注册并获取 DSN。缓解:DSN 为空时 SDK 自动禁用,不阻塞开发。 +- **Docker 镜像拆分可能影响 CI E2E 测试。** CI 的 docker-compose 需要使用 `target: e2e` 而非 `target: runtime`。缓解:U6 中同步更新 CI 配置。 +- **测试文件迁移可能引入 import 路径问题。** 迁移后 pytest 可能找不到某些 fixture 或 conftest。缓解:U7 中先记录基线再迁移,逐个验证。 +- **next.config.mjs 启用类型检查可能导致构建失败。** 前端代码可能存在未修复的类型错误。缓解:先运行 `npx tsc --noEmit` 检查类型错误数量,如果过多则分批修复。 + +--- + +## Open Questions + +**Deferred to implementation:** +- Sentry 项目是否已创建?团队使用 SaaS 还是自托管? +- CI runner 环境:GitHub Actions 还是自建 Runner? +- 前端 TypeScript 类型错误数量——决定是否需要分批修复 + +--- + +## Sources & Research + +- `docs/plans/2026-06-02-007-chore-geo-launch-sprint-plan.md` — 上线冲刺计划(status: completed) +- `docs/plans/2026-06-01-005-chore-geo-tech-debt-cleanup-plan.md` — 技术债清理计划(status: completed) +- `docs/plans/2026-05-31-002-test-quality-assurance-system-plan.md` — 测试质量保障体系(status: active) +- `backend/app/agent_framework/agents/__init__.py` — 仅导出 5/8 个 Agent +- `backend/Dockerfile` — runtime 阶段包含 Playwright+Chromium +- `docker-compose.prod.yml` — 缺少 backend/frontend healthcheck +- `.github/workflows/ci.yml` — CI 流水线配置 +- `.github/workflows/pr-check.yml` — PR 检查工作流(步骤 ID 引用失效) +- `backend/app/config.py` — REDIS_URL 默认值不含密码 +- `backend/app/middleware/metrics.py` — 2 处 Sentry TODO +- `frontend/components/ErrorBoundary.tsx` — 1 处 Sentry TODO +- `backend/tests/` — 11 个根目录散落测试文件,4 个与子目录重复 diff --git a/docs/plans/2026-06-04-009-chore-geo-production-hardening-test-infra-plan.md b/docs/plans/2026-06-04-009-chore-geo-production-hardening-test-infra-plan.md new file mode 100644 index 0000000..30fa196 --- /dev/null +++ b/docs/plans/2026-06-04-009-chore-geo-production-hardening-test-infra-plan.md @@ -0,0 +1,438 @@ +--- +title: "chore: GEO Platform Production Hardening & Test Infrastructure" +type: chore +status: active +date: 2026-06-04 +--- + +# GEO Platform Production Hardening & Test Infrastructure + +## Summary + +在 Plan 008(生产就绪)基础上,完成收尾工作并进一步加固生产环境:实现限流中间件 Redis 双后端、补全标签验证逻辑、加固认证错误处理、清理过时注释。同时全面深化 E2E 测试体系:提取共享 Playwright fixture、补充交互测试和错误状态测试、建立 API mock 层。完成后平台具备多实例部署能力和高质量测试保障。 + +## Problem Frame + +Plan 008 的 U1-U6 已完成代码变更,但 U7 验证和 U8 尚未收尾,所有变更未提交 Git。深度调研发现多个生产阻塞问题:限流中间件基于内存存储,多实例部署时限流失效;`platform_rules.py` 中标签验证逻辑(min_tags/max_tags)未实现;认证模块 catch 块吞掉错误无日志;Sentry 集成已完成但 TODO 注释未清理。E2E 测试方面,19 个测试文件中 loginAndWait 函数重复定义,测试以页面渲染验证为主,缺少交互测试、错误状态测试和 API mock 层,测试深度不足以支撑生产信心。 + +--- + +## Requirements + +**Plan 008 收尾** + +R1. Plan 008 U7 测试文件迁移后 pytest 验证通过 +R2. Plan 008 U8 pytest-cov 覆盖率配置完成,`pytest` 自动生成覆盖率报告 + +**限流中间件加固** + +R3. 限流中间件支持 Redis 后端,生产环境自动使用 Redis +R4. Redis 不可用时自动降级到内存后端,不影响服务启动 +R5. 内存后端添加定期清理机制,防止长期运行内存增长 + +**标签验证逻辑** + +R6. `platform_rules.py` 中 min_tags/max_tags 规则实际执行校验 + +**认证错误处理** + +R7. 认证模块 authorize 和 refreshAccessToken 中 catch 块记录错误日志 + +**代码卫生** + +R8. 已完成的 Sentry 集成点 TODO 注释清理 + +**E2E 测试基础设施** + +R9. 19 个 E2E 测试文件中 loginAndWait 提取为共享 Playwright fixture +R10. 关键业务流程补充交互测试(数据输入、提交、验证) +R11. 补充错误状态测试(网络错误、API 500、空数据) +R12. 建立 API mock 层,E2E 测试不依赖真实后端数据 + +--- + +## Key Technical Decisions + +KTD1. **限流中间件采用 Redis 优先 + 内存 fallback 双后端。** 生产环境通过 `RATE_LIMIT_BACKEND=redis` 使用 Redis,开发/测试环境默认使用内存后端。Redis 连接失败时自动降级到内存后端并记录警告,不阻塞服务启动。内存后端添加 `asyncio.create_task` 后台清理任务。 + +KTD2. **E2E API mock 层使用 Playwright route interception。** 不引入额外的 mock server 依赖,利用 Playwright 原生的 `page.route()` 拦截 API 请求并返回预设数据。mock 数据按场景组织为 JSON fixture 文件,测试可按需加载。 + +KTD3. **共享 Playwright fixture 放在 `e2e/fixtures/` 目录。** 包含 `authenticatedPage`(自动登录的 page)、`mockApi`(API mock 辅助函数)、`testData`(测试数据工厂)。各测试文件通过 `import` 引用,消除 loginAndWait 重复。 + +KTD4. **标签验证在现有 `validate_content` 方法中扩展。** 不新建验证器类,在 `platform_rules.py` 的 `validate_content` 方法中增加标签数量校验分支,读取 content 的 tags 字段与 min_tags/max_tags 规则比对。 + +--- + +## High-Level Technical Design + +```mermaid +flowchart TB + subgraph Phase1["Phase 1 — Plan 008 收尾"] + U1[U1. U7 验证 + U8 覆盖率配置] + end + + subgraph Phase2["Phase 2 — 生产加固"] + U2[U2. 限流中间件双后端] + U3[U3. 标签验证逻辑] + U4[U4. 认证错误处理] + U5[U5. 代码卫生清理] + end + + subgraph Phase3["Phase 3 — E2E 测试基础设施"] + U6[U6. 共享 Playwright fixture] + U7[U7. API mock 层] + U8[U8. 交互测试 + 错误状态测试] + end + + U1 --> U2 + U2 --> U3 + U2 --> U4 + U4 --> U5 + U5 --> U6 + U6 --> U7 + U7 --> U8 +``` + +--- + +## Implementation Units + +### U1. Plan 008 Completion — U7 Verification + U8 Coverage Config + +**Goal:** 完成 Plan 008 剩余工作:验证测试文件迁移无回归,配置 pytest-cov 自动覆盖率 + +**Requirements:** R1, R2 + +**Dependencies:** none(Plan 008 U1-U7 代码变更已存在) + +**Files:** +- `backend/pyproject.toml` — 添加 pytest-cov addopts 和 coverage 配置 +- `backend/requirements.txt` — 确认 pytest-cov 已存在 +- `backend/tests/test_infrastructure/` — 验证迁移后测试通过 +- `backend/tests/test_services/` — 验证迁移后测试通过 + +**Approach:** +1. 运行 `pytest tests/` 验证 U7 测试文件迁移后无回归 +2. 在 `pyproject.toml` 的 `[tool.pytest.ini_options]` 添加 `addopts = "--cov=app --cov-report=term-missing --cov-report=html"` +3. 添加 `[tool.coverage.run]` 配置 omit 列表(排除 `tests/*`、`migrations/*`、`__pycache__/*`) +4. 添加 `[tool.coverage.report]` 配置 `exclude_lines`(排除 `pragma: no cover`、`if __name__` 等) +5. 验证 `pytest` 自动生成覆盖率报告 + +**Patterns to follow:** pytest-cov 标准配置模式 + +**Test scenarios:** +- `pytest tests/` 全部通过,通过率与迁移前一致 +- `pytest` 自动生成覆盖率报告,显示各模块覆盖率百分比 +- 覆盖率报告排除 migrations 和测试文件 +- HTML 覆盖率报告可正常打开查看 + +**Verification:** `pytest` 成功生成覆盖率报告,`find tests/ -maxdepth 1 -name "test_*.py"` 返回空 + +--- + +### U2. Rate Limiter Dual Backend — Redis + Memory Fallback + +**Goal:** 限流中间件支持 Redis 后端,生产环境自动使用 Redis,Redis 不可用时降级到内存 + +**Requirements:** R3, R4, R5 + +**Dependencies:** U1(Plan 008 收尾后再改限流) + +**Files:** +- `backend/app/middleware/rate_limit.py` — 重构为双后端架构 +- `backend/app/config.py` — 添加 `RATE_LIMIT_BACKEND` 配置 +- `backend/tests/test_infrastructure/test_rate_limit.py` — 新建限流测试 +- `.env.production.example` — 添加 `RATE_LIMIT_BACKEND=redis` + +**Approach:** +1. 定义 `RateLimitBackend` 抽象接口(`is_rate_limited`、`reset` 方法) +2. 实现 `MemoryRateLimitBackend`(当前逻辑提取,添加后台清理 task) +3. 实现 `RedisRateLimitBackend`(使用 Redis sorted set + ZRANGEBYSCORE 实现滑动窗口) +4. `RateLimitMiddleware.__init__` 根据 `RATE_LIMIT_BACKEND` 配置选择后端 +5. Redis 后端初始化时连接失败则降级到内存后端并记录警告 +6. 内存后端添加 `asyncio.create_task` 后台清理任务,每 60 秒清理过期记录 +7. 保留 `RATE_LIMIT_DISABLED` 环境变量用于 E2E 测试 + +**Patterns to follow:** 策略模式(Strategy Pattern),现有 Redis 连接管理(`app/core/redis.py`) + +**Test scenarios:** +- `RATE_LIMIT_BACKEND=memory` 时使用内存后端,限流正常工作 +- `RATE_LIMIT_BACKEND=redis` 且 Redis 可用时使用 Redis 后端 +- `RATE_LIMIT_BACKEND=redis` 且 Redis 不可用时降级到内存后端,记录警告 +- Redis 后端多实例共享限流状态(模拟两个客户端连接同一 Redis) +- 内存后端后台清理任务运行,过期记录被清理 +- `RATE_LIMIT_DISABLED=1` 时所有后端跳过限流 +- 现有 4 层限流规则(auth_strict、auth、query_run、global)在新后端下均正常 + +**Verification:** `pytest tests/test_infrastructure/test_rate_limit.py` 通过,生产配置 Redis 后端时限流生效 + +--- + +### U3. Tag Validation Logic Implementation + +**Goal:** 实现 `platform_rules.py` 中 min_tags/max_tags 标签数量校验 + +**Requirements:** R6 + +**Dependencies:** U2 + +**Files:** +- `backend/app/services/distribution/platform_rules.py` — 补全标签验证逻辑 +- `backend/tests/test_services/test_platform_rules.py` — 新建或扩展标签验证测试 + +**Approach:** +1. 定位 `validate_content` 方法中 TODO 注释(约第 1015 行) +2. 从 content 数据中提取 tags 字段(支持 list 和 comma-separated string 两种格式) +3. 与规则中的 min_tags/max_tags 比对 +4. 返回验证结果:tags 数量不足时返回错误信息,包含当前 tags 数和要求的范围 +5. 无 tags 字段时视为 0 个标签 + +**Patterns to follow:** 现有 `validate_content` 中其他规则的校验模式 + +**Test scenarios:** +- content tags 数量在 min_tags 和 max_tags 范围内 → 验证通过 +- content tags 数量 < min_tags → 验证失败,错误信息包含当前数量和最小要求 +- content tags 数量 > max_tags → 验证失败,错误信息包含当前数量和最大限制 +- content 无 tags 字段 → 视为 0 个标签,按 min_tags 规则校验 +- content tags 为 comma-separated string → 正确解析并计数 +- 规则无 min_tags/max_tags → 跳过标签校验 + +**Verification:** `pytest tests/test_services/test_platform_rules.py` 通过,标签规则校验逻辑覆盖全部场景 + +--- + +### U4. Auth Error Handling Hardening + +**Goal:** 认证模块 catch 块添加错误日志记录,不再静默吞掉异常 + +**Requirements:** R7 + +**Dependencies:** U2 + +**Files:** +- `frontend/lib/auth.ts` — authorize 和 refreshAccessToken 添加错误日志 +- `frontend/lib/logger.ts` — 新建前端日志工具(或使用 console.error + Sentry) + +**Approach:** +1. 在 `authorize` 函数的 catch 块中添加 `console.error("[Auth] Login failed:", error)` +2. 在 `refreshAccessToken` 的 catch 块中添加 `console.error("[Auth] Token refresh failed:", error)` +3. 如果 Sentry 已初始化,同时调用 `Sentry.captureException(error)` 上报 +4. 保持现有返回值逻辑不变(authorize 返回 null,refreshAccessToken 标记 error) +5. 可选:创建简单的 `logger.ts` 工具封装 console.error + Sentry 上报 + +**Patterns to follow:** 后端 `app/core/logging.py` 的日志模式 + +**Test scenarios:** +- 登录失败时控制台输出错误日志 +- Token 刷新失败时控制台输出错误日志 +- Sentry 已配置时错误自动上报 +- 错误日志不影响现有认证流程(返回值不变) + +**Verification:** 模拟登录失败和 token 刷新失败,确认日志输出和 Sentry 上报 + +--- + +### U5. Code Hygiene — Sentry Comment Cleanup + +**Goal:** 清理已完成的 Sentry 集成点 TODO 注释,更新为准确描述 + +**Requirements:** R8 + +**Dependencies:** U4 + +**Files:** +- `backend/app/middleware/metrics.py` — 更新 Sentry 集成点注释 +- `frontend/components/ErrorBoundary.tsx` — 更新 Sentry 集成点注释 + +**Approach:** +1. `metrics.py`:将"预留 Sentry 集成点(TODO 注释标注)"更新为"已集成 Sentry 性能监控" +2. `ErrorBoundary.tsx`:将"预留 Sentry 集成点(搜索 TODO:SENTRY)"更新为"已集成 Sentry 错误上报" +3. 确认两处实际 Sentry 代码正常工作 + +**Test expectation:** none — 纯注释更新,无行为变更 + +**Verification:** grep 搜索确认无残留的 Sentry TODO 注释 + +--- + +### U6. Shared Playwright Fixtures + +**Goal:** 提取 19 个 E2E 测试文件中重复的 loginAndWait 为共享 Playwright fixture + +**Requirements:** R9 + +**Dependencies:** U5 + +**Files:** +- `frontend/e2e/fixtures/auth.ts` — 新建,authenticatedPage fixture +- `frontend/e2e/fixtures/index.ts` — 新建,统一导出 +- `frontend/e2e/tests/*.spec.ts` — 19 个文件重构为使用共享 fixture + +**Approach:** +1. 创建 `e2e/fixtures/auth.ts`,定义 `authenticatedPage` fixture: + - 基于 Playwright `test.extend()` 扩展 + - 自动执行登录流程(复用当前 loginAndWait 逻辑) + - 测试结束后自动清理 +2. 创建 `e2e/fixtures/index.ts`,统一导出所有 fixture +3. 逐个重构 19 个测试文件: + - 移除文件内的 `loginAndWait` 函数定义 + - 将 `loginAndWait(page)` 调用替换为 `authenticatedPage` fixture 使用 + - 保持测试逻辑不变 +4. 验证重构后所有 E2E 测试通过 + +**Patterns to follow:** Playwright 官方 fixture 模式(`test.extend()`) + +**Test scenarios:** +- `authenticatedPage` fixture 自动完成登录,测试可直接使用已认证的 page +- 所有 19 个 E2E 测试重构后行为与重构前一致 +- 无测试文件内重复定义 loginAndWait + +**Verification:** `npx playwright test` 全部通过,grep 确认无残留的 loginAndWait 函数定义 + +--- + +### U7. E2E API Mock Layer + +**Goal:** 建立 Playwright route interception API mock 层,E2E 测试不依赖真实后端数据 + +**Requirements:** R12 + +**Dependencies:** U6 + +**Files:** +- `frontend/e2e/fixtures/api-mock.ts` — 新建,API mock 辅助函数 +- `frontend/e2e/fixtures/mock-data/` — 新建,mock 数据 JSON 文件目录 +- `frontend/e2e/fixtures/index.ts` — 更新导出 + +**Approach:** +1. 创建 `api-mock.ts`,提供以下功能: + - `mockApi(page, endpoint, response)` — 拦截指定 API 请求返回预设数据 + - `mockApiError(page, endpoint, statusCode)` — 模拟 API 错误响应 + - `mockApiDelay(page, endpoint, ms)` — 模拟 API 延迟 + - `clearApiMocks(page)` — 清除所有 mock +2. 创建 `mock-data/` 目录,按业务场景组织 JSON fixture: + - `health-score.json` — 健康评分数据 + - `citations.json` — 引用记录数据 + - `competitors.json` — 竞品分析数据 + - `analytics.json` — 数据监测数据 + - `knowledge.json` — 知识库数据 + - `empty-state.json` — 空状态数据 +3. 所有 mock 使用 Playwright `page.route()` 实现,不引入额外依赖 +4. mock 辅助函数作为 fixture 导出,测试可按需使用 + +**Patterns to follow:** Playwright route interception 官方文档模式 + +**Test scenarios:** +- `mockApi` 成功拦截 API 请求并返回预设数据 +- `mockApiError` 成功模拟 500 错误响应 +- `mockApiDelay` 成功模拟 API 延迟 +- 多个 mock 同时生效不冲突 +- `clearApiMocks` 清除后请求正常发送到真实后端 +- mock 数据 JSON 格式正确,可被前端正确解析 + +**Verification:** 使用 mock 的 E2E 测试在无后端环境下通过 + +--- + +### U8. E2E Interaction and Error State Tests + +**Goal:** 补充关键业务流程的交互测试和错误状态测试 + +**Requirements:** R10, R11 + +**Dependencies:** U7 + +**Files:** +- `frontend/e2e/tests/health-score-interaction.spec.ts` — 新建,健康评分交互测试 +- `frontend/e2e/tests/citation-flow.spec.ts` — 新建,引用记录完整流程测试 +- `frontend/e2e/tests/competitor-interaction.spec.ts` — 新建,竞品分析交互测试 +- `frontend/e2e/tests/error-states.spec.ts` — 新建,错误状态测试 +- `frontend/e2e/tests/knowledge-interaction.spec.ts` — 新建,知识库交互测试 + +**Approach:** +1. **交互测试**(3 个文件): + - 健康评分:选择时间范围 → 查看评分变化 → 查看维度详情 + - 引用记录:搜索引用 → 筛选来源 → 查看引用详情 + - 竞品分析:添加竞品 → 查看对比 → 删除竞品 + - 知识库:上传文档 → 查看文档列表 → 删除文档 +2. **错误状态测试**(1 个文件): + - API 500 错误:页面显示错误提示,提供重试按钮 + - 网络断开:页面显示离线提示 + - 空数据状态:页面显示空状态引导 + - 认证过期:自动跳转登录页 +3. 所有测试使用 U7 的 API mock 层,不依赖真实后端 +4. 使用 U6 的 `authenticatedPage` fixture,无需手动登录 + +**Patterns to follow:** 现有 E2E 测试结构,Playwright 最佳实践 + +**Test scenarios:** +- 健康评分交互:切换时间范围后数据更新 +- 引用记录搜索:输入关键词后结果过滤 +- 竞品添加:填写表单提交后列表更新 +- API 500:页面显示错误提示,点击重试后重新请求 +- 空数据:页面显示引导文案和操作按钮 +- 认证过期:跳转登录页,重新登录后恢复 + +**Verification:** `npx playwright test` 全部通过,新增测试覆盖交互和错误状态 + +--- + +## Scope Boundaries + +**In scope:** +- Plan 008 收尾(U7 验证 + U8 覆盖率配置) +- 限流中间件 Redis 双后端实现 +- 标签验证逻辑补全 +- 认证错误处理加固 +- Sentry 注释清理 +- E2E 共享 fixture 提取 +- E2E API mock 层建立 +- E2E 交互测试和错误状态测试 + +**Deferred for later:** +- 真实微信/支付宝 SDK 商户密钥配置(需外部资质审批) +- Redis 连接池统一管理 +- 前端组件级单元测试扩展(当前仅 13 个) +- 依赖大版本升级(Next.js 14→15、Tailwind 3→4、ESLint 8→9) +- Playwright 移动端设备测试 +- 性能/负载测试框架(Locust/k6) +- 视觉回归测试 +- 生产环境域名和 HTTPS 配置 +- Docker 日志驱动和日志轮转配置 +- Plan 001/002 状态更新(行政性工作,随 Git 提交一起处理) + +**Outside this plan:** +- 新功能开发 +- 代码重构(除限流中间件架构调整外) +- UI 打磨 + +--- + +## Risks & Dependencies + +- **限流中间件重构可能影响现有请求处理。** 内存后端逻辑提取为独立类时需保持行为一致。缓解:先写测试覆盖现有行为,再重构。 +- **Redis 后端实现需确保原子性。** 滑动窗口的 ZADD + ZRANGEBYSCORE 需要使用 Redis pipeline 或 Lua script 保证原子性。缓解:使用 pipeline 执行多命令。 +- **E2E fixture 重构涉及 19 个文件。** 逐个重构时可能遗漏或引入不一致。缓解:重构后全量运行 E2E 测试验证。 +- **API mock 数据可能与真实 API 响应格式不同步。** 后端 API 变更时 mock 数据可能过时。缓解:mock 数据基于 TypeScript 类型定义生成,类型变更时编译报错。 +- **交互测试依赖前端组件稳定性。** 前端 UI 变更可能导致选择器失效。缓解:使用 data-testid 属性而非 CSS 选择器。 + +--- + +## Open Questions + +**Deferred to implementation:** +- Redis 连接使用现有 `app/core/redis.py` 还是新建连接?需确认现有 Redis 客户端配置 +- E2E mock 数据的初始数据集从哪里获取?可从开发环境 API 响应录制 +- 前端 logger 是否需要统一的日志级别控制?还是简单的 console.error + Sentry 即可 + +--- + +## Sources & Research + +- `docs/plans/2026-06-04-008-chore-geo-production-readiness-plan.md` — 当前计划(status: active) +- `docs/plans/2026-05-31-002-test-quality-assurance-system-plan.md` — 测试质量体系(status: active) +- `backend/app/middleware/rate_limit.py` — 当前内存限流实现 +- `backend/app/services/distribution/platform_rules.py` — 标签验证 TODO(第 1015 行) +- `frontend/lib/auth.ts` — 认证模块 +- `frontend/e2e/tests/` — 19 个 E2E 测试文件 +- `backend/pyproject.toml` — 缺少 pytest-cov 配置