1+
2+ package test .org .springdoc .api .v30 .app11 ;
3+
4+ import org .junit .jupiter .api .Test ;
5+ import org .springdoc .core .utils .Constants ;
6+ import org .springframework .boot .autoconfigure .SpringBootApplication ;
7+ import org .springframework .boot .test .context .SpringBootTest ;
8+ import org .springframework .context .annotation .ComponentScan ;
9+ import org .springframework .test .context .TestPropertySource ;
10+ import org .springframework .test .web .servlet .MvcResult ;
11+ import test .org .springdoc .api .v30 .AbstractSpringDocTest ;
12+
13+ import static org .hamcrest .Matchers .is ;
14+ import static org .skyscreamer .jsonassert .JSONAssert .assertEquals ;
15+ import static org .springframework .test .web .servlet .request .MockMvcRequestBuilders .get ;
16+ import static org .springframework .test .web .servlet .result .MockMvcResultMatchers .jsonPath ;
17+ import static org .springframework .test .web .servlet .result .MockMvcResultMatchers .status ;
18+
19+ /**
20+ * Test for issue #3161: Wrong HAL _links are generated in sub type of a schema
21+ * Verifies that _links field is not duplicated in extended schemas using allOf composition
22+ * Tests OpenAPI 3.0.1 spec compliance
23+ */
24+ @ SpringBootTest
25+ @ TestPropertySource (properties = { "springdoc.api-docs.version=openapi_3_0" })
26+ public class SpringDocApp11Test extends AbstractSpringDocTest {
27+
28+ /**
29+ * Integration test: Validates the entire OpenAPI specification JSON against the expected schema.
30+ *
31+ * This is the main integration test that ensures:
32+ * 1. The OpenAPI version is correctly set to 3.0.1
33+ * 2. The generated OpenAPI document matches the expected JSON structure exactly
34+ * 3. All schema definitions, paths, and components are correct
35+ * 4. Issue #3161 is resolved (no duplicate _links in child schemas)
36+ *
37+ * The test compares the actual HTTP response from /v3/api-docs endpoint with
38+ * the expected specification stored in results/3.0.1/app11.json file.
39+ *
40+ * @throws Exception if the test fails or HTTP request encounters an error
41+ */
42+ @ Test
43+ public void testApp () throws Exception {
44+ // Extract test number from class name (11 from SpringDocApp11Test)
45+ String className = getClass ().getSimpleName ();
46+ String testNumber = className .replaceAll ("[^0-9]" , "" );
47+
48+ // Perform GET request to OpenAPI documentation endpoint
49+ MvcResult mockMvcResult = mockMvc .perform (get (Constants .DEFAULT_API_DOCS_URL ))
50+ // Verify HTTP status is 200 OK
51+ .andExpect (status ().isOk ())
52+ // Verify OpenAPI version is 3.0.1
53+ .andExpect (jsonPath ("$.openapi" , is ("3.0.1" )))
54+ .andReturn ();
55+
56+ // Get the actual generated JSON response
57+ String result = mockMvcResult .getResponse ().getContentAsString ();
58+ // Load the expected JSON specification from classpath resource
59+ String expected = getContent ("results/3.0.1/app" + testNumber + ".json" );
60+
61+ // Compare expected and actual JSON in lenient mode (true parameter)
62+ // Lenient mode allows flexibility in JSON comparison (e.g., field order independence)
63+ try {
64+ assertEquals (expected , result , true );
65+ } catch (AssertionError e ) {
66+ // Log detailed comparison results for debugging purposes
67+ System .out .println ("Expected: " + expected );
68+ System .out .println ("Actual: " + result );
69+ throw e ;
70+ }
71+ }
72+
73+ /**
74+ * Unit test: Verifies that the parent TestDto includes the _links property from RepresentationModel.
75+ *
76+ * This test ensures that:
77+ * 1. TestDto correctly extends RepresentationModel
78+ * 2. The _links field is automatically included in the OpenAPI schema
79+ * 3. HATEOAS links support is properly recognized and documented
80+ *
81+ * The _links field is essential for REST API clients to navigate between resources
82+ * using HATEOAS (Hypermedia As The Engine Of Application State) principles.
83+ *
84+ * @throws Exception if the test fails or HTTP request encounters an error
85+ */
86+ @ Test
87+ public void testTestDtoHasHateoasLinks () throws Exception {
88+ // Perform GET request to OpenAPI documentation endpoint
89+ mockMvc .perform (get (Constants .DEFAULT_API_DOCS_URL ))
90+ // Verify HTTP status is 200 OK
91+ .andExpect (status ().isOk ())
92+ // Verify that _links property exists in TestDto schema
93+ // Path: $.components.schemas.TestDto.properties._links
94+ .andExpect (jsonPath ("$.components.schemas.TestDto.properties._links" ).exists ())
95+ .andReturn ();
96+ }
97+
98+ /**
99+ * Unit test: Verifies that ExtendedTestDto correctly uses allOf composition to inherit from TestDto.
100+ *
101+ * This test validates the OpenAPI schema composition pattern:
102+ * 1. ExtendedTestDto uses allOf keyword for schema composition
103+ * 2. The first allOf item is a $ref pointing to the parent TestDto
104+ * 3. The second allOf item contains ExtendedTestDto's own properties
105+ *
106+ * The allOf pattern ensures proper inheritance in OpenAPI where child schemas
107+ * automatically inherit all properties from parent schemas without duplication.
108+ *
109+ * @throws Exception if the test fails or HTTP request encounters an error
110+ */
111+ @ Test
112+ public void testExtendedTestDtoAllOfInheritance () throws Exception {
113+ // Perform GET request to OpenAPI documentation endpoint
114+ mockMvc .perform (get (Constants .DEFAULT_API_DOCS_URL ))
115+ // Verify HTTP status is 200 OK
116+ .andExpect (status ().isOk ())
117+ // Verify that allOf array exists in ExtendedTestDto schema
118+ .andExpect (jsonPath ("$.components.schemas.ExtendedTestDto.allOf" ).exists ())
119+ // Verify that the first allOf item references the parent TestDto
120+ // Path: $.components.schemas.ExtendedTestDto.allOf[0].$ref
121+ .andExpect (jsonPath ("$.components.schemas.ExtendedTestDto.allOf[0].$ref" )
122+ .value ("#/components/schemas/TestDto" ))
123+ .andReturn ();
124+ }
125+
126+ /**
127+ * Critical test: Verifies that ExtendedTestDto does NOT have duplicate _links in its own properties.
128+ *
129+ * This test is the core validation for issue #3161:
130+ * "Wrong HAL _links are generated in sub type of a schema"
131+ *
132+ * The problem was that child schemas incorrectly duplicated the _links field
133+ * even though it was already inherited from the parent schema via allOf.
134+ *
135+ * This test confirms:
136+ * 1. ExtendedTestDto exists in the schema definitions
137+ * 2. ExtendedTestDto uses allOf composition (does not duplicate parent properties)
138+ * 3. The _links field is inherited from TestDto, not redefined in ExtendedTestDto
139+ *
140+ * @throws Exception if the test fails or HTTP request encounters an error
141+ */
142+ @ Test
143+ public void testExtendedTestDtoNoLinksInOwnProperties () throws Exception {
144+ // Perform GET request to OpenAPI documentation endpoint
145+ MvcResult result = mockMvc .perform (get (Constants .DEFAULT_API_DOCS_URL ))
146+ // Verify HTTP status is 200 OK
147+ .andExpect (status ().isOk ())
148+ .andReturn ();
149+
150+ // Get the response body as JSON string for content-based assertions
151+ String content = result .getResponse ().getContentAsString ();
152+
153+ // Verify that ExtendedTestDto schema is defined in components
154+ assert (content .contains ("\" ExtendedTestDto\" " )) : "ExtendedTestDto not found in schema" ;
155+
156+ // Verify that ExtendedTestDto uses allOf composition pattern
157+ assert (content .contains ("\" allOf\" " )) : "allOf not found in ExtendedTestDto" ;
158+
159+ // Note: Full validation of _links absence is performed by testApp()
160+ // which compares the complete JSON structure with the expected specification
161+ }
162+
163+ /**
164+ * Unit test: Verifies that ExtendedTestDto contains its own unique properties.
165+ *
166+ * This test ensures that:
167+ * 1. ExtendedTestDto defines its own properties in the second allOf item
168+ * 2. The child-specific property "otherField" is correctly included
169+ * 3. Properties are nested in allOf[1].properties structure
170+ *
171+ * The structure should be:
172+ * ExtendedTestDto {
173+ * allOf: [
174+ * { $ref: "#/components/schemas/TestDto" }, // allOf[0] - parent
175+ * {
176+ * type: "object",
177+ * properties: {
178+ * otherField: { ... } // allOf[1].properties.otherField
179+ * }
180+ * }
181+ * ]
182+ * }
183+ *
184+ * @throws Exception if the test fails or HTTP request encounters an error
185+ */
186+ @ Test
187+ public void testExtendedTestDtoHasOwnProperties () throws Exception {
188+ // Perform GET request to OpenAPI documentation endpoint
189+ mockMvc .perform (get (Constants .DEFAULT_API_DOCS_URL ))
190+ // Verify HTTP status is 200 OK
191+ .andExpect (status ().isOk ())
192+ // Verify that otherField property exists in the second allOf item
193+ // Path: $.components.schemas.ExtendedTestDto.allOf[1].properties.otherField
194+ .andExpect (jsonPath ("$.components.schemas.ExtendedTestDto.allOf[1].properties.otherField" ).exists ())
195+ .andReturn ();
196+ }
197+
198+ /**
199+ * Spring Boot test configuration class.
200+ *
201+ * This inner static class configures the embedded Spring context for testing:
202+ * 1. @SpringBootApplication enables auto-configuration and component scanning
203+ * 2. @ComponentScan explicitly specifies the base package for component discovery
204+ *
205+ * The ComponentScan ensures that HateoasController and other components
206+ * in the test.org.springdoc.api.v30.app11 package are properly registered
207+ * in the Spring context and available for the integration tests.
208+ */
209+ @ SpringBootApplication
210+ @ ComponentScan (basePackages = "test.org.springdoc.api.v30.app11" )
211+ static class SpringDocTestApp {
212+ }
213+ }
0 commit comments