Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:47:07 +08:00
commit 7422bc109d
32 changed files with 7456 additions and 0 deletions

View File

@@ -0,0 +1,351 @@
---
name: backend-bugfix
description: |
This skill should be used when the user asks to "debug backend tests", "fix pytest failures", "analyze Python errors", "fix FastAPI bugs", or mentions keywords like "pytest", "IntegrityError", "ValidationError", "SQLAlchemy", "FastAPI". It provides the complete bugfix workflow knowledge including error classification, confidence scoring, and TDD best practices for Python/FastAPI backends.
version: 2.1.0
---
# Backend Bugfix Workflow Skill
本 skill 提供后端测试 bugfix 的完整工作流知识,包括错误分类体系、置信度评分系统和 TDD 最佳实践。
## 错误分类体系
后端测试失败主要分为以下类型(按频率排序):
### 1. 数据库错误30%
**症状**:数据库连接失败、查询错误、事务问题
**识别特征**
- `IntegrityError``OperationalError`
- `sqlalchemy.exc.*` 异常
- `UNIQUE constraint failed`
- 事务未提交或未回滚
**解决策略**:正确处理事务边界
```python
# Before - 事务未正确处理
def create_user(db: Session, user: UserCreate):
db_user = User(**user.dict())
db.add(db_user)
db.commit() # 失败时无回滚
return db_user
# After - 使用 try/except 确保事务安全
def create_user(db: Session, user: UserCreate):
try:
db_user = User(**user.dict())
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
except IntegrityError:
db.rollback()
raise HTTPException(status_code=409, detail="User already exists")
```
### 2. 验证错误25%
**症状**输入验证失败、Schema 不匹配
**识别特征**
- `ValidationError`
- `pydantic.error_wrappers`
- `422 Unprocessable Entity`
- `field required` 错误
**解决策略**:完善 Pydantic Schema
```python
# Before - 缺少验证
class UserCreate(BaseModel):
email: str # 没有格式验证
# After - 使用 Pydantic 验证器
class UserCreate(BaseModel):
email: EmailStr
@field_validator('email')
@classmethod
def email_must_be_valid(cls, v):
if not v or '@' not in v:
raise ValueError('Invalid email format')
return v.lower()
```
### 3. API 错误20%
**症状**:端点返回错误状态码、路由不匹配
**识别特征**
- `HTTPException`
- `404 Not Found``405 Method Not Allowed`
- 响应格式不符合预期
**解决策略**:检查路由定义和请求方法
```python
# 确保端点定义正确
@router.get("/users/{user_id}", response_model=UserResponse)
async def get_user(user_id: int, db: Session = Depends(get_db)):
user = db.query(User).filter(User.id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user
```
### 4. 认证错误10%
**症状**:认证失败、权限不足
**识别特征**
- `401 Unauthorized`
- `403 Forbidden`
- Token 相关错误
- `credentials` 验证失败
**解决策略**:检查认证流程和 Token 处理
```python
# 确保 Token 验证正确
async def get_current_user(token: str = Depends(oauth2_scheme)):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
user_id: str = payload.get("sub")
if user_id is None:
raise credentials_exception
except JWTError:
raise credentials_exception
return user_id
```
### 5. 异步错误8%
**症状**:异步操作超时、并发问题
**识别特征**
- `TimeoutError`
- `CancelledError`
- `asyncio` 相关异常
- 缺少 `await` 关键字
**解决策略**:正确使用 async/await
```python
# Before - 忘记 await
async def get_data():
result = fetch_from_external_api() # 缺少 await
return result
# After - 正确等待异步操作
async def get_data():
result = await fetch_from_external_api()
return result
```
### 6. 配置错误5%
**症状**:配置加载失败、环境变量缺失
**识别特征**
- `KeyError`
- `environment` 相关错误
- `settings` 加载失败
**解决策略**:使用 Pydantic Settings 管理配置
```python
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_url: str
secret_key: str
class Config:
env_file = ".env"
settings = Settings()
```
## 置信度评分系统
### 评分标准0-100
| 分数 | 级别 | 行为 |
| ------ | ------ | ------ |
| 80+ | 高 | 自动执行 |
| 60-79 | 中 | 标记验证后继续 |
| 40-59 | 低 | 暂停询问用户 |
| <40 | 不确定 | 停止收集信息 |
### 置信度计算
```text
置信度 = 证据质量(40%) + 模式匹配(30%) + 上下文完整性(20%) + 可复现性(10%)
```
**证据质量**
- 高:有完整堆栈、行号、可稳定复现
- 中:有错误信息但缺上下文
- 低:仅有模糊描述
**模式匹配**
- 高:完全匹配已知错误模式
- 中:部分匹配
- 低:未知错误类型
**上下文完整性**
- 高:测试代码 + 源代码 + 配置 + 数据库 Schema
- 中:只有测试或源代码
- 低:只有错误信息
**可复现性**
- 高:每次运行都复现
- 中:偶发(可能与数据或并发相关)
- 低:环境相关
## TDD 流程
### RED Phase写失败测试
```python
import pytest
from fastapi.testclient import TestClient
def test_create_user_duplicate_email(client: TestClient, db_session):
"""测试重复邮箱应返回 409"""
# 1. 设置前置条件
client.post("/api/users", json={"email": "test@example.com", "name": "User 1"})
# 2. 执行被测操作
response = client.post("/api/users", json={"email": "test@example.com", "name": "User 2"})
# 3. 断言期望结果
assert response.status_code == 409
assert "already exists" in response.json()["detail"]
```
### GREEN Phase最小实现
```python
# 只写让测试通过的最小代码
# 不要优化,不要添加额外功能
def create_user(db: Session, user: UserCreate):
existing = db.query(User).filter(User.email == user.email).first()
if existing:
raise HTTPException(status_code=409, detail="User already exists")
# ... 创建用户逻辑
```
### REFACTOR Phase重构
```python
# 改善代码结构
# 保持测试通过
# 消除重复
# 提取公共逻辑到服务层
```
## 质量门禁
| 检查项 | 标准 |
| ---------- | ------ |
| 测试通过率 | 100% |
| 代码覆盖率 | >= 90% |
| 新代码覆盖率 | 100% |
| Lint (flake8) | 无错误 |
| TypeCheck (mypy) | 无错误 |
## pytest 常用模式
### Fixtures
```python
@pytest.fixture
def db_session():
"""创建测试数据库会话"""
engine = create_engine("sqlite:///:memory:")
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
yield session
session.close()
@pytest.fixture
def client(db_session):
"""创建测试客户端"""
def override_get_db():
yield db_session
app.dependency_overrides[get_db] = override_get_db
return TestClient(app)
```
### 异步测试
```python
import pytest
@pytest.mark.asyncio
async def test_async_operation():
result = await some_async_function()
assert result is not None
```
### 参数化测试
```python
@pytest.mark.parametrize("status_code,detail", [
(400, "Invalid input"),
(404, "Not found"),
(409, "Already exists"),
])
def test_error_responses(client, status_code, detail):
# 测试多种错误场景
pass
```
## 常用命令
```bash
# 运行后端测试
make test TARGET=backend
# 运行特定测试
make test TARGET=backend FILTER=test_create_user
# 或使用 pytest 直接运行
pytest tests/ -k "test_create_user" -v
# 覆盖率检查
pytest --cov=app --cov-report=term-missing --cov-fail-under=90
# Lint 检查
flake8 app/ tests/
# 类型检查
mypy app/
# 完整 QA
make qa
```
## 相关文档
文档路径由配置指定(`best_practices_dir`),使用以下关键词搜索:
- **测试最佳实践**:关键词 "testing", "pytest", "backend"
- **数据库操作**:关键词 "database", "sqlalchemy", "transaction"
- **API 设计**:关键词 "api", "endpoint", "fastapi"
- **问题诊断**:关键词 "troubleshooting", "debugging"

