@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.
- package/README.md +351 -0
- package/bin/shiftleft.js +95 -0
- package/package.json +57 -0
- package/src/commands/doctor.js +208 -0
- package/src/commands/init-postman.js +298 -0
- package/src/commands/init-rules.js +78 -0
- package/src/commands/link.js +172 -0
- package/src/commands/protect.js +61 -0
- package/src/commands/run-tests.js +182 -0
- package/src/commands/setup-pipeline.js +209 -0
- package/src/commands/update.js +203 -0
- package/src/index.js +4 -0
- package/src/utils/copy-tree.js +98 -0
- package/src/utils/gitignore.js +26 -0
- package/src/utils/logger.js +9 -0
- package/src/utils/manifest.js +145 -0
- package/src/utils/stack.js +80 -0
- package/src/utils/template.js +135 -0
- package/templates/AGENTS.md +109 -0
- package/templates/CLAUDE.md +3 -0
- package/templates/jenkins/Jenkinsfile-java.groovy +432 -0
- package/templates/jenkins/Jenkinsfile-node.groovy +450 -0
- package/templates/postman/.husky/pre-commit +19 -0
- package/templates/postman/.prettierrc.json +5 -0
- package/templates/postman/README.md.ejs +147 -0
- package/templates/postman/collections/01-core.json.ejs +91 -0
- package/templates/postman/config/local.json.ejs +12 -0
- package/templates/postman/config/staging.json.ejs +26 -0
- package/templates/postman/environments/local.postman_environment.json.ejs +31 -0
- package/templates/postman/environments/staging.postman_environment.json.ejs +31 -0
- package/templates/postman/gitignore +16 -0
- package/templates/postman/npmrc +31 -0
- package/templates/postman/package.json.ejs +66 -0
- package/templates/postman/run-all-shim.sh +16 -0
- package/templates/postman/scripts/auth/generate-jwt.sh +113 -0
- package/templates/postman/scripts/auth/get-issuer-secret.sh +140 -0
- package/templates/postman/scripts/infra/start-mocks.sh +138 -0
- package/templates/postman/scripts/infra/stop-mocks.sh +43 -0
- package/templates/postman/scripts/lib/api_coverage.py +1122 -0
- package/templates/postman/scripts/lib/cleanup-reports.sh +101 -0
- package/templates/postman/scripts/lib/cleanup-stryker.sh +44 -0
- package/templates/postman/scripts/lib/report_combined.py +527 -0
- package/templates/postman/scripts/lib/report_consolidated.py +363 -0
- package/templates/postman/scripts/lib/report_generator.py +121 -0
- package/templates/postman/scripts/lib/report_migration.py +156 -0
- package/templates/postman/scripts/lib/report_mutation.py +110 -0
- package/templates/postman/scripts/lib/report_unit.py +353 -0
- package/templates/postman/scripts/lib/report_utils.py +973 -0
- package/templates/postman/scripts/report-generators/generate-consolidated-report.sh +445 -0
- package/templates/postman/scripts/report-generators/java-api-coverage-matrix.sh +257 -0
- package/templates/postman/scripts/report-generators/mutation-report.sh +672 -0
- package/templates/postman/scripts/report-generators/node-api-coverage-matrix.sh +167 -0
- package/templates/postman/scripts/report-generators/stage-report-artifacts.sh +27 -0
- package/templates/postman/scripts/run-all.sh +452 -0
- package/templates/postman/scripts/runners/run-mutation-tests.sh +113 -0
- package/templates/postman/scripts/runners/run-tests-local.sh +936 -0
- package/templates/postman/scripts/runners/run-tests-staging.sh +741 -0
- package/templates/postman-node/README.md.ejs +26 -0
- package/templates/postman-node/collections/crud/01-bootstrap.json.ejs +34 -0
- package/templates/postman-node/config/local.json.ejs +46 -0
- package/templates/postman-node/config/staging.json.ejs +31 -0
- package/templates/postman-node/local.test.env.ejs +3 -0
- package/templates/postman-node/mocks/external.js +14 -0
- package/templates/postman-node/package.json.ejs +39 -0
- package/templates/postman-node/requirements.txt +1 -0
- package/templates/postman-node/scripts/database/cleanup-mysql.sh +12 -0
- package/templates/postman-node/scripts/database/run-migrations.js +29 -0
- package/templates/postman-node/scripts/database/start-mysql.sh +34 -0
- package/templates/postman-node/scripts/database/wait-for-mysql.sh +36 -0
- package/templates/postman-node/scripts/lib/api_coverage_node.py +1137 -0
- package/templates/postman-node/scripts/lib/fetch-jwt.sh +86 -0
- package/templates/postman-node/scripts/lib/run-newman.sh +104 -0
- package/templates/postman-node/scripts/lib/setup-database.sh +55 -0
- package/templates/postman-node/scripts/lib/start-app.sh +48 -0
- package/templates/postman-node/scripts/lib/utils.sh +114 -0
- package/templates/postman-node/scripts/report-generators/stage-report-artifacts.sh +26 -0
- package/templates/postman-node/scripts/run-all.sh +303 -0
- package/templates/postman-node/scripts/runners/run-tests.sh +123 -0
- package/templates/postman-node/scripts/setup-mocks.js.ejs +29 -0
- package/templates/postman-node/stryker.config.js.ejs +51 -0
- package/templates/rules/local-test-setup.mdc +420 -0
- package/templates/rules/testing-node.mdc +66 -0
- package/templates/rules/testing.mdc +248 -0
- package/templates/skills/_shared/postman-standards.md +380 -0
- package/templates/skills/enhance-test-pipeline/SKILL-java.md +483 -0
- package/templates/skills/enhance-test-pipeline/SKILL-node.md +431 -0
- package/templates/skills/enhance-test-pipeline/SKILL.md +9 -0
- package/templates/skills/review-test-suite/SKILL-java.md +137 -0
- package/templates/skills/review-test-suite/SKILL-node.md +78 -0
- package/templates/skills/review-test-suite/SKILL.md +9 -0
- package/templates/skills/run-test-suite/SKILL-java.md +186 -0
- package/templates/skills/run-test-suite/SKILL-node.md +191 -0
- package/templates/skills/run-test-suite/SKILL.md +9 -0
- package/templates/skills/setup-api-tests/SKILL-java.md +1094 -0
- package/templates/skills/setup-api-tests/SKILL-node.md +141 -0
- package/templates/skills/setup-api-tests/SKILL.md +9 -0
- package/templates/skills/setup-mutation-tests/SKILL-java.md +303 -0
- package/templates/skills/setup-mutation-tests/SKILL-node.md +408 -0
- package/templates/skills/setup-mutation-tests/SKILL.md +9 -0
- package/templates/skills/setup-test-pipeline/SKILL-java.md +454 -0
- package/templates/skills/setup-test-pipeline/SKILL-node.md +318 -0
- package/templates/skills/setup-test-pipeline/SKILL.md +9 -0
- package/templates/skills/write-api-tests/SKILL-java.md +115 -0
- package/templates/skills/write-api-tests/SKILL-node.md +83 -0
- package/templates/skills/write-api-tests/SKILL.md +9 -0
- 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.
|