Tests pass but logging is still required

This commit is contained in:
2024-07-19 17:21:27 +01:00
parent 982b4e8d5d
commit 5bb6ccb789
20 changed files with 468 additions and 104 deletions

2
.idea/misc.xml generated
View File

@@ -8,7 +8,7 @@
</list> </list>
</option> </option>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_20" default="true" project-jdk-name="20" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17 (2)" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>
</project> </project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

47
pom.xml
View File

@@ -65,6 +65,14 @@
<mainClass>MainKt</mainClass> <mainClass>MainKt</mainClass>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
@@ -81,18 +89,17 @@
<version>5.10.0</version> <version>5.10.0</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.jetbrains.kotlin</groupId> <groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId> <artifactId>kotlin-stdlib</artifactId>
<version>1.9.0</version> <version>1.9.0</version>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/io.rest-assured/rest-assured -->
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>5.5.0</version>
<scope>test</scope>
</dependency>
<!-- jUnit --> <!-- jUnit -->
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
@@ -111,6 +118,12 @@
<artifactId>retrofit</artifactId> <artifactId>retrofit</artifactId>
<version>2.11.0</version> <version>2.11.0</version>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/com.squareup.retrofit2/converter-gson -->
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-gson</artifactId>
<version>2.9.0</version>
</dependency>
<dependency> <dependency>
<groupId>com.squareup.okhttp3</groupId> <groupId>com.squareup.okhttp3</groupId>
<artifactId>logging-interceptor</artifactId> <artifactId>logging-interceptor</artifactId>
@@ -121,19 +134,11 @@
<artifactId>api-testing-automation-framework</artifactId> <artifactId>api-testing-automation-framework</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/com.squareup.retrofit2/converter-gson -->
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-gson</artifactId>
<version>2.11.0</version>
</dependency>
<dependency> <dependency>
<groupId>org.jetbrains.kotlinx</groupId> <groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId> <artifactId>kotlinx-coroutines-core</artifactId>
<version>1.6.2</version> <version>1.6.2</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.logging.log4j</groupId> <groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId> <artifactId>log4j-core</artifactId>
@@ -144,6 +149,18 @@
<artifactId>log4j-api</artifactId> <artifactId>log4j-api</artifactId>
<version>2.19.0</version> <version>2.19.0</version>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.assertj/assertj-core -->
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.26.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>6.20.0</version>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -1,26 +1,54 @@
package api package api
import com.google.gson.GsonBuilder
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory import retrofit2.converter.gson.GsonConverterFactory
import java.security.SecureRandom
import java.security.cert.CertificateException
import java.security.cert.X509Certificate
import java.util.concurrent.TimeUnit
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager
class BookerApi { class BookerApi {
private val baseUrl = "https://restful-booker.herokuapp.com/" private val baseUrl = "https://restful-booker.herokuapp.com/"
private val loggingInterceptor = HttpLoggingInterceptor().apply { var trustAllCerts = arrayOf<TrustManager>(
level = HttpLoggingInterceptor.Level.BODY object : X509TrustManager {
@Throws(CertificateException::class)
override fun checkClientTrusted(chain: Array<X509Certificate?>?, authType: String?) {
} }
@Throws(CertificateException::class)
override fun checkServerTrusted(chain: Array<X509Certificate?>?, authType: String?) {
}
override fun getAcceptedIssuers(): Array<X509Certificate?> {
return arrayOfNulls<X509Certificate>(0)
}
}
)
var gson = GsonBuilder()
.setLenient()
.create()
private fun buildOkHttpClient(timeoutSeconds: Long = 30L): OkHttpClient { private fun buildOkHttpClient(timeoutSeconds: Long = 30L): OkHttpClient {
val builder = OkHttpClient.Builder() val builder = OkHttpClient.Builder()
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, SecureRandom())
builder builder
.addInterceptor(loggingInterceptor)
.connectTimeout(timeoutSeconds, TimeUnit.SECONDS) .connectTimeout(timeoutSeconds, TimeUnit.SECONDS)
.writeTimeout(timeoutSeconds, TimeUnit.SECONDS) .writeTimeout(timeoutSeconds, TimeUnit.SECONDS)
.readTimeout(timeoutSeconds, TimeUnit.SECONDS) .readTimeout(timeoutSeconds, TimeUnit.SECONDS)
.sslSocketFactory(sslContext.socketFactory, trustAllCerts[0] as X509TrustManager)
.hostnameVerifier { _, _ -> true }
return builder.build() return builder.build()
} }
@@ -30,7 +58,7 @@ class BookerApi {
return Retrofit.Builder() return Retrofit.Builder()
.client(okHttpClient) .client(okHttpClient)
.baseUrl(baseUrl) .baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create(gson))
.build() .build()
.create(RestfulBookerApi::class.java) .create(RestfulBookerApi::class.java)
} }