429
skills/e2e-bugfix/SKILL.md Normal file
View File

@@ -0,0 +1,429 @@
---
name: e2e-bugfix
description: |
This skill should be used when the user asks to "debug E2E tests", "fix Playwright failures", "fix Cypress tests", "analyze timeout errors", or mentions keywords like "Playwright", "Cypress", "Timeout exceeded", "locator", "selector", "flaky test". It provides the complete bugfix workflow knowledge including error classification, confidence scoring, and E2E-specific debugging techniques.
version: 2.1.0
---
# E2E Bugfix Workflow Skill
本 skill 提供端到端测试 bugfix 的完整工作流知识,包括错误分类体系、置信度评分系统和 E2E 特有的调试技巧。
## 错误分类体系
E2E 测试失败主要分为以下类型(按频率排序):
### 1. 超时错误35%
**症状**:元素等待超时、操作超时
**识别特征**
- `Timeout 30000ms exceeded`
- `waiting for locator`
- `waiting for element`
- `TimeoutError`
**解决策略**:使用显式等待和合理超时
```typescript
// Before - 硬编码等待
await page.waitForTimeout(5000);
await page.click('.submit-button');
// After - 等待特定条件
await page.waitForSelector('.submit-button', { state: 'visible' });
await page.click('.submit-button');
// 或使用 Playwright 的自动等待
await page.getByRole('button', { name: 'Submit' }).click();
```
**常见原因**
- 页面加载慢
- 动态内容未渲染
- 网络请求延迟
- 元素被遮挡或不可见
### 2. 选择器错误25%
**症状**:找不到元素、选择器匹配多个元素
**识别特征**
- `strict mode violation`
- `resolved to X elements`
- `element not found`
- `locator.click: Error`
**解决策略**:使用更精确的选择器
```typescript
// Before - 模糊选择器
await page.click('button'); // 可能匹配多个
// After - 精确选择器
// 方法 1使用 data-testid
await page.click('[data-testid="submit-button"]');
// 方法 2使用角色和文本
await page.getByRole('button', { name: 'Submit' }).click();
// 方法 3使用组合选择器
await page.locator('.form-container').getByRole('button').click();
```
**Playwright 推荐选择器优先级**
1. `getByRole()` - 最语义化
2. `getByTestId()` - 最稳定
3. `getByText()` - 用户可见
4. CSS/XPath - 最后手段
### 3. 断言错误15%
**症状**:期望值与实际值不匹配
**识别特征**
- `expect(...).toHave*`
- `Expected:` vs `Received:`
- `AssertionError`
**解决策略**:使用正确的断言和等待
```typescript
// Before - 立即断言
expect(await page.textContent('.message')).toBe('Success');
// After - 使用自动重试的断言
await expect(page.locator('.message')).toHaveText('Success');
// 异步内容断言
await expect(page.locator('.user-list')).toContainText('John');
// 可见性断言
await expect(page.locator('.modal')).toBeVisible();
```
### 4. 网络错误12%
**症状**API 请求失败、网络拦截问题
**识别特征**
- `Route handler` 错误
- `net::ERR_*`
- `request failed`
- Mock 数据不生效
**解决策略**:正确配置网络拦截
```typescript
// Mock API 响应
await page.route('**/api/users', async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ users: [{ id: 1, name: 'Test' }] }),
});
});
// 等待网络请求完成
const responsePromise = page.waitForResponse('**/api/users');
await page.click('.load-users');
const response = await responsePromise;
expect(response.status()).toBe(200);
```
### 5. 导航错误8%
**症状**页面导航失败、URL 不匹配
**识别特征**
- `page.goto: Error`
- `ERR_NAME_NOT_RESOLVED`
- `navigation timeout`
- URL 重定向问题
**解决策略**:正确处理导航
```typescript
// 等待导航完成
await page.goto('http://localhost:3000/login');
await page.waitForURL('**/dashboard');
// 处理重定向
await Promise.all([
page.waitForNavigation(),
page.click('.login-button'),
]);
// 验证 URL
await expect(page).toHaveURL(/.*dashboard/);
```
### 6. 环境错误3%
**症状**:浏览器启动失败、测试环境问题
**识别特征**
- `browser.launch` 失败
- `Target closed`
- `context` 错误
- 端口冲突
**解决策略**:检查环境配置
```typescript
// playwright.config.ts
export default defineConfig({
webServer: {
command: 'npm run start',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
timeout: 120 * 1000,
},
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
});
```
## 置信度评分系统
### 评分标准0-100
| 分数 | 级别 | 行为 |
| ------ | ------ | ------ |
| 80+ | 高 | 自动执行 |
| 60-79 | 中 | 标记验证后继续 |
| 40-59 | 低 | 暂停询问用户 |
| <40 | 不确定 | 停止收集信息 |
### 置信度计算
```text
置信度 = 证据质量(40%) + 模式匹配(30%) + 上下文完整性(20%) + 可复现性(10%)
```
**证据质量**
-有截图、trace、完整堆栈
- 中:有错误信息但缺上下文
- 低:仅有失败描述
**模式匹配**
- 高:完全匹配已知错误模式
- 中:部分匹配
- 低:未知错误类型
**上下文完整性**
- 高:测试代码 + 页面代码 + trace + 截图
- 中:只有测试代码
- 低:只有错误信息
**可复现性**
- 高:每次运行都复现
-偶发flaky test
- 低:仅在特定环境失败
## E2E 调试技巧
### 使用 Trace Viewer
```bash
# 运行测试并收集 trace
npx playwright test --trace on
# 查看 trace
npx playwright show-trace trace.zip
```
### 使用 UI 模式调试
```bash
# 启动 UI 模式
npx playwright test --ui
# 或使用调试模式
npx playwright test --debug
```
### 截图和录像
```typescript
// 测试失败时自动截图
test.afterEach(async ({ page }, testInfo) => {
if (testInfo.status !== 'passed') {
await page.screenshot({ path: `screenshots/${testInfo.title}.png` });
}
});
// 录制视频
// playwright.config.ts
use: {
video: 'on-first-retry',
}
```
### 处理 Flaky Tests
```typescript
// 重试不稳定的测试
test.describe.configure({ retries: 2 });
// 或在配置中设置
export default defineConfig({
retries: process.env.CI ? 2 : 0,
});
```
## TDD 流程
### RED Phase写失败测试
```typescript
import { test, expect } from '@playwright/test';
test('should display error message on invalid login', async ({ page }) => {
// 1. 导航到页面
await page.goto('/login');
// 2. 执行操作
await page.fill('[data-testid="email"]', 'invalid@email');
await page.fill('[data-testid="password"]', 'wrong');
await page.click('[data-testid="submit"]');
// 3. 断言期望结果
await expect(page.locator('.error-message')).toHaveText('Invalid credentials');
});
```
### GREEN Phase最小实现
```typescript
// 只实现让测试通过的最小功能
// 不要优化,不要添加额外功能
```
### REFACTOR Phase重构
```typescript
// 改善测试结构
// 提取 Page Object
// 复用测试辅助函数
```
## Page Object 模式
```typescript
// pages/LoginPage.ts
export class LoginPage {
constructor(private page: Page) {}
async goto() {
await this.page.goto('/login');
}
async login(email: string, password: string) {
await this.page.fill('[data-testid="email"]', email);
await this.page.fill('[data-testid="password"]', password);
await this.page.click('[data-testid="submit"]');
}
async getErrorMessage() {
return this.page.locator('.error-message');
}
}
// 使用 Page Object
test('login with invalid credentials', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('invalid@email', 'wrong');
await expect(loginPage.getErrorMessage()).toHaveText('Invalid credentials');
});
```
## 质量门禁
| 检查项 | 标准 |
| ---------- | ------ |
| 测试通过率 | 100% |
| 代码覆盖率 | >= 90%(如适用) |
| Lint | 无错误 |
| Flaky Rate | < 5% |
## 常用命令
```bash
# 运行所有 E2E 测试
make test TARGET=e2e
# 或使用 Playwright 直接运行
npx playwright test
# 运行特定测试文件
npx playwright test tests/login.spec.ts
# 运行带标签的测试
npx playwright test --grep @smoke
# 运行 UI 模式
npx playwright test --ui
# 生成测试代码
npx playwright codegen localhost:3000
# 查看测试报告
npx playwright show-report
```
## Playwright 配置示例
```typescript
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
],
webServer: {
command: 'npm run start',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
});
```
## 相关文档
文档路径由配置指定(`best_practices_dir`),使用以下关键词搜索:
- **选择器策略**:关键词 "selector", "locator", "data-testid"
- **等待策略**:关键词 "wait", "timeout", "retry"
- **网络拦截**:关键词 "intercept", "mock", "route"
- **问题诊断**:关键词 "troubleshooting", "debugging", "flaky"

