digiqagent 1.2.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 (33) hide show
  1. package/.cursor-plugin/plugin.json +29 -0
  2. package/README.md +197 -0
  3. package/agents/code-reviewer.md +48 -0
  4. package/bin/cli.js +210 -0
  5. package/package.json +41 -0
  6. package/skills/playwright-test-generator/SKILL.md +563 -0
  7. package/skills/playwright-test-generator/interaction-test-patterns.md +987 -0
  8. package/skills/playwright-test-generator/page-object-patterns.md +833 -0
  9. package/skills/postman-collection-generator/SKILL.md +310 -0
  10. package/skills/postman-collection-generator/collection-patterns.md +493 -0
  11. package/skills/postman-test-suite-generator/SKILL.md +653 -0
  12. package/skills/postman-test-suite-generator/test-scenario-patterns.md +612 -0
  13. package/skills/receiving-code-review/SKILL.md +213 -0
  14. package/skills/requesting-code-review/SKILL.md +105 -0
  15. package/skills/requesting-code-review/code-reviewer.md +146 -0
  16. package/skills/review-prompts/code-quality-reviewer-prompt.md +26 -0
  17. package/skills/review-prompts/spec-reviewer-prompt.md +61 -0
  18. package/skills/swagger-generator/SKILL.md +238 -0
  19. package/skills/swagger-generator/openapi-patterns.md +667 -0
  20. package/skills/systematic-debugging/CREATION-LOG.md +119 -0
  21. package/skills/systematic-debugging/SKILL.md +296 -0
  22. package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
  23. package/skills/systematic-debugging/condition-based-waiting.md +115 -0
  24. package/skills/systematic-debugging/defense-in-depth.md +122 -0
  25. package/skills/systematic-debugging/find-polluter.sh +63 -0
  26. package/skills/systematic-debugging/root-cause-tracing.md +169 -0
  27. package/skills/systematic-debugging/test-academic.md +14 -0
  28. package/skills/systematic-debugging/test-pressure-1.md +58 -0
  29. package/skills/systematic-debugging/test-pressure-2.md +68 -0
  30. package/skills/systematic-debugging/test-pressure-3.md +69 -0
  31. package/skills/test-driven-development/SKILL.md +371 -0
  32. package/skills/test-driven-development/testing-anti-patterns.md +299 -0
  33. package/skills/verification-before-completion/SKILL.md +139 -0
