Skip to content

Commit 10fb59e

Browse files
author
Fahad Zubair
committed
Test on all JSON supported protocols
1 parent 8a00258 commit 10fb59e

File tree

6 files changed

+211
-99
lines changed

6 files changed

+211
-99
lines changed

codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/CodegenIntegrationTest.kt

+134
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,19 @@
55

66
package software.amazon.smithy.rust.codegen.core.testutil
77

8+
import software.amazon.smithy.aws.traits.protocols.AwsJson1_0Trait
9+
import software.amazon.smithy.aws.traits.protocols.AwsJson1_1Trait
10+
import software.amazon.smithy.aws.traits.protocols.RestJson1Trait
11+
import software.amazon.smithy.aws.traits.protocols.RestXmlTrait
812
import software.amazon.smithy.build.PluginContext
913
import software.amazon.smithy.model.Model
1014
import software.amazon.smithy.model.node.ObjectNode
1115
import software.amazon.smithy.model.node.ToNode
16+
import software.amazon.smithy.model.shapes.ServiceShape
17+
import software.amazon.smithy.model.shapes.ShapeId
18+
import software.amazon.smithy.model.traits.AbstractTrait
19+
import software.amazon.smithy.model.transform.ModelTransformer
20+
import software.amazon.smithy.protocol.traits.Rpcv2CborTrait
1221
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig
1322
import software.amazon.smithy.rust.codegen.core.util.runCommand
1423
import java.io.File
@@ -153,3 +162,128 @@ fun codegenIntegrationTest(
153162
logger.fine(out.toString())
154163
return testDir
155164
}
165+
166+
/**
167+
* Metadata associated with a protocol that provides additional information needed for testing.
168+
*
169+
* @property protocol The protocol enum value this metadata is associated with
170+
* @property contentType The HTTP Content-Type header value associated with this protocol.
171+
*/
172+
data class ProtocolMetadata(
173+
val protocol: ModelProtocol,
174+
val contentType: String,
175+
)
176+
177+
/**
178+
* Represents the supported protocol traits in Smithy models.
179+
*
180+
* @property trait The Smithy trait instance with which the service shape must be annotated.
181+
*/
182+
enum class ModelProtocol(val trait: AbstractTrait) {
183+
AwsJson10(AwsJson1_0Trait.builder().build()),
184+
AwsJson11(AwsJson1_1Trait.builder().build()),
185+
RestJson(RestJson1Trait.builder().build()),
186+
RestXml(RestXmlTrait.builder().build()),
187+
Rpcv2Cbor(Rpcv2CborTrait.builder().build()),
188+
;
189+
190+
// Create metadata after enum is initialized
191+
val metadata: ProtocolMetadata by lazy {
192+
when (this) {
193+
AwsJson10 -> ProtocolMetadata(this, "application/x-amz-json-1.0")
194+
AwsJson11 -> ProtocolMetadata(this, "application/x-amz-json-1.1")
195+
RestJson -> ProtocolMetadata(this, "application/json")
196+
RestXml -> ProtocolMetadata(this, "application/xml")
197+
Rpcv2Cbor -> ProtocolMetadata(this, "application/cbor")
198+
}
199+
}
200+
201+
companion object {
202+
private val TRAIT_IDS = values().map { it.trait.toShapeId() }.toSet()
203+
val ALL: Set<ModelProtocol> = values().toSet()
204+
205+
fun getTraitIds() = TRAIT_IDS
206+
}
207+
}
208+
209+
/**
210+
* Removes all existing protocol traits annotated on the given service,
211+
* then sets the provided `protocol` as the sole protocol trait for the service.
212+
*/
213+
fun Model.replaceProtocolTraitOnServerShapeId(
214+
serviceShapeId: ShapeId,
215+
modelProtocol: ModelProtocol,
216+
): Model {
217+
val serviceShape = this.expectShape(serviceShapeId, ServiceShape::class.java)
218+
return replaceProtocolTraitOnServiceShape(serviceShape, modelProtocol)
219+
}
220+
221+
/**
222+
* Removes all existing protocol traits annotated on the given service shape,
223+
* then sets the provided `protocol` as the sole protocol trait for the service.
224+
*/
225+
fun Model.replaceProtocolTraitOnServiceShape(
226+
serviceShape: ServiceShape,
227+
modelProtocol: ModelProtocol,
228+
): Model {
229+
val serviceBuilder = serviceShape.toBuilder()
230+
ModelProtocol.getTraitIds().forEach { traitId ->
231+
serviceBuilder.removeTrait(traitId)
232+
}
233+
val service = serviceBuilder.addTrait(modelProtocol.trait).build()
234+
return ModelTransformer.create().replaceShapes(this, listOf(service))
235+
}
236+
237+
/**
238+
* Processes a Smithy model string by applying different protocol traits and invoking the tests block on the model.
239+
* For each protocol, this function:
240+
* 1. Parses the Smithy model string
241+
* 2. Replaces any existing protocol traits on service shapes with the specified protocol
242+
* 3. Runs the provided test with the transformed model and protocol metadata
243+
*
244+
* @param protocolTraitIds Set of protocols to test against
245+
* @param test Function that receives the transformed model and protocol metadata for testing
246+
*/
247+
fun String.forProtocols(
248+
protocolTraitIds: Set<ModelProtocol>,
249+
test: (Model, ProtocolMetadata) -> Unit,
250+
) {
251+
val baseModel = this.asSmithyModel(smithyVersion = "2")
252+
val serviceShapes = baseModel.serviceShapes.toList()
253+
254+
protocolTraitIds.forEach { protocol ->
255+
val transformedModel =
256+
serviceShapes.fold(baseModel) { acc, shape ->
257+
acc.replaceProtocolTraitOnServiceShape(shape, protocol)
258+
}
259+
test(transformedModel, protocol.metadata)
260+
}
261+
}
262+
263+
/**
264+
* Convenience overload that accepts vararg protocols instead of a Set.
265+
*
266+
* @param protocols Variable number of protocols to test against
267+
* @param test Function that receives the transformed model and protocol metadata for testing
268+
* @see forProtocols
269+
*/
270+
fun String.forProtocols(
271+
vararg protocols: ModelProtocol,
272+
test: (Model, ProtocolMetadata) -> Unit,
273+
) {
274+
forProtocols(protocols.toSet(), test)
275+
}
276+
277+
/**
278+
* Tests a Smithy model string against all supported protocols, with optional exclusions.
279+
*
280+
* @param exclude Set of protocols to exclude from testing (default is empty)
281+
* @param test Function that receives the transformed model and protocol metadata for testing
282+
* @see forProtocols
283+
*/
284+
fun String.forAllProtocols(
285+
exclude: Set<ModelProtocol> = emptySet(),
286+
test: (Model, ProtocolMetadata) -> Unit,
287+
) {
288+
forProtocols(ModelProtocol.ALL - exclude, test)
289+
}

codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt

+1-2
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ fun jsonParserGenerator(
134134
codegenContext: ServerCodegenContext,
135135
httpBindingResolver: HttpBindingResolver,
136136
jsonName: (MemberShape) -> String,
137-
additionalParserCustomizations: List<JsonParserCustomization> = listOf()
137+
additionalParserCustomizations: List<JsonParserCustomization> = listOf(),
138138
): JsonParserGenerator =
139139
JsonParserGenerator(
140140
codegenContext,
@@ -152,7 +152,6 @@ fun jsonParserGenerator(
152152
} else {
153153
RuntimeType.smithyJson(codegenContext.runtimeConfig)
154154
},
155-
,
156155
)
157156

158157
class ServerAwsJsonProtocol(

codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstraintsTest.kt

+3-32
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@ import io.kotest.inspectors.forAll
99
import io.kotest.matchers.ints.shouldBeGreaterThan
1010
import io.kotest.matchers.shouldBe
1111
import org.junit.jupiter.api.Test
12-
import software.amazon.smithy.aws.traits.protocols.AwsJson1_0Trait
13-
import software.amazon.smithy.aws.traits.protocols.AwsJson1_1Trait
14-
import software.amazon.smithy.aws.traits.protocols.RestJson1Trait
15-
import software.amazon.smithy.aws.traits.protocols.RestXmlTrait
1612
import software.amazon.smithy.model.Model
1713
import software.amazon.smithy.model.shapes.BooleanShape
1814
import software.amazon.smithy.model.shapes.ListShape
@@ -22,35 +18,27 @@ import software.amazon.smithy.model.shapes.ServiceShape
2218
import software.amazon.smithy.model.shapes.ShapeId
2319
import software.amazon.smithy.model.shapes.StringShape
2420
import software.amazon.smithy.model.shapes.StructureShape
25-
import software.amazon.smithy.model.traits.AbstractTrait
2621
import software.amazon.smithy.model.transform.ModelTransformer
27-
import software.amazon.smithy.protocol.traits.Rpcv2CborTrait
2822
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
2923
import software.amazon.smithy.rust.codegen.core.smithy.RustCrate
3024
import software.amazon.smithy.rust.codegen.core.testutil.IntegrationTestParams
25+
import software.amazon.smithy.rust.codegen.core.testutil.ModelProtocol
3126
import software.amazon.smithy.rust.codegen.core.testutil.ServerAdditionalSettings
3227
import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
28+
import software.amazon.smithy.rust.codegen.core.testutil.replaceProtocolTraitOnServerShapeId
3329
import software.amazon.smithy.rust.codegen.core.testutil.unitTest
3430
import software.amazon.smithy.rust.codegen.core.util.lookup
3531
import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverIntegrationTest
3632
import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymbolProvider
3733
import java.io.File
3834

39-
enum class ModelProtocol(val trait: AbstractTrait) {
40-
AwsJson10(AwsJson1_0Trait.builder().build()),
41-
AwsJson11(AwsJson1_1Trait.builder().build()),
42-
RestJson(RestJson1Trait.builder().build()),
43-
RestXml(RestXmlTrait.builder().build()),
44-
Rpcv2Cbor(Rpcv2CborTrait.builder().build()),
45-
}
46-
4735
/**
4836
* Returns the Smithy constraints model from the common repository, with the specified protocol
4937
* applied to the service.
5038
*/
5139
fun loadSmithyConstraintsModelForProtocol(modelProtocol: ModelProtocol): Pair<Model, ShapeId> {
5240
val (model, serviceShapeId) = loadSmithyConstraintsModel()
53-
return Pair(model.replaceProtocolTrait(serviceShapeId, modelProtocol), serviceShapeId)
41+
return Pair(model.replaceProtocolTraitOnServerShapeId(serviceShapeId, modelProtocol), serviceShapeId)
5442
}
5543

5644
/**
@@ -65,23 +53,6 @@ fun loadSmithyConstraintsModel(): Pair<Model, ShapeId> {
6553
return Pair(model, serviceShapeId)
6654
}
6755

68-
/**
69-
* Removes all existing protocol traits annotated on the given service,
70-
* then sets the provided `protocol` as the sole protocol trait for the service.
71-
*/
72-
fun Model.replaceProtocolTrait(
73-
serviceShapeId: ShapeId,
74-
modelProtocol: ModelProtocol,
75-
): Model {
76-
val serviceBuilder =
77-
this.expectShape(serviceShapeId, ServiceShape::class.java).toBuilder()
78-
for (p in ModelProtocol.values()) {
79-
serviceBuilder.removeTrait(p.trait.toShapeId())
80-
}
81-
val service = serviceBuilder.addTrait(modelProtocol.trait).build()
82-
return ModelTransformer.create().replaceShapes(this, listOf(service))
83-
}
84-
8556
fun List<ShapeId>.containsAnyShapeId(ids: Collection<ShapeId>): Boolean {
8657
return ids.any { id -> this.any { shape -> shape == id } }
8758
}

0 commit comments

Comments
 (0)