@freshworks/shiftleft-tools 1.1.8

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 (106) hide show
  1. package/README.md +351 -0
  2. package/bin/shiftleft.js +95 -0
  3. package/package.json +57 -0
  4. package/src/commands/doctor.js +208 -0
  5. package/src/commands/init-postman.js +298 -0
  6. package/src/commands/init-rules.js +78 -0
  7. package/src/commands/link.js +172 -0
  8. package/src/commands/protect.js +61 -0
  9. package/src/commands/run-tests.js +182 -0
  10. package/src/commands/setup-pipeline.js +209 -0
  11. package/src/commands/update.js +203 -0
  12. package/src/index.js +4 -0
  13. package/src/utils/copy-tree.js +98 -0
  14. package/src/utils/gitignore.js +26 -0
  15. package/src/utils/logger.js +9 -0
  16. package/src/utils/manifest.js +145 -0
  17. package/src/utils/stack.js +80 -0
  18. package/src/utils/template.js +135 -0
  19. package/templates/AGENTS.md +109 -0
  20. package/templates/CLAUDE.md +3 -0
  21. package/templates/jenkins/Jenkinsfile-java.groovy +432 -0
  22. package/templates/jenkins/Jenkinsfile-node.groovy +450 -0
  23. package/templates/postman/.husky/pre-commit +19 -0
  24. package/templates/postman/.prettierrc.json +5 -0
  25. package/templates/postman/README.md.ejs +147 -0
  26. package/templates/postman/collections/01-core.json.ejs +91 -0
  27. package/templates/postman/config/local.json.ejs +12 -0
  28. package/templates/postman/config/staging.json.ejs +26 -0
  29. package/templates/postman/environments/local.postman_environment.json.ejs +31 -0
  30. package/templates/postman/environments/staging.postman_environment.json.ejs +31 -0
  31. package/templates/postman/gitignore +16 -0
  32. package/templates/postman/npmrc +31 -0
  33. package/templates/postman/package.json.ejs +66 -0
  34. package/templates/postman/run-all-shim.sh +16 -0
  35. package/templates/postman/scripts/auth/generate-jwt.sh +113 -0
  36. package/templates/postman/scripts/auth/get-issuer-secret.sh +140 -0
  37. package/templates/postman/scripts/infra/start-mocks.sh +138 -0
  38. package/templates/postman/scripts/infra/stop-mocks.sh +43 -0
  39. package/templates/postman/scripts/lib/api_coverage.py +1122 -0
  40. package/templates/postman/scripts/lib/cleanup-reports.sh +101 -0
  41. package/templates/postman/scripts/lib/cleanup-stryker.sh +44 -0
  42. package/templates/postman/scripts/lib/report_combined.py +527 -0
  43. package/templates/postman/scripts/lib/report_consolidated.py +363 -0
  44. package/templates/postman/scripts/lib/report_generator.py +121 -0
  45. package/templates/postman/scripts/lib/report_migration.py +156 -0
  46. package/templates/postman/scripts/lib/report_mutation.py +110 -0
  47. package/templates/postman/scripts/lib/report_unit.py +353 -0
  48. package/templates/postman/scripts/lib/report_utils.py +973 -0
  49. package/templates/postman/scripts/report-generators/generate-consolidated-report.sh +445 -0
  50. package/templates/postman/scripts/report-generators/java-api-coverage-matrix.sh +257 -0
  51. package/templates/postman/scripts/report-generators/mutation-report.sh +672 -0
  52. package/templates/postman/scripts/report-generators/node-api-coverage-matrix.sh +167 -0
  53. package/templates/postman/scripts/report-generators/stage-report-artifacts.sh +27 -0
  54. package/templates/postman/scripts/run-all.sh +452 -0
  55. package/templates/postman/scripts/runners/run-mutation-tests.sh +113 -0
  56. package/templates/postman/scripts/runners/run-tests-local.sh +936 -0
  57. package/templates/postman/scripts/runners/run-tests-staging.sh +741 -0
  58. package/templates/postman-node/README.md.ejs +26 -0
  59. package/templates/postman-node/collections/crud/01-bootstrap.json.ejs +34 -0
  60. package/templates/postman-node/config/local.json.ejs +46 -0
  61. package/templates/postman-node/config/staging.json.ejs +31 -0
  62. package/templates/postman-node/local.test.env.ejs +3 -0
  63. package/templates/postman-node/mocks/external.js +14 -0
  64. package/templates/postman-node/package.json.ejs +39 -0
  65. package/templates/postman-node/requirements.txt +1 -0
  66. package/templates/postman-node/scripts/database/cleanup-mysql.sh +12 -0
  67. package/templates/postman-node/scripts/database/run-migrations.js +29 -0
  68. package/templates/postman-node/scripts/database/start-mysql.sh +34 -0
  69. package/templates/postman-node/scripts/database/wait-for-mysql.sh +36 -0
  70. package/templates/postman-node/scripts/lib/api_coverage_node.py +1137 -0
  71. package/templates/postman-node/scripts/lib/fetch-jwt.sh +86 -0
  72. package/templates/postman-node/scripts/lib/run-newman.sh +104 -0
  73. package/templates/postman-node/scripts/lib/setup-database.sh +55 -0
  74. package/templates/postman-node/scripts/lib/start-app.sh +48 -0
  75. package/templates/postman-node/scripts/lib/utils.sh +114 -0
  76. package/templates/postman-node/scripts/report-generators/stage-report-artifacts.sh +26 -0
  77. package/templates/postman-node/scripts/run-all.sh +303 -0
  78. package/templates/postman-node/scripts/runners/run-tests.sh +123 -0
  79. package/templates/postman-node/scripts/setup-mocks.js.ejs +29 -0
  80. package/templates/postman-node/stryker.config.js.ejs +51 -0
  81. package/templates/rules/local-test-setup.mdc +420 -0
  82. package/templates/rules/testing-node.mdc +66 -0
  83. package/templates/rules/testing.mdc +248 -0
  84. package/templates/skills/_shared/postman-standards.md +380 -0
  85. package/templates/skills/enhance-test-pipeline/SKILL-java.md +483 -0
  86. package/templates/skills/enhance-test-pipeline/SKILL-node.md +431 -0
  87. package/templates/skills/enhance-test-pipeline/SKILL.md +9 -0
  88. package/templates/skills/review-test-suite/SKILL-java.md +137 -0
  89. package/templates/skills/review-test-suite/SKILL-node.md +78 -0
  90. package/templates/skills/review-test-suite/SKILL.md +9 -0
  91. package/templates/skills/run-test-suite/SKILL-java.md +186 -0
  92. package/templates/skills/run-test-suite/SKILL-node.md +191 -0
  93. package/templates/skills/run-test-suite/SKILL.md +9 -0
  94. package/templates/skills/setup-api-tests/SKILL-java.md +1094 -0
  95. package/templates/skills/setup-api-tests/SKILL-node.md +141 -0
  96. package/templates/skills/setup-api-tests/SKILL.md +9 -0
  97. package/templates/skills/setup-mutation-tests/SKILL-java.md +303 -0
  98. package/templates/skills/setup-mutation-tests/SKILL-node.md +408 -0
  99. package/templates/skills/setup-mutation-tests/SKILL.md +9 -0
  100. package/templates/skills/setup-test-pipeline/SKILL-java.md +454 -0
  101. package/templates/skills/setup-test-pipeline/SKILL-node.md +318 -0
  102. package/templates/skills/setup-test-pipeline/SKILL.md +9 -0
  103. package/templates/skills/write-api-tests/SKILL-java.md +115 -0
  104. package/templates/skills/write-api-tests/SKILL-node.md +83 -0
  105. package/templates/skills/write-api-tests/SKILL.md +9 -0
  106. package/templates/stryker.config.js +50 -0