View File

@@ -0,0 +1,215 @@
---
name: frontend-bugfix
description: |
Use this skill when debugging frontend test failures (React/TypeScript, Vitest, etc.), fixing bugs in React/TypeScript code, or following TDD methodology for frontend bug fixes. This skill provides the complete bugfix workflow knowledge including error classification, confidence scoring, and TDD best practices.
version: 2.1.0
---
# Frontend Bugfix Workflow Skill
本 skill 提供前端测试 bugfix 的完整工作流知识,包括错误分类体系、置信度评分系统和 TDD 最佳实践。
## 错误分类体系
前端测试失败主要分为以下类型(按频率排序):
### 1. Mock 层次冲突71%
**症状**Mock 不生效,组件行为异常
**识别特征**
- 同时存在 `vi.mock``server.use`
- Hook 返回值与预期不符
- API 调用未被拦截
**解决策略**:选择单一 Mock 层
```typescript
// 选项 AHTTP Mock推荐用于集成测试
server.use(
http.get('/api/data', () => HttpResponse.json({ data: 'test' }))
);
// 选项 BHook Mock用于单元测试
vi.mock('@/hooks/useData', () => ({
useData: () => ({ data: 'test', isLoading: false })
}));
```
### 2. TypeScript 类型不匹配15%
**症状**类型错误、Mock 数据不完整
**识别特征**
- `as any` 或类型断言
- 缺少必需字段
- 类型定义过时
**解决策略**:使用工厂函数
```typescript
const createMockData = (overrides?: Partial<DataType>): DataType => ({
id: 1,
name: 'default',
...overrides
});
```
### 3. 异步时序问题8%
**症状**:测试间歇性失败
**识别特征**
- 缺少 `await`
- 使用 `getBy` 而非 `findBy`
- setTimeout 后立即断言
**解决策略**:正确等待
```typescript
// Before
render(<Component />);
expect(screen.getByText('Loaded')).toBeInTheDocument();
// After
render(<Component />);
expect(await screen.findByText('Loaded')).toBeInTheDocument();
```
### 4. 组件渲染问题4%
**症状**:组件未按预期渲染
**识别特征**
- 条件渲染不触发
- 状态更新未反映
- Props 传递错误
**解决策略**:验证渲染条件和状态
### 5. Hook 缓存依赖问题2%
**症状**Hook 返回过时数据
**识别特征**
- `useEffect` 依赖数组不完整
- `useMemo`/`useCallback` 缓存问题
- 闭包陷阱
**解决策略**:检查并修复依赖数组
## 置信度评分系统
### 评分标准0-100
| 分数 | 级别 | 行为 |
| ------ | ------ | ------ |
| 80+ | 高 | 自动执行 |
| 60-79 | 中 | 标记验证后继续 |
| 40-59 | 低 | 暂停询问用户 |
| <40 | 不确定 | 停止收集信息 |
### 置信度计算
```text
置信度 = 证据质量(40%) + 模式匹配(30%) + 上下文完整性(20%) + 可复现性(10%)
```
**证据质量**
- 高:有代码行号、堆栈、可复现
- 中:有错误信息但缺上下文
- 低:仅有模糊描述
**模式匹配**
- 高:完全匹配已知模式
- 中:部分匹配
- 低:未知错误类型
**上下文完整性**
- 高:测试代码 + 源代码 + 配置
- 中:只有测试或源代码
- 低:只有错误信息
**可复现性**
- 高:稳定复现
- 中:偶发
- 低:环境相关
## TDD 流程
### RED Phase写失败测试
```typescript
// 1. 明确期望行为
it('should display error when API fails', async () => {
// 2. 设置失败场景
server.use(
http.get('/api/data', () => HttpResponse.error())
);
// 3. 渲染组件
render(<DataComponent />);
// 4. 断言期望结果
expect(await screen.findByText('Error loading data')).toBeInTheDocument();
});
```
### GREEN Phase最小实现
```typescript
// 只写让测试通过的最小代码
// 不要优化,不要添加额外功能
```
### REFACTOR Phase重构
```typescript
// 改善代码结构
// 保持测试通过
// 消除重复
```
## 质量门禁
| 检查项 | 标准 |
| ---------- | ------ |
| 测试通过率 | 100% |
| 代码覆盖率 | >= 90% |
| 新代码覆盖率 | 100% |
| Lint | 无错误 |
| TypeCheck | 无错误 |
## 常用命令
```bash
# 运行前端测试
make test TARGET=frontend
# 运行特定测试
make test TARGET=frontend FILTER=ComponentName
# 覆盖率检查
make test TARGET=frontend MODE=coverage
# 完整 QA
make qa
```
## 相关文档
文档路径由配置指定(`best_practices_dir`),使用以下关键词搜索:
- **测试最佳实践**:关键词 "testing", "best-practices"
- **Mock 策略**:关键词 "mock", "msw", "vi.mock"
- **问题诊断**:关键词 "troubleshooting", "debugging"
- **实现指南**:关键词 "implementation", "guide"