omgkit 2.1.1 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/package.json +1 -1
  2. package/plugin/skills/databases/mongodb/SKILL.md +81 -28
  3. package/plugin/skills/databases/prisma/SKILL.md +87 -32
  4. package/plugin/skills/databases/redis/SKILL.md +80 -27
  5. package/plugin/skills/devops/aws/SKILL.md +80 -26
  6. package/plugin/skills/devops/github-actions/SKILL.md +84 -32
  7. package/plugin/skills/devops/kubernetes/SKILL.md +94 -32
  8. package/plugin/skills/devops/performance-profiling/SKILL.md +59 -863
  9. package/plugin/skills/frameworks/django/SKILL.md +158 -24
  10. package/plugin/skills/frameworks/express/SKILL.md +153 -33
  11. package/plugin/skills/frameworks/fastapi/SKILL.md +153 -34
  12. package/plugin/skills/frameworks/laravel/SKILL.md +146 -33
  13. package/plugin/skills/frameworks/nestjs/SKILL.md +137 -25
  14. package/plugin/skills/frameworks/rails/SKILL.md +594 -28
  15. package/plugin/skills/frameworks/react/SKILL.md +94 -962
  16. package/plugin/skills/frameworks/spring/SKILL.md +528 -35
  17. package/plugin/skills/frameworks/vue/SKILL.md +147 -25
  18. package/plugin/skills/frontend/accessibility/SKILL.md +145 -36
  19. package/plugin/skills/frontend/frontend-design/SKILL.md +114 -29
  20. package/plugin/skills/frontend/responsive/SKILL.md +131 -28
  21. package/plugin/skills/frontend/shadcn-ui/SKILL.md +133 -43
  22. package/plugin/skills/frontend/tailwindcss/SKILL.md +105 -37
  23. package/plugin/skills/frontend/threejs/SKILL.md +110 -35
  24. package/plugin/skills/languages/javascript/SKILL.md +195 -34
  25. package/plugin/skills/methodology/brainstorming/SKILL.md +98 -30
  26. package/plugin/skills/methodology/defense-in-depth/SKILL.md +83 -37
  27. package/plugin/skills/methodology/dispatching-parallel-agents/SKILL.md +92 -31
  28. package/plugin/skills/methodology/executing-plans/SKILL.md +117 -28
  29. package/plugin/skills/methodology/finishing-development-branch/SKILL.md +111 -32
  30. package/plugin/skills/methodology/problem-solving/SKILL.md +65 -311
  31. package/plugin/skills/methodology/receiving-code-review/SKILL.md +76 -27
  32. package/plugin/skills/methodology/requesting-code-review/SKILL.md +93 -22
  33. package/plugin/skills/methodology/root-cause-tracing/SKILL.md +75 -40
  34. package/plugin/skills/methodology/sequential-thinking/SKILL.md +75 -224
  35. package/plugin/skills/methodology/systematic-debugging/SKILL.md +81 -35
  36. package/plugin/skills/methodology/test-driven-development/SKILL.md +120 -26
  37. package/plugin/skills/methodology/testing-anti-patterns/SKILL.md +88 -35
  38. package/plugin/skills/methodology/token-optimization/SKILL.md +73 -34
  39. package/plugin/skills/methodology/verification-before-completion/SKILL.md +128 -28
  40. package/plugin/skills/methodology/writing-plans/SKILL.md +105 -20
  41. package/plugin/skills/omega/omega-architecture/SKILL.md +178 -40
  42. package/plugin/skills/omega/omega-coding/SKILL.md +247 -41
  43. package/plugin/skills/omega/omega-sprint/SKILL.md +208 -46
  44. package/plugin/skills/omega/omega-testing/SKILL.md +253 -42
  45. package/plugin/skills/omega/omega-thinking/SKILL.md +263 -51
  46. package/plugin/skills/security/better-auth/SKILL.md +83 -34
  47. package/plugin/skills/security/oauth/SKILL.md +118 -35
  48. package/plugin/skills/security/owasp/SKILL.md +112 -35
  49. package/plugin/skills/testing/playwright/SKILL.md +141 -38
  50. package/plugin/skills/testing/pytest/SKILL.md +137 -38
  51. package/plugin/skills/testing/vitest/SKILL.md +124 -39
  52. package/plugin/skills/tools/document-processing/SKILL.md +111 -838
  53. package/plugin/skills/tools/image-processing/SKILL.md +126 -659
  54. package/plugin/skills/tools/mcp-development/SKILL.md +85 -758
  55. package/plugin/skills/tools/media-processing/SKILL.md +118 -735
  56. package/plugin/stdrules/SKILL_STANDARDS.md +490 -0
