Skip to content

Commit

Permalink
Create scala-extensions module for Scala 3
Browse files Browse the repository at this point in the history
  • Loading branch information
carlosedp committed Feb 5, 2024
1 parent 034ebfb commit 0101269
Show file tree
Hide file tree
Showing 6 changed files with 351 additions and 3 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ target
.settings
.springBeans
.idea
.metals
.vscode
.bloop
classes/
lib/
rest-assured-all/dependency-reduced-pom.xml
rest-assured-all/dependency-reduced-pom.xml
4 changes: 3 additions & 1 deletion modules/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
~ limitations under the License.
-->

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>rest-assured-parent</artifactId>
<groupId>io.rest-assured</groupId>
Expand All @@ -29,6 +30,7 @@
<module>json-schema-validator</module>
<module>spring-mock-mvc</module>
<module>scala-support</module>
<module>scala-extensions</module>
<module>spring-web-test-client</module>
<module>spring-commons</module>
<module>kotlin-extensions</module>
Expand Down
110 changes: 110 additions & 0 deletions modules/scala-extensions/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?xml version="1.0"?>
<!--
~ Copyright 2024 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
~
~ http://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.
-->

<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.rest-assured</groupId>
<artifactId>modules</artifactId>
<version>5.4.1-SNAPSHOT</version>
</parent>
<artifactId>scala-extensions</artifactId>
<version>5.4.1-SNAPSHOT</version>
<name>scala-extensions</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<scala-maven-plugin.version>4.8.1</scala-maven-plugin.version>
<scala.version>3.3.1</scala.version>
</properties>

<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala3-library_3</artifactId>
<version>${scala.version}</version>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>5.4.1-SNAPSHOT</version>
</dependency>

<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<profiles>
<profile>
<id>release</id>
<build>
<plugins>
</plugins>
</build>
</profile>
</profiles>

<build>
<sourceDirectory>src/main/scala</sourceDirectory>
<testSourceDirectory>src/test/scala</testSourceDirectory>
<plugins>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>${scala-maven-plugin.version}</version>
<executions>
<execution>
<?m2e ignore?>
<id>scala-compile-first</id>
<phase>process-resources</phase>
<goals>
<goal>add-source</goal>
<goal>compile</goal>
</goals>
</execution>
<execution>
<?m2e ignore?>
<id>scala-test-compile</id>
<phase>process-test-resources</phase>
<goals>
<goal>add-source</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<configuration>
<scalaVersion>${scala.version}</scalaVersion>
<args>
<arg>-feature</arg>
<arg>-deprecation</arg>
<arg>-Ysemanticdb</arg>
</args>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package io.restassured.module.scala.extensions

import scala.util.chaining.*
import scala.reflect.ClassTag
import io.restassured.RestAssured.`given`
import io.restassured.RestAssured.`when`
import io.restassured.internal.{ResponseSpecificationImpl, ValidatableResponseImpl}
import io.restassured.response.{ExtractableResponse, Response, ValidatableResponse}
import io.restassured.specification.{RequestSender, RequestSpecification}

// Main wrappers

/**
* A wrapper around [given] that starts building the request part of the test.
* @see
* given
*/
def Given(block: RequestSpecification => RequestSpecification): RequestSpecification =
`given`().tap(block)

/**
* A wrapper around [RequestSpecification.when] that configures how the request
* is dispatched.
* @see
* RequestSpecification.when
*/
extension (spec: RequestSpecification)
infix def When(block: RequestSpecification => Response): Response =
block(spec.`when`())

/**
* A wrapper around [io.restassured.RestAssured.when] that configures how the
* request is dispatched.
* @see
* io.restassured.RestAssured.when
*/
def When(block: RequestSender => Response): Response =
block(`when`())

/**
* A wrapper around [then] that allows configuration of response expectations.
* @see
* then
*/
extension (resp: Response)
infix def Then(block: ValidatableResponse => Unit): ValidatableResponse =
resp.`then`()
.tap(block)

/**
* A wrapper around [ExtractableResponse] that allows extracting data out of the
* response
* @see
* ExtractableResponse
*/
extension [T]( resp: Response )
infix def Extract(
block: ExtractableResponse[Response] => T
)(
using ClassTag[T]
): T =
val res = resp.`then`().extract().pipe(block)
summon[ClassTag[T]].runtimeClass match
case cls if cls.isAssignableFrom(res.getClass) => res.asInstanceOf[T]
case _ => throw new Exception("Unsupported type")

