-
Notifications
You must be signed in to change notification settings - Fork 180
/
ValidateUnsupportedConstraintsAreNotUsedTest.kt
275 lines (224 loc) · 9.49 KB
/
ValidateUnsupportedConstraintsAreNotUsedTest.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package software.amazon.smithy.rust.codegen.server.smithy
import io.kotest.inspectors.forSome
import io.kotest.inspectors.shouldForAll
import io.kotest.matchers.collections.shouldHaveAtLeastSize
import io.kotest.matchers.collections.shouldHaveSize
import io.kotest.matchers.shouldBe
import io.kotest.matchers.string.shouldContain
import org.junit.jupiter.api.Test
import software.amazon.smithy.model.Model
import software.amazon.smithy.model.shapes.ServiceShape
import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
import software.amazon.smithy.rust.codegen.core.util.lookup
import java.util.logging.Level
internal class ValidateUnsupportedConstraintsAreNotUsedTest {
private val baseModel =
"""
namespace test
service TestService {
version: "123",
operations: [TestOperation]
}
operation TestOperation {
input: TestInputOutput,
output: TestInputOutput,
}
"""
private fun validateModel(model: Model, serverCodegenConfig: ServerCodegenConfig = ServerCodegenConfig()): ValidationResult {
val service = model.lookup<ServiceShape>("test#TestService")
return validateUnsupportedConstraints(model, service, serverCodegenConfig)
}
@Test
fun `it should detect when an operation with constrained input but that does not have ValidationException attached in errors`() {
val model =
"""
$baseModel
structure TestInputOutput {
@required
requiredString: String
}
""".asSmithyModel()
val service = model.lookup<ServiceShape>("test#TestService")
val validationResult = validateOperationsWithConstrainedInputHaveValidationExceptionAttached(model, service)
validationResult.messages shouldHaveSize 1
validationResult.messages[0].message shouldContain "Operation test#TestOperation takes in input that is constrained"
}
@Test
fun `it should detect when unsupported constraint traits on member shapes are used`() {
val model =
"""
$baseModel
structure TestInputOutput {
@length(min: 1, max: 69)
lengthString: String
}
""".asSmithyModel()
val validationResult = validateModel(model)
validationResult.messages shouldHaveSize 1
validationResult.messages[0].message shouldContain "The member shape `test#TestInputOutput\$lengthString` has the constraint trait `smithy.api#length` attached"
}
@Test
fun `it should not detect when the required trait on a member shape is used`() {
val model =
"""
$baseModel
structure TestInputOutput {
@required
string: String
}
""".asSmithyModel()
val validationResult = validateModel(model)
validationResult.messages shouldHaveSize 0
}
private val constraintTraitOnStreamingBlobShapeModel =
"""
$baseModel
structure TestInputOutput {
@required
streamingBlob: StreamingBlob
}
@streaming
@length(min: 69)
blob StreamingBlob
""".asSmithyModel()
@Test
fun `it should detect when constraint traits on streaming blob shapes are used`() {
val validationResult = validateModel(constraintTraitOnStreamingBlobShapeModel)
validationResult.messages shouldHaveSize 2
validationResult.messages.forSome {
it.message shouldContain
"""
The blob shape `test#StreamingBlob` has both the `smithy.api#length` and `smithy.api#streaming` constraint traits attached.
It is unclear what the semantics for streaming blob shapes are.
""".trimIndent().replace("\n", " ")
}
}
@Test
fun `it should detect when constraint traits in event streams are used`() {
val model =
"""
$baseModel
structure TestInputOutput {
eventStream: EventStream
}
@streaming
union EventStream {
message: Message
}
structure Message {
lengthString: LengthString
}
@length(min: 1)
string LengthString
""".asSmithyModel()
val validationResult = validateModel(model)
validationResult.messages shouldHaveSize 1
validationResult.messages[0].message shouldContain
"""
The string shape `test#LengthString` has the constraint trait `smithy.api#length` attached.
This shape is also part of an event stream; it is unclear what the semantics for constrained shapes in event streams are.
""".trimIndent().replace("\n", " ")
}
@Test
fun `it should detect when the length trait on collection shapes or on blob shapes is used`() {
val model =
"""
$baseModel
structure TestInputOutput {
collection: LengthCollection,
blob: LengthBlob
}
@length(min: 1)
list LengthCollection {
member: String
}
@length(min: 1)
blob LengthBlob
""".asSmithyModel()
val validationResult = validateModel(model)
validationResult.messages shouldHaveSize 2
validationResult.messages.forSome { it.message shouldContain "The list shape `test#LengthCollection` has the constraint trait `smithy.api#length` attached" }
validationResult.messages.forSome { it.message shouldContain "The blob shape `test#LengthBlob` has the constraint trait `smithy.api#length` attached" }
}
@Test
fun `it should detect when the pattern trait on string shapes is used`() {
val model =
"""
$baseModel
structure TestInputOutput {
patternString: PatternString
}
@pattern("^[A-Za-z]+$")
string PatternString
""".asSmithyModel()
val validationResult = validateModel(model)
validationResult.messages shouldHaveSize 1
validationResult.messages[0].message shouldContain "The string shape `test#PatternString` has the constraint trait `smithy.api#pattern` attached"
}
@Test
fun `it should detect when the range trait is used`() {
val model =
"""
$baseModel
structure TestInputOutput {
rangeInteger: RangeInteger
}
@range(min: 1)
integer RangeInteger
""".asSmithyModel()
val validationResult = validateModel(model)
validationResult.messages shouldHaveSize 1
validationResult.messages[0].message shouldContain "The integer shape `test#RangeInteger` has the constraint trait `smithy.api#range` attached"
}
@Test
fun `it should detect when the unique items trait is used`() {
val model =
"""
$baseModel
structure TestInputOutput {
uniqueItemsList: UniqueItemsList
}
@uniqueItems
list UniqueItemsList {
member: String
}
""".asSmithyModel()
val validationResult = validateModel(model)
validationResult.messages shouldHaveSize 1
validationResult.messages[0].message shouldContain "The list shape `test#UniqueItemsList` has the constraint trait `smithy.api#uniqueItems` attached"
}
@Test
fun `it should abort when ignoreUnsupportedConstraints is false and unsupported constraints are used`() {
val validationResult = validateModel(constraintTraitOnStreamingBlobShapeModel, ServerCodegenConfig())
validationResult.messages shouldHaveAtLeastSize 1
validationResult.shouldAbort shouldBe true
}
@Test
fun `it should not abort when ignoreUnsupportedConstraints is true and unsupported constraints are used`() {
val validationResult = validateModel(
constraintTraitOnStreamingBlobShapeModel,
ServerCodegenConfig().copy(ignoreUnsupportedConstraints = true),
)
validationResult.messages shouldHaveAtLeastSize 1
validationResult.shouldAbort shouldBe false
}
@Test
fun `it should set log level to error when ignoreUnsupportedConstraints is false and unsupported constraints are used`() {
val validationResult = validateModel(constraintTraitOnStreamingBlobShapeModel, ServerCodegenConfig())
validationResult.messages shouldHaveAtLeastSize 1
validationResult.messages.shouldForAll { it.level shouldBe Level.SEVERE }
}
@Test
fun `it should set log level to warn when ignoreUnsupportedConstraints is true and unsupported constraints are used`() {
val validationResult = validateModel(
constraintTraitOnStreamingBlobShapeModel,
ServerCodegenConfig().copy(ignoreUnsupportedConstraints = true),
)
validationResult.messages shouldHaveAtLeastSize 1
validationResult.messages.shouldForAll { it.level shouldBe Level.WARNING }
}
}