@@ -1,57 +1,134 @@
1
1
  ---
2
- name: owasp
3
- description: OWASP security. Use for security best practices, vulnerability prevention.
2
+ name: Applying OWASP Security
3
+ description: Claude applies OWASP security best practices to web applications. Use when preventing vulnerabilities, implementing input validation, securing authentication, configuring security headers, or conducting security reviews.
4
4
  ---
5
5
 
6
- # OWASP Skill
6
+ # Applying OWASP Security
7
7
 
8
- ## Top 10 Prevention
8
+ ## Quick Start
9
9
 
10
- ### 1. Injection
11
10
  ```typescript
12
- // Bad
13
- db.query(`SELECT * FROM users WHERE id = ${id}`);
11
+ // lib/security/validation.ts
12
+ import { z } from "zod";
13
+ import DOMPurify from "isomorphic-dompurify";
14
14
 
15
- // Good
16
- db.query('SELECT * FROM users WHERE id = $1', [id]);
17
- ```
15
+ // Input validation
16
+ export const userSchema = z.object({
17
+ email: z.string().email().max(254),
18
+ password: z.string().min(12).max(128),
19
+ name: z.string().min(2).max(100).regex(/^[\p{L}\s'-]+$/u),
20
+ });
18
21
 
19
- ### 2. Broken Authentication
20
- ```typescript
21
- // Use secure session management
22
- // Implement MFA
23
- // Rate limit login attempts
22
+ // HTML sanitization
23
+ export const sanitizeHtml = (dirty: string) =>
24
+ DOMPurify.sanitize(dirty, { ALLOWED_TAGS: ["b", "i", "em", "strong", "a", "p"] });
24
25
  ```
25
26
 
26
- ### 3. XSS Prevention
27
+ ## Features
28
+
29
+ | Feature | Description | Reference |
30
+ |---------|-------------|-----------|
31
+ | Injection Prevention | SQL, NoSQL, command injection protection | [OWASP Injection](https://owasp.org/Top10/A03_2021-Injection/) |
32
+ | XSS Prevention | Output encoding and HTML sanitization | [OWASP XSS](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html) |
33
+ | CSRF Protection | Token-based cross-site request forgery defense | [OWASP CSRF](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html) |
34
+ | Authentication Security | Password hashing, rate limiting, session management | [OWASP Auth](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html) |
35
+ | Security Headers | CSP, HSTS, X-Frame-Options configuration | [OWASP Headers](https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html) |
36
+ | Input Validation | Schema validation and sanitization | [OWASP Validation](https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html) |
37
+
38
+ ## Common Patterns
39
+
40
+ ### Parameterized Queries (SQL Injection Prevention)
41
+
27
42
  ```typescript
28
- // Bad
29
- element.innerHTML = userInput;
43
+ // BAD - SQL injection vulnerable
44
+ const result = await db.$queryRawUnsafe(`SELECT * FROM users WHERE id = '${userId}'`);
30
45
 
31
- // Good
32
- element.textContent = userInput;
33
- // Or use DOMPurify
46
+ // GOOD - Parameterized query
47
+ const result = await db.user.findUnique({ where: { id: userId } });
48
+ const result = await db.$queryRaw`SELECT * FROM users WHERE id = ${userId}`;
34
49
  ```
35
50
 
36
- ### 4. CSRF Prevention
51
+ ### CSRF Protection Middleware
52
+
37
53
  ```typescript
38
- // Use CSRF tokens
39
- // SameSite cookies
40
- // Verify Origin header
54
+ import crypto from "crypto";
55
+
56
+ export function csrfProtection(req: Request, res: Response, next: NextFunction) {
57
+ if (["GET", "HEAD", "OPTIONS"].includes(req.method)) return next();
58
+
59
+ const cookieToken = req.cookies["csrf_token"];
60
+ const headerToken = req.headers["x-csrf-token"];
61
+
62
+ if (!cookieToken || !headerToken || cookieToken !== headerToken) {
63
+ return res.status(403).json({ error: "CSRF validation failed" });
64
+ }
65
+ next();
66
+ }
41
67
  ```
42
68
 
43
- ### 5. Security Headers
69
+ ### Security Headers Configuration
70
+
44
71
  ```typescript
72
+ import helmet from "helmet";
73
+
45
74
  app.use(helmet({
46
- contentSecurityPolicy: true,
47
- hsts: true,
48
- noSniff: true,
75
+ contentSecurityPolicy: {
76
+ directives: {
77
+ defaultSrc: ["'self'"],
78
+ scriptSrc: ["'self'", "'strict-dynamic'"],
79
+ styleSrc: ["'self'", "'unsafe-inline'"],
80
+ imgSrc: ["'self'", "data:", "https:"],
81
+ frameSrc: ["'none'"],
82
+ objectSrc: ["'none'"],
83
+ },
84
+ },
85
+ strictTransportSecurity: { maxAge: 31536000, includeSubDomains: true, preload: true },
86
+ frameguard: { action: "deny" },
49
87
  }));
50
88
  ```
51
89
 
52
- ## Checklist
53
- - [ ] Input validation
54
- - [ ] Output encoding
55
- - [ ] Parameterized queries
56
- - [ ] Secure headers
57
- - [ ] HTTPS only
90
+ ### Password Security
91
+
92
+ ```typescript
93
+ import bcrypt from "bcrypt";
94
+
95
+ const SALT_ROUNDS = 12;
96
+
97
+ export async function hashPassword(password: string): Promise<string> {
98
+ return bcrypt.hash(password, SALT_ROUNDS);
99
+ }
100
+
101
+ export async function verifyPassword(password: string, hash: string): Promise<boolean> {
102
+ return bcrypt.compare(password, hash);
103
+ }
104
+
105
+ export function validatePasswordStrength(password: string): string[] {
106
+ const errors: string[] = [];
107
+ if (password.length < 12) errors.push("Must be at least 12 characters");
108
+ if (!/[a-z]/.test(password)) errors.push("Must contain lowercase");
109
+ if (!/[A-Z]/.test(password)) errors.push("Must contain uppercase");
110
+ if (!/\d/.test(password)) errors.push("Must contain digit");
111
+ if (!/[!@#$%^&*]/.test(password)) errors.push("Must contain special character");
112
+ return errors;
113
+ }
114
+ ```
115
+
116
+ ## Best Practices
117
+
118
+ | Do | Avoid |
119
+ |----|-------|
120
+ | Validate all input on the server side | Trusting client-side validation alone |
121
+ | Use parameterized queries for all DB access | String concatenation in queries |
122
+ | Set security headers on all responses | Disabling security features for convenience |
123
+ | Implement rate limiting on sensitive endpoints | Allowing unlimited attempts |
124
+ | Hash passwords with bcrypt (12+ rounds) | Using weak/deprecated crypto algorithms |
125
+ | Log security events for monitoring | Exposing detailed error messages to users |
126
+ | Keep dependencies updated | Ignoring security warnings |
127
+ | Use HTTPS for all communications | Hardcoding secrets in source code |
128
+
129
+ ## References
130
+
131
+ - [OWASP Top 10](https://owasp.org/www-project-top-ten/)
132
+ - [OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org/)
133
+ - [OWASP Testing Guide](https://owasp.org/www-project-web-security-testing-guide/)
134
+ - [OWASP ASVS](https://owasp.org/www-project-application-security-verification-standard/)
@@ -1,60 +1,163 @@
1
1
  ---
2
- name: playwright
3
- description: Playwright E2E testing. Use for browser automation, E2E tests.
2
+ name: Testing with Playwright
3
+ description: Claude writes reliable E2E tests using Playwright for browser automation. Use when writing end-to-end tests, implementing Page Object Model, visual regression testing, API mocking, or cross-browser testing.
4
4
  ---
5
5
 
6
- # Playwright Skill
6
+ # Testing with Playwright
7
7
 
8
- ## Basic Test
9
- ```typescript
10
- import { test, expect } from '@playwright/test';
8
+ ## Quick Start
11
9
 
12
- test('homepage has title', async ({ page }) => {
13
- await page.goto('/');
14
- await expect(page).toHaveTitle(/My App/);
15
- });
10
+ ```typescript
11
+ // playwright.config.ts
12
+ import { defineConfig, devices } from "@playwright/test";
16
13
 
17
- test('login flow', async ({ page }) => {
18
- await page.goto('/login');
19
- await page.fill('[name="email"]', 'test@example.com');
20
- await page.fill('[name="password"]', 'password');
21
- await page.click('button[type="submit"]');
22
- await expect(page).toHaveURL('/dashboard');
14
+ export default defineConfig({
15
+ testDir: "./tests/e2e",
16
+ fullyParallel: true,
17
+ retries: process.env.CI ? 2 : 0,
18
+ reporter: [["list"], ["html"]],
19
+ use: {
20
+ baseURL: "http://localhost:3000",
21
+ trace: "on-first-retry",
22
+ screenshot: "only-on-failure",
23
+ },
24
+ projects: [
25
+ { name: "chromium", use: { ...devices["Desktop Chrome"] } },
26
+ { name: "firefox", use: { ...devices["Desktop Firefox"] } },
27
+ { name: "mobile", use: { ...devices["iPhone 12"] } },
28
+ ],
29
+ webServer: { command: "npm run dev", url: "http://localhost:3000" },
23
30
  });
24
31
  ```
25
32
 
26
- ## Page Object Model
33
+ ## Features
34
+
35
+ | Feature | Description | Reference |
36
+ |---------|-------------|-----------|
37
+ | Page Object Model | Maintainable test architecture pattern | [POM Guide](https://playwright.dev/docs/pom) |
38
+ | Auto-Waiting | Built-in waiting for elements and assertions | [Auto-Waiting](https://playwright.dev/docs/actionability) |
39
+ | Network Mocking | Intercept and mock API responses | [Network](https://playwright.dev/docs/network) |
40
+ | Visual Testing | Screenshot comparison for regression testing | [Visual Comparisons](https://playwright.dev/docs/test-snapshots) |
41
+ | Cross-Browser | Chrome, Firefox, Safari, mobile devices | [Browsers](https://playwright.dev/docs/browsers) |
42
+ | Trace Viewer | Debug failing tests with timeline | [Trace Viewer](https://playwright.dev/docs/trace-viewer) |
43
+
44
+ ## Common Patterns
45
+
46
+ ### Page Object Model
47
+
27
48
  ```typescript
28
- class LoginPage {
29
- constructor(private page: Page) {}
49
+ // tests/pages/login.page.ts
50
+ import { Page, Locator, expect } from "@playwright/test";
51
+
52
+ export class LoginPage {
53
+ readonly emailInput: Locator;
54
+ readonly passwordInput: Locator;
55
+ readonly submitButton: Locator;
56
+
57
+ constructor(private page: Page) {
58
+ this.emailInput = page.getByLabel("Email");
59
+ this.passwordInput = page.getByLabel("Password");
60
+ this.submitButton = page.getByRole("button", { name: "Sign in" });
61
+ }
30
62
 
31
63
  async login(email: string, password: string) {
32
- await this.page.fill('[name="email"]', email);
33
- await this.page.fill('[name="password"]', password);
34
- await this.page.click('button[type="submit"]');
64
+ await this.emailInput.fill(email);
65
+ await this.passwordInput.fill(password);
66
+ await this.submitButton.click();
67
+ }
68
+
69
+ async expectError(message: string) {
70
+ await expect(this.page.getByRole("alert")).toContainText(message);
35
71
  }
36
72
  }
37
73
  ```
38
74
 
39
- ## Config
75
+ ### API Mocking
76
+
40
77
  ```typescript
41
- // playwright.config.ts
42
- export default defineConfig({
43
- testDir: './tests',
44
- use: {
45
- baseURL: 'http://localhost:3000',
46
- screenshot: 'only-on-failure',
78
+ import { test, expect } from "@playwright/test";
79
+
80
+ test("mock API response", async ({ page }) => {
81
+ await page.route("**/api/users", (route) =>
82
+ route.fulfill({
83
+ status: 200,
84
+ contentType: "application/json",
85
+ body: JSON.stringify({ users: [{ id: 1, name: "John" }] }),
86
+ })
87
+ );
88
+
89
+ await page.goto("/users");
90
+ await expect(page.getByText("John")).toBeVisible();
91
+ });
92
+
93
+ test("capture network requests", async ({ page }) => {
94
+ const requestPromise = page.waitForRequest("**/api/analytics");
95
+ await page.goto("/dashboard");
96
+ const request = await requestPromise;
97
+ expect(request.postDataJSON()).toMatchObject({ event: "page_view" });
98
+ });
99
+ ```
100
+
101
+ ### Authentication Fixture
102
+
103
+ ```typescript
104
+ // tests/fixtures/auth.fixture.ts
105
+ import { test as base } from "@playwright/test";
106
+ import { LoginPage } from "../pages/login.page";
107
+
108
+ export const test = base.extend<{ authenticatedPage: Page }>({
109
+ authenticatedPage: async ({ page }, use) => {
110
+ // Fast auth via API
111
+ const response = await page.request.post("/api/auth/login", {
112
+ data: { email: "test@example.com", password: "password" },
113
+ });
114
+ const { token } = await response.json();
115
+
116
+ await page.context().addCookies([
117
+ { name: "auth_token", value: token, domain: "localhost", path: "/" },
118
+ ]);
119
+
120
+ await page.goto("/dashboard");
121
+ await use(page);
47
122
  },
48
- projects: [
49
- { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
50
- { name: 'mobile', use: { ...devices['iPhone 12'] } },
51
- ],
52
123
  });
53
124
  ```
54
125
 
55
- ## Run
56
- ```bash
57
- npx playwright test
58
- npx playwright test --ui
59
- npx playwright codegen
126
+ ### Visual Regression Testing
127
+
128
+ ```typescript
129
+ test("visual snapshot", async ({ page }) => {
130
+ await page.goto("/");
131
+ await page.addStyleTag({
132
+ content: "*, *::before, *::after { animation-duration: 0s !important; }",
133
+ });
134
+
135
+ await expect(page).toHaveScreenshot("homepage.png", {
136
+ fullPage: true,
137
+ maxDiffPixels: 100,
138
+ });
139
+
140
+ // Mask dynamic content
141
+ await expect(page).toHaveScreenshot("dashboard.png", {
142
+ mask: [page.getByTestId("timestamp"), page.getByTestId("avatar")],
143
+ });
144
+ });
60
145
  ```
146
+
147
+ ## Best Practices
148
+
149
+ | Do | Avoid |
150
+ |----|-------|
151
+ | Use Page Object Model for maintainability | Fragile CSS selectors |
152
+ | Prefer user-facing locators (getByRole, getByLabel) | Relying on arbitrary waits |
153
+ | Use API auth for faster test setup | Sharing state between tests |
154
+ | Enable traces and screenshots for debugging | Testing third-party services directly |
155
+ | Run tests in parallel for speed | Skipping flaky tests without fixing |
156
+ | Mock external APIs for reliability | Hardcoding test data |
157
+
158
+ ## References
159
+
160
+ - [Playwright Documentation](https://playwright.dev/docs/intro)
161
+ - [Playwright Best Practices](https://playwright.dev/docs/best-practices)
162
+ - [Page Object Model](https://playwright.dev/docs/pom)
163
+ - [Visual Comparisons](https://playwright.dev/docs/test-snapshots)
@@ -1,58 +1,157 @@
1
1
  ---
2
- name: pytest
3
- description: Python testing with pytest. Use for unit tests, fixtures, mocking.
2
+ name: Testing with Pytest
3
+ description: Claude writes comprehensive Python tests using pytest. Use when writing unit tests, creating fixtures, parametrizing tests, mocking dependencies, testing async code, or setting up CI test pipelines.
4
4
  ---
5
5
 
6
- # Pytest Skill
6
+ # Testing with Pytest
7
7
 
8
- ## Basic Tests
9
- ```python
10
- def test_add():
11
- assert add(1, 2) == 3
8
+ ## Quick Start
12
9
 
13
- def test_raises():
14
- with pytest.raises(ValueError):
15
- validate("")
10
+ ```ini
11
+ # pytest.ini
12
+ [pytest]
13
+ testpaths = tests
14
+ python_files = test_*.py
15
+ addopts = -v --strict-markers --cov=src --cov-report=term-missing --cov-fail-under=80
16
+ markers =
17
+ slow: marks tests as slow
18
+ integration: marks tests as integration tests
19
+ asyncio_mode = auto
16
20
  ```
17
21
 
18
- ## Fixtures
19
22
  ```python
20
- @pytest.fixture
21
- def user():
22
- return User(email="test@example.com")
23
+ # tests/conftest.py
24
+ import pytest
25
+ from sqlalchemy.orm import Session
23
26
 
24
27
  @pytest.fixture
25
- async def db():
26
- conn = await create_connection()
27
- yield conn
28
- await conn.close()
28
+ def db_session(engine) -> Session:
29
+ """Create database session with automatic rollback."""
30
+ connection = engine.connect()
31
+ transaction = connection.begin()
32
+ session = sessionmaker(bind=connection)()
33
+ yield session
34
+ session.close()
35
+ transaction.rollback()
36
+ connection.close()
37
+ ```
38
+
39
+ ## Features
40
+
41
+ | Feature | Description | Reference |
42
+ |---------|-------------|-----------|
43
+ | Fixtures | Reusable test setup with dependency injection | [Fixtures Guide](https://docs.pytest.org/en/latest/how-to/fixtures.html) |
44
+ | Parametrization | Test multiple inputs with @pytest.mark.parametrize | [Parametrize](https://docs.pytest.org/en/latest/how-to/parametrize.html) |
45
+ | Mocking | unittest.mock integration for patching | [pytest-mock](https://pytest-mock.readthedocs.io/) |
46
+ | Async Testing | Native async/await support with pytest-asyncio | [pytest-asyncio](https://pytest-asyncio.readthedocs.io/) |
47
+ | Markers | Categorize and filter tests | [Markers](https://docs.pytest.org/en/latest/how-to/mark.html) |
48
+ | Coverage | Code coverage with pytest-cov | [pytest-cov](https://pytest-cov.readthedocs.io/) |
49
+
50
+ ## Common Patterns
51
+
52
+ ### Parametrized Validation Tests
29
53
 
30
- def test_user_email(user):
31
- assert user.email == "test@example.com"
54
+ ```python
55
+ import pytest
56
+ from src.validators import validate_email, ValidationError
57
+
58
+ class TestEmailValidation:
59
+ @pytest.mark.parametrize("email", [
60
+ "user@example.com",
61
+ "user.name@example.com",
62
+ "user+tag@example.co.uk",
63
+ ])
64
+ def test_valid_emails(self, email: str):
65
+ assert validate_email(email) is True
66
+
67
+ @pytest.mark.parametrize("email,error", [
68
+ ("", "Email is required"),
69
+ ("invalid", "Invalid email format"),
70
+ ("@example.com", "Invalid email format"),
71
+ ])
72
+ def test_invalid_emails(self, email: str, error: str):
73
+ with pytest.raises(ValidationError, match=error):
74
+ validate_email(email)
32
75
  ```
33
76
 
34
- ## Parametrize
77
+ ### Factory Fixtures
78
+
35
79
  ```python
36
- @pytest.mark.parametrize("input,expected", [
37
- (1, 2),
38
- (2, 4),
39
- (3, 6),
40
- ])
41
- def test_double(input, expected):
42
- assert double(input) == expected
80
+ @pytest.fixture
81
+ def user_factory(db_session):
82
+ """Factory for creating test users."""
83
+ def _create_user(email="test@example.com", name="Test User", **kwargs):
84
+ user = User(email=email, name=name, **kwargs)
85
+ db_session.add(user)
86
+ db_session.commit()
87
+ return user
88
+ return _create_user
89
+
90
+ @pytest.fixture
91
+ def test_user(user_factory):
92
+ return user_factory(email="testuser@example.com")
43
93
  ```
44
94
 
45
- ## Mocking
95
+ ### Mocking External Services
96
+
46
97
  ```python
47
- def test_api_call(mocker):
48
- mocker.patch('module.fetch', return_value={'data': 'test'})
49
- result = get_data()
50
- assert result == {'data': 'test'}
98
+ from unittest.mock import patch, MagicMock
99
+
100
+ class TestUserService:
101
+ def test_create_user_sends_email(self, user_service, mock_email_service):
102
+ user = user_service.create_user(email="new@example.com", name="New")
103
+
104
+ mock_email_service.send_email.assert_called_once_with(
105
+ to="new@example.com",
106
+ template="welcome",
107
+ context={"name": "New"},
108
+ )
109
+
110
+ @patch("src.services.user_service.datetime")
111
+ def test_last_login_updated(self, mock_datetime, user_service, test_user):
112
+ from datetime import datetime
113
+ mock_datetime.utcnow.return_value = datetime(2024, 1, 15, 10, 30)
114
+
115
+ user_service.authenticate(test_user.email, "password")
116
+ assert test_user.last_login == datetime(2024, 1, 15, 10, 30)
51
117
  ```
52
118
 
53
- ## Run
54
- ```bash
55
- pytest -v
56
- pytest --cov=src
57
- pytest -k "test_user"
119
+ ### Async API Testing
120
+
121
+ ```python
122
+ import pytest
123
+ from httpx import AsyncClient
124
+
125
+ @pytest.mark.asyncio
126
+ class TestAPIEndpoints:
127
+ async def test_get_users(self, async_client: AsyncClient, test_user):
128
+ response = await async_client.get("/api/users")
129
+ assert response.status_code == 200
130
+ assert len(response.json()["users"]) >= 1
131
+
132
+ async def test_create_user(self, async_client: AsyncClient):
133
+ response = await async_client.post("/api/users", json={
134
+ "email": "new@example.com",
135
+ "name": "New User",
136
+ "password": "SecurePass123!",
137
+ })
138
+ assert response.status_code == 201
58
139
  ```
140
+
141
+ ## Best Practices
142
+
143
+ | Do | Avoid |
144
+ |----|-------|
145
+ | Use descriptive test names explaining the scenario | Sharing state between tests |
146
+ | Create reusable fixtures for common setup | Using sleep for timing issues |
147
+ | Use parametrize for testing multiple inputs | Testing implementation details |
148
+ | Mock external dependencies | Writing tests that depend on order |
149
+ | Group related tests in classes | Using hardcoded file paths |
150
+ | Use factories for test data creation | Leaving commented-out test code |
151
+
152
+ ## References
153
+
154
+ - [Pytest Documentation](https://docs.pytest.org/)
155
+ - [pytest-asyncio](https://pytest-asyncio.readthedocs.io/)
156
+ - [pytest-mock](https://pytest-mock.readthedocs.io/)
157
+ - [pytest-cov](https://pytest-cov.readthedocs.io/)