Skip to content

Commit bf56873

Browse files
committed
add tests
1 parent ce62f22 commit bf56873

4 files changed

Lines changed: 314 additions & 1 deletion

File tree

samples/server/petstore/kotlin-springboot-sort-validation/.openapi-generator/FILES

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
.openapi-generator-ignore
21
README.md
32
build.gradle.kts
43
gradle/wrapper/gradle-wrapper.jar
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.openapitools
2+
3+
import org.springframework.boot.autoconfigure.SpringBootApplication
4+
import org.springframework.boot.runApplication
5+
import org.springframework.context.annotation.ComponentScan
6+
7+
@SpringBootApplication
8+
@ComponentScan("org.openapitools")
9+
class Application
10+
11+
fun main(args: Array<String>) {
12+
runApplication<Application>(*args)
13+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package org.openapitools.api
2+
3+
import org.openapitools.model.Pet
4+
import org.springframework.data.domain.Pageable
5+
import org.springframework.data.domain.Sort
6+
import org.springframework.http.ResponseEntity
7+
import org.springframework.stereotype.Component
8+
import org.springframework.web.bind.annotation.RestController
9+
10+
/**
11+
* Sample implementation of [PetApi] demonstrating that the generated
12+
* annotations (@ValidSort, @ValidPageable, @PageableDefault, @SortDefault)
13+
* behave correctly at runtime.
14+
*
15+
* Methods whose endpoint carries pagination/sort defaults assert the exact
16+
* expected values inside the method body. When the Spring argument resolvers
17+
* apply the annotated defaults correctly, the assertions pass and HTTP 200 is
18+
* returned. If a default is missing or wrong, the assertion throws
19+
* [IllegalStateException] and the request fails with HTTP 500, which causes
20+
* any calling test to fail with a clear message.
21+
*
22+
* Methods that only carry @ValidSort / @ValidPageable constraints need no body
23+
* logic — the constraint annotations reject invalid input before this code is
24+
* ever reached, and the [DefaultExceptionHandler] maps the resulting
25+
* [jakarta.validation.ConstraintViolationException] to HTTP 400.
26+
*/
27+
@RestController
28+
class PetApiImpl : PetApi {
29+
30+
// ── no pageable / no special defaults ────────────────────────────────────
31+
32+
override fun findPetsAutoDetectedWithSort(
33+
status: String?,
34+
page: Int,
35+
size: Int,
36+
sort: String?,
37+
): ResponseEntity<List<Pet>> = ResponseEntity.ok(emptyList())
38+
39+
override fun findPetsNonPaginatedWithSortEnum(
40+
sort: String?,
41+
): ResponseEntity<List<Pet>> = ResponseEntity.ok(emptyList())
42+
43+
// ── @ValidSort only (+ @PageableDefault) ─────────────────────────────────
44+
// Validation rejects bad sort values before the method is called.
45+
46+
override fun findPetsWithArraySortEnum(pageable: Pageable): ResponseEntity<List<Pet>> =
47+
ResponseEntity.ok(emptyList())
48+
49+
override fun findPetsWithArraySortRefEnum(pageable: Pageable): ResponseEntity<List<Pet>> =
50+
ResponseEntity.ok(emptyList())
51+
52+
override fun findPetsWithExternalParamRefArraySort(pageable: Pageable): ResponseEntity<List<Pet>> =
53+
ResponseEntity.ok(emptyList())
54+
55+
override fun findPetsWithNonExplodedExternalParamRefArraySort(pageable: Pageable): ResponseEntity<List<Pet>> =
56+
ResponseEntity.ok(emptyList())
57+
58+
override fun findPetsWithRefSort(pageable: Pageable): ResponseEntity<List<Pet>> =
59+
ResponseEntity.ok(emptyList())
60+
61+
override fun findPetsWithSortEnum(status: String?, pageable: Pageable): ResponseEntity<List<Pet>> =
62+
ResponseEntity.ok(emptyList())
63+
64+
override fun findPetsWithoutSortEnum(pageable: Pageable): ResponseEntity<List<Pet>> =
65+
ResponseEntity.ok(emptyList())
66+
67+
// ── @ValidPageable only ───────────────────────────────────────────────────
68+
// Validation rejects out-of-range page / size before the method is called.
69+
70+
override fun findPetsWithSizeConstraint(pageable: Pageable): ResponseEntity<List<Pet>> =
71+
ResponseEntity.ok(emptyList())
72+
73+
override fun findPetsWithPageAndSizeConstraint(pageable: Pageable): ResponseEntity<List<Pet>> =
74+
ResponseEntity.ok(emptyList())
75+
76+
// ── @PageableDefault ─────────────────────────────────────────────────────
77+
// @PageableDefault(page = 0, size = 25)
78+
79+
override fun findPetsWithPageSizeDefaultsOnly(pageable: Pageable): ResponseEntity<List<Pet>> {
80+
check(pageable.pageNumber == 0) { "@PageableDefault page: expected 0, got ${pageable.pageNumber}" }
81+
check(pageable.pageSize == 25) { "@PageableDefault size: expected 25, got ${pageable.pageSize}" }
82+
return ResponseEntity.ok(emptyList())
83+
}
84+
85+
// ── @SortDefault ─────────────────────────────────────────────────────────
86+
// @SortDefault(sort = ["name"], direction = DESC)
87+
88+
override fun findPetsWithSortDefaultOnly(pageable: Pageable): ResponseEntity<List<Pet>> {
89+
check(pageable.sort.getOrderFor("name")?.direction == Sort.Direction.DESC) {
90+
"@SortDefault sort: expected name DESC, got ${pageable.sort}"
91+
}
92+
return ResponseEntity.ok(emptyList())
93+
}
94+
95+
// @SortDefault(sort = ["id"], direction = ASC)
96+
97+
override fun findPetsWithSortDefaultAsc(pageable: Pageable): ResponseEntity<List<Pet>> {
98+
check(pageable.sort.getOrderFor("id")?.direction == Sort.Direction.ASC) {
99+
"@SortDefault sort: expected id ASC, got ${pageable.sort}"
100+
}
101+
return ResponseEntity.ok(emptyList())
102+
}
103+
104+
// ── @SortDefault.SortDefaults ─────────────────────────────────────────────
105+
// @SortDefaults(SortDefault(sort = ["name"], direction = DESC), SortDefault(sort = ["id"], direction = ASC))
106+
107+
override fun findPetsWithMixedSortDefaults(pageable: Pageable): ResponseEntity<List<Pet>> {
108+
check(pageable.sort.getOrderFor("name")?.direction == Sort.Direction.DESC) {
109+
"@SortDefaults sort: expected name DESC, got ${pageable.sort}"
110+
}
111+
check(pageable.sort.getOrderFor("id")?.direction == Sort.Direction.ASC) {
112+
"@SortDefaults sort: expected id ASC, got ${pageable.sort}"
113+
}
114+
return ResponseEntity.ok(emptyList())
115+
}
116+
117+
// ── @PageableDefault + @SortDefault.SortDefaults combined ─────────────────
118+
// @PageableDefault(page = 0, size = 10)
119+
// @SortDefaults(SortDefault(sort = ["name"], direction = DESC), SortDefault(sort = ["id"], direction = ASC))
120+
121+
override fun findPetsWithAllDefaults(pageable: Pageable): ResponseEntity<List<Pet>> {
122+
check(pageable.pageNumber == 0) { "@PageableDefault page: expected 0, got ${pageable.pageNumber}" }
123+
check(pageable.pageSize == 10) { "@PageableDefault size: expected 10, got ${pageable.pageSize}" }
124+
check(pageable.sort.getOrderFor("name")?.direction == Sort.Direction.DESC) {
125+
"@SortDefaults sort: expected name DESC, got ${pageable.sort}"
126+
}
127+
check(pageable.sort.getOrderFor("id")?.direction == Sort.Direction.ASC) {
128+
"@SortDefaults sort: expected id ASC, got ${pageable.sort}"
129+
}
130+
return ResponseEntity.ok(emptyList())
131+
}
132+
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package org.openapitools.api
2+
3+
import org.junit.jupiter.api.Test
4+
import org.springframework.beans.factory.annotation.Autowired
5+
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
6+
import org.springframework.boot.test.context.SpringBootTest
7+
import org.springframework.test.web.servlet.MockMvc
8+
import org.springframework.test.web.servlet.get
9+
10+
/**
11+
* Verifies the runtime behaviour of the annotations generated onto [PetApi]:
12+
*
13+
* - **@ValidSort** — invalid sort field/direction combinations are rejected with 400.
14+
* - **@ValidPageable** — page number or size that exceeds the configured limit is rejected with 400.
15+
* - **@PageableDefault** — when page/size query params are absent, the configured defaults are
16+
* forwarded to the controller method (verified by assertions inside [PetApiImpl]).
17+
* - **@SortDefault / @SortDefaults** — when the sort query param is absent, the configured
18+
* default sort order is forwarded to the controller method (verified inside [PetApiImpl]).
19+
*
20+
* HTTP 200 responses confirm both that the request was accepted *and* that [PetApiImpl]'s
21+
* internal assertions about the received defaults passed.
22+
* HTTP 400 responses confirm that the constraint annotation rejected the invalid input.
23+
*/
24+
@SpringBootTest
25+
@AutoConfigureMockMvc
26+
class PetApiValidationTest {
27+
28+
@Autowired
29+
lateinit var mockMvc: MockMvc
30+
31+
// ── @ValidSort ────────────────────────────────────────────────────────────
32+
// Endpoint: GET /pet/findWithArraySortEnum allowed: id,asc | id,desc | name,asc | name,desc
33+
34+
@Test
35+
fun `ValidSort - valid sort value returns 200`() {
36+
mockMvc.get("${PetApi.BASE_PATH}${PetApi.PATH_FIND_PETS_WITH_ARRAY_SORT_ENUM}") {
37+
param("sort", "id,asc")
38+
}.andExpect { status { isOk() } }
39+
}
40+
41+
@Test
42+
fun `ValidSort - multiple valid sort values return 200`() {
43+
mockMvc.get("${PetApi.BASE_PATH}${PetApi.PATH_FIND_PETS_WITH_ARRAY_SORT_ENUM}") {
44+
param("sort", "id,desc")
45+
param("sort", "name,asc")
46+
}.andExpect { status { isOk() } }
47+
}
48+
49+
@Test
50+
fun `ValidSort - invalid sort property returns 400`() {
51+
mockMvc.get("${PetApi.BASE_PATH}${PetApi.PATH_FIND_PETS_WITH_ARRAY_SORT_ENUM}") {
52+
param("sort", "unknown,asc")
53+
}.andExpect { status { isBadRequest() } }
54+
}
55+
56+
@Test
57+
fun `ValidSort - invalid sort direction returns 400`() {
58+
mockMvc.get("${PetApi.BASE_PATH}${PetApi.PATH_FIND_PETS_WITH_ARRAY_SORT_ENUM}") {
59+
param("sort", "id,random")
60+
}.andExpect { status { isBadRequest() } }
61+
}
62+
63+
@Test
64+
fun `ValidSort - one invalid sort among multiple valid values returns 400`() {
65+
mockMvc.get("${PetApi.BASE_PATH}${PetApi.PATH_FIND_PETS_WITH_ARRAY_SORT_ENUM}") {
66+
param("sort", "id,asc")
67+
param("sort", "unknown,desc")
68+
}.andExpect { status { isBadRequest() } }
69+
}
70+
71+
// ── @ValidPageable — size constraint only ─────────────────────────────────
72+
// Endpoint: GET /pet/findWithSizeConstraint maxSize = 100
73+
74+
@Test
75+
fun `ValidPageable - size below maximum returns 200`() {
76+
mockMvc.get("${PetApi.BASE_PATH}${PetApi.PATH_FIND_PETS_WITH_SIZE_CONSTRAINT}") {
77+
param("size", "50")
78+
}.andExpect { status { isOk() } }
79+
}
80+
81+
@Test
82+
fun `ValidPageable - size at maximum returns 200`() {
83+
mockMvc.get("${PetApi.BASE_PATH}${PetApi.PATH_FIND_PETS_WITH_SIZE_CONSTRAINT}") {
84+
param("size", "100")
85+
}.andExpect { status { isOk() } }
86+
}
87+
88+
@Test
89+
fun `ValidPageable - size exceeds maximum returns 400`() {
90+
mockMvc.get("${PetApi.BASE_PATH}${PetApi.PATH_FIND_PETS_WITH_SIZE_CONSTRAINT}") {
91+
param("size", "101")
92+
}.andExpect { status { isBadRequest() } }
93+
}
94+
95+
// ── @ValidPageable — size and page constraints combined ───────────────────
96+
// Endpoint: GET /pet/findWithPageAndSizeConstraint maxSize = 50, maxPage = 999
97+
98+
@Test
99+
fun `ValidPageable - size and page at their maximums return 200`() {
100+
mockMvc.get("${PetApi.BASE_PATH}${PetApi.PATH_FIND_PETS_WITH_PAGE_AND_SIZE_CONSTRAINT}") {
101+
param("size", "50")
102+
param("page", "999")
103+
}.andExpect { status { isOk() } }
104+
}
105+
106+
@Test
107+
fun `ValidPageable - size exceeds maximum for combined constraint returns 400`() {
108+
mockMvc.get("${PetApi.BASE_PATH}${PetApi.PATH_FIND_PETS_WITH_PAGE_AND_SIZE_CONSTRAINT}") {
109+
param("size", "51")
110+
}.andExpect { status { isBadRequest() } }
111+
}
112+
113+
@Test
114+
fun `ValidPageable - page exceeds maximum returns 400`() {
115+
mockMvc.get("${PetApi.BASE_PATH}${PetApi.PATH_FIND_PETS_WITH_PAGE_AND_SIZE_CONSTRAINT}") {
116+
param("page", "1000")
117+
}.andExpect { status { isBadRequest() } }
118+
}
119+
120+
// ── @PageableDefault ─────────────────────────────────────────────────────
121+
// Endpoint: GET /pet/findWithPageSizeDefaultsOnly @PageableDefault(page = 0, size = 25)
122+
// PetApiImpl asserts page == 0 and size == 25; returns 200 on success, throws on mismatch.
123+
124+
@Test
125+
fun `PageableDefault - absent params resolve to configured page and size defaults`() {
126+
mockMvc.get("${PetApi.BASE_PATH}${PetApi.PATH_FIND_PETS_WITH_PAGE_SIZE_DEFAULTS_ONLY}")
127+
.andExpect { status { isOk() } }
128+
}
129+
130+
// ── @SortDefault ─────────────────────────────────────────────────────────
131+
// Endpoint: GET /pet/findWithSortDefaultOnly @SortDefault(sort = ["name"], direction = DESC)
132+
// PetApiImpl asserts name DESC; returns 200 on success, throws on mismatch.
133+
134+
@Test
135+
fun `SortDefault - absent sort param resolves to name DESC default`() {
136+
mockMvc.get("${PetApi.BASE_PATH}${PetApi.PATH_FIND_PETS_WITH_SORT_DEFAULT_ONLY}")
137+
.andExpect { status { isOk() } }
138+
}
139+
140+
// Endpoint: GET /pet/findWithSortDefaultAsc @SortDefault(sort = ["id"], direction = ASC)
141+
142+
@Test
143+
fun `SortDefault - absent sort param resolves to id ASC default`() {
144+
mockMvc.get("${PetApi.BASE_PATH}${PetApi.PATH_FIND_PETS_WITH_SORT_DEFAULT_ASC}")
145+
.andExpect { status { isOk() } }
146+
}
147+
148+
// ── @SortDefault.SortDefaults ─────────────────────────────────────────────
149+
// Endpoint: GET /pet/findWithMixedSortDefaults
150+
// @SortDefaults(SortDefault(["name"], DESC), SortDefault(["id"], ASC))
151+
// PetApiImpl asserts both orders; returns 200 on success, throws on mismatch.
152+
153+
@Test
154+
fun `SortDefaults - absent sort param resolves all configured sort defaults`() {
155+
mockMvc.get("${PetApi.BASE_PATH}${PetApi.PATH_FIND_PETS_WITH_MIXED_SORT_DEFAULTS}")
156+
.andExpect { status { isOk() } }
157+
}
158+
159+
// ── @PageableDefault + @SortDefault.SortDefaults combined ─────────────────
160+
// Endpoint: GET /pet/findWithAllDefaults
161+
// @PageableDefault(page = 0, size = 10) + @SortDefaults(name DESC, id ASC)
162+
// PetApiImpl asserts page, size, and both sort orders.
163+
164+
@Test
165+
fun `PageableDefault and SortDefaults combined - absent params resolve all defaults`() {
166+
mockMvc.get("${PetApi.BASE_PATH}${PetApi.PATH_FIND_PETS_WITH_ALL_DEFAULTS}")
167+
.andExpect { status { isOk() } }
168+
}
169+
}

0 commit comments

Comments
 (0)