@corbat-tech/coding-standards-mcp 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 (89) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +371 -0
  3. package/assets/demo.gif +0 -0
  4. package/dist/agent.d.ts +53 -0
  5. package/dist/agent.d.ts.map +1 -0
  6. package/dist/agent.js +629 -0
  7. package/dist/agent.js.map +1 -0
  8. package/dist/cli/init.d.ts +3 -0
  9. package/dist/cli/init.d.ts.map +1 -0
  10. package/dist/cli/init.js +651 -0
  11. package/dist/cli/init.js.map +1 -0
  12. package/dist/config.d.ts +73 -0
  13. package/dist/config.d.ts.map +1 -0
  14. package/dist/config.js +105 -0
  15. package/dist/config.js.map +1 -0
  16. package/dist/index.d.ts +3 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +73 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/profiles.d.ts +39 -0
  21. package/dist/profiles.d.ts.map +1 -0
  22. package/dist/profiles.js +526 -0
  23. package/dist/profiles.js.map +1 -0
  24. package/dist/prompts-legacy.d.ts +25 -0
  25. package/dist/prompts-legacy.d.ts.map +1 -0
  26. package/dist/prompts-legacy.js +600 -0
  27. package/dist/prompts-legacy.js.map +1 -0
  28. package/dist/prompts-v2.d.ts +30 -0
  29. package/dist/prompts-v2.d.ts.map +1 -0
  30. package/dist/prompts-v2.js +310 -0
  31. package/dist/prompts-v2.js.map +1 -0
  32. package/dist/prompts.d.ts +30 -0
  33. package/dist/prompts.d.ts.map +1 -0
  34. package/dist/prompts.js +310 -0
  35. package/dist/prompts.js.map +1 -0
  36. package/dist/resources.d.ts +18 -0
  37. package/dist/resources.d.ts.map +1 -0
  38. package/dist/resources.js +95 -0
  39. package/dist/resources.js.map +1 -0
  40. package/dist/tools-legacy.d.ts +196 -0
  41. package/dist/tools-legacy.d.ts.map +1 -0
  42. package/dist/tools-legacy.js +1230 -0
  43. package/dist/tools-legacy.js.map +1 -0
  44. package/dist/tools-v2.d.ts +92 -0
  45. package/dist/tools-v2.d.ts.map +1 -0
  46. package/dist/tools-v2.js +410 -0
  47. package/dist/tools-v2.js.map +1 -0
  48. package/dist/tools.d.ts +92 -0
  49. package/dist/tools.d.ts.map +1 -0
  50. package/dist/tools.js +410 -0
  51. package/dist/tools.js.map +1 -0
  52. package/dist/types.d.ts +3054 -0
  53. package/dist/types.d.ts.map +1 -0
  54. package/dist/types.js +515 -0
  55. package/dist/types.js.map +1 -0
  56. package/dist/utils/index.d.ts +6 -0
  57. package/dist/utils/index.d.ts.map +1 -0
  58. package/dist/utils/index.js +5 -0
  59. package/dist/utils/index.js.map +1 -0
  60. package/dist/utils/retry.d.ts +44 -0
  61. package/dist/utils/retry.d.ts.map +1 -0
  62. package/dist/utils/retry.js +74 -0
  63. package/dist/utils/retry.js.map +1 -0
  64. package/package.json +79 -0
  65. package/profiles/README.md +199 -0
  66. package/profiles/custom/.gitkeep +2 -0
  67. package/profiles/templates/_template.yaml +159 -0
  68. package/profiles/templates/angular.yaml +494 -0
  69. package/profiles/templates/java-spring-backend.yaml +512 -0
  70. package/profiles/templates/minimal.yaml +102 -0
  71. package/profiles/templates/nodejs.yaml +338 -0
  72. package/profiles/templates/python.yaml +340 -0
  73. package/profiles/templates/react.yaml +331 -0
  74. package/profiles/templates/vue.yaml +598 -0
  75. package/standards/architecture/ddd.md +173 -0
  76. package/standards/architecture/hexagonal.md +97 -0
  77. package/standards/cicd/github-actions.md +567 -0
  78. package/standards/clean-code/naming.md +175 -0
  79. package/standards/clean-code/principles.md +179 -0
  80. package/standards/containerization/dockerfile.md +419 -0
  81. package/standards/database/selection-guide.md +443 -0
  82. package/standards/documentation/guidelines.md +189 -0
  83. package/standards/event-driven/domain-events.md +527 -0
  84. package/standards/kubernetes/deployment.md +518 -0
  85. package/standards/observability/guidelines.md +665 -0
  86. package/standards/project-setup/initialization-checklist.md +650 -0
  87. package/standards/spring-boot/best-practices.md +598 -0
  88. package/standards/testing/guidelines.md +559 -0
  89. package/standards/workflow/llm-development-workflow.md +542 -0