/**
* A wrapper around [ExtractableResponse] that allows extracting data out of the
* response
* @see
* ExtractableResponse
*/
extension [T]( resp: ValidatableResponse )
infix def Extract(
block: ExtractableResponse[Response] => T
)(
using ClassTag[T]
): T =
val res = resp.extract().pipe(block)
summon[ClassTag[T]].runtimeClass match
case cls if cls.isAssignableFrom(res.getClass) => res.asInstanceOf[T]
case _ => throw new Exception("Unsupported type")

// End main wrappers

private def doIfValidatableResponseImpl(fn: ResponseSpecificationImpl => Unit): ValidatableResponse => Unit =
resp =>
if resp.isInstanceOf[ValidatableResponseImpl] then fn(resp.asInstanceOf[ValidatableResponseImpl].responseSpec)
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* Copyright 2024 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
*
* http://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 io.restassured.module.scala.extensions

import io.restassured.RestAssured
import io.restassured.builder.ResponseBuilder
import io.restassured.filter.Filter
import io.restassured.http.ContentType.JSON
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.catchThrowable
import org.hamcrest.Matchers.*
import org.junit.Before
import org.junit.Test

class RestAssuredScalaExtensionsTest:

@Before
def `rest assured is configured`: Unit = {
RestAssured.filters((_, _, _) => {
new ResponseBuilder() .setStatusCode(200) .setContentType(JSON) .setBody("""{ "message" : "Hello World"}""").build()
})
}


@Test
def `rest assured is reset after each test`: Unit =
RestAssured.reset()

@Test
def `basic rest assured scala extensions are compilable`: Unit =
Given(req =>
req.port(7000)
req.header("Header", "Header")
req.body("hello")
)
.When(
_.put("/the/path")
)
.Then(res =>
res.statusCode(200)
res.body("message", equalTo("Hello World"))
)

@Test
def `extraction with rest assured scala extensions`: Unit =
val message: String = Given(req =>
req.port(7000)
req.header("Header", "Header")
req.body("hello")
)
.When(
_.put("/the/path")
)
.Extract(
_.path("message")
)
assertThat(message).isEqualTo("Hello World")

@Test
def `extraction after 'then', when path is not used in 'Then', with rest assured scala extensions`: Unit =
val message: String = Given(req =>
req.port(7000)
req.header("Header", "Header")
req.body("hello")
req.filter((_, _, _) =>
new ResponseBuilder().setStatusCode(200).setContentType(JSON).setBody("""{ "message" : "Hello World"}""").build()
)
)
.When(
_.put("/the/path")
)
.Then(
_.statusCode(200)
)
.Extract(
_.path("message")
)
assertThat(message).isEqualTo("Hello World")

@Test
def `extraction after 'then', when path is used in 'Then', with rest assured scala extensions`: Unit =
val message: String = Given(req =>
req.port(7000)
req.header("Header", "Header")
req.body("hello")
req.filter((_, _, _) =>
new ResponseBuilder().setStatusCode(200).setContentType(JSON).setBody("""{ "message" : "Hello World"}""").build()
)
)
.When(
_.put("/the/path")
)
.Then(res =>
res.statusCode(200)
res.body("message", not(emptyOrNullString()))
)
.Extract(
_.path("message")
)
assertThat(message).isEqualTo("Hello World")

@Test
def `all expectations error messages are included in the error message`: Unit =
try {
Given(req =>
req.port(7000)
req.header("Header", "Header")
req.body("hello")
)
.When(
_.put("/the/path")
)
.Then(res =>
res.statusCode(400)
res.body("message", equalTo("Another World"))
res.body("message", equalTo("Brave new world"))
)
} catch {
case e: AssertionError =>
assertThat(e).hasMessage(
"""1 expectation failed.
|Expected status code <400> but was <200>.
|""".stripMargin)
}
9 changes: 8 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
~ limitations under the License.
-->

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.sonatype.oss</groupId>
Expand Down Expand Up @@ -70,6 +71,7 @@
<maven-javadoc.version>2.9.1</maven-javadoc.version>
<surefire.version>2.22.0</surefire.version>
<kotlin.version>1.9.21</kotlin.version>
<scala.version>3.3.1</scala.version>
<assertj-core.version>3.16.1</assertj-core.version>
<scribe.version>2.5.3</scribe.version>
<sundrio.version>0.93.0</sundrio.version>
Expand Down Expand Up @@ -471,6 +473,11 @@
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala3-interfaces</artifactId>
<version>${scala.version}</version>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
Expand Down

0 comments on commit 0101269

Please sign in to comment.