jsharness 1.0.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 (68) hide show
  1. package/.harness/README.md +199 -0
  2. package/.harness/agents/code-reviewer/contract.yaml +64 -0
  3. package/.harness/agents/developer/contract.yaml +72 -0
  4. package/.harness/agents/gate-controller/contract.yaml +64 -0
  5. package/.harness/agents/project-manager/contract.yaml +77 -0
  6. package/.harness/agents/prompt-templates.md +352 -0
  7. package/.harness/agents/requirements-analyst/contract.yaml +64 -0
  8. package/.harness/agents/solution-designer/contract.yaml +75 -0
  9. package/.harness/agents/tester/contract.yaml +92 -0
  10. package/.harness/config/models.yaml +67 -0
  11. package/.harness/dev-map/backend/api-definition.md +131 -0
  12. package/.harness/dev-map/backend/auth-security.md +131 -0
  13. package/.harness/dev-map/backend/conventions-java.md +471 -0
  14. package/.harness/dev-map/backend/conventions.md +192 -0
  15. package/.harness/dev-map/backend/database.md +106 -0
  16. package/.harness/dev-map/backend/structure.md +140 -0
  17. package/.harness/dev-map/decisions.md +275 -0
  18. package/.harness/dev-map/frontend/api-integration.md +139 -0
  19. package/.harness/dev-map/frontend/components.md +178 -0
  20. package/.harness/dev-map/frontend/conventions.md +416 -0
  21. package/.harness/dev-map/frontend/state-management.md +170 -0
  22. package/.harness/dev-map/frontend/structure.md +103 -0
  23. package/.harness/dev-map/overview.md +267 -0
  24. package/.harness/docs/integration-test-plan.md +248 -0
  25. package/.harness/docs/team-guidelines/README.md +161 -0
  26. package/.harness/docs/team-guidelines/arch-team.md +811 -0
  27. package/.harness/docs/team-guidelines/collaboration.md +556 -0
  28. package/.harness/docs/team-guidelines/pm-team.md +337 -0
  29. package/.harness/docs/team-guidelines/qa-team.md +562 -0
  30. package/.harness/docs/team-guidelines/rd-team.md +714 -0
  31. package/.harness/docs/training-materials.md +280 -0
  32. package/.harness/gate/baseline.js +220 -0
  33. package/.harness/gate/checks/build-gates-frontend.js +152 -0
  34. package/.harness/gate/checks/build-gates-java.js +155 -0
  35. package/.harness/gate/checks/build-gates.js +119 -0
  36. package/.harness/gate/checks/engineering-consistency.js +138 -0
  37. package/.harness/gate/checks/security-quality.js +129 -0
  38. package/.harness/gate/checks/static-compliance.js +313 -0
  39. package/.harness/gate/checks/test-compliance.js +114 -0
  40. package/.harness/gate/index.js +315 -0
  41. package/.harness/mcp/config.yaml +435 -0
  42. package/.harness/rules/global/coding-standard.md +232 -0
  43. package/.harness/rules/global/commit-convention.md +165 -0
  44. package/.harness/rules/global/process-discipline.md +192 -0
  45. package/.harness/rules/global/security-baseline.md +306 -0
  46. package/.harness/rules/project/frontend-vue3.md +293 -0
  47. package/.harness/rules/project/java-backend.md +460 -0
  48. package/.harness/rules/project/web-specific.md +231 -0
  49. package/.harness/skills/build.md +192 -0
  50. package/.harness/skills/code-review.md +251 -0
  51. package/.harness/skills/docker-build.md +227 -0
  52. package/.harness/skills/docs-update.md +164 -0
  53. package/.harness/skills/java-build.md +261 -0
  54. package/.harness/skills/lint-check.md +482 -0
  55. package/.harness/skills/task-board-maintenance.md +105 -0
  56. package/.harness/skills/test-api.md +461 -0
  57. package/.harness/skills/test-e2e.md +431 -0
  58. package/.harness/skills/test-unit.md +649 -0
  59. package/.harness/skills/vue-frontend-build.md +344 -0
  60. package/.harness/specs/quality-feedback/implementation-guide.md +350 -0
  61. package/.harness/task-board.md +121 -0
  62. package/.harness/workflow/definition.yaml +504 -0
  63. package/.harness/workflow/validate.js +320 -0
  64. package/.harness/workflow/variants.yaml +253 -0
  65. package/README.md +237 -0
  66. package/bin/jsharness.js +53 -0
  67. package/lib/index.mjs +778 -0
  68. package/package.json +1 -0