@@ -0,0 +1,559 @@
1
+ # Testing Guidelines
2
+
3
+ ## Overview
4
+
5
+ Testing is essential for maintainable software. These guidelines cover unit tests, integration tests, architecture tests, and testing best practices for Java/Spring Boot applications.
6
+
7
+ ## Test Pyramid
8
+
9
+ ```
10
+ /\
11
+ / \ E2E Tests (few)
12
+ /----\
13
+ / \ Integration Tests (some)
14
+ /--------\
15
+ / \ Unit Tests (many)
16
+ --------------
17
+ ```
18
+
19
+ - **Unit Tests**: 70% - Fast, isolated, test single units
20
+ - **Integration Tests**: 20% - Test component interactions
21
+ - **E2E Tests**: 10% - Test full user flows
22
+
23
+ ## Test Types and Naming Conventions
24
+
25
+ | Type | Suffix | Plugin | Phase | Example |
26
+ |------|--------|--------|-------|---------|
27
+ | Unit | `*Test.java` | maven-surefire | test | `OrderServiceTest.java` |
28
+ | Integration | `*IT.java` | maven-failsafe | integration-test | `OrderRepositoryIT.java` |
29
+ | E2E | `*E2ETest.java` | maven-failsafe | integration-test | `OrderFlowE2ETest.java` |
30
+ | Architecture | `*ArchTest.java` | maven-surefire | test | `HexagonalArchTest.java` |
31
+
32
+ ### Maven Configuration for IT Tests
33
+
34
+ ```xml
35
+ <plugin>
36
+ <groupId>org.apache.maven.plugins</groupId>
37
+ <artifactId>maven-failsafe-plugin</artifactId>
38
+ <version>${maven-failsafe-plugin.version}</version>
39
+ <executions>
40
+ <execution>
41
+ <goals>
42
+ <goal>integration-test</goal>
43
+ <goal>verify</goal>
44
+ </goals>
45
+ </execution>
46
+ </executions>
47
+ <configuration>
48
+ <includes>
49
+ <include>**/*IT.java</include>
50
+ <include>**/*E2ETest.java</include>
51
+ </includes>
52
+ </configuration>
53
+ </plugin>
54
+ ```
55
+
56
+ ## Unit Testing
57
+
58
+ ### Structure: Arrange-Act-Assert (AAA)
59
+
60
+ ```java
61
+ @Test
62
+ void should_CalculateTotal_When_OrderHasMultipleLines() {
63
+ // Arrange
64
+ Order order = Order.create(OrderId.generate(), customerId);
65
+ order.addLine(productId1, 2, Money.of(10, "USD"));
66
+ order.addLine(productId2, 1, Money.of(25, "USD"));
67
+
68
+ // Act
69
+ Money total = order.calculateTotal();
70
+
71
+ // Assert
72
+ assertThat(total).isEqualTo(Money.of(45, "USD"));
73
+ }
74
+ ```
75
+
76
+ ### Test Method Naming Convention
77
+
78
+ Pattern: `should_ExpectedBehavior_When_Condition`
79
+
80
+ ```java
81
+ @Test
82
+ void should_ThrowException_When_OrderIsEmpty() { }
83
+
84
+ @Test
85
+ void should_ApplyDiscount_When_CustomerIsPremium() { }
86
+
87
+ @Test
88
+ void should_ReturnEmptyList_When_NoOrdersExist() { }
89
+
90
+ @Test
91
+ void should_CreateOrder_When_AllFieldsAreValid() { }
92
+ ```
93
+
94
+ ### Use AssertJ for Assertions
95
+
96
+ ```java
97
+ // Bad - JUnit assertions
98
+ assertEquals(expected, actual);
99
+ assertTrue(list.isEmpty());
100
+
101
+ // Good - AssertJ
102
+ assertThat(actual).isEqualTo(expected);
103
+ assertThat(list).isEmpty();
104
+ assertThat(order.getStatus()).isEqualTo(OrderStatus.CONFIRMED);
105
+ assertThat(orders)
106
+ .hasSize(3)
107
+ .extracting(Order::getStatus)
108
+ .containsOnly(OrderStatus.PENDING);
109
+ ```
110
+
111
+ ### Testing Domain Objects
112
+
113
+ ```java
114
+ class MoneyTest {
115
+
116
+ @Test
117
+ void should_CreateMoney_When_AmountIsPositive() {
118
+ Money money = Money.of(100, "USD");
119
+
120
+ assertThat(money.getAmount()).isEqualByComparingTo(new BigDecimal("100"));
121
+ assertThat(money.getCurrency()).isEqualTo(Currency.getInstance("USD"));
122
+ }
123
+
124
+ @Test
125
+ void should_ThrowException_When_AmountIsNegative() {
126
+ assertThatThrownBy(() -> Money.of(-100, "USD"))
127
+ .isInstanceOf(IllegalArgumentException.class)
128
+ .hasMessageContaining("negative");
129
+ }
130
+
131
+ @Test
132
+ void should_AddMoney_When_CurrenciesMatch() {
133
+ Money a = Money.of(100, "USD");
134
+ Money b = Money.of(50, "USD");
135
+
136
+ Money result = a.add(b);
137
+
138
+ assertThat(result).isEqualTo(Money.of(150, "USD"));
139
+ }
140
+ }
141
+ ```
142
+
143
+ ### Mocking with Mockito
144
+
145
+ ```java
146
+ @ExtendWith(MockitoExtension.class)
147
+ class PlaceOrderUseCaseTest {
148
+
149
+ @Mock
150
+ private OrderRepository orderRepository;
151
+
152
+ @Mock
153
+ private EventPublisher eventPublisher;
154
+
155
+ @InjectMocks
156
+ private PlaceOrderUseCase useCase;
157
+
158
+ @Test
159
+ void should_SaveOrder_When_CommandIsValid() {
160
+ // Arrange
161
+ PlaceOrderCommand command = new PlaceOrderCommand(customerId, lines);
162
+
163
+ // Act
164
+ OrderId result = useCase.execute(command);
165
+
166
+ // Assert
167
+ verify(orderRepository).save(any(Order.class));
168
+ verify(eventPublisher).publish(any(OrderPlaced.class));
169
+ assertThat(result).isNotNull();
170
+ }
171
+
172
+ @Test
173
+ void should_ThrowException_When_CustomerNotFound() {
174
+ // Arrange
175
+ when(customerRepository.findById(any())).thenReturn(Optional.empty());
176
+
177
+ // Act & Assert
178
+ assertThatThrownBy(() -> useCase.execute(command))
179
+ .isInstanceOf(CustomerNotFoundException.class);
180
+
181
+ verify(orderRepository, never()).save(any());
182
+ }
183
+ }
184
+ ```
185
+
186
+ ## Integration Testing
187
+
188
+ ### Naming Convention: Use `*IT.java` Suffix
189
+
190
+ Integration tests must end with `IT` for maven-failsafe automatic detection:
191
+
192
+ ```java
193
+ // File: OrderRepositoryIT.java
194
+ @SpringBootTest
195
+ @Testcontainers
196
+ class OrderRepositoryIT {
197
+ // ...
198
+ }
199
+
200
+ // File: OrderControllerIT.java
201
+ @SpringBootTest
202
+ @AutoConfigureMockMvc
203
+ class OrderControllerIT {
204
+ // ...
205
+ }
206
+ ```
207
+
208
+ ### Spring Boot Integration Tests
209
+
210
+ ```java
211
+ @SpringBootTest
212
+ @AutoConfigureMockMvc
213
+ @Transactional
214
+ class OrderControllerIT {
215
+
216
+ @Autowired
217
+ private MockMvc mockMvc;
218
+
219
+ @Autowired
220
+ private OrderRepository orderRepository;
221
+
222
+ @Test
223
+ void should_CreateOrder_When_RequestIsValid() throws Exception {
224
+ String requestBody = """
225
+ {
226
+ "customerId": "cust-123",
227
+ "lines": [
228
+ {"productId": "prod-1", "quantity": 2}
229
+ ]
230
+ }
231
+ """;
232
+
233
+ mockMvc.perform(post("/api/v1/orders")
234
+ .contentType(MediaType.APPLICATION_JSON)
235
+ .content(requestBody))
236
+ .andExpect(status().isCreated())
237
+ .andExpect(jsonPath("$.id").exists())
238
+ .andExpect(jsonPath("$.status").value("PENDING"));
239
+ }
240
+ }
241
+ ```
242
+
243
+ ### Testcontainers for Database Tests
244
+
245
+ ```java
246
+ @SpringBootTest
247
+ @Testcontainers
248
+ class OrderRepositoryIT {
249
+
250
+ @Container
251
+ static PostgreSQLContainer<?> postgres =
252
+ new PostgreSQLContainer<>("postgres:15-alpine");
253
+
254
+ @DynamicPropertySource
255
+ static void configureProperties(DynamicPropertyRegistry registry) {
256
+ registry.add("spring.datasource.url", postgres::getJdbcUrl);
257
+ registry.add("spring.datasource.username", postgres::getUsername);
258
+ registry.add("spring.datasource.password", postgres::getPassword);
259
+ }
260
+
261
+ @Autowired
262
+ private OrderRepository orderRepository;
263
+
264
+ @Test
265
+ void should_PersistAndRetrieveOrder() {
266
+ Order order = createTestOrder();
267
+
268
+ orderRepository.save(order);
269
+ Optional<Order> found = orderRepository.findById(order.getId());
270
+
271
+ assertThat(found)
272
+ .isPresent()
273
+ .get()
274
+ .satisfies(o -> {
275
+ assertThat(o.getId()).isEqualTo(order.getId());
276
+ assertThat(o.getStatus()).isEqualTo(order.getStatus());
277
+ });
278
+ }
279
+ }
280
+ ```
281
+
282
+ ### Kafka Integration Tests
283
+
284
+ ```java
285
+ @SpringBootTest
286
+ @Testcontainers
287
+ @EmbeddedKafka(partitions = 1, topics = {"orders.order.created"})
288
+ class OrderEventPublisherIT {
289
+
290
+ @Container
291
+ static KafkaContainer kafka = new KafkaContainer(
292
+ DockerImageName.parse("confluentinc/cp-kafka:7.4.0")
293
+ );
294
+
295
+ @DynamicPropertySource
296
+ static void configureProperties(DynamicPropertyRegistry registry) {
297
+ registry.add("spring.kafka.bootstrap-servers", kafka::getBootstrapServers);
298
+ }
299
+
300
+ @Autowired
301
+ private KafkaTemplate<String, OrderCreatedEvent> kafkaTemplate;
302
+
303
+ @Test
304
+ void should_PublishEvent_When_OrderIsCreated() {
305
+ // Test Kafka integration
306
+ }
307
+ }
308
+ ```
309
+
310
+ ## Architecture Testing with ArchUnit
311
+
312
+ ArchUnit tests verify architectural constraints are respected.
313
+
314
+ ### Setup
315
+
316
+ ```xml
317
+ <dependency>
318
+ <groupId>com.tngtech.archunit</groupId>
319
+ <artifactId>archunit-junit5</artifactId>
320
+ <version>${archunit.version}</version>
321
+ <scope>test</scope>
322
+ </dependency>
323
+ ```
324
+
325
+ ### Hexagonal Architecture Tests
326
+
327
+ ```java
328
+ @AnalyzeClasses(packages = "com.example.myapp")
329
+ class HexagonalArchTest {
330
+
331
+ @ArchTest
332
+ static final ArchRule domain_should_not_depend_on_infrastructure =
333
+ noClasses()
334
+ .that().resideInAPackage("..domain..")
335
+ .should().dependOnClassesThat()
336
+ .resideInAPackage("..infrastructure..");
337
+
338
+ @ArchTest
339
+ static final ArchRule domain_should_not_depend_on_application =
340
+ noClasses()
341
+ .that().resideInAPackage("..domain..")
342
+ .should().dependOnClassesThat()
343
+ .resideInAPackage("..application..");
344
+
345
+ @ArchTest
346
+ static final ArchRule application_should_not_depend_on_infrastructure =
347
+ noClasses()
348
+ .that().resideInAPackage("..application..")
349
+ .should().dependOnClassesThat()
350
+ .resideInAPackage("..infrastructure..");
351
+
352
+ @ArchTest
353
+ static final ArchRule domain_should_not_use_spring_annotations =
354
+ noClasses()
355
+ .that().resideInAPackage("..domain..")
356
+ .should().dependOnClassesThat()
357
+ .resideInAPackage("org.springframework..");
358
+ }
359
+ ```
360
+
361
+ ### Naming Convention Tests
362
+
363
+ ```java
364
+ @AnalyzeClasses(packages = "com.example.myapp")
365
+ class NamingConventionArchTest {
366
+
367
+ @ArchTest
368
+ static final ArchRule controllers_should_be_suffixed =
369
+ classes()
370
+ .that().resideInAPackage("..adapter.in.rest..")
371
+ .and().areAnnotatedWith(RestController.class)
372
+ .should().haveSimpleNameEndingWith("Controller");
373
+
374
+ @ArchTest
375
+ static final ArchRule use_cases_should_be_suffixed =
376
+ classes()
377
+ .that().resideInAPackage("..application.usecase..")
378
+ .should().haveSimpleNameEndingWith("UseCase");
379
+
380
+ @ArchTest
381
+ static final ArchRule repositories_should_be_suffixed =
382
+ classes()
383
+ .that().implement(Repository.class)
384
+ .should().haveSimpleNameEndingWith("Repository");
385
+ }
386
+ ```
387
+
388
+ ### Layer Definition Tests
389
+
390
+ ```java
391
+ @AnalyzeClasses(packages = "com.example.myapp")
392
+ class LayeredArchitectureTest {
393
+
394
+ @ArchTest
395
+ static final ArchRule layer_dependencies_are_respected =
396
+ layeredArchitecture()
397
+ .consideringAllDependencies()
398
+ .layer("Domain").definedBy("..domain..")
399
+ .layer("Application").definedBy("..application..")
400
+ .layer("Infrastructure").definedBy("..infrastructure..")
401
+
402
+ .whereLayer("Domain").mayOnlyBeAccessedByLayers("Application", "Infrastructure")
403
+ .whereLayer("Application").mayOnlyBeAccessedByLayers("Infrastructure")
404
+ .whereLayer("Infrastructure").mayNotBeAccessedByAnyLayer();
405
+ }
406
+ ```
407
+
408
+ ## Test Data
409
+
410
+ ### Use Test Fixtures / Builders
411
+
412
+ ```java
413
+ public class OrderFixtures {
414
+
415
+ public static Order aDefaultOrder() {
416
+ return Order.create(
417
+ OrderId.of("order-123"),
418
+ CustomerId.of("customer-456")
419
+ );
420
+ }
421
+
422
+ public static Order anOrderWithLines(int lineCount) {
423
+ Order order = aDefaultOrder();
424
+ for (int i = 0; i < lineCount; i++) {
425
+ order.addLine(ProductId.of("prod-" + i), 1, Money.of(10, "USD"));
426
+ }
427
+ return order;
428
+ }
429
+ }
430
+ ```
431
+
432
+ ### Use Builder Pattern for Test Data
433
+
434
+ ```java
435
+ public class OrderTestBuilder {
436
+ private OrderId id = OrderId.generate();
437
+ private CustomerId customerId = CustomerId.of("default-customer");
438
+ private List<OrderLine> lines = new ArrayList<>();
439
+
440
+ public OrderTestBuilder withId(String id) {
441
+ this.id = OrderId.of(id);
442
+ return this;
443
+ }
444
+
445
+ public OrderTestBuilder withLine(String productId, int qty, int price) {
446
+ lines.add(new OrderLine(ProductId.of(productId), qty, Money.of(price, "USD")));
447
+ return this;
448
+ }
449
+
450
+ public Order build() {
451
+ Order order = Order.create(id, customerId);
452
+ lines.forEach(line -> order.addLine(line.productId(), line.quantity(), line.price()));
453
+ return order;
454
+ }
455
+ }
456
+
457
+ // Usage
458
+ Order order = new OrderTestBuilder()
459
+ .withId("order-123")
460
+ .withLine("prod-1", 2, 100)
461
+ .withLine("prod-2", 1, 50)
462
+ .build();
463
+ ```
464
+
465
+ ### Object Mother Pattern
466
+
467
+ ```java
468
+ public class OrderMother {
469
+
470
+ public static Order pendingOrder() {
471
+ return Order.create(OrderId.generate(), CustomerId.of("customer-1"));
472
+ }
473
+
474
+ public static Order confirmedOrder() {
475
+ Order order = pendingOrder();
476
+ order.confirm();
477
+ return order;
478
+ }
479
+
480
+ public static Order shippedOrder() {
481
+ Order order = confirmedOrder();
482
+ order.ship(TrackingNumber.of("TRACK-123"));
483
+ return order;
484
+ }
485
+ }
486
+ ```
487
+
488
+ ## Slice Tests (Spring Boot)
489
+
490
+ ### Web Layer Tests
491
+
492
+ ```java
493
+ @WebMvcTest(OrderController.class)
494
+ class OrderControllerTest {
495
+
496
+ @Autowired
497
+ private MockMvc mockMvc;
498
+
499
+ @MockBean
500
+ private PlaceOrderUseCase placeOrderUseCase;
501
+
502
+ @MockBean
503
+ private GetOrderUseCase getOrderUseCase;
504
+
505
+ @Test
506
+ void should_Return201_When_OrderCreated() throws Exception {
507
+ when(placeOrderUseCase.execute(any()))
508
+ .thenReturn(OrderId.of("order-123"));
509
+
510
+ mockMvc.perform(post("/api/v1/orders")
511
+ .contentType(MediaType.APPLICATION_JSON)
512
+ .content("{\"customerId\": \"cust-1\"}"))
513
+ .andExpect(status().isCreated());
514
+ }
515
+ }
516
+ ```
517
+
518
+ ### Repository Layer Tests
519
+
520
+ ```java
521
+ @DataJpaTest
522
+ class OrderJpaRepositoryTest {
523
+
524
+ @Autowired
525
+ private TestEntityManager entityManager;
526
+
527
+ @Autowired
528
+ private OrderJpaRepository repository;
529
+
530
+ @Test
531
+ void should_FindOrdersByCustomer() {
532
+ // Arrange
533
+ OrderEntity order = new OrderEntity();
534
+ order.setCustomerId("cust-123");
535
+ entityManager.persist(order);
536
+
537
+ // Act
538
+ List<OrderEntity> found = repository.findByCustomerId("cust-123");
539
+
540
+ // Assert
541
+ assertThat(found).hasSize(1);
542
+ }
543
+ }
544
+ ```
545
+
546
+ ## Best Practices
547
+
548
+ 1. **Test behavior, not implementation**: Focus on what, not how
549
+ 2. **One assertion concept per test**: Keep tests focused
550
+ 3. **Tests should be independent**: No shared state between tests
551
+ 4. **Tests should be fast**: Unit tests < 100ms, integration < 5s
552
+ 5. **Tests should be deterministic**: Same result every time
553
+ 6. **Use meaningful test names**: They serve as documentation
554
+ 7. **Don't test private methods**: Test through public API
555
+ 8. **Mock external dependencies**: Database, APIs, file system
556
+ 9. **Cover edge cases**: Null, empty, boundary values
557
+ 10. **Maintain test code quality**: Same standards as production code
558
+ 11. **Use `*IT.java` suffix for integration tests**: Maven Failsafe detects them automatically
559
+ 12. **Use ArchUnit for architecture validation**: Enforce layer dependencies