@@ -0,0 +1,420 @@
1
+ ---
2
+ description: Local test environment setup - H2, WireMock, mocking dependencies
3
+ globs:
4
+ - "**/application-integration.properties"
5
+ - "**/application-integration.yml"
6
+ - "**/application-test.properties"
7
+ - "**/application-test.yml"
8
+ - "**/*IntegrationTest.java"
9
+ - "**/wiremock/**"
10
+ - "**/src/test/resources/**"
11
+ ---
12
+
13
+ # Local Test Environment Setup - MANDATORY (Java / Spring Boot)
14
+
15
+ For Postman/Newman tests to run locally on **Java** services, you MUST set up H2, WireMock, and mock all external dependencies.
16
+
17
+ > **Node.js services:** use in-process nock mocks (`postman/scripts/setup-mocks.js`) instead of WireMock. See `setup-api-tests` SKILL-node.md.
18
+
19
+ ## CRITICAL: Explore Codebase First
20
+
21
+ **Before creating any files, explore the project structure:**
22
+
23
+ 1. Read `application.properties` → get server.port, context-path, Feign base URLs
24
+ 2. Find all `@FeignClient` interfaces → these need WireMock stubs
25
+ 3. Read `db/migration/*.sql` → check for H2-incompatible syntax
26
+ 4. Check if `application-integration.properties` exists already
27
+
28
+ **Adapt all templates below based on what you find in the project.**
29
+
30
+ ## H2 Database Configuration
31
+
32
+ ### Spring Profile for Integration Tests
33
+
34
+ Create `src/main/resources/application-integration.properties`:
35
+
36
+ ```properties
37
+ # =============================================================================
38
+ # H2 Database Configuration for Local Integration Tests
39
+ # =============================================================================
40
+
41
+ # H2 in-memory database with MySQL compatibility mode
42
+ spring.datasource.url=jdbc:h2:mem:testdb;MODE=MySQL;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE
43
+ spring.datasource.driver-class-name=org.h2.Driver
44
+ spring.datasource.username=sa
45
+ spring.datasource.password=
46
+
47
+ # JPA/Hibernate settings
48
+ spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
49
+ spring.jpa.hibernate.ddl-auto=none
50
+ spring.jpa.show-sql=false
51
+
52
+ # Flyway configuration
53
+ spring.flyway.enabled=true
54
+ spring.flyway.locations=classpath:db/migration,classpath:db/testdata
55
+ spring.flyway.baseline-on-migrate=true
56
+ spring.flyway.validate-on-migrate=false
57
+
58
+ # H2 Console (optional, for debugging)
59
+ spring.h2.console.enabled=true
60
+ spring.h2.console.path=/h2-console
61
+
62
+ # Disable features not needed for local tests
63
+ spring.cache.type=none
64
+ spring.redis.enabled=false
65
+ ```
66
+
67
+ ### H2 Maven Dependency
68
+
69
+ Ensure `pom.xml` has H2 as test dependency:
70
+
71
+ ```xml
72
+ <dependency>
73
+ <groupId>com.h2database</groupId>
74
+ <artifactId>h2</artifactId>
75
+ <scope>test</scope>
76
+ </dependency>
77
+ ```
78
+
79
+ ## Flyway Migrations - H2 Compatibility
80
+
81
+ ### CRITICAL: MySQL vs H2 Syntax Differences
82
+
83
+ | MySQL Syntax | H2 Compatible Alternative |
84
+ |-------------|---------------------------|
85
+ | `DATETIME` | `TIMESTAMP` |
86
+ | `TINYINT(1)` | `BOOLEAN` |
87
+ | `AUTO_INCREMENT` | `AUTO_INCREMENT` (same) |
88
+ | `ENGINE=InnoDB` | Remove (not supported) |
89
+ | `CHARSET=utf8mb4` | Remove (not supported) |
90
+ | `ON UPDATE CURRENT_TIMESTAMP` | Remove (use `@PreUpdate` instead) |
91
+ | `JSON` | `CLOB` or `VARCHAR(MAX)` |
92
+
93
+ ### Migration File Strategy
94
+
95
+ **Option 1: H2-Compatible MySQL Migrations (Recommended)**
96
+
97
+ Write migrations that work in both MySQL and H2:
98
+
99
+ ```sql
100
+ -- V20240224120000__create_apps_table.sql
101
+ CREATE TABLE IF NOT EXISTS apps (
102
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
103
+ external_id VARCHAR(255) NOT NULL UNIQUE,
104
+ name VARCHAR(255) NOT NULL,
105
+ type VARCHAR(50) NOT NULL,
106
+ status VARCHAR(50) NOT NULL DEFAULT 'ACTIVE',
107
+ created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
108
+ updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
109
+ deleted_at TIMESTAMP NULL
110
+ );
111
+
112
+ CREATE INDEX idx_apps_external_id ON apps(external_id);
113
+ CREATE INDEX idx_apps_type ON apps(type);
114
+ ```
115
+
116
+ **Option 2: Separate H2 Migrations**
117
+
118
+ For complex cases, create H2-specific migrations:
119
+
120
+ ```
121
+ src/main/resources/
122
+ ├── db/migration/ # MySQL migrations (production)
123
+ │ └── V1__create_tables.sql
124
+ └── db/h2/ # H2 overrides (test only)
125
+ └── V1__create_tables.sql
126
+ ```
127
+
128
+ Then configure Flyway per profile:
129
+ ```properties
130
+ # application-integration.properties
131
+ spring.flyway.locations=classpath:db/h2
132
+ ```
133
+
134
+ ### Test Data Seed File
135
+
136
+ Create `src/main/resources/db/testdata/V9999__test_seed_data.sql`:
137
+
138
+ ```sql
139
+ -- Test data for integration tests (high version to run last)
140
+ -- This file should be idempotent (use INSERT IGNORE or MERGE)
141
+
142
+ MERGE INTO apps (id, external_id, name, type, status, created_at, updated_at) KEY(id)
143
+ VALUES
144
+ (1, 'app-001', 'Test Platform App', 'PLATFORM', 'ACTIVE', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
145
+ (2, 'app-002', 'Test Custom App', 'CUSTOM', 'ACTIVE', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
146
+ (3, 'app-003', 'Test Native App', 'NATIVE', 'ACTIVE', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
147
+
148
+ MERGE INTO app_versions (id, app_id, version_number, status, created_at, updated_at) KEY(id)
149
+ VALUES
150
+ (1, 1, '1.0.0', 'PUBLISHED', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
151
+ (2, 2, '1.0.0', 'DRAFT', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
152
+ ```
153
+
154
+ ## WireMock Setup for External Services
155
+
156
+ ### WireMock Directory Structure
157
+
158
+ ```
159
+ postman/scripts/wiremock/
160
+ ├── mappings/ # Stub definitions
161
+ │ ├── freshid-api.json # FreshID API mocks
162
+ │ ├── marketplace-api.json # Other service mocks
163
+ │ └── ...
164
+ └── __files/ # Response body files (optional)
165
+ └── user-response.json
166
+ ```
167
+
168
+ ### WireMock Stub Example
169
+
170
+ Create `postman/scripts/wiremock/mappings/freshid-api.json`:
171
+
172
+ ```json
173
+ {
174
+ "mappings": [
175
+ {
176
+ "name": "FreshID - Get User Success",
177
+ "request": {
178
+ "method": "GET",
179
+ "urlPathPattern": "/freshid/api/v2/users/.*"
180
+ },
181
+ "response": {
182
+ "status": 200,
183
+ "headers": {
184
+ "Content-Type": "application/json"
185
+ },
186
+ "jsonBody": {
187
+ "id": "user-123",
188
+ "email": "test@example.com",
189
+ "name": "Test User",
190
+ "role": "admin"
191
+ }
192
+ }
193
+ },
194
+ {
195
+ "name": "FreshID - User Not Found",
196
+ "request": {
197
+ "method": "GET",
198
+ "urlPath": "/freshid/api/v2/users/nonexistent"
199
+ },
200
+ "response": {
201
+ "status": 404,
202
+ "headers": {
203
+ "Content-Type": "application/json"
204
+ },
205
+ "jsonBody": {
206
+ "error": "User not found"
207
+ }
208
+ }
209
+ },
210
+ {
211
+ "name": "FreshID - Create User",
212
+ "request": {
213
+ "method": "POST",
214
+ "urlPath": "/freshid/api/v2/users",
215
+ "bodyPatterns": [
216
+ {
217
+ "matchesJsonPath": "$.email"
218
+ }
219
+ ]
220
+ },
221
+ "response": {
222
+ "status": 201,
223
+ "headers": {
224
+ "Content-Type": "application/json"
225
+ },
226
+ "jsonBody": {
227
+ "id": "new-user-456",
228
+ "email": "{{jsonPath request.body '$.email'}}",
229
+ "created": true
230
+ },
231
+ "transformers": ["response-template"]
232
+ }
233
+ }
234
+ ]
235
+ }
236
+ ```
237
+
238
+ ### Start/Stop WireMock Scripts
239
+
240
+ The `start-mocks.sh` script starts WireMock on a configured port:
241
+
242
+ ```bash
243
+ # In run-tests-local.sh, WireMock is started before tests:
244
+ ./start-mocks.sh
245
+
246
+ # After tests complete:
247
+ ./stop-mocks.sh
248
+ ```
249
+
250
+ ### Configure Application to Use WireMock
251
+
252
+ In `application-integration.properties`:
253
+
254
+ ```properties
255
+ # Point Feign clients to WireMock
256
+ freshid.api.base-url=http://localhost:8089
257
+ marketplace.api.base-url=http://localhost:8089
258
+ external-service.base-url=http://localhost:8089
259
+ ```
260
+
261
+ ## Mocking Feign Clients
262
+
263
+ ### Option 1: WireMock (Recommended for Postman Tests)
264
+
265
+ WireMock intercepts HTTP calls - no code changes needed. Just configure the base URL.
266
+
267
+ ### Option 2: @MockBean for Java Integration Tests
268
+
269
+ For Java integration tests using MockMvc:
270
+
271
+ ```java
272
+ @SpringBootTest
273
+ @AutoConfigureMockMvc
274
+ @ActiveProfiles("integration")
275
+ class AppControllerIntegrationTest {
276
+
277
+ @MockBean
278
+ private FreshIDApiClient freshIDApiClient; // Mock the Feign client
279
+
280
+ @MockBean
281
+ private MarketplaceApiClient marketplaceApiClient;
282
+
283
+ @BeforeEach
284
+ void setUp() {
285
+ // Configure mock responses
286
+ when(freshIDApiClient.getUser(anyString()))
287
+ .thenReturn(new UserResponse("user-123", "test@example.com"));
288
+
289
+ when(freshIDApiClient.getUser("nonexistent"))
290
+ .thenThrow(new FeignException.NotFound("User not found", mock(Request.class), null, null));
291
+ }
292
+
293
+ @Test
294
+ void createApp_shouldCallFreshID() {
295
+ // Test that uses the mocked FreshID client
296
+ }
297
+ }
298
+ ```
299
+
300
+ ## Mocking Redis Cache
301
+
302
+ ### Disable Redis for Local Tests
303
+
304
+ In `application-integration.properties`:
305
+
306
+ ```properties
307
+ # Disable Redis - use simple cache or no cache
308
+ spring.cache.type=simple
309
+ spring.data.redis.enabled=false
310
+
311
+ # Or disable caching entirely
312
+ spring.cache.type=none
313
+ ```
314
+
315
+ ### Mock Redis Template (if needed)
316
+
317
+ ```java
318
+ @TestConfiguration
319
+ public class TestCacheConfig {
320
+
321
+ @Bean
322
+ @Primary
323
+ public RedisTemplate<String, Object> redisTemplate() {
324
+ // Return a mock or in-memory implementation
325
+ return mock(RedisTemplate.class);
326
+ }
327
+ }
328
+ ```
329
+
330
+ ## Mocking AWS S3
331
+
332
+ ### Use LocalStack or Mock
333
+
334
+ **Option 1: Disable S3 for Tests**
335
+
336
+ ```properties
337
+ # application-integration.properties
338
+ aws.s3.enabled=false
339
+ ```
340
+
341
+ **Option 2: Mock S3Client**
342
+
343
+ ```java
344
+ @MockBean
345
+ private S3Client s3Client;
346
+
347
+ @BeforeEach
348
+ void setUp() {
349
+ when(s3Client.putObject(any(PutObjectRequest.class), any(RequestBody.class)))
350
+ .thenReturn(PutObjectResponse.builder().eTag("test-etag").build());
351
+ }
352
+ ```
353
+
354
+ **Option 3: Use LocalStack (Docker)**
355
+
356
+ ```yaml
357
+ # docker-compose.test.yml
358
+ services:
359
+ localstack:
360
+ image: localstack/localstack
361
+ ports:
362
+ - "4566:4566"
363
+ environment:
364
+ - SERVICES=s3
365
+ ```
366
+
367
+ ```properties
368
+ # application-integration.properties
369
+ aws.s3.endpoint=http://localhost:4566
370
+ aws.s3.region=us-east-1
371
+ ```
372
+
373
+ ## Application Startup for Local Tests
374
+
375
+ ### run-tests-local.sh Flow
376
+
377
+ 1. Start WireMock (`./start-mocks.sh`)
378
+ 2. Start application with integration profile
379
+ 3. Wait for health check
380
+ 4. Run Newman tests
381
+ 5. Stop application
382
+ 6. Stop WireMock (`./stop-mocks.sh`)
383
+
384
+ ### Application Start Command
385
+
386
+ ```bash
387
+ # Start with integration profile
388
+ java -jar target/your-app.jar \
389
+ --spring.profiles.active=integration \
390
+ --server.port=9090 &
391
+
392
+ # Wait for startup
393
+ until curl -s http://localhost:9090/actuator/health | grep -q "UP"; do
394
+ sleep 2
395
+ done
396
+ ```
397
+
398
+ ## Checklist for Local Test Setup
399
+
400
+ Before running `./runners/run-tests-local.sh`, ensure:
401
+
402
+ - [ ] `application-integration.properties` exists with H2 config
403
+ - [ ] H2 dependency in `pom.xml` (test scope)
404
+ - [ ] Flyway migrations are H2-compatible
405
+ - [ ] Test seed data in `db/testdata/`
406
+ - [ ] WireMock mappings for all external services in `postman/scripts/wiremock/mappings/`
407
+ - [ ] Feign client base URLs point to WireMock port
408
+ - [ ] Redis/cache disabled or mocked
409
+ - [ ] S3/AWS disabled or mocked
410
+ - [ ] `start-mocks.sh` and `stop-mocks.sh` configured
411
+
412
+ ## Common Issues
413
+
414
+ | Issue | Cause | Fix |
415
+ |-------|-------|-----|
416
+ | "Table not found" | Flyway migration failed | Check H2 compatibility of SQL |
417
+ | Connection refused | WireMock not started | Run `./start-mocks.sh` first |
418
+ | 404 from external service | Missing WireMock mapping | Add stub to `wiremock/mappings/` |
419
+ | "Unknown column type: JSON" | MySQL-specific type | Use `CLOB` or `VARCHAR` instead |
420
+ | Test data not found | Seed file version too low | Use high version like `V9999__` |
@@ -0,0 +1,66 @@
1
+ ---
2
+ description: Test writing rules for Node.js — MANDATORY when editing test files
3
+ globs:
4
+ - "**/*.test.js"
5
+ - "**/test/**/*.js"
6
+ - "**/test/unit/**/*.js"
7
+ ---
8
+
9
+ # Node.js Test Writing Rules
10
+
11
+ All unit tests MUST survive Stryker mutation testing. Weak assertions allow mutants to survive.
12
+
13
+ ## Test stack
14
+
15
+ - **Runner**: Mocha
16
+ - **Assertions**: Chai `expect`
17
+ - **Mocks**: Sinon + proxyquire
18
+ - **Coverage**: nyc (Istanbul)
19
+ - **Mutation**: Stryker — target 80%+
20
+
21
+ Run mutation tests: `yarn mutation-tests` (fast) or `yarn mutation-tests:full` (CI)
22
+
23
+ ## Assertion strength — CRITICAL
24
+
25
+ ### WRONG
26
+
27
+ ```javascript
28
+ expect(result).to.exist;
29
+ expect(list.length).to.be.above(0);
30
+ expect(response.status).to.equal(200); // without body checks
31
+ ```
32
+
33
+ ### CORRECT
34
+
35
+ ```javascript
36
+ expect(result.id).to.equal(42);
37
+ expect(result.name).to.equal('expected-name');
38
+ expect(list).to.deep.equal([{ id: 1, name: 'a' }]);
39
+ ```
40
+
41
+ If a function returns N fields, assert on all N.
42
+
43
+ ## Stryker scope — keep it small
44
+
45
+ Stryker is slow. **Never** use `src/**` in `mutate`. Target **under 80 files**:
46
+
47
+ - **Include:** `src/actions/`, `src/services/`, `src/serializers/`, `src/middlewares/` (only files with branching logic)
48
+ - **Exclude:** `src/controllers/`, `src/routes/`, `src/models/`, `src/config/`, migrations, seeders
49
+ - **Helpers:** audit file-by-file — exclude SDK wrappers (aws, freshid, chargebee, etc.)
50
+ - **Local:** `yarn mutation-tests` (since mode — changed files only)
51
+ - **CI:** `yarn mutation-tests:full` (full narrow scope only)
52
+
53
+ ## Postman / Newman rules
54
+
55
+ See `postman-standards` skill shared doc:
56
+ - One HTTP status assertion per request
57
+ - Cover all query params with dedicated tests
58
+ - Document environment skips with `[SKIP] Reason: ... | Env: ...`
59
+
60
+ ## API coverage
61
+
62
+ ```bash
63
+ ./postman/scripts/report-generators/node-api-coverage-matrix.sh ./src/controllers ./postman
64
+ ```
65
+
66
+ Target: 100% 2xx coverage before merge.