@patricio0312rev/skillset 0.1.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.
- package/CHANGELOG.md +29 -0
- package/LICENSE +21 -0
- package/README.md +176 -0
- package/bin/cli.js +37 -0
- package/package.json +55 -0
- package/src/commands/init.js +301 -0
- package/src/index.js +168 -0
- package/src/lib/config.js +200 -0
- package/src/lib/generator.js +166 -0
- package/src/utils/display.js +95 -0
- package/src/utils/readme.js +196 -0
- package/src/utils/tool-specific.js +233 -0
- package/templates/ai-engineering/agent-orchestration-planner/ SKILL.md +266 -0
- package/templates/ai-engineering/cost-latency-optimizer/ SKILL.md +270 -0
- package/templates/ai-engineering/doc-to-vector-dataset-generator/ SKILL.md +239 -0
- package/templates/ai-engineering/evaluation-harness/ SKILL.md +219 -0
- package/templates/ai-engineering/guardrails-safety-filter-builder/ SKILL.md +226 -0
- package/templates/ai-engineering/llm-debugger/ SKILL.md +283 -0
- package/templates/ai-engineering/prompt-regression-tester/ SKILL.md +216 -0
- package/templates/ai-engineering/prompt-template-builder/ SKILL.md +393 -0
- package/templates/ai-engineering/rag-pipeline-builder/ SKILL.md +244 -0
- package/templates/ai-engineering/tool-function-schema-designer/ SKILL.md +219 -0
- package/templates/architecture/adr-writer/ SKILL.md +250 -0
- package/templates/architecture/api-versioning-deprecation-planner/ SKILL.md +331 -0
- package/templates/architecture/domain-model-boundaries-mapper/ SKILL.md +300 -0
- package/templates/architecture/migration-planner/ SKILL.md +376 -0
- package/templates/architecture/performance-budget-setter/ SKILL.md +318 -0
- package/templates/architecture/reliability-strategy-builder/ SKILL.md +286 -0
- package/templates/architecture/rfc-generator/ SKILL.md +362 -0
- package/templates/architecture/scalability-playbook/ SKILL.md +279 -0
- package/templates/architecture/system-design-generator/ SKILL.md +339 -0
- package/templates/architecture/tech-debt-prioritizer/ SKILL.md +329 -0
- package/templates/backend/api-contract-normalizer/ SKILL.md +487 -0
- package/templates/backend/api-endpoint-generator/ SKILL.md +415 -0
- package/templates/backend/auth-module-builder/ SKILL.md +99 -0
- package/templates/backend/background-jobs-designer/ SKILL.md +166 -0
- package/templates/backend/caching-strategist/ SKILL.md +190 -0
- package/templates/backend/error-handling-standardizer/ SKILL.md +174 -0
- package/templates/backend/rate-limiting-abuse-protection/ SKILL.md +147 -0
- package/templates/backend/rbac-permissions-builder/ SKILL.md +158 -0
- package/templates/backend/service-layer-extractor/ SKILL.md +269 -0
- package/templates/backend/webhook-receiver-hardener/ SKILL.md +211 -0
- package/templates/ci-cd/artifact-sbom-publisher/ SKILL.md +236 -0
- package/templates/ci-cd/caching-strategy-optimizer/ SKILL.md +195 -0
- package/templates/ci-cd/deployment-checklist-generator/ SKILL.md +381 -0
- package/templates/ci-cd/github-actions-pipeline-creator/ SKILL.md +348 -0
- package/templates/ci-cd/monorepo-ci-optimizer/ SKILL.md +298 -0
- package/templates/ci-cd/preview-environments-builder/ SKILL.md +187 -0
- package/templates/ci-cd/quality-gates-enforcer/ SKILL.md +342 -0
- package/templates/ci-cd/release-automation-builder/ SKILL.md +281 -0
- package/templates/ci-cd/rollback-workflow-builder/ SKILL.md +372 -0
- package/templates/ci-cd/secrets-env-manager/ SKILL.md +242 -0
- package/templates/db-management/backup-restore-runbook-generator/ SKILL.md +505 -0
- package/templates/db-management/data-integrity-auditor/ SKILL.md +505 -0
- package/templates/db-management/data-retention-archiving-planner/ SKILL.md +430 -0
- package/templates/db-management/data-seeding-fixtures-builder/ SKILL.md +375 -0
- package/templates/db-management/db-performance-watchlist/ SKILL.md +425 -0
- package/templates/db-management/etl-sync-job-builder/ SKILL.md +457 -0
- package/templates/db-management/multi-tenant-safety-checker/ SKILL.md +398 -0
- package/templates/db-management/prisma-migration-assistant/ SKILL.md +379 -0
- package/templates/db-management/schema-consistency-checker/ SKILL.md +440 -0
- package/templates/db-management/sql-query-optimizer/ SKILL.md +324 -0
- package/templates/foundation/changelog-writer/ SKILL.md +431 -0
- package/templates/foundation/code-formatter-installer/ SKILL.md +320 -0
- package/templates/foundation/codebase-summarizer/ SKILL.md +360 -0
- package/templates/foundation/dependency-doctor/ SKILL.md +163 -0
- package/templates/foundation/dev-environment-bootstrapper/ SKILL.md +259 -0
- package/templates/foundation/dev-onboarding-builder/ SKILL.md +556 -0
- package/templates/foundation/docs-starter-kit/ SKILL.md +574 -0
- package/templates/foundation/explaining-code/SKILL.md +13 -0
- package/templates/foundation/git-hygiene-enforcer/ SKILL.md +455 -0
- package/templates/foundation/project-scaffolder/ SKILL.md +65 -0
- package/templates/foundation/project-scaffolder/references/templates.md +126 -0
- package/templates/foundation/repo-structure-linter/ SKILL.md +0 -0
- package/templates/foundation/repo-structure-linter/references/conventions.md +98 -0
- package/templates/frontend/animation-micro-interaction-pack/ SKILL.md +41 -0
- package/templates/frontend/component-scaffold-generator/ SKILL.md +562 -0
- package/templates/frontend/design-to-component-translator/ SKILL.md +547 -0
- package/templates/frontend/form-wizard-builder/ SKILL.md +553 -0
- package/templates/frontend/frontend-refactor-planner/ SKILL.md +37 -0
- package/templates/frontend/i18n-frontend-implementer/ SKILL.md +44 -0
- package/templates/frontend/modal-drawer-system/ SKILL.md +377 -0
- package/templates/frontend/page-layout-builder/ SKILL.md +630 -0
- package/templates/frontend/state-ux-flow-builder/ SKILL.md +23 -0
- package/templates/frontend/table-builder/ SKILL.md +350 -0
- package/templates/performance/alerting-dashboard-builder/ SKILL.md +162 -0
- package/templates/performance/backend-latency-profiler-helper/ SKILL.md +108 -0
- package/templates/performance/caching-cdn-strategy-planner/ SKILL.md +150 -0
- package/templates/performance/capacity-planning-helper/ SKILL.md +242 -0
- package/templates/performance/core-web-vitals-tuner/ SKILL.md +126 -0
- package/templates/performance/incident-runbook-generator/ SKILL.md +162 -0
- package/templates/performance/load-test-scenario-builder/ SKILL.md +256 -0
- package/templates/performance/observability-setup/ SKILL.md +232 -0
- package/templates/performance/postmortem-writer/ SKILL.md +203 -0
- package/templates/performance/structured-logging-standardizer/ SKILL.md +122 -0
- package/templates/security/auth-security-reviewer/ SKILL.md +428 -0
- package/templates/security/dependency-vulnerability-triage/ SKILL.md +495 -0
- package/templates/security/input-validation-sanitization-auditor/ SKILL.md +76 -0
- package/templates/security/pii-redaction-logging-policy-builder/ SKILL.md +65 -0
- package/templates/security/rbac-policy-tester/ SKILL.md +80 -0
- package/templates/security/secrets-scanner/ SKILL.md +462 -0
- package/templates/security/secure-headers-csp-builder/ SKILL.md +404 -0
- package/templates/security/security-incident-playbook-generator/ SKILL.md +76 -0
- package/templates/security/security-pr-checklist-skill/ SKILL.md +62 -0
- package/templates/security/threat-model-generator/ SKILL.md +394 -0
- package/templates/testing/contract-testing-builder/ SKILL.md +492 -0
- package/templates/testing/coverage-strategist/ SKILL.md +436 -0
- package/templates/testing/e2e-test-builder/ SKILL.md +382 -0
- package/templates/testing/flaky-test-detective/ SKILL.md +416 -0
- package/templates/testing/integration-test-builder/ SKILL.md +525 -0
- package/templates/testing/mocking-assistant/ SKILL.md +383 -0
- package/templates/testing/snapshot-test-refactorer/ SKILL.md +375 -0
- package/templates/testing/test-data-factory-builder/ SKILL.md +449 -0
- package/templates/testing/test-reporting-triage-skill/ SKILL.md +469 -0
- package/templates/testing/unit-test-generator/ SKILL.md +548 -0
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: integration-test-builder
|
|
3
|
+
description: Creates integration tests for API endpoints with database flows, including test harness setup, fixtures, setup/teardown, database seeding, and CI-friendly strategies. Use for "integration testing", "API tests", "database tests", or "test harness".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Integration Test Builder
|
|
7
|
+
|
|
8
|
+
Build comprehensive integration tests for APIs and database flows.
|
|
9
|
+
|
|
10
|
+
## Test Harness Setup
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
// tests/setup/test-harness.ts
|
|
14
|
+
import { PrismaClient } from "@prisma/client";
|
|
15
|
+
import { execSync } from "child_process";
|
|
16
|
+
|
|
17
|
+
export class TestHarness {
|
|
18
|
+
prisma: PrismaClient;
|
|
19
|
+
|
|
20
|
+
async setup() {
|
|
21
|
+
// Setup test database
|
|
22
|
+
process.env.DATABASE_URL = process.env.TEST_DATABASE_URL;
|
|
23
|
+
|
|
24
|
+
// Run migrations
|
|
25
|
+
execSync("npx prisma migrate deploy");
|
|
26
|
+
|
|
27
|
+
// Initialize Prisma client
|
|
28
|
+
this.prisma = new PrismaClient();
|
|
29
|
+
|
|
30
|
+
// Clear all data
|
|
31
|
+
await this.clearDatabase();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async teardown() {
|
|
35
|
+
await this.prisma.$disconnect();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async clearDatabase() {
|
|
39
|
+
const tables = await this.prisma.$queryRaw<{ tablename: string }[]>`
|
|
40
|
+
SELECT tablename FROM pg_tables WHERE schemaname = 'public'
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
for (const { tablename } of tables) {
|
|
44
|
+
if (tablename !== "_prisma_migrations") {
|
|
45
|
+
await this.prisma.$executeRawUnsafe(
|
|
46
|
+
`TRUNCATE TABLE "${tablename}" CASCADE`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async seedFixtures() {
|
|
53
|
+
// Seed test data
|
|
54
|
+
await this.prisma.user.create({
|
|
55
|
+
data: {
|
|
56
|
+
email: "test@example.com",
|
|
57
|
+
name: "Test User",
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## API Integration Tests
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
// tests/api/users.test.ts
|
|
68
|
+
import request from "supertest";
|
|
69
|
+
import { app } from "@/app";
|
|
70
|
+
import { TestHarness } from "../setup/test-harness";
|
|
71
|
+
|
|
72
|
+
describe("User API", () => {
|
|
73
|
+
let harness: TestHarness;
|
|
74
|
+
|
|
75
|
+
beforeAll(async () => {
|
|
76
|
+
harness = new TestHarness();
|
|
77
|
+
await harness.setup();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
afterAll(async () => {
|
|
81
|
+
await harness.teardown();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
beforeEach(async () => {
|
|
85
|
+
await harness.clearDatabase();
|
|
86
|
+
await harness.seedFixtures();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe("POST /api/users", () => {
|
|
90
|
+
it("should create new user", async () => {
|
|
91
|
+
// Arrange
|
|
92
|
+
const userData = {
|
|
93
|
+
email: "new@example.com",
|
|
94
|
+
name: "New User",
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Act
|
|
98
|
+
const response = await request(app)
|
|
99
|
+
.post("/api/users")
|
|
100
|
+
.send(userData)
|
|
101
|
+
.expect(201);
|
|
102
|
+
|
|
103
|
+
// Assert
|
|
104
|
+
expect(response.body).toMatchObject({
|
|
105
|
+
email: userData.email,
|
|
106
|
+
name: userData.name,
|
|
107
|
+
});
|
|
108
|
+
expect(response.body.id).toBeDefined();
|
|
109
|
+
|
|
110
|
+
// Verify in database
|
|
111
|
+
const user = await harness.prisma.user.findUnique({
|
|
112
|
+
where: { email: userData.email },
|
|
113
|
+
});
|
|
114
|
+
expect(user).toBeDefined();
|
|
115
|
+
expect(user!.name).toBe(userData.name);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("should return 400 for invalid email", async () => {
|
|
119
|
+
// Arrange
|
|
120
|
+
const userData = {
|
|
121
|
+
email: "invalid-email",
|
|
122
|
+
name: "Test User",
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// Act
|
|
126
|
+
const response = await request(app)
|
|
127
|
+
.post("/api/users")
|
|
128
|
+
.send(userData)
|
|
129
|
+
.expect(400);
|
|
130
|
+
|
|
131
|
+
// Assert
|
|
132
|
+
expect(response.body.error).toContain("Invalid email");
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("should return 409 for duplicate email", async () => {
|
|
136
|
+
// Arrange
|
|
137
|
+
const userData = {
|
|
138
|
+
email: "test@example.com", // Already exists
|
|
139
|
+
name: "Duplicate User",
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// Act
|
|
143
|
+
const response = await request(app)
|
|
144
|
+
.post("/api/users")
|
|
145
|
+
.send(userData)
|
|
146
|
+
.expect(409);
|
|
147
|
+
|
|
148
|
+
// Assert
|
|
149
|
+
expect(response.body.error).toContain("already exists");
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe("GET /api/users/:id", () => {
|
|
154
|
+
it("should get user by id", async () => {
|
|
155
|
+
// Arrange
|
|
156
|
+
const user = await harness.prisma.user.findFirst();
|
|
157
|
+
|
|
158
|
+
// Act
|
|
159
|
+
const response = await request(app)
|
|
160
|
+
.get(`/api/users/${user!.id}`)
|
|
161
|
+
.expect(200);
|
|
162
|
+
|
|
163
|
+
// Assert
|
|
164
|
+
expect(response.body).toMatchObject({
|
|
165
|
+
id: user!.id,
|
|
166
|
+
email: user!.email,
|
|
167
|
+
name: user!.name,
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("should return 404 for non-existent user", async () => {
|
|
172
|
+
// Act
|
|
173
|
+
const response = await request(app).get("/api/users/99999").expect(404);
|
|
174
|
+
|
|
175
|
+
// Assert
|
|
176
|
+
expect(response.body.error).toContain("not found");
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
describe("PUT /api/users/:id", () => {
|
|
181
|
+
it("should update user", async () => {
|
|
182
|
+
// Arrange
|
|
183
|
+
const user = await harness.prisma.user.findFirst();
|
|
184
|
+
const updates = { name: "Updated Name" };
|
|
185
|
+
|
|
186
|
+
// Act
|
|
187
|
+
const response = await request(app)
|
|
188
|
+
.put(`/api/users/${user!.id}`)
|
|
189
|
+
.send(updates)
|
|
190
|
+
.expect(200);
|
|
191
|
+
|
|
192
|
+
// Assert
|
|
193
|
+
expect(response.body.name).toBe("Updated Name");
|
|
194
|
+
|
|
195
|
+
// Verify in database
|
|
196
|
+
const updatedUser = await harness.prisma.user.findUnique({
|
|
197
|
+
where: { id: user!.id },
|
|
198
|
+
});
|
|
199
|
+
expect(updatedUser!.name).toBe("Updated Name");
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
describe("DELETE /api/users/:id", () => {
|
|
204
|
+
it("should delete user", async () => {
|
|
205
|
+
// Arrange
|
|
206
|
+
const user = await harness.prisma.user.findFirst();
|
|
207
|
+
|
|
208
|
+
// Act
|
|
209
|
+
await request(app).delete(`/api/users/${user!.id}`).expect(204);
|
|
210
|
+
|
|
211
|
+
// Assert - verify deletion in database
|
|
212
|
+
const deletedUser = await harness.prisma.user.findUnique({
|
|
213
|
+
where: { id: user!.id },
|
|
214
|
+
});
|
|
215
|
+
expect(deletedUser).toBeNull();
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Database Transaction Tests
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
// tests/integration/order-flow.test.ts
|
|
225
|
+
describe("Order Flow", () => {
|
|
226
|
+
it("should create order with items in transaction", async () => {
|
|
227
|
+
// Arrange
|
|
228
|
+
const user = await harness.prisma.user.findFirst();
|
|
229
|
+
const product = await harness.prisma.product.create({
|
|
230
|
+
data: {
|
|
231
|
+
name: "Test Product",
|
|
232
|
+
price: 99.99,
|
|
233
|
+
stock: 10,
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const orderData = {
|
|
238
|
+
userId: user!.id,
|
|
239
|
+
items: [
|
|
240
|
+
{
|
|
241
|
+
productId: product.id,
|
|
242
|
+
quantity: 2,
|
|
243
|
+
price: product.price,
|
|
244
|
+
},
|
|
245
|
+
],
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
// Act
|
|
249
|
+
const response = await request(app)
|
|
250
|
+
.post("/api/orders")
|
|
251
|
+
.send(orderData)
|
|
252
|
+
.expect(201);
|
|
253
|
+
|
|
254
|
+
// Assert
|
|
255
|
+
const order = await harness.prisma.order.findUnique({
|
|
256
|
+
where: { id: response.body.id },
|
|
257
|
+
include: { items: true },
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
expect(order).toBeDefined();
|
|
261
|
+
expect(order!.items).toHaveLength(1);
|
|
262
|
+
expect(order!.items[0].quantity).toBe(2);
|
|
263
|
+
|
|
264
|
+
// Verify stock was decremented
|
|
265
|
+
const updatedProduct = await harness.prisma.product.findUnique({
|
|
266
|
+
where: { id: product.id },
|
|
267
|
+
});
|
|
268
|
+
expect(updatedProduct!.stock).toBe(8); // 10 - 2
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it("should rollback transaction if order creation fails", async () => {
|
|
272
|
+
// Arrange
|
|
273
|
+
const user = await harness.prisma.user.findFirst();
|
|
274
|
+
const product = await harness.prisma.product.create({
|
|
275
|
+
data: {
|
|
276
|
+
name: "Test Product",
|
|
277
|
+
price: 99.99,
|
|
278
|
+
stock: 1, // Only 1 in stock
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
const orderData = {
|
|
283
|
+
userId: user!.id,
|
|
284
|
+
items: [
|
|
285
|
+
{
|
|
286
|
+
productId: product.id,
|
|
287
|
+
quantity: 10, // Requesting more than available
|
|
288
|
+
price: product.price,
|
|
289
|
+
},
|
|
290
|
+
],
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
// Act
|
|
294
|
+
await request(app).post("/api/orders").send(orderData).expect(400);
|
|
295
|
+
|
|
296
|
+
// Assert - verify rollback
|
|
297
|
+
const orders = await harness.prisma.order.findMany();
|
|
298
|
+
expect(orders).toHaveLength(0);
|
|
299
|
+
|
|
300
|
+
// Verify stock unchanged
|
|
301
|
+
const unchangedProduct = await harness.prisma.product.findUnique({
|
|
302
|
+
where: { id: product.id },
|
|
303
|
+
});
|
|
304
|
+
expect(unchangedProduct!.stock).toBe(1);
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## Authentication Tests
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
// tests/integration/auth.test.ts
|
|
313
|
+
describe("Authentication", () => {
|
|
314
|
+
describe("POST /api/auth/login", () => {
|
|
315
|
+
it("should login with valid credentials", async () => {
|
|
316
|
+
// Arrange
|
|
317
|
+
await harness.prisma.user.create({
|
|
318
|
+
data: {
|
|
319
|
+
email: "auth@example.com",
|
|
320
|
+
password: await hash("password123"),
|
|
321
|
+
},
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// Act
|
|
325
|
+
const response = await request(app)
|
|
326
|
+
.post("/api/auth/login")
|
|
327
|
+
.send({
|
|
328
|
+
email: "auth@example.com",
|
|
329
|
+
password: "password123",
|
|
330
|
+
})
|
|
331
|
+
.expect(200);
|
|
332
|
+
|
|
333
|
+
// Assert
|
|
334
|
+
expect(response.body.token).toBeDefined();
|
|
335
|
+
expect(response.body.user.email).toBe("auth@example.com");
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it("should reject invalid password", async () => {
|
|
339
|
+
// Act
|
|
340
|
+
const response = await request(app)
|
|
341
|
+
.post("/api/auth/login")
|
|
342
|
+
.send({
|
|
343
|
+
email: "test@example.com",
|
|
344
|
+
password: "wrong-password",
|
|
345
|
+
})
|
|
346
|
+
.expect(401);
|
|
347
|
+
|
|
348
|
+
// Assert
|
|
349
|
+
expect(response.body.error).toContain("Invalid credentials");
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
describe("Protected routes", () => {
|
|
354
|
+
let authToken: string;
|
|
355
|
+
|
|
356
|
+
beforeEach(async () => {
|
|
357
|
+
// Login to get token
|
|
358
|
+
const response = await request(app).post("/api/auth/login").send({
|
|
359
|
+
email: "test@example.com",
|
|
360
|
+
password: "password123",
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
authToken = response.body.token;
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it("should access protected route with valid token", async () => {
|
|
367
|
+
await request(app)
|
|
368
|
+
.get("/api/profile")
|
|
369
|
+
.set("Authorization", `Bearer ${authToken}`)
|
|
370
|
+
.expect(200);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it("should reject request without token", async () => {
|
|
374
|
+
await request(app).get("/api/profile").expect(401);
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
it("should reject request with invalid token", async () => {
|
|
378
|
+
await request(app)
|
|
379
|
+
.get("/api/profile")
|
|
380
|
+
.set("Authorization", "Bearer invalid-token")
|
|
381
|
+
.expect(401);
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
## Fixtures Management
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
// tests/fixtures/users.ts
|
|
391
|
+
export const userFixtures = {
|
|
392
|
+
admin: {
|
|
393
|
+
email: "admin@example.com",
|
|
394
|
+
name: "Admin User",
|
|
395
|
+
role: "ADMIN",
|
|
396
|
+
},
|
|
397
|
+
regularUser: {
|
|
398
|
+
email: "user@example.com",
|
|
399
|
+
name: "Regular User",
|
|
400
|
+
role: "USER",
|
|
401
|
+
},
|
|
402
|
+
testUser: {
|
|
403
|
+
email: "test@example.com",
|
|
404
|
+
name: "Test User",
|
|
405
|
+
role: "USER",
|
|
406
|
+
},
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
// tests/fixtures/products.ts
|
|
410
|
+
export const productFixtures = {
|
|
411
|
+
laptop: {
|
|
412
|
+
name: "MacBook Pro",
|
|
413
|
+
price: 2499.99,
|
|
414
|
+
stock: 10,
|
|
415
|
+
category: "Electronics",
|
|
416
|
+
},
|
|
417
|
+
phone: {
|
|
418
|
+
name: "iPhone 15",
|
|
419
|
+
price: 999.99,
|
|
420
|
+
stock: 50,
|
|
421
|
+
category: "Electronics",
|
|
422
|
+
},
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
// Usage in tests
|
|
426
|
+
await harness.prisma.user.create({
|
|
427
|
+
data: userFixtures.admin,
|
|
428
|
+
});
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
## CI-Friendly Strategy
|
|
432
|
+
|
|
433
|
+
```yaml
|
|
434
|
+
# .github/workflows/integration-tests.yml
|
|
435
|
+
name: Integration Tests
|
|
436
|
+
|
|
437
|
+
on: [push, pull_request]
|
|
438
|
+
|
|
439
|
+
services:
|
|
440
|
+
postgres:
|
|
441
|
+
image: postgres:15
|
|
442
|
+
env:
|
|
443
|
+
POSTGRES_USER: test
|
|
444
|
+
POSTGRES_PASSWORD: test
|
|
445
|
+
POSTGRES_DB: test_db
|
|
446
|
+
ports:
|
|
447
|
+
- 5432:5432
|
|
448
|
+
options: >-
|
|
449
|
+
--health-cmd pg_isready
|
|
450
|
+
--health-interval 10s
|
|
451
|
+
--health-timeout 5s
|
|
452
|
+
--health-retries 5
|
|
453
|
+
|
|
454
|
+
jobs:
|
|
455
|
+
test:
|
|
456
|
+
runs-on: ubuntu-latest
|
|
457
|
+
|
|
458
|
+
steps:
|
|
459
|
+
- uses: actions/checkout@v4
|
|
460
|
+
|
|
461
|
+
- uses: actions/setup-node@v4
|
|
462
|
+
with:
|
|
463
|
+
node-version: "20"
|
|
464
|
+
|
|
465
|
+
- run: npm ci
|
|
466
|
+
|
|
467
|
+
- name: Run migrations
|
|
468
|
+
run: npx prisma migrate deploy
|
|
469
|
+
env:
|
|
470
|
+
DATABASE_URL: postgresql://test:test@localhost:5432/test_db
|
|
471
|
+
|
|
472
|
+
- name: Run integration tests
|
|
473
|
+
run: npm run test:integration
|
|
474
|
+
env:
|
|
475
|
+
DATABASE_URL: postgresql://test:test@localhost:5432/test_db
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
## Parallel Test Execution
|
|
479
|
+
|
|
480
|
+
```typescript
|
|
481
|
+
// vitest.config.ts
|
|
482
|
+
export default defineConfig({
|
|
483
|
+
test: {
|
|
484
|
+
pool: "forks",
|
|
485
|
+
poolOptions: {
|
|
486
|
+
forks: {
|
|
487
|
+
singleFork: false, // Run tests in parallel
|
|
488
|
+
},
|
|
489
|
+
},
|
|
490
|
+
isolate: true, // Isolate each test file
|
|
491
|
+
setupFiles: ["./tests/setup/global-setup.ts"],
|
|
492
|
+
},
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
// Ensure each test file uses separate database
|
|
496
|
+
const TEST_DB_PREFIX = "test_db_";
|
|
497
|
+
|
|
498
|
+
function getDatabaseUrl(): string {
|
|
499
|
+
const workerId = process.env.VITEST_WORKER_ID || "1";
|
|
500
|
+
return `postgresql://test:test@localhost:5432/${TEST_DB_PREFIX}${workerId}`;
|
|
501
|
+
}
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
## Best Practices
|
|
505
|
+
|
|
506
|
+
1. **Isolated tests**: Each test can run independently
|
|
507
|
+
2. **Clean state**: Clear database between tests
|
|
508
|
+
3. **Fast fixtures**: Minimal data seeding
|
|
509
|
+
4. **Transactions**: Test rollbacks explicitly
|
|
510
|
+
5. **Real database**: Don't mock database in integration tests
|
|
511
|
+
6. **CI-ready**: Use Docker containers
|
|
512
|
+
7. **Parallel execution**: Independent test databases
|
|
513
|
+
|
|
514
|
+
## Output Checklist
|
|
515
|
+
|
|
516
|
+
- [ ] Test harness created
|
|
517
|
+
- [ ] Database setup/teardown
|
|
518
|
+
- [ ] Fixture management
|
|
519
|
+
- [ ] API endpoint tests
|
|
520
|
+
- [ ] Database transaction tests
|
|
521
|
+
- [ ] Authentication tests
|
|
522
|
+
- [ ] Error case coverage
|
|
523
|
+
- [ ] CI workflow configured
|
|
524
|
+
- [ ] Parallel execution support
|
|
525
|
+
- [ ] Clear test naming
|