Skip to content

Commit ee81575

Browse files
committed
test: add SwaggerResponseCustomizer unit test, extend CustomerControllerIT with extra scenarios, and add CustomerServiceApplicationIT for context load
1 parent 5eb7223 commit ee81575

File tree

3 files changed

+115
-0
lines changed

3 files changed

+115
-0
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package io.github.bsayli.customerservice;
2+
3+
import org.junit.jupiter.api.DisplayName;
4+
import org.junit.jupiter.api.Tag;
5+
import org.junit.jupiter.api.Test;
6+
import org.springframework.boot.test.context.SpringBootTest;
7+
8+
@SpringBootTest
9+
@Tag("integration")
10+
@DisplayName("Integration Test: Application Context")
11+
class CustomerServiceApplicationIT {
12+
13+
@Test
14+
@DisplayName("Spring context should load without issues")
15+
void contextLoads() {
16+
// If the context fails to start, this test will fail
17+
}
18+
}

customer-service/src/test/java/io/github/bsayli/customerservice/api/controller/CustomerControllerIT.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616
import io.github.bsayli.customerservice.testconfig.TestControllerMocksConfig;
1717
import java.util.List;
1818
import java.util.NoSuchElementException;
19+
import org.junit.jupiter.api.AfterEach;
1920
import org.junit.jupiter.api.DisplayName;
2021
import org.junit.jupiter.api.Tag;
2122
import org.junit.jupiter.api.Test;
23+
import org.mockito.Mockito;
2224
import org.springframework.beans.factory.annotation.Autowired;
2325
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
2426
import org.springframework.context.annotation.Import;
@@ -161,4 +163,50 @@ void deleteCustomer_ok200() throws Exception {
161163
.andExpect(jsonPath("$.data.customerId").value(1))
162164
.andExpect(jsonPath("$.data.deletedAt").exists());
163165
}
166+
167+
@Test
168+
@DisplayName("POST /v1/customers -> 400 Bad Request when JSON is malformed")
169+
void createCustomer_badJson_notReadable() throws Exception {
170+
var malformed = "{ \"name\": \"John\", \"email\": }"; // invalid JSON
171+
172+
mvc.perform(
173+
post("/v1/customers")
174+
.contentType(MediaType.APPLICATION_JSON)
175+
.content(malformed))
176+
.andExpect(status().isBadRequest())
177+
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
178+
.andExpect(jsonPath("$.message").value("BAD_REQUEST"))
179+
.andExpect(jsonPath("$.data.code").value("VALIDATION_FAILED"))
180+
.andExpect(jsonPath("$.data.violations").isArray());
181+
}
182+
183+
@Test
184+
@DisplayName("GET /v1/customers/{id} -> 400 Bad Request on @Min violation (id=0)")
185+
void getCustomer_constraintViolation_min() throws Exception {
186+
mvc.perform(get("/v1/customers/{id}", 0)) // @Min(1) violated
187+
.andExpect(status().isBadRequest())
188+
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
189+
.andExpect(jsonPath("$.message").value("BAD_REQUEST"))
190+
.andExpect(jsonPath("$.data.code").value("VALIDATION_FAILED"))
191+
.andExpect(jsonPath("$.data.violations").isArray())
192+
.andExpect(jsonPath("$.data.violations[0].message").exists());
193+
}
194+
195+
@Test
196+
@DisplayName("GET /v1/customers/{id} -> 500 Internal Server Error handled by advice")
197+
void getCustomer_internalServerError_generic() throws Exception {
198+
when(customerService.getCustomer(1)).thenThrow(new RuntimeException("Boom"));
199+
200+
mvc.perform(get("/v1/customers/{id}", 1))
201+
.andExpect(status().isInternalServerError())
202+
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
203+
.andExpect(jsonPath("$.message").value("INTERNAL_ERROR"))
204+
.andExpect(jsonPath("$.data.code").value("INTERNAL_ERROR"))
205+
.andExpect(jsonPath("$.data.message").value("Boom"));
206+
}
207+
208+
@AfterEach
209+
void resetMocks() {
210+
Mockito.reset(customerService);
211+
}
164212
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package io.github.bsayli.customerservice.common.openapi;
2+
3+
import static org.junit.jupiter.api.Assertions.*;
4+
5+
import io.swagger.v3.oas.models.Components;
6+
import io.swagger.v3.oas.models.OpenAPI;
7+
import io.swagger.v3.oas.models.media.Schema;
8+
import java.util.HashMap;
9+
import java.util.Map;
10+
import org.junit.jupiter.api.DisplayName;
11+
import org.junit.jupiter.api.Tag;
12+
import org.junit.jupiter.api.Test;
13+
import org.springdoc.core.customizers.OpenApiCustomizer;
14+
15+
@Tag("unit")
16+
@DisplayName("Unit Test: SwaggerResponseCustomizer")
17+
class SwaggerResponseCustomizerTest {
18+
19+
private final SwaggerResponseCustomizer config = new SwaggerResponseCustomizer();
20+
private final OpenApiCustomizer customizer = config.responseEnvelopeSchemas();
21+
22+
@Test
23+
@DisplayName("Should add missing schemas when absent")
24+
void shouldAddSchemasWhenAbsent() {
25+
OpenAPI openAPI = new OpenAPI().components(new Components().schemas(new HashMap<>()));
26+
27+
customizer.customise(openAPI);
28+
29+
@SuppressWarnings("rawtypes")
30+
Map<String, Schema> schemas = openAPI.getComponents().getSchemas();
31+
assertNotNull(schemas.get(OpenApiSchemas.SCHEMA_SERVICE_RESPONSE));
32+
assertNotNull(schemas.get(OpenApiSchemas.SCHEMA_SERVICE_RESPONSE_VOID));
33+
}
34+
35+
@Test
36+
@DisplayName("Should not override existing schemas")
37+
void shouldNotOverrideExistingSchemas() {
38+
Schema<?> existing = new Schema<>().description("pre-existing");
39+
Components components = new Components().schemas(new HashMap<>());
40+
components.addSchemas(OpenApiSchemas.SCHEMA_SERVICE_RESPONSE, existing);
41+
OpenAPI openAPI = new OpenAPI().components(components);
42+
43+
customizer.customise(openAPI);
44+
45+
Schema<?> result =
46+
openAPI.getComponents().getSchemas().get(OpenApiSchemas.SCHEMA_SERVICE_RESPONSE);
47+
assertSame(existing, result, "Existing schema should remain unchanged");
48+
}
49+
}

0 commit comments

Comments
 (0)