Validate WCAG 2.1 Level AA compliance and accessibility best practices. Use when performing accessibility audits and WCAG certification.
日本語の概要は準備中です。原文の説明を表示しています。
Generate comprehensive API endpoint tests for REST and GraphQL APIs. Creates tests for all HTTP methods, status codes, authentication, and validation.
インストール方法を見るインストールする前に、エージェントに与えられる指示の中身を確認できます。
This skill generates comprehensive integration tests for API endpoints, covering all HTTP methods, status codes, authentication, authorization, request/response validation, and error handling.
For each endpoint, test:
Identify endpoints:
# Read route definitions
cat src/routes/users.py
cat src/controllers/user_controller.py
# Identify:
# - Endpoints and HTTP methods
# - Path parameters
# - Query parameters
# - Request body schemas
# - Response schemas
# - Authentication requirements
# - Authorization requirements
Map endpoints:
GET /api/users - List users (public)
GET /api/users/:id - Get user (public)
POST /api/users - Create user (admin only)
PUT /api/users/:id - Update user (auth required, owner or admin)
DELETE /api/users/:id - Delete user (auth required, owner or admin)
Deliverable: API endpoint inventory
Test file structure:
"""
Integration tests for Users API endpoints.
Endpoints tested:
- GET /api/users
- GET /api/users/:id
- POST /api/users
- PUT /api/users/:id
- PATCH /api/users/:id
- DELETE /api/users/:id
"""
import pytest
from fastapi.testclient import TestClient
from sqlalchemy.orm import Session
from src.models import User
# ============================================================================
# GET /api/users - List Users
# ============================================================================
class TestGetUsers:
"""Tests for GET /api/users endpoint."""
def test_get_users_empty_returns_empty_list(self, client: TestClient):
"""Test GET /api/users with no users returns empty list."""
# Act
response = client.get("/api/users")
# Assert
assert response.status_code == 200
assert response.json() == []
def test_get_users_returns_user_list(
self, client: TestClient, db: Session
):
"""Test GET /api/users returns list of users."""
# Arrange: Create test users
users = [
User(name=f"User {i}", email=f"user{i}@example.com")
for i in range(3)
]
db.add_all(users)
db.commit()
# Act
response = client.get("/api/users")
# Assert
assert response.status_code == 200
data = response.json()
assert len(data) == 3
assert all("id" in user for user in data)
assert all("name" in user for user in data)
assert all("email" in user for user in data)
def test_get_users_with_pagination(
self, client: TestClient, db: Session
):
"""Test GET /api/users with pagination parameters."""
# Arrange: Create 10 users
users = [
User(name=f"User {i}", email=f"user{i}@example.com")
for i in range(10)
]
db.add_all(users)
db.commit()
# Act
response = client.get("/api/users?limit=5&offset=0")
# Assert
assert response.status_code == 200
data = response.json()
assert len(data) == 5
def test_get_users_with_search_filter(
self, client: TestClient, db: Session
):
"""Test GET /api/users with search query."""
# Arrange
users = [
User(name="Alice", email="alice@example.com"),
User(name="Bob", email="bob@example.com"),
User(name="Alice Smith", email="asmith@example.com"),
]
db.add_all(users)
db.commit()
# Act
response = client.get("/api/users?search=alice")
# Assert
assert response.status_code == 200
data = response.json()
assert len(data) == 2
assert all("alice" in user["name"].lower() for user in data)
def test_get_users_invalid_limit_returns_400(self, client: TestClient):
"""Test GET /api/users with invalid limit parameter."""
# Act
response = client.get("/api/users?limit=-1")
# Assert
assert response.status_code == 400
assert "limit" in response.json()["detail"].lower()
# ============================================================================
# GET /api/users/:id - Get User by ID
# ============================================================================
class TestGetUserById:
"""Tests for GET /api/users/:id endpoint."""
def test_get_user_by_id_returns_user(
self, client: TestClient, test_user: User
):
"""Test GET /api/users/:id returns specific user."""
# Act
response = client.get(f"/api/users/{test_user.id}")
# Assert
assert response.status_code == 200
data = response.json()
assert data["id"] == test_user.id
assert data["name"] == test_user.name
assert data["email"] == test_user.email
assert "password" not in data # Sensitive data not included
def test_get_user_nonexistent_id_returns_404(self, client: TestClient):
"""Test GET /api/users/:id with nonexistent ID returns 404."""
# Act
response = client.get("/api/users/99999")
# Assert
assert response.status_code == 404
assert "not found" in response.json()["detail"].lower()
def test_get_user_invalid_id_format_returns_400(self, client: TestClient):
"""Test GET /api/users/:id with invalid ID format returns 400."""
# Act
response = client.get("/api/users/invalid-id")
# Assert
assert response.status_code == 400
# ============================================================================
# POST /api/users - Create User
# ============================================================================
class TestCreateUser:
"""Tests for POST /api/users endpoint."""
def test_create_user_valid_data_returns_created(
self, client: TestClient, db: Session, admin_headers: dict
):
"""Test POST /api/users with valid data creates user."""
# Arrange
user_data = {
"name": "New User",
"email": "newuser@example.com",
"password": "SecurePass123"
}
# Act
response = client.post(
"/api/users",
json=user_data,
headers=admin_headers
)
# Assert
assert response.status_code == 201
data = response.json()
assert data["name"] == user_data["name"]
assert data["email"] == user_data["email"]
assert "id" in data
assert "password" not in data # Password not returned
assert "created_at" in data
# Verify in database
user = db.query(User).filter_by(email=user_data["email"]).first()
assert user is not None
assert user.name == user_data["name"]
def test_create_user_missing_required_field_returns_400(
self, client: TestClient, admin_headers: dict
):
"""Test POST /api/users with missing required field returns 400."""
# Arrange: Missing email
invalid_data = {
"name": "User",
"password": "password"
}
# Act
response = client.post(
"/api/users",
json=invalid_data,
headers=admin_headers
)
# Assert
assert response.status_code == 400
assert "email" in response.json()["detail"].lower()
def test_create_user_invalid_email_returns_400(
self, client: TestClient, admin_headers: dict
):
"""Test POST /api/users with invalid email format returns 400."""
# Arrange
invalid_data = {
"name": "User",
"email": "not-an-email",
"password": "password"
}
# Act
response = client.post(
"/api/users",
json=invalid_data,
headers=admin_headers
)
# Assert
assert response.status_code == 400
assert "email" in response.json()["detail"].lower()
def test_create_user_weak_password_returns_400(
self, client: TestClient, admin_headers: dict
):
"""Test POST /api/users with weak password returns 400."""
# Arrange
invalid_data = {
"name": "User",
"email": "user@example.com",
"password": "123" # Too short
}
# Act
response = client.post(
"/api/users",
json=invalid_data,
headers=admin_headers
)
# Assert
assert response.status_code == 400
assert "password" in response.json()["detail"].lower()
def test_create_user_duplicate_email_returns_409(
self, client: TestClient, test_user: User, admin_headers: dict
):
"""Test POST /api/users with duplicate email returns 409."""
# Arrange
duplicate_data = {
"name": "Another User",
"email": test_user.email, # Duplicate
"password": "password"
}
# Act
response = client.post(
"/api/users",
json=duplicate_data,
headers=admin_headers
)
# Assert
assert response.status_code == 409
assert "already exists" in response.json()["detail"].lower()
def test_create_user_without_auth_returns_401(
self, client: TestClient
):
"""Test POST /api/users without authentication returns 401."""
# Arrange
user_data = {
"name": "User",
"email": "user@example.com",
"password": "password"
}
# Act
response = client.post("/api/users", json=user_data)
# Assert
assert response.status_code == 401
def test_create_user_as_non_admin_returns_403(
self, client: TestClient, auth_headers: dict
):
"""Test POST /api/users as non-admin user returns 403."""
# Arrange
user_data = {
"name": "User",
"email": "user@example.com",
"password": "password"
}
# Act
response = client.post(
"/api/users",
json=user_data,
headers=auth_headers # Regular user, not admin
)
# Assert
assert response.status_code == 403
# ============================================================================
# PUT /api/users/:id - Update User (Full Replace)
# ============================================================================
class TestUpdateUser:
"""Tests for PUT /api/users/:id endpoint."""
def test_update_user_own_account_returns_updated(
self, client: TestClient, test_user: User, auth_headers: dict, db: Session
):
"""Test PUT /api/users/:id to update own account."""
# Arrange
update_data = {
"name": "Updated Name",
"email": test_user.email # Same email
}
# Act
response = client.put(
f"/api/users/{test_user.id}",
json=update_data,
headers=auth_headers
)
# Assert
assert response.status_code == 200
data = response.json()
assert data["name"] == "Updated Name"
# Verify in database
db.refresh(test_user)
assert test_user.name == "Updated Name"
def test_update_user_other_account_as_admin_succeeds(
self, client: TestClient, test_user: User, admin_headers: dict, db: Session
):
"""Test PUT /api/users/:id as admin to update other user."""
# Arrange
update_data = {
"name": "Admin Updated",
"email": test_user.email
}
# Act
response = client.put(
f"/api/users/{test_user.id}",
json=update_data,
headers=admin_headers
)
# Assert
assert response.status_code == 200
def test_update_user_other_account_as_regular_user_returns_403(
self, client: TestClient, db: Session, auth_headers: dict
):
"""Test PUT /api/users/:id to update other user returns 403."""
# Arrange: Create another user
other_user = User(name="Other", email="other@example.com")
db.add(other_user)
db.commit()
update_data = {"name": "Unauthorized Update"}
# Act
response = client.put(
f"/api/users/{other_user.id}",
json=update_data,
headers=auth_headers # Regular user, not admin
)
# Assert
assert response.status_code == 403
def test_update_user_nonexistent_returns_404(
self, client: TestClient, admin_headers: dict
):
"""Test PUT /api/users/:id with nonexistent ID returns 404."""
# Arrange
update_data = {"name": "Updated"}
# Act
response = client.put(
"/api/users/99999",
json=update_data,
headers=admin_headers
)
# Assert
assert response.status_code == 404
# ============================================================================
# PATCH /api/users/:id - Partial Update User
# ============================================================================
class TestPatchUser:
"""Tests for PATCH /api/users/:id endpoint."""
def test_patch_user_single_field_updates(
self, client: TestClient, test_user: User, auth_headers: dict, db: Session
):
"""Test PATCH /api/users/:id updates only specified field."""
# Arrange
original_email = test_user.email
patch_data = {"name": "Patched Name"}
# Act
response = client.patch(
f"/api/users/{test_user.id}",
json=patch_data,
headers=auth_headers
)
# Assert
assert response.status_code == 200
data = response.json()
assert data["name"] == "Patched Name"
assert data["email"] == original_email # Unchanged
# Verify in database
db.refresh(test_user)
assert test_user.name == "Patched Name"
assert test_user.email == original_email
# ============================================================================
# DELETE /api/users/:id - Delete User
# ============================================================================
class TestDeleteUser:
"""Tests for DELETE /api/users/:id endpoint."""
def test_delete_user_own_account_returns_no_content(
self, client: TestClient, test_user: User, auth_headers: dict, db: Session
):
"""Test DELETE /api/users/:id to delete own account."""
# Act
response = client.delete(
f"/api/users/{test_user.id}",
headers=auth_headers
)
# Assert
assert response.status_code == 204
# Verify in database
deleted_user = db.query(User).filter_by(id=test_user.id).first()
assert deleted_user is None
def test_delete_user_as_admin_succeeds(
self, client: TestClient, test_user: User, admin_headers: dict, db: Session
):
"""Test DELETE /api/users/:id as admin."""
# Act
response = client.delete(
f"/api/users/{test_user.id}",
headers=admin_headers
)
# Assert
assert response.status_code == 204
def test_delete_user_other_account_returns_403(
self, client: TestClient, db: Session, auth_headers: dict
):
"""Test DELETE /api/users/:id to delete other user returns 403."""
# Arrange
other_user = User(name="Other", email="other@example.com")
db.add(other_user)
db.commit()
# Act
response = client.delete(
f"/api/users/{other_user.id}",
headers=auth_headers
)
# Assert
assert response.status_code == 403
def test_delete_user_without_auth_returns_401(
self, client: TestClient, test_user: User
):
"""Test DELETE /api/users/:id without auth returns 401."""
# Act
response = client.delete(f"/api/users/{test_user.id}")
# Assert
assert response.status_code == 401
def test_delete_user_nonexistent_returns_404(
self, client: TestClient, admin_headers: dict
):
"""Test DELETE /api/users/:id with nonexistent ID returns 404."""
# Act
response = client.delete(
"/api/users/99999",
headers=admin_headers
)
# Assert
assert response.status_code == 404
Deliverable: Comprehensive REST API tests
GraphQL test structure:
"""
Integration tests for GraphQL API.
"""
import pytest
from fastapi.testclient import TestClient
from sqlalchemy.orm import Session
class TestGraphQLQueries:
"""Tests for GraphQL queries."""
def test_query_users_returns_list(
self, client: TestClient, db: Session
):
"""Test users query returns list."""
# Arrange
create_test_users(db, count=3)
query = """
query {
users {
id
name
email
}
}
"""
# Act
response = client.post("/graphql", json={"query": query})
# Assert
assert response.status_code == 200
data = response.json()["data"]
assert len(data["users"]) == 3
def test_query_user_by_id_returns_user(
self, client: TestClient, test_user: User
):
"""Test user query by ID returns specific user."""
# Arrange
query = f"""
query {{
user(id: {test_user.id}) {{
id
name
email
}}
}}
"""
# Act
response = client.post("/graphql", json={"query": query})
# Assert
assert response.status_code == 200
data = response.json()["data"]["user"]
assert data["id"] == test_user.id
assert data["name"] == test_user.name
class TestGraphQLMutations:
"""Tests for GraphQL mutations."""
def test_create_user_mutation_creates_user(
self, client: TestClient, db: Session, admin_headers: dict
):
"""Test createUser mutation creates new user."""
# Arrange
mutation = """
mutation {
createUser(input: {
name: "New User",
email: "newuser@example.com",
password: "SecurePass123"
}) {
user {
id
name
email
}
}
}
"""
# Act
response = client.post(
"/graphql",
json={"query": mutation},
headers=admin_headers
)
# Assert
assert response.status_code == 200
data = response.json()["data"]["createUser"]["user"]
assert data["name"] == "New User"
assert data["email"] == "newuser@example.com"
# Verify in database
user = db.query(User).filter_by(email="newuser@example.com").first()
assert user is not None
Deliverable: GraphQL API tests
Test all relevant status codes:
# 200 OK - Successful GET/PUT/PATCH
def test_returns_200_on_success(client):
response = client.get("/api/resource")
assert response.status_code == 200
# 201 Created - Successful POST
def test_returns_201_on_create(client):
response = client.post("/api/resource", json=data)
assert response.status_code == 201
# 204 No Content - Successful DELETE
def test_returns_204_on_delete(client):
response = client.delete("/api/resource/1")
assert response.status_code == 204
# 400 Bad Request - Validation error
def test_returns_400_on_invalid_input(client):
response = client.post("/api/resource", json=invalid_data)
assert response.status_code == 400
# 401 Unauthorized - Missing/invalid auth
def test_returns_401_without_auth(client):
response = client.get("/api/protected")
assert response.status_code == 401
# 403 Forbidden - Insufficient permissions
def test_returns_403_without_permission(client, user_token):
response = client.delete("/api/admin/resource", headers=user_token)
assert response.status_code == 403
# 404 Not Found - Resource doesn't exist
def test_returns_404_for_nonexistent(client):
response = client.get("/api/resource/99999")
assert response.status_code == 404
# 409 Conflict - Duplicate resource
def test_returns_409_on_duplicate(client):
response = client.post("/api/resource", json=existing_data)
assert response.status_code == 409
# 422 Unprocessable Entity - Semantic error
def test_returns_422_on_semantic_error(client):
response = client.post("/api/resource", json=invalid_semantic_data)
assert response.status_code == 422
# 500 Internal Server Error - Server error
def test_returns_500_on_server_error(client, mock_error):
response = client.get("/api/resource")
assert response.status_code == 500
Before completing API tests:
Input: API routes and endpoints Process: Analyze → Generate tests → Run & verify Output: Comprehensive API test suite Next Step: API documentation or deployment
まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Validate WCAG 2.1 Level AA compliance and accessibility best practices. Use when performing accessibility audits and WCAG certification.
日本語の概要は準備中です。原文の説明を表示しています。
Analyze feature requirements, dependencies, and security considerations. Use when starting feature implementation from GitHub issues to understand scope, technical feasibility, and risks.
日本語の概要は準備中です。原文の説明を表示しています。
Run SQL queries against psql, BigQuery, or MySQL from the terminal, including natural-language-to-SQL and schema exploration. Use when analyzing data, inspecting DB state, or debugging tables. Trigger on "query the database", "SQL", "show me data from", "explore table".
日本語の概要は準備中です。原文の説明を表示しています。
Design REST APIs or function contracts with clear request/response specifications, error handling patterns, authentication strategies, and comprehensive documentation.
日本語の概要は準備中です。原文の説明を表示しています。
Design component architecture and module structure using established architectural patterns for clean, maintainable, and scalable systems.
日本語の概要は準備中です。原文の説明を表示しています。
Generate CHANGELOG entries following conventional commits format with semantic versioning. Use when finalizing features to maintain proper change history.
日本語の概要は準備中です。原文の説明を表示しています。