View File

@@ -6,19 +6,15 @@ import retrofit2.http.*
interface RestfulBookerApi { interface RestfulBookerApi {
@Headers("Content-Type:application/json") @Headers("Content-Type:application/json", "Accept: application/json")
@POST("auth") @POST("auth")
suspend fun createAuthToken( suspend fun createAuthToken(
@Field("username") username: String, @Body authRequest: AuthRequest
@Field("password") password: String,
): Response<AuthResponse> ): Response<AuthResponse>
@GET("booking") @GET("booking")
suspend fun getBookingIds( suspend fun getBookingIds(
@Field("firstname") firstname: String? = null,
@Field("lastname") lastname: String? = null,
@Field("checkin") checkin: String? = null,
@Field("checkout") checkout: String? = null,
): Response<List<BookingIdResponse>> ): Response<List<BookingIdResponse>>
@GET("booking/{id}") @GET("booking/{id}")
@@ -26,7 +22,7 @@ interface RestfulBookerApi {
@Path("id") id: String @Path("id") id: String
): Response<BookingResponse> ): Response<BookingResponse>
@Headers("Content-Type:application/json") @Headers("Content-Type:application/json", "Accept: application/json")
@POST("booking") @POST("booking")
suspend fun createBooking( suspend fun createBooking(
@Body booking: BookingRequest, @Body booking: BookingRequest,
@@ -45,17 +41,12 @@ interface RestfulBookerApi {
suspend fun partialUpdateBooking( suspend fun partialUpdateBooking(
@Path("id") id: String, @Path("id") id: String,
@Header("Authorization") token: String, @Header("Authorization") token: String,
@Field("firstname") firstname: String? = null, @Body update: UpdateBookingRequest
@Field("lastname") lastname: String? = null,
@Field("totalprice") totalprice: Float? = null,
@Field("depositpaid") depositpaid: Boolean? = null,
@Field("checkin") checkin: String? = null,
@Field("checkout") checkout: String? = null,
@Field("additionalneeds") additionalneeds: String? = null
): Response<BookingResponse> ): Response<BookingResponse>
@DELETE("booking/{id}") @DELETE("booking/{id}")
suspend fun deleteBooking( suspend fun deleteBooking(
@Path("id") id: String @Path("id") id: String,
@Header("Authorization") token: String
): Response<Any> ): Response<Any>
} }

View File

@@ -0,0 +1,6 @@
package model
data class AuthRequest(
val username: String,
val password: String
)

View File

@@ -1,5 +1,5 @@
package model package model
class AuthResponse { data class AuthResponse (
var token: String? = null val token: String
} )

View File

@@ -1,5 +1,5 @@
package model package model
class BookingIdResponse { data class BookingIdResponse(
var bookingid = 0 val bookingid: Int
} )

View File

@@ -1,12 +1,12 @@
package model package model
data class BookingRequest ( data class BookingRequest (
var firstname: String? = null, var firstname: String,
var lastname: String? = null, var lastname: String,
var totalprice: Int = 0, var totalprice: Int,
var depositpaid: Boolean = false, var depositpaid: Boolean,
var bookingdates: Bookingdates? = null, var bookingdates: Bookingdates,
var additionalneeds: String? = null, var additionalneeds: String,
) )

View File

@@ -1,11 +1,11 @@
package model package model
data class BookingResponse ( data class BookingResponse(
var firstname: String? = null, var firstname: String,
var lastname: String? = null, var lastname: String,
var totalprice: Int = 0, var totalprice: Int,
var depositpaid: Boolean = false, var depositpaid: Boolean,
var bookingdates: Bookingdates? = null, var bookingdates: Bookingdates,
var additionalneeds: String? = null var additionalneeds: String
) )

View File

@@ -1,6 +1,6 @@
package model package model
data class Bookingdates ( data class Bookingdates (
var checkin: String? = null, var checkin: String,
var checkout: String? = null, var checkout: String,
) )

View File

@@ -1,6 +1,6 @@
package model package model
class CreateBookingResponse { data class CreateBookingResponse(
var bookingid = 0 var bookingid: Int,
var booking: BookingResponse? = null var booking: BookingResponse
} )

View File

@@ -0,0 +1,10 @@
package model
data class UpdateBookingRequest(
var firstname: String? = null,
var lastname: String? = null,
var totalprice: Int? = null,
var depositpaid: Boolean? = null,
var bookingdates: Bookingdates? = null,
var additionalneeds: String? = null,
)

View File

@@ -0,0 +1,92 @@
package storage
import model.BookingResponse
import model.Bookingdates
class OrdersDatabase {
private val storage = mutableMapOf<Int, BookingResponse>()
// Create
fun insertBooking(id: Int, booking: BookingResponse) {
if (storage.contains(id)) {
storage.replace(id, booking)
} else {
storage[id] = booking
}
}
// Read
fun getIdsOfBookingsAvailable() = storage.keys.toList()
fun getBookingsAvailable() = storage.values.toList()
fun getIdsAndBookings() = storage.toMap()
fun getIdsOfOrderBasedOnValues(
firstname: String? = null,
lastname: String? = null,
totalprice: Int? = null,
depositpaid: Boolean? = null,
checkin: String? = null,
checkout: String? = null,
additionalneeds: String? = null
): List<Int> {
return storage.filterValues {
firstname?.let { f -> f == it.firstname } ?: true &&
lastname?.let { f -> f == it.lastname } ?: true &&
totalprice?.let { f -> f == it.totalprice } ?: true &&
depositpaid?.let { f -> f == it.depositpaid } ?: true &&
checkin?.let { f -> f == it.bookingdates.checkin } ?: true &&
checkout?.let { f -> f == it.bookingdates.checkout } ?: true &&
additionalneeds?.let { f -> f == it.additionalneeds } ?: true
}.keys.toList()
}
fun getIdsOfOrderBasedOnValues(
id: Int
): BookingResponse? {
return storage[id]
}
// Update
fun updateCompleteOrder(id: Int, newBookingResponse: BookingResponse) {
insertBooking(id, newBookingResponse)
}
fun updateOrderPartial(
id: Int,
firstname: String? = null,
lastname: String? = null,
totalprice: Int? = null,
depositpaid: Boolean? = null,
checkin: String? = null,
checkout: String? = null,
additionalneeds: String? = null
) {
if (storage.keys.remove(id)) {
storage.compute(id) { k, v ->
val mFirstName = firstname ?: v!!.firstname
val mlastname = lastname ?: v!!.lastname
val mTotalprice = totalprice ?: v!!.totalprice
val mDepositpaid = depositpaid ?: v!!.depositpaid
val mCheckin = checkin ?: v!!.bookingdates.checkin
val mCheckout = checkout ?: v!!.bookingdates.checkout
val mAdditionalneeds = additionalneeds ?: v!!.additionalneeds
BookingResponse(
firstname = mFirstName,
lastname = mlastname,
totalprice = mTotalprice,
depositpaid = mDepositpaid,
bookingdates = Bookingdates(
checkin = mCheckin,
checkout = mCheckout
),
additionalneeds = mAdditionalneeds
)
}
}
}
// Delete
fun clearAllData() = storage.clear()
fun deleteSingleEntry(id: Int) = storage.remove(id)
}

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">
<Properties>
<Property name="basePath">C:\\logs</Property>
</Properties>
<Appenders>
<RollingFile name="fileLogger" fileName="${basePath}/app-info.html"
filePattern="${basePath}/app-info-%d{yyyy-MM-dd}.html">
<HTMLLayout charset="UTF-8" title="Howtodoinjava Info Logs" locationInfo="true" />
<Policies>
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
<SizeBasedTriggeringPolicy size="10 MB" />
</Policies>
</RollingFile>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Logger name="com.howtodoinjava" level="debug" additivity="false">
<appender-ref ref="fileLogger" level="debug" />
</Logger>
<Root level="debug" additivity="false">
<appender-ref ref="console" />
</Root>
</Loggers>
</Configuration>

View File

@@ -4,6 +4,7 @@ import java.io.IOException
abstract class NetworkTests { abstract class NetworkTests {
// Call retrofit API and unwrap response or throw exception
fun <T : Any> responseUnwrap( fun <T : Any> responseUnwrap(
call: suspend () -> Response<T> call: suspend () -> Response<T>
): T { ): T {
@@ -12,9 +13,11 @@ abstract class NetworkTests {
if (response.isSuccessful) { if (response.isSuccessful) {
return response.body()!! return response.body()!!
} else { } else {
val error = response.errorBody()?.string() val error = StringBuilder().append(response.code()).append(" : ")
.append(response.errorBody()?.string() ?: "Unable to handle end point").toString()
print(response.raw())
throw IOException(error)
}
}
throw IOException(error ?: "Unable to handle end point")
}
}
} }

View File

@@ -1,35 +1,56 @@
import api.BookerApi import api.BookerApi
import api.RestfulBookerApi import api.RestfulBookerApi
import io.restassured.RestAssured.given import model.AuthRequest
import kotlinx.coroutines.runBlocking
import model.BookingRequest import model.BookingRequest
import model.Bookingdates import model.Bookingdates
import model.UpdateBookingRequest
import net.sf.jasperreports.engine.JasperCompileManager
import net.sf.jasperreports.engine.JasperFillManager
import net.sf.jasperreports.engine.JasperReport
import net.sf.jasperreports.engine.export.HtmlExporter
import net.sf.jasperreports.engine.util.JRSaver
import net.sf.jasperreports.export.SimpleHtmlExporterOutput
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import org.junit.FixMethodOrder import org.apache.logging.log4j.message.MessageFormatMessage
import org.junit.jupiter.api.AfterAll import org.assertj.core.api.AssertionsForClassTypes.assertThat
import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.*
import org.junit.jupiter.api.Test import storage.OrdersDatabase
import org.junit.runners.MethodSorters
import utils.FileReader import utils.FileReader
import java.io.InputStream
import java.util.*
@FixMethodOrder(MethodSorters.DEFAULT)
class Tests : NetworkTests(){ @TestMethodOrder(MethodOrderer.OrderAnnotation::class)
class Tests : NetworkTests() {
companion object { companion object {
private lateinit var bookerApi: RestfulBookerApi private lateinit var bookerApi: RestfulBookerApi
private lateinit var fileReader: FileReader private val fileReader = FileReader()
private val logger = LogManager.getLogger(Tests::class.java) private val logger = LogManager.getLogger("Test")
val bookingsReportStream: InputStream = javaClass.getResourceAsStream("/bookingsReport.jrxml")
val jasperReport: JasperReport = JasperCompileManager.compileReport(bookingsReportStream)
private val storage = OrdersDatabase()
private lateinit var bookingRequestTestOne: BookingRequest
private lateinit var bookingRequestTestTwo: BookingRequest
@BeforeAll @BeforeAll
@JvmStatic @JvmStatic
internal fun beforeAll() { internal fun beforeAll() {
bookerApi = BookerApi().invoke() bookerApi = BookerApi().invoke()
bookingRequestTestOne = fileReader.readJsonFileFromResources("test1", BookingRequest::class.java)
bookingRequestTestTwo = fileReader.readJsonFileFromResources("test2", BookingRequest::class.java)
} }
@AfterAll @AfterAll
@JvmStatic @JvmStatic
internal fun afterAll() { internal fun afterAll() {
storage.clearAllData()
JRSaver.saveObject(jasperReport, "bookingReport.jasper");
} }
} }
@@ -40,10 +61,11 @@ class Tests : NetworkTests(){
* o Above added 3 new booking details * o Above added 3 new booking details
*/ */
@Test() @Test()
@Order(1)
fun testScenarioOne() { fun testScenarioOne() {
// Given /*
val bookingRequestOne = fileReader.readJsonFileFromResources<BookingRequest>("test1") * Given
val bookingRequestTwo = fileReader.readJsonFileFromResources<BookingRequest>("test2") */
val bookingRequestThree = BookingRequest( val bookingRequestThree = BookingRequest(
firstname = "Mark", firstname = "Mark",
lastname = "Wahlberg", lastname = "Wahlberg",
@@ -56,17 +78,36 @@ class Tests : NetworkTests(){
additionalneeds = "Breakfast" additionalneeds = "Breakfast"
) )
// When /*
val createBookingOneResponse = responseUnwrap { bookerApi.createBooking(bookingRequestOne) } * When
val createBookingTwoResponse = responseUnwrap { bookerApi.createBooking(bookingRequestTwo) } */
val createBookingOneResponse = responseUnwrap { bookerApi.createBooking(bookingRequestTestOne) }
val createBookingTwoResponse = responseUnwrap { bookerApi.createBooking(bookingRequestTestTwo) }
val createBookingThreeResponse = responseUnwrap { bookerApi.createBooking(bookingRequestThree) } val createBookingThreeResponse = responseUnwrap { bookerApi.createBooking(bookingRequestThree) }
val bookingResponses = listOf(createBookingOneResponse, createBookingTwoResponse, createBookingThreeResponse)
// Then /*
* Then
*/
val bookingIds = responseUnwrap { bookerApi.getBookingIds() } val bookingIds = responseUnwrap { bookerApi.getBookingIds() }
assertThat(bookingIds.size)
.withFailMessage("Did not find 3 bookings")
.isGreaterThanOrEqualTo(3)
JasperFillManager.
logger.trace("Available booking IDs: ${bookingIds.joinToString()}") logger.trace("Available booking IDs: ${bookingIds.joinToString()}")
bookingIds.forEach {
val currentBookingResponse = responseUnwrap { bookerApi.getSingleBooking("$it") } // Add the booking details and idea for later
logger.trace(currentBookingResponse) bookingResponses.forEach { response ->
storage.insertBooking(response.bookingid, response.booking)
logger.trace(
MessageFormatMessage(
"Booking with ID: {0} has been added: {1}",
response.bookingid,
response.booking
)
)
} }
} }
@@ -77,29 +118,133 @@ class Tests : NetworkTests(){
* *
*/ */
@Test() @Test()
@Order(2)
fun testScenarioTwo() { fun testScenarioTwo() {
// Given /*
* Given
*/
// Find my booking ids
val orderIdTestOne = storage.getIdsOfOrderBasedOnValues(
firstname = bookingRequestTestOne.firstname,
lastname = bookingRequestTestOne.lastname,
checkin = bookingRequestTestOne.bookingdates.checkin,
checkout = bookingRequestTestOne.bookingdates.checkout
).first()
val orderIdTestTwo = storage.getIdsOfOrderBasedOnValues(
firstname = bookingRequestTestTwo.firstname,
lastname = bookingRequestTestTwo.lastname,
checkin = bookingRequestTestTwo.bookingdates.checkin,
checkout = bookingRequestTestTwo.bookingdates.checkout
).first()
// When /*
* When
*/
val auth = responseUnwrap {
bookerApi.createAuthToken(
AuthRequest(
UUID.randomUUID().toString(),
UUID.randomUUID().toString()
)
)
}
val tokenBuilder =
StringBuilder("Basic ").append(Base64.getEncoder().encodeToString("admin:password123".toByteArray()))
.toString()
val updateTestOneResponse = responseUnwrap {
bookerApi.partialUpdateBooking(
id = orderIdTestOne.toString(),
token = tokenBuilder,
update = UpdateBookingRequest(
totalprice = 1000
)
)
}.also {
storage.updateCompleteOrder(orderIdTestOne, it)
}
val updateTestTwoResponse = responseUnwrap {
bookerApi.partialUpdateBooking(
id = orderIdTestTwo.toString(),
token = tokenBuilder,
update = UpdateBookingRequest(
totalprice = 1500
)
)
}.also {
storage.updateCompleteOrder(orderIdTestTwo, it)
}
val updateResponseList = listOf(updateTestOneResponse, updateTestTwoResponse)
// Then /*
* Then
*/
logger.trace(
MessageFormatMessage(
"Booking with ID: {0} has been updated to the following: {1}",
orderIdTestOne,
updateTestOneResponse
)
)
logger.trace(
MessageFormatMessage(
"Booking with ID: {0} has been updated to the following: {1}",
orderIdTestTwo,
updateTestTwoResponse
)
)
} }
@Test() @Test()
@Order(3)
fun testScenarioThree() { fun testScenarioThree() {
// Given /*
* Given
*/
val idOfAny = storage.getIdsOfBookingsAvailable().random()
val tokenBuilder =
StringBuilder("Basic ").append(Base64.getEncoder().encodeToString("admin:password123".toByteArray()))
.toString()
// When /*
* When
*/
val deleteResponse = responseUnwrap {
bookerApi.deleteBooking(id = idOfAny.toString(), tokenBuilder)
}.also { storage.deleteSingleEntry(id = idOfAny) }
// Then /*
* Then
*/
logger.trace(
MessageFormatMessage(
"Booking with ID: {0} has been delete and given the following response: {1}",
idOfAny,
deleteResponse
)
)
} }
@Test() @Test()
@Order(4)
fun testScenarioFour() { fun testScenarioFour() {
// Given /*
* Given
*/
// When /*
* When
*/
// Then /*
* Then
*/
val exporter = HtmlExporter()
// Set input ...
// Set input ...
exporter.exporterOutput = SimpleHtmlExporterOutput("bookingsReport.html")
exporter.exportReport()
} }
} }

View File

@@ -3,19 +3,19 @@ package utils
import com.google.gson.Gson import com.google.gson.Gson
import java.io.BufferedReader import java.io.BufferedReader
import java.lang.reflect.ParameterizedType import java.lang.reflect.ParameterizedType
import kotlin.reflect.KClass
class FileReader { class FileReader {
private val gson by lazy { Gson() } private val gson by lazy { Gson() }
fun <T : Any?> readJsonFileFromResources(fileName: String): T { /**
val iStream = this::class.java.getResourceAsStream("$fileName.json") * Read json files from resources and turn into object of type <T>
*/
fun <T : Any?> readJsonFileFromResources(fileName: String, clazz: Class<T>): T {
val iStream = this::class.java.getResourceAsStream("/$fileName.json")
?: throw IllegalStateException("Unable to read the file requested") ?: throw IllegalStateException("Unable to read the file requested")
val data = iStream.bufferedReader().use(BufferedReader::readText) val data = iStream.bufferedReader().use(BufferedReader::readText)
val genericType = ((javaClass.genericSuperclass as? ParameterizedType) return gson.fromJson(data, clazz)
?.actualTypeArguments?.getOrNull(0) as? Class<T>)
?: throw IllegalStateException("Can not find class from generic argument")
return gson.fromJson(data, genericType)
} }
} }

View File

@@ -0,0 +1,28 @@
<jasperReport>
<field name="FIRST_NAME" class="java.lang.String"/>
<field name="LAST_NAME" class="java.lang.String"/>
<field name="SALARY" class="java.lang.Double"/>
<field name="ID" class="java.lang.Integer"/>
<detail>
<band height="51" splitType="Stretch">
<textField>
<reportElement x="0" y="0" width="100" height="20"/>
<textElement/>
<textFieldExpression class="java.lang.String">
<![CDATA[$F{FIRST_NAME}]]></textFieldExpression>
</textField>
<textField>
<reportElement x="100" y="0" width="100" height="20"/>
<textElement/>
<textFieldExpression class="java.lang.String">
<![CDATA[$F{LAST_NAME}]]></textFieldExpression>
</textField>
<textField>
<reportElement x="200" y="0" width="100" height="20"/>
<textElement/>
<textFieldExpression class="java.lang.String">
<![CDATA[$F{SALARY}]]></textFieldExpression>
</textField>
</band>
</detail>
</jasperReport>

View File

@@ -0,0 +1,11 @@
{
"firstname" : "Jim",
"lastname" : "Brown",
"totalprice" : 500,
"depositpaid" : true,
"bookingdates" : {
"checkin" : "2025-01-01",
"checkout" : "2025-01-10"
},
"additionalneeds" : "Lunch"
}