diff --git a/samples/standalone/kotlin/http-server/src/test/resources/contracts/multipart/shouldWorkForMultipartAsBytesWithContentType.kts b/samples/standalone/kotlin/http-server/src/test/resources/contracts/multipart/shouldWorkForMultipartAsBytesWithContentType.kts index a1fc08b242..160e42e09f 100644 --- a/samples/standalone/kotlin/http-server/src/test/resources/contracts/multipart/shouldWorkForMultipartAsBytesWithContentType.kts +++ b/samples/standalone/kotlin/http-server/src/test/resources/contracts/multipart/shouldWorkForMultipartAsBytesWithContentType.kts @@ -23,18 +23,18 @@ contract { request { method = POST url = url("/tests") - multipart = multipart(mapOf( - "file1" to named( - value(consumer(regex(nonEmpty())), producer("filename1")), - value(consumer(regex(nonEmpty())), producer("content1"))), - "file2" to named( - value(consumer(regex(nonEmpty())), producer("filename1")), - value(consumer(regex(nonEmpty())), producer("content1"))), - "test" to named( - value(consumer(regex(nonEmpty())), producer("filename1")), - value(consumer(regex(nonEmpty())), producer(fileAsBytes("test.json"))), - value("application/json")) - )) + multipart { + field("file1", named( + value(consumer(regex(nonEmpty())), producer("filename1")), + value(consumer(regex(nonEmpty())), producer("content1")))) + field("file2", named( + value(consumer(regex(nonEmpty())), producer("filename2")), + value(consumer(regex(nonEmpty())), producer("content2")))) + field("test", named( + value(consumer(regex(nonEmpty())), producer("filename3")), + value(consumer(regex(nonEmpty())), producer(fileAsBytes("test.json"))), + value("application/json"))) + } headers { contentType = "multipart/form-data" } diff --git a/samples/standalone/kotlin/http-server/src/test/resources/contracts/multipart/shouldWorkForMultipartWithContentType.kts b/samples/standalone/kotlin/http-server/src/test/resources/contracts/multipart/shouldWorkForMultipartWithContentType.kts index 25621a7a8b..479844934d 100644 --- a/samples/standalone/kotlin/http-server/src/test/resources/contracts/multipart/shouldWorkForMultipartWithContentType.kts +++ b/samples/standalone/kotlin/http-server/src/test/resources/contracts/multipart/shouldWorkForMultipartWithContentType.kts @@ -23,18 +23,18 @@ contract { request { method = POST url = url("/tests") - multipart = multipart(mapOf( - "file1" to named( - value(consumer(regex(nonEmpty())), producer("filename1")), - value(consumer(regex(nonEmpty())), producer("content1"))), - "file2" to named( - value(consumer(regex(nonEmpty())), producer("filename1")), - value(consumer(regex(nonEmpty())), producer("content2"))), - "test" to named( - value(consumer(regex(nonEmpty())), producer("filename1")), - value(consumer(regex(nonEmpty())), producer(file("test.json"))), - value("application/json")) - )) + multipart { + field("file1", named( + value(consumer(regex(nonEmpty())), producer("filename1")), + value(consumer(regex(nonEmpty())), producer("content1")))) + field("file2", named( + value(consumer(regex(nonEmpty())), producer("filename2")), + value(consumer(regex(nonEmpty())), producer("content2")))) + field("test", named( + value(consumer(regex(nonEmpty())), producer("filename3")), + value(consumer(regex(nonEmpty())), producer(file("test.json"))), + value("application/json"))) + } headers { contentType = "multipart/form-data" } diff --git a/specs/spring-cloud-contract-spec-kotlin/src/main/kotlin/org/springframework/cloud/contract/spec/internal/MultipartDsl.kt b/specs/spring-cloud-contract-spec-kotlin/src/main/kotlin/org/springframework/cloud/contract/spec/internal/MultipartDsl.kt new file mode 100644 index 0000000000..f1c4f3d486 --- /dev/null +++ b/specs/spring-cloud-contract-spec-kotlin/src/main/kotlin/org/springframework/cloud/contract/spec/internal/MultipartDsl.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.spec.internal + +import org.springframework.cloud.contract.spec.toDslProperty + +/** + * @author Tim Ysewyn + */ +class MultipartDsl { + + private val fields = LinkedHashMap>() + + fun field(name: String, content: Any) { + this.fields[name] = content.toDslProperty() + } + + internal fun get(): Multipart { + return Multipart(fields) + } + +} \ No newline at end of file diff --git a/specs/spring-cloud-contract-spec-kotlin/src/main/kotlin/org/springframework/cloud/contract/spec/internal/RequestDsl.kt b/specs/spring-cloud-contract-spec-kotlin/src/main/kotlin/org/springframework/cloud/contract/spec/internal/RequestDsl.kt index cdbd282225..f4288fc1cb 100644 --- a/specs/spring-cloud-contract-spec-kotlin/src/main/kotlin/org/springframework/cloud/contract/spec/internal/RequestDsl.kt +++ b/specs/spring-cloud-contract-spec-kotlin/src/main/kotlin/org/springframework/cloud/contract/spec/internal/RequestDsl.kt @@ -68,16 +68,12 @@ open class RequestDsl : CommonDsl(), RegexCreatingProperty { fun body(body: Any) = Body(body) - fun multipart(multipart: Map) = Multipart(multipart.toDslProperties()) - - fun multipart(multipart: List) = Multipart(multipart.toDslProperties()) - - fun multipart(multipart: DslProperty) = Multipart(multipart) - - fun multipart(multipart: Any) = Multipart(multipart) + fun multipart(configurer: MultipartDsl.() -> Unit) { + this.multipart = MultipartDsl().apply(configurer).get() + } fun bodyMatchers(configurer: BodyMatchersDsl.() -> Unit) { - bodyMatchers = BodyMatchersDsl().apply(configurer).get() + this.bodyMatchers = BodyMatchersDsl().apply(configurer).get() } /* HELPER VARIABLES */ diff --git a/specs/spring-cloud-contract-spec-kotlin/src/test/kotlin/org/springframework/cloud/contract/spec/ContractTests.kt b/specs/spring-cloud-contract-spec-kotlin/src/test/kotlin/org/springframework/cloud/contract/spec/ContractTests.kt index 3d9b657c58..537e29447c 100644 --- a/specs/spring-cloud-contract-spec-kotlin/src/test/kotlin/org/springframework/cloud/contract/spec/ContractTests.kt +++ b/specs/spring-cloud-contract-spec-kotlin/src/test/kotlin/org/springframework/cloud/contract/spec/ContractTests.kt @@ -16,13 +16,20 @@ package org.springframework.cloud.contract.spec +import java.io.File + import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertDoesNotThrow import org.junit.jupiter.api.assertThrows + import org.springframework.cloud.contract.spec.ContractDsl.Companion.contract import org.springframework.cloud.contract.spec.internal.Cookie +import org.springframework.cloud.contract.spec.internal.DslProperty +import org.springframework.cloud.contract.spec.internal.FromFileProperty +import org.springframework.cloud.contract.spec.internal.KotlinContractConverter import org.springframework.cloud.contract.spec.internal.MatchingType +import org.springframework.cloud.contract.spec.internal.NamedProperty import org.springframework.cloud.contract.spec.internal.RegexProperty /** @@ -708,4 +715,50 @@ then: } } + @Test + @Suppress("UNCHECKED_CAST") + fun `should support multipart`() { + val contract = KotlinContractConverter() + .convertFrom(File(javaClass.classLoader.getResource("contracts/multipart.kts")!!.toURI())) + .elementAt(0) + + assertDoesNotThrow { + Contract.assertContract(contract) + }.also { + val request = contract.request + assertThat(request.multipart).isNotNull() + assertThat(request.multipart.clientValue).isNotNull() + assertThat(request.multipart.clientValue).isInstanceOf(LinkedHashMap::class.java) + val multipartData = request.multipart.clientValue as LinkedHashMap + assertThat(multipartData).hasSize(3) + assertThat(multipartData.keys).containsExactly("file1", "file2", "test") + var namedProperty: NamedProperty + + assertThat(multipartData["file1"]).isInstanceOf(NamedProperty::class.java) + namedProperty = multipartData["file1"] as NamedProperty + assertThat(((namedProperty.name as DslProperty).clientValue as RegexProperty).pattern()).isEqualTo("[\\S\\s]+") + assertThat((namedProperty.name as DslProperty).serverValue as String).isEqualTo("filename1") + assertThat(((namedProperty.value as DslProperty).clientValue as RegexProperty).pattern()).isEqualTo("[\\S\\s]+") + assertThat((namedProperty.value as DslProperty).serverValue as String).isEqualTo("content1") + assertThat(namedProperty.contentType).isNull() + + assertThat(multipartData["file2"]).isInstanceOf(NamedProperty::class.java) + namedProperty = multipartData["file2"] as NamedProperty + assertThat(((namedProperty.name as DslProperty).clientValue as RegexProperty).pattern()).isEqualTo("[\\S\\s]+") + assertThat((namedProperty.name as DslProperty).serverValue as String).isEqualTo("filename2") + assertThat(((namedProperty.value as DslProperty).clientValue as RegexProperty).pattern()).isEqualTo("[\\S\\s]+") + assertThat((namedProperty.value as DslProperty).serverValue as String).isEqualTo("content2") + assertThat(namedProperty.contentType).isNull() + + assertThat(multipartData["test"]).isInstanceOf(NamedProperty::class.java) + namedProperty = multipartData["test"] as NamedProperty + assertThat(((namedProperty.name as DslProperty).clientValue as RegexProperty).pattern()).isEqualTo("[\\S\\s]+") + assertThat((namedProperty.name as DslProperty).serverValue as String).isEqualTo("filename3") + assertThat(((namedProperty.value as DslProperty).clientValue as RegexProperty).pattern()).isEqualTo("[\\S\\s]+") + assertThat(((namedProperty.value as DslProperty).serverValue as FromFileProperty).fileName()).isEqualTo("test.json") + assertThat((namedProperty.contentType as DslProperty).clientValue).isEqualTo("application/json") + assertThat((namedProperty.contentType as DslProperty).serverValue).isEqualTo("application/json") + } + } + } \ No newline at end of file diff --git a/specs/spring-cloud-contract-spec-kotlin/src/test/resources/contracts/multipart.kts b/specs/spring-cloud-contract-spec-kotlin/src/test/resources/contracts/multipart.kts new file mode 100644 index 0000000000..67c8ca1c3a --- /dev/null +++ b/specs/spring-cloud-contract-spec-kotlin/src/test/resources/contracts/multipart.kts @@ -0,0 +1,47 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package contracts + +import org.springframework.cloud.contract.spec.ContractDsl.Companion.contract + +contract { + request { + method = POST + url = url("/tests") + multipart { + field("file1", named( + value(consumer(regex(nonEmpty())), producer("filename1")), + value(consumer(regex(nonEmpty())), producer("content1")))) + field("file2", named( + value(consumer(regex(nonEmpty())), producer("filename2")), + value(consumer(regex(nonEmpty())), producer("content2")))) + field("test", named( + value(consumer(regex(nonEmpty())), producer("filename3")), + value(consumer(regex(nonEmpty())), producer(file("test.json"))), + value("application/json"))) + } + headers { + contentType = "multipart/form-data" + } + } + response { + status = OK + headers { + contentType = "application/json" + } + } +} diff --git a/specs/spring-cloud-contract-spec-kotlin/src/test/resources/contracts/test.json b/specs/spring-cloud-contract-spec-kotlin/src/test/resources/contracts/test.json new file mode 100644 index 0000000000..984c08fbdf --- /dev/null +++ b/specs/spring-cloud-contract-spec-kotlin/src/test/resources/contracts/test.json @@ -0,0 +1,3 @@ +{ + "status": "test" +}