@@ -0,0 +1,612 @@
1
+ # Test Scenario Patterns
2
+
3
+ Reference patterns for Postman test scripts. Copy and adapt for generated test suites.
4
+
5
+ ## Complete CRUD Test Suite Example — `/api/users`
6
+
7
+ A full working test suite for a Users resource showing both positive and negative scenarios.
8
+
9
+ ### Setup — Login
10
+
11
+ ```javascript
12
+ // Test script for POST /api/auth/login
13
+ pm.test("Login successful", function () {
14
+ pm.response.to.have.status(200);
15
+ });
16
+
17
+ pm.test("Response contains token", function () {
18
+ const response = pm.response.json();
19
+ pm.expect(response).to.have.property("accessToken");
20
+ pm.environment.set("authToken", response.accessToken);
21
+ if (response.user) {
22
+ pm.environment.set("currentUserId", response.user.id);
23
+ }
24
+ });
25
+ ```
26
+
27
+ ### Positive — Create User
28
+
29
+ ```javascript
30
+ // Test script for POST /api/users
31
+ // Body: { "name": "Jane Smith", "email": "jane.smith@example.com", "password": "SecureP@ss123", "role": "user" }
32
+
33
+ pm.test("Status code is 201", function () {
34
+ pm.response.to.have.status(201);
35
+ });
36
+
37
+ pm.test("Response time is acceptable", function () {
38
+ pm.expect(pm.response.responseTime).to.be.below(2000);
39
+ });
40
+
41
+ pm.test("Response has user object with all expected fields", function () {
42
+ const user = pm.response.json();
43
+ pm.expect(user).to.have.property("id");
44
+ pm.expect(user).to.have.property("name");
45
+ pm.expect(user).to.have.property("email");
46
+ pm.expect(user).to.have.property("role");
47
+ pm.expect(user).to.have.property("createdAt");
48
+ });
49
+
50
+ pm.test("Response data matches request", function () {
51
+ const request = JSON.parse(pm.request.body.raw);
52
+ const user = pm.response.json();
53
+ pm.expect(user.name).to.eql(request.name);
54
+ pm.expect(user.email).to.eql(request.email);
55
+ pm.expect(user.role).to.eql(request.role);
56
+ });
57
+
58
+ pm.test("Password is not returned in response", function () {
59
+ const user = pm.response.json();
60
+ pm.expect(user).to.not.have.property("password");
61
+ pm.expect(user).to.not.have.property("passwordHash");
62
+ });
63
+
64
+ pm.test("ID is a valid format", function () {
65
+ const user = pm.response.json();
66
+ pm.expect(user.id).to.be.a("string");
67
+ pm.expect(user.id).to.not.be.empty;
68
+ });
69
+
70
+ // Capture for subsequent tests
71
+ if (pm.response.code === 201) {
72
+ pm.environment.set("testUserId", pm.response.json().id);
73
+ pm.environment.set("testUserEmail", pm.response.json().email);
74
+ }
75
+ ```
76
+
77
+ ### Positive — List Users
78
+
79
+ ```javascript
80
+ // Test script for GET /api/users?page=1&limit=10
81
+
82
+ pm.test("Status code is 200", function () {
83
+ pm.response.to.have.status(200);
84
+ });
85
+
86
+ pm.test("Response is paginated array", function () {
87
+ const response = pm.response.json();
88
+ pm.expect(response).to.have.property("data");
89
+ pm.expect(response.data).to.be.an("array");
90
+ pm.expect(response).to.have.property("meta");
91
+ });
92
+
93
+ pm.test("Pagination metadata is correct", function () {
94
+ const meta = pm.response.json().meta;
95
+ pm.expect(meta.page).to.be.a("number");
96
+ pm.expect(meta.limit).to.be.a("number");
97
+ pm.expect(meta.totalItems).to.be.a("number");
98
+ pm.expect(meta.totalPages).to.be.a("number");
99
+ pm.expect(meta.page).to.eql(1);
100
+ pm.expect(meta.limit).to.eql(10);
101
+ });
102
+
103
+ pm.test("Each user has expected fields", function () {
104
+ const users = pm.response.json().data;
105
+ if (users.length > 0) {
106
+ users.forEach(function (user) {
107
+ pm.expect(user).to.have.property("id");
108
+ pm.expect(user).to.have.property("name");
109
+ pm.expect(user).to.have.property("email");
110
+ });
111
+ }
112
+ });
113
+
114
+ pm.test("Previously created user is in the list", function () {
115
+ const users = pm.response.json().data;
116
+ const testUserId = pm.environment.get("testUserId");
117
+ if (testUserId) {
118
+ const found = users.some(u => u.id === testUserId);
119
+ pm.expect(found).to.be.true;
120
+ }
121
+ });
122
+ ```
123
+
124
+ ### Positive — Get User by ID
125
+
126
+ ```javascript
127
+ // Test script for GET /api/users/{{testUserId}}
128
+
129
+ pm.test("Status code is 200", function () {
130
+ pm.response.to.have.status(200);
131
+ });
132
+
133
+ pm.test("Returns the correct user", function () {
134
+ const user = pm.response.json();
135
+ pm.expect(user.id).to.eql(pm.environment.get("testUserId"));
136
+ pm.expect(user.email).to.eql(pm.environment.get("testUserEmail"));
137
+ });
138
+
139
+ pm.test("User has all expected fields", function () {
140
+ const user = pm.response.json();
141
+ pm.expect(user).to.have.all.keys("id", "name", "email", "role", "createdAt", "updatedAt");
142
+ });
143
+ ```
144
+
145
+ ### Positive — Update User
146
+
147
+ ```javascript
148
+ // Test script for PUT /api/users/{{testUserId}}
149
+ // Body: { "name": "Jane Smith Updated" }
150
+
151
+ pm.test("Status code is 200", function () {
152
+ pm.response.to.have.status(200);
153
+ });
154
+
155
+ pm.test("Name is updated", function () {
156
+ const user = pm.response.json();
157
+ pm.expect(user.name).to.eql("Jane Smith Updated");
158
+ });
159
+
160
+ pm.test("Other fields unchanged", function () {
161
+ const user = pm.response.json();
162
+ pm.expect(user.email).to.eql(pm.environment.get("testUserEmail"));
163
+ pm.expect(user.id).to.eql(pm.environment.get("testUserId"));
164
+ });
165
+
166
+ pm.test("updatedAt is after createdAt", function () {
167
+ const user = pm.response.json();
168
+ if (user.updatedAt && user.createdAt) {
169
+ pm.expect(new Date(user.updatedAt).getTime()).to.be.at.least(new Date(user.createdAt).getTime());
170
+ }
171
+ });
172
+ ```
173
+
174
+ ### Positive — Delete User
175
+
176
+ ```javascript
177
+ // Test script for DELETE /api/users/{{testUserId}}
178
+
179
+ pm.test("Status code is 200 or 204", function () {
180
+ pm.expect(pm.response.code).to.be.oneOf([200, 204]);
181
+ });
182
+ ```
183
+
184
+ ### Positive — Verify Deletion
185
+
186
+ ```javascript
187
+ // Test script for GET /api/users/{{testUserId}} after deletion
188
+
189
+ pm.test("Returns 404 after deletion", function () {
190
+ pm.response.to.have.status(404);
191
+ });
192
+ ```
193
+
194
+ ### Negative — Missing Required Fields
195
+
196
+ ```javascript
197
+ // POST /api/users — missing "name"
198
+ // Body: { "email": "test@example.com", "password": "SecureP@ss123" }
199
+
200
+ pm.test("Returns 400 for missing name", function () {
201
+ pm.response.to.have.status(400);
202
+ });
203
+
204
+ pm.test("Error response has expected structure", function () {
205
+ const error = pm.response.json();
206
+ pm.expect(error).to.have.property("message");
207
+ pm.expect(error).to.have.property("error");
208
+ });
209
+
210
+ pm.test("Error message references the missing field", function () {
211
+ const error = pm.response.json();
212
+ const msg = Array.isArray(error.message) ? error.message.join(" ") : error.message;
213
+ pm.expect(msg.toLowerCase()).to.include("name");
214
+ });
215
+ ```
216
+
217
+ ```javascript
218
+ // POST /api/users — missing "email"
219
+ // Body: { "name": "Jane Smith", "password": "SecureP@ss123" }
220
+
221
+ pm.test("Returns 400 for missing email", function () {
222
+ pm.response.to.have.status(400);
223
+ });
224
+
225
+ pm.test("Error message references email", function () {
226
+ const error = pm.response.json();
227
+ const msg = Array.isArray(error.message) ? error.message.join(" ") : error.message;
228
+ pm.expect(msg.toLowerCase()).to.include("email");
229
+ });
230
+ ```
231
+
232
+ ```javascript
233
+ // POST /api/users — missing "password"
234
+ // Body: { "name": "Jane Smith", "email": "test@example.com" }
235
+
236
+ pm.test("Returns 400 for missing password", function () {
237
+ pm.response.to.have.status(400);
238
+ });
239
+ ```
240
+
241
+ ### Negative — Invalid Email Format
242
+
243
+ ```javascript
244
+ // POST /api/users
245
+ // Body: { "name": "Jane", "email": "not-an-email", "password": "SecureP@ss123" }
246
+
247
+ pm.test("Returns 400 for invalid email format", function () {
248
+ pm.response.to.have.status(400);
249
+ });
250
+
251
+ pm.test("Error mentions email validation", function () {
252
+ const error = pm.response.json();
253
+ const msg = Array.isArray(error.message) ? error.message.join(" ") : error.message;
254
+ pm.expect(msg.toLowerCase()).to.satisfy(function (m) {
255
+ return m.includes("email") || m.includes("valid");
256
+ });
257
+ });
258
+ ```
259
+
260
+ ### Negative — Password Too Short
261
+
262
+ ```javascript
263
+ // POST /api/users
264
+ // Body: { "name": "Jane", "email": "jane@example.com", "password": "ab" }
265
+
266
+ pm.test("Returns 400 for short password", function () {
267
+ pm.response.to.have.status(400);
268
+ });
269
+
270
+ pm.test("Error mentions password requirements", function () {
271
+ const error = pm.response.json();
272
+ const msg = Array.isArray(error.message) ? error.message.join(" ") : error.message;
273
+ pm.expect(msg.toLowerCase()).to.satisfy(function (m) {
274
+ return m.includes("password") || m.includes("length") || m.includes("characters");
275
+ });
276
+ });
277
+ ```
278
+
279
+ ### Negative — Empty String Values
280
+
281
+ ```javascript
282
+ // POST /api/users
283
+ // Body: { "name": "", "email": "jane@example.com", "password": "SecureP@ss123" }
284
+
285
+ pm.test("Returns 400 for empty name", function () {
286
+ pm.response.to.have.status(400);
287
+ });
288
+ ```
289
+
290
+ ```javascript
291
+ // POST /api/users
292
+ // Body: { "name": " ", "email": "jane@example.com", "password": "SecureP@ss123" }
293
+
294
+ pm.test("Returns 400 for whitespace-only name", function () {
295
+ pm.response.to.have.status(400);
296
+ });
297
+ ```
298
+
299
+ ## Auth Test Patterns
300
+
301
+ ### Valid Token
302
+
303
+ ```javascript
304
+ pm.test("Authenticated request succeeds", function () {
305
+ pm.response.to.have.status(200);
306
+ });
307
+ ```
308
+
309
+ ### No Token
310
+
311
+ ```javascript
312
+ // Remove Authorization header entirely
313
+
314
+ pm.test("Returns 401 without auth token", function () {
315
+ pm.response.to.have.status(401);
316
+ });
317
+
318
+ pm.test("Error indicates authentication is required", function () {
319
+ const error = pm.response.json();
320
+ const msg = (error.message || error.error || "").toLowerCase();
321
+ pm.expect(msg).to.satisfy(function (m) {
322
+ return m.includes("unauthorized") || m.includes("token") || m.includes("authentication");
323
+ });
324
+ });
325
+ ```
326
+
327
+ ### Malformed Token
328
+
329
+ ```javascript
330
+ // Authorization: Bearer not-a-jwt-token
331
+
332
+ pm.test("Returns 401 for malformed token", function () {
333
+ pm.response.to.have.status(401);
334
+ });
335
+ ```
336
+
337
+ ### Expired Token
338
+
339
+ ```javascript
340
+ // Authorization: Bearer <expired-jwt>
341
+
342
+ pm.test("Returns 401 for expired token", function () {
343
+ pm.response.to.have.status(401);
344
+ });
345
+
346
+ pm.test("Error indicates token is expired", function () {
347
+ const error = pm.response.json();
348
+ const msg = (error.message || "").toLowerCase();
349
+ pm.expect(msg).to.satisfy(function (m) {
350
+ return m.includes("expired") || m.includes("invalid") || m.includes("unauthorized");
351
+ });
352
+ });
353
+ ```
354
+
355
+ ### Insufficient Permissions (Role-Based)
356
+
357
+ ```javascript
358
+ // Use a regular user token for an admin-only endpoint
359
+
360
+ pm.test("Returns 403 for insufficient permissions", function () {
361
+ pm.response.to.have.status(403);
362
+ });
363
+
364
+ pm.test("Error indicates permission denied", function () {
365
+ const error = pm.response.json();
366
+ const msg = (error.message || error.error || "").toLowerCase();
367
+ pm.expect(msg).to.satisfy(function (m) {
368
+ return m.includes("forbidden") || m.includes("permission") || m.includes("access denied");
369
+ });
370
+ });
371
+ ```
372
+
373
+ ## Schema Validation Patterns
374
+
375
+ ### Inline Schema Validation
376
+
377
+ ```javascript
378
+ const userSchema = {
379
+ type: "object",
380
+ required: ["id", "name", "email", "createdAt"],
381
+ properties: {
382
+ id: { type: "string" },
383
+ name: { type: "string" },
384
+ email: { type: "string" },
385
+ role: { type: "string", enum: ["admin", "user", "moderator"] },
386
+ createdAt: { type: "string" },
387
+ updatedAt: { type: "string" }
388
+ },
389
+ additionalProperties: false
390
+ };
391
+
392
+ pm.test("Response matches user schema", function () {
393
+ pm.response.to.have.jsonSchema(userSchema);
394
+ });
395
+ ```
396
+
397
+ ### List Response Schema Validation
398
+
399
+ ```javascript
400
+ const paginatedSchema = {
401
+ type: "object",
402
+ required: ["data", "meta"],
403
+ properties: {
404
+ data: {
405
+ type: "array",
406
+ items: {
407
+ type: "object",
408
+ required: ["id", "name", "email"],
409
+ properties: {
410
+ id: { type: "string" },
411
+ name: { type: "string" },
412
+ email: { type: "string" }
413
+ }
414
+ }
415
+ },
416
+ meta: {
417
+ type: "object",
418
+ required: ["page", "limit", "totalItems", "totalPages"],
419
+ properties: {
420
+ page: { type: "number" },
421
+ limit: { type: "number" },
422
+ totalItems: { type: "number" },
423
+ totalPages: { type: "number" }
424
+ }
425
+ }
426
+ }
427
+ };
428
+
429
+ pm.test("List response matches paginated schema", function () {
430
+ pm.response.to.have.jsonSchema(paginatedSchema);
431
+ });
432
+ ```
433
+
434
+ ### Error Response Schema Validation
435
+
436
+ ```javascript
437
+ const errorSchema = {
438
+ type: "object",
439
+ required: ["statusCode", "message", "error"],
440
+ properties: {
441
+ statusCode: { type: "number" },
442
+ message: {
443
+ oneOf: [
444
+ { type: "string" },
445
+ { type: "array", items: { type: "string" } }
446
+ ]
447
+ },
448
+ error: { type: "string" }
449
+ }
450
+ };
451
+
452
+ pm.test("Error response matches error schema", function () {
453
+ pm.response.to.have.jsonSchema(errorSchema);
454
+ });
455
+ ```
456
+
457
+ ## Chained Test Patterns
458
+
459
+ ### Create -> Read -> Update -> Read -> Delete -> Read (Verify Gone)
460
+
461
+ This sequence must run in order. Each step captures or uses variables.
462
+
463
+ ```
464
+ Request 1: POST /api/products → capture productId
465
+ Assert: 201, has id, matches input
466
+
467
+ Request 2: GET /api/products/:id → uses productId
468
+ Assert: 200, matches created data
469
+
470
+ Request 3: PUT /api/products/:id → uses productId
471
+ Assert: 200, fields updated
472
+
473
+ Request 4: GET /api/products/:id → uses productId
474
+ Assert: 200, reflects update
475
+
476
+ Request 5: DELETE /api/products/:id → uses productId
477
+ Assert: 200 or 204
478
+
479
+ Request 6: GET /api/products/:id → uses productId
480
+ Assert: 404
481
+ ```
482
+
483
+ ### Parent-Child Chain
484
+
485
+ ```
486
+ Request 1: POST /api/categories → capture categoryId
487
+ Request 2: POST /api/products → uses categoryId in body
488
+ Body: { "categoryId": "{{categoryId}}", ... }
489
+ Assert: 201, product.categoryId matches
490
+
491
+ Request 3: GET /api/categories/:id/products → uses categoryId
492
+ Assert: 200, array includes created product
493
+
494
+ Request 4: DELETE /api/categories/:id → uses categoryId
495
+ Assert: 409 (has children) OR 204 (cascade delete, verify products gone)
496
+ ```
497
+
498
+ ## Idempotency Test Patterns
499
+
500
+ ### PUT Is Idempotent
501
+
502
+ ```javascript
503
+ // Run the same PUT request twice in sequence
504
+
505
+ // First PUT
506
+ pm.test("First PUT returns 200", function () {
507
+ pm.response.to.have.status(200);
508
+ });
509
+
510
+ // Second PUT (same data)
511
+ pm.test("Second PUT also returns 200 (idempotent)", function () {
512
+ pm.response.to.have.status(200);
513
+ });
514
+
515
+ pm.test("Response is identical to first PUT", function () {
516
+ const response = pm.response.json();
517
+ pm.expect(response.name).to.eql("Updated Name");
518
+ });
519
+ ```
520
+
521
+ ### DELETE Is Idempotent (or Not)
522
+
523
+ ```javascript
524
+ // First DELETE
525
+ pm.test("First DELETE succeeds", function () {
526
+ pm.expect(pm.response.code).to.be.oneOf([200, 204]);
527
+ });
528
+
529
+ // Second DELETE (same resource)
530
+ pm.test("Second DELETE returns 404 (already deleted)", function () {
531
+ pm.response.to.have.status(404);
532
+ });
533
+ ```
534
+
535
+ ## Rate Limiting Test Patterns
536
+
537
+ ```javascript
538
+ pm.test("Rate limit headers are present", function () {
539
+ pm.response.to.have.header("X-RateLimit-Limit");
540
+ pm.response.to.have.header("X-RateLimit-Remaining");
541
+ });
542
+
543
+ pm.test("Rate limit remaining decreases", function () {
544
+ const remaining = parseInt(pm.response.headers.get("X-RateLimit-Remaining"));
545
+ pm.expect(remaining).to.be.a("number");
546
+ pm.expect(remaining).to.be.at.least(0);
547
+ });
548
+
549
+ // After exceeding the limit
550
+ pm.test("Returns 429 when rate limit exceeded", function () {
551
+ pm.response.to.have.status(429);
552
+ });
553
+
554
+ pm.test("Rate limit response includes retry information", function () {
555
+ const response = pm.response.json();
556
+ const msg = (response.message || "").toLowerCase();
557
+ pm.expect(msg).to.satisfy(function (m) {
558
+ return m.includes("rate limit") || m.includes("too many requests");
559
+ });
560
+ });
561
+ ```
562
+
563
+ ## Response Time Benchmarks
564
+
565
+ ```javascript
566
+ pm.test("GET responds under 500ms", function () {
567
+ pm.expect(pm.response.responseTime).to.be.below(500);
568
+ });
569
+
570
+ pm.test("POST responds under 1000ms", function () {
571
+ pm.expect(pm.response.responseTime).to.be.below(1000);
572
+ });
573
+
574
+ pm.test("List with pagination responds under 1000ms", function () {
575
+ pm.expect(pm.response.responseTime).to.be.below(1000);
576
+ });
577
+
578
+ pm.test("Search/filter responds under 2000ms", function () {
579
+ pm.expect(pm.response.responseTime).to.be.below(2000);
580
+ });
581
+ ```
582
+
583
+ ## Environment Variable Helpers
584
+
585
+ ### Capture and Log
586
+
587
+ ```javascript
588
+ const response = pm.response.json();
589
+ const id = response.id || response.data?.id;
590
+ pm.environment.set("lastCreatedId", id);
591
+ console.log("Captured ID:", id);
592
+ ```
593
+
594
+ ### Clear After Test Suite
595
+
596
+ ```javascript
597
+ // Place in Teardown folder's test script
598
+ pm.environment.unset("testUserId");
599
+ pm.environment.unset("testUserEmail");
600
+ pm.environment.unset("authToken");
601
+ console.log("Test environment variables cleared");
602
+ ```
603
+
604
+ ### Generate Unique Test Data
605
+
606
+ ```javascript
607
+ // Pre-request script for unique data per run
608
+ const timestamp = Date.now();
609
+ pm.environment.set("testEmail", `test_${timestamp}@example.com`);
610
+ pm.environment.set("testName", `TestUser_${timestamp}`);
611
+ pm.environment.set("testSku", `SKU-${timestamp}`);
612
+ ```