@@ -0,0 +1,461 @@
1
+ # API 测试技能 (test-api)
2
+
3
+ > **执行角色**: 测试验证 Agent / CI Pipeline
4
+ > **触发时机**: API 变更后、PR 提交前、每日定时运行
5
+
6
+ ---
7
+
8
+ ## API 集成测试执行
9
+
10
+ ### 执行命令
11
+
12
+ ```bash
13
+ # 运行 API 集成测试
14
+ npm run test:api
15
+
16
+ # 或使用指定框架
17
+ npx jest --config jest.api.config.js
18
+ npx vitest run --config vitest.api.config.ts
19
+
20
+ # 带环境变量启动
21
+ DATABASE_URL=test://localhost/testdb npm run test:api
22
+ ```
23
+
24
+ ### 环境准备
25
+
26
+ ```bash
27
+ # 启动测试依赖服务
28
+ docker compose up -d db redis
29
+
30
+ # 执行数据库迁移(使用测试数据库)
31
+ npm run migrate:test
32
+
33
+ # 播种测试数据
34
+ npm run seed:test
35
+ ```
36
+
37
+ ### 测试数据库原则
38
+
39
+ - **隔离性**:每个测试套件使用独立事务,测试结束后回滚
40
+ - **确定性**:种子数据固定,测试结果可重复
41
+ - **轻量化**:只包含必要的关联数据
42
+
43
+ ---
44
+
45
+ ## 契约测试验证
46
+
47
+ ### 目的
48
+
49
+ 确保前后端接口约定不被破坏。即使内部实现改变,API 契约保持稳定。
50
+
51
+ ### 工具选择
52
+
53
+ | 场景 | 推荐工具 |
54
+ |------|---------|
55
+ | REST API | Pact / Newman (Postman) |
56
+ | GraphQL | Apollo Schema Checking |
57
+ | OpenAPI 规范 | Dredd / Spector |
58
+
59
+ ### 契约测试结构
60
+
61
+ ```typescript
62
+ import { pactum } from 'pactum';
63
+
64
+ describe('用户 API 契约测试', () => {
65
+ const baseUrl = 'http://localhost:3000/api';
66
+
67
+ before(() => {
68
+ pactum.request.setBaseUrl(baseUrl);
69
+ });
70
+
71
+ it('GET /api/v1/users/:id 应符合响应契约', async () => {
72
+ await pactum.spec()
73
+ .get('/users/{id}')
74
+ .withPathParams('id', 'user-001')
75
+ .expectStatus(200)
76
+ .expectHeaderContains('content-type', 'application/json')
77
+ .expectJsonMatchStrict({
78
+ code: 0,
79
+ data: {
80
+ id: 'user-001',
81
+ name: pactum.string(), // 任意非空字符串
82
+ email: pactum.like('user@'), // 匹配模式
83
+ createdAt: pactum.isoDateTime(), // ISO 时间格式
84
+ },
85
+ message: pactum.string(),
86
+ timestamp: pactum.r('^\\d+$'), // 正则匹配
87
+ })
88
+ .toss(); // 发送请求并验证
89
+ });
90
+
91
+ it('POST /api/v1/users 应校验必填字段', async () => {
92
+ await pactum.spec()
93
+ .post('/users')
94
+ .withJson({ name: '' }) // 缺少 email
95
+ .expectStatus(400)
96
+ .expectJsonLike({
97
+ code: 10001, // 参数错误码
98
+ message: 'email is required',
99
+ })
100
+ .toss();
101
+ });
102
+ });
103
+ ```
104
+
105
+ ### 必须验证的契约维度
106
+
107
+ | 维度 | 验证内容 |
108
+ |------|----------|
109
+ | **HTTP 方法** | GET/POST/PUT/DELETE 使用正确 |
110
+ | **状态码** | 成功(200/201)、客户端错误(4xx)、服务端错误(5xx) |
111
+ | **Content-Type** | 响应格式正确(application/json) |
112
+ | **Response Schema** | 字段名、类型、嵌套结构符合定义 |
113
+ | **分页参数** | page/pageSize/total/count 字段齐全 |
114
+ | **错误格式** | 统一错误响应结构 |
115
+ | **认证头** | Bearer Token 格式正确 |
116
+
117
+ ---
118
+
119
+ ## API 测试用例清单模板
120
+
121
+ ### 每个 API 端点必须覆盖
122
+
123
+ ```
124
+ 端点: POST /api/v1/users
125
+
126
+ ✅ 正常用例:
127
+ [ ] 所有必填字段齐全 → 201 Created
128
+ [ ] 所有可选字段都提供 → 完整对象返回
129
+ [ ] 含特殊字符(中文/emoji)→ 正确存储
130
+
131
+ ❌ 异常用例:
132
+ [ ] 缺少必填字段 → 400 + 明确的错误信息
133
+ [ ] 字段类型错误(字符串传数字)→ 400
134
+ [ ] 字段长度超限 → 400
135
+ [ ] 邮箱格式无效 → 400
136
+ [ ] 重复的唯一字段(如邮箱)→ 409 Conflict
137
+ [ ] 未认证访问 → 401 Unauthorized
138
+ [ ] 权限不足 → 403 Forbidden
139
+ [ ] 请求体非 JSON → 415 Unsupported Media Type
140
+ [ ] 超大 payload → 413 Payload Too Large
141
+
142
+ 🔒 安全用例:
143
+ [ ] SQL 注入尝试 → 400(被拦截)
144
+ [ ] XSS payload → 安全存储(转义后)
145
+ [ ] 越权操作(A 用户操作 B 的资源)→ 403
146
+ ```
147
+
148
+ ## API 测试结果输出模板
149
+
150
+ ```yaml
151
+ test_result_api:
152
+ timestamp: "2026-05-20T22:00:00Z"
153
+ framework: "pactum" | "jest" | "newman"
154
+ status: pass | fail
155
+
156
+ endpoints_tested: 24
157
+ total_assertions: 342
158
+
159
+ summary:
160
+ passed: 338
161
+ failed: 4
162
+
163
+ contract_compliance:
164
+ breaking_changes: 0 # 破坏性变更数量(门禁关键项)
165
+ new_fields: 2 # 新增字段(向后兼容)
166
+ removed_fields: 0 # 删除字段(阻塞项)
167
+
168
+ failed_assertions:
169
+ - endpoint: "PATCH /api/v1/users/:id/profile"
170
+ assertion: "expect response phone to match pattern"
171
+ actual: '"13800138000"'
172
+ expected: 'pattern: /^1[3-9]\d{9}$/'
173
+
174
+ performance_p95:
175
+ avg_response_time_ms: 85
176
+ max_response_time_ms: 320
177
+ slow_endpoints:
178
+ - path: "/api/v1/reports/export"
179
+ avg_ms: 280
180
+
181
+ baseline_comparison:
182
+ regression: false
183
+ ```
184
+
185
+ ---
186
+
187
+ # ════════════════════════════════════════════════════════
188
+ # Java 后端 API 集成测试(REST Assured + WireMock)
189
+ # ════════════════════════════════════════════════════════
190
+
191
+ > **适用技术栈**: Spring Boot / JDK21
192
+ > **参考规则**: `rules/project/java-backend.md` §4 Controller + §5 Service
193
+
194
+ ---
195
+
196
+ ## REST Assured — Java HTTP 测试 DSL
197
+
198
+ ### Maven 依赖
199
+
200
+ ```xml
201
+ <!-- pom.xml -->
202
+ <dependency>
203
+ <groupId>io.rest-assured</groupId>
204
+ <artifactId>rest-assured</artifactId>
205
+ <version>5.4.0</version>
206
+ <scope>test</scope>
207
+ </dependency>
208
+ <dependency>
209
+ <groupId>io.rest-assured</groupId>
210
+ <artifactId>spring-mock-mvc-module</artifactId>
211
+ <version>5.4.0</version>
212
+ <scope>test</scope>
213
+ </dependency>
214
+ ```
215
+
216
+ ### 核心测试结构
217
+
218
+ ```java
219
+ @SpringBootTest
220
+ @AutoConfigureMockMvc
221
+ class UserApiIntegrationTest {
222
+
223
+ @Autowired
224
+ private MockMvc mockMvc;
225
+
226
+ @MockBean
227
+ private UserService userService;
228
+
229
+ @Autowired
230
+ private ObjectMapper objectMapper;
231
+
232
+ @Test
233
+ @DisplayName("POST /api/v1/users — 创建用户成功")
234
+ void shouldCreateUser() throws Exception {
235
+ // === GIVEN (准备) ===
236
+ UserReqVO req = UserReqVO.builder()
237
+ .phone("13800138000").name("张三").build();
238
+
239
+ User created = User.builder().id("user-001").name("张三").build();
240
+ when(userService.createUser(any(UserReqVO.class)))
241
+ .thenReturn(RespVO.success(created));
242
+
243
+ // === WHEN & THEN (执行 + 断言) ===
244
+ mockMvc.perform(post("/api/v1/users")
245
+ .contentType(MediaType.APPLICATION_JSON)
246
+ .content(objectMapper.writeValueAsString(req)))
247
+ .andExpect(status().isCreated()) // 201
248
+ .andExpect(jsonPath("$.code").value(0))
249
+ .andExpect(jsonPath("$.data.id").isNotEmpty())
250
+ .andExpect(jsonPath("$.data.name").value("张三"))
251
+ .andDo(print()); // 打印完整响应(调试用)
252
+ }
253
+
254
+ @Test
255
+ @DisplayName("GET /api/v1/users/{id} — 用户存在时返回 200")
256
+ void shouldReturnUserWhenExists() throws Exception {
257
+ // GIVEN
258
+ when(userService.getUserById("user-001"))
259
+ .thenReturn(RespVO.success(
260
+ UserVO.builder().id("user-001").name("李四")
261
+ .status("ACTIVE").build()));
262
+
263
+ // WHEN & THEN
264
+ mockMvc.perform(get("/api/v1/users/user-001"))
265
+ .andExpect(status().isOk())
266
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON))
267
+ .andExpect(jsonPath("$.data.name").value("李四"));
268
+ }
269
+
270
+ @Test
271
+ @DisplayName("POST /api/v1/users — 缺少必填字段返回 400")
272
+ void shouldReturn400WhenMissingFields() throws Exception {
273
+ // 缺少 phone 字段
274
+ String invalidJson = "{\"name\":\"\"}";
275
+
276
+ mockMvc.perform(post("/api/v1/users")
277
+ .contentType(MediaType.APPLICATION_JSON)
278
+ .content(invalidJson))
279
+ .andExpect(status().isBadRequest())
280
+ .andExpect(jsonPath("$.code").value(10001)) // 参数错误码
281
+ .andExpect(jsonPath("$.message").isNotEmpty());
282
+ }
283
+ }
284
+ ```
285
+
286
+ ### 契约测试维度检查清单
287
+
288
+ | 维度 | 验证方式 | 示例断言 |
289
+ |------|---------|---------|
290
+ | **HTTP 方法** | `.getMethod()` 匹配 | `.request().method(HttpMethod.POST)` |
291
+ | **状态码** | `.andExpect(status())` | `isOk()` / `isCreated()` / `isBadRequest()` / `isUnauthorized()` |
292
+ | **Content-Type** | header 检查 | `.contentType(MediaType.APPLICATION_JSON)` |
293
+ | **Response Schema** | jsonPath 字段存在性 | `jsonPath("$.data.id").exists()` |
294
+ | **分页结构** | 分页字段完整性 | `jsonPath("$.data.list")` + `jsonPath("$.total")` |
295
+ | **错误格式** | 统一错误响应 | `jsonPath("$.code").isNumber()` + `jsonPath("$.message")` |
296
+ | **认证头** | 未携带 Token 时 | `isUnauthorized()` 或 `isForbidden()` |
297
+
298
+ ---
299
+
300
+ ## WireMock — 服务端 Mock
301
+
302
+ ### Maven 依赖
303
+
304
+ ```xml
305
+ <dependency>
306
+ <groupId>org.wiremock</groupId>
307
+ <artifactId>wiremock-standalone</artifactId>
308
+ <version>3.3.1</version>
309
+ <scope>test</scope>
310
+ </dependency>
311
+ ```
312
+
313
+ ### Mock 外部服务
314
+
315
+ ```java
316
+ @ExtendWith(WireMockExtension.class)
317
+ @WireMockConfig(wiremockPort = 9090)
318
+ class PaymentServiceIntegrationTest {
319
+
320
+ @RegisterExtension
321
+ static WireMockExtension wireMock = WireMockExtension.newInstance()
322
+ .options(wireMockConfig().port(9090))
323
+ .build();
324
+
325
+ @Test
326
+ @DisplayName("调用支付网关成功")
327
+ void shouldCallPaymentGateway() {
328
+ // Stub:模拟支付网关响应
329
+ stubFor(post(urlEqualTo("/api/pay"))
330
+ .willReturn(aResponse()
331
+ .withStatus(200)
332
+ .withHeader("Content-Type", "application/json")
333
+ .withBody("{\"code\":0,\"tradeNo\":\"T202605210001\"}")));
334
+
335
+ // 执行业务代码
336
+ PayResult result = paymentService.pay(PayRequest.builder()
337
+ .amount(new BigDecimal("100.00")).orderId("ORD-001").build());
338
+
339
+ // 断言
340
+ assertThat(result.getTradeNo()).isEqualTo("T202605210001");
341
+
342
+ // 验证确实调用了外部服务
343
+ verify(postRequestedFor(urlEqualTo("/api/pay"))
344
+ .withRequestBody(containing("100.00")));
345
+ }
346
+ }
347
+ ```
348
+
349
+ ---
350
+
351
+ ## 数据库迁移测试
352
+
353
+ ### Flyway 集成
354
+
355
+ ```bash
356
+ # 迁移命令
357
+ mvn flyway:migrate # 执行所有待执行的迁移
358
+ mvn flyway:info # 查看迁移历史和状态
359
+ mvn flyway:validate # 校验迁移文件一致性
360
+
361
+ # 清理重建(仅开发环境)
362
+ mvn flyway:clean migrate # ⚠️ 会删除所有数据!仅本地使用
363
+ ```
364
+
365
+ ### 迁移脚本规范
366
+
367
+ ```sql
368
+ -- 文件命名格式: V{版本号}__{描述}.sql
369
+ -- 例: V20260521_001__add_user_phone_encryption.sql
370
+
371
+ -- ✅ 正确做法:增量 ALTER 脚本
372
+ ALTER TABLE t_user ADD COLUMN phone_encrypted VARCHAR(128) DEFAULT '';
373
+ CREATE INDEX idx_user_phone_encrypted ON t_user(phone_encrypted);
374
+
375
+ -- ❌ 错误做法:DROP 后重建(会丢失数据!)
376
+ -- DROP TABLE t_user;
377
+ -- CREATE TABLE t_user (...);
378
+ ```
379
+
380
+ ---
381
+
382
+ ## Postman Newman — API 回归测试
383
+
384
+ ### 从 CI/CD 运行
385
+
386
+ ```bash
387
+ # 安装 newman
388
+ npm install -g newman newman-reporter-htmlextra
389
+
390
+ # 运行集合
391
+ newman run postman_collections/backend-api.json \
392
+ -e postman_environments/staging.json \
393
+ --reporters cli,htmlextra \
394
+ --reporter-htmlextra-export api-test-report.html \
395
+ --bail # 第一个失败即停止
396
+
397
+ # Jenkins/GitLab CI 集成
398
+ # newman run ... --reporters junit --reporter-junit-export results.xml
399
+ ```
400
+
401
+ ### 集合组织建议
402
+
403
+ ```
404
+ backend-api.collection/
405
+ ├── auth/
406
+ │ ├── POST_login.json
407
+ │ └── POST_refresh-token.json
408
+ ├── user/
409
+ │ ├── GET_users-id.json
410
+ │ ├── POST_users.json
411
+ │ └── PUT_users-id-profile.json
412
+ ├── order/
413
+ │ ├── GET_orders.json
414
+ │ └── POST_orders.json
415
+ └── health/
416
+ └── GET_health.json
417
+ ```
418
+
419
+ ---
420
+
421
+ ## Java API 测试结果输出模板
422
+
423
+ ```yaml
424
+ test_result_api_java:
425
+ timestamp: "2026-05-20T22:00:00Z"
426
+ framework: "REST Assured (Spring MockMvc) + Newman"
427
+ status: pass | fail
428
+
429
+ endpoints_tested: 32
430
+ total_assertions: 486
431
+
432
+ summary:
433
+ passed: 482
434
+ failed: 4
435
+
436
+ contract_compliance:
437
+ breaking_changes: 0
438
+ new_fields: 1
439
+ removed_fields: 0
440
+ deprecated_fields: 0
441
+
442
+ failed_assertions:
443
+ - endpoint: "PUT /api/v1/users/:id/profile"
444
+ method: "PUT"
445
+ assertion: "expect response.phone to match regex ^1[3-9]\\d{9}$"
446
+ actual: '"13800138000"'
447
+ expected: 'pattern match'
448
+
449
+ security_checks:
450
+ sql_injection_attempt_blocked: true
451
+ xss_payload_sanitized: true
452
+ unauthorized_access_rejected: true
453
+ missing_auth_returns_401: true
454
+
455
+ performance_p95:
456
+ avg_response_time_ms: 65
457
+ max_response_time_ms: 280
458
+
459
+ baseline_comparison:
460
+ regression: false
461
+ ```