From 982b4e8d5d612384ed3d8c982dc2e421df3c56d0 Mon Sep 17 00:00:00 2001 From: hmalik144 Date: Thu, 18 Jul 2024 17:04:56 +0100 Subject: [PATCH] Initial commit - Retrofit used to call API --- .gitignore | 38 +++++ .idea/.gitignore | 3 + .idea/encodings.xml | 7 + .idea/inspectionProfiles/Project_Default.xml | 6 + .idea/kotlinc.xml | 6 + .idea/misc.xml | 14 ++ pom.xml | 149 ++++++++++++++++++ src/main/kotlin/api/BookerApi.kt | 37 +++++ src/main/kotlin/api/RestfulBookerApi.kt | 61 +++++++ src/main/kotlin/model/AuthResponse.kt | 5 + src/main/kotlin/model/BookingIdResponse.kt | 5 + src/main/kotlin/model/BookingRequest.kt | 12 ++ src/main/kotlin/model/BookingResponse.kt | 11 ++ src/main/kotlin/model/Bookingdates.kt | 6 + .../kotlin/model/CreateBookingResponse.kt | 6 + src/test/kotlin/NetworkTests.kt | 20 +++ src/test/kotlin/Tests.kt | 105 ++++++++++++ src/test/kotlin/utils/FileReader.kt | 21 +++ src/test/resources/test1.json | 11 ++ src/test/resources/test2.json | 11 ++ 20 files changed, 534 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/encodings.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/kotlinc.xml create mode 100644 .idea/misc.xml create mode 100644 pom.xml create mode 100644 src/main/kotlin/api/BookerApi.kt create mode 100644 src/main/kotlin/api/RestfulBookerApi.kt create mode 100644 src/main/kotlin/model/AuthResponse.kt create mode 100644 src/main/kotlin/model/BookingIdResponse.kt create mode 100644 src/main/kotlin/model/BookingRequest.kt create mode 100644 src/main/kotlin/model/BookingResponse.kt create mode 100644 src/main/kotlin/model/Bookingdates.kt create mode 100644 src/main/kotlin/model/CreateBookingResponse.kt create mode 100644 src/test/kotlin/NetworkTests.kt create mode 100644 src/test/kotlin/Tests.kt create mode 100644 src/test/kotlin/utils/FileReader.kt create mode 100644 src/test/resources/test1.json create mode 100644 src/test/resources/test2.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..942f3a2 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..df543e3 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..fdf8d99 --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..dd5e3ad --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..f3e2172 --- /dev/null +++ b/pom.xml @@ -0,0 +1,149 @@ + + + 4.0.0 + + api-testing-automation-framework + org.example + 1.0-SNAPSHOT + jar + + consoleApp + + + UTF-8 + official + 1.8 + + + + + mavenCentral + https://repo1.maven.org/maven2/ + + + + + src/main/kotlin + src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + 1.9.0 + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + + maven-surefire-plugin + 2.22.2 + + + maven-failsafe-plugin + 2.22.2 + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + MainKt + + + + + + + + org.jetbrains.kotlin + kotlin-test-junit5 + 1.9.0 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.10.0 + test + + + org.jetbrains.kotlin + kotlin-stdlib + 1.9.0 + + + + io.rest-assured + rest-assured + 5.5.0 + test + + + + org.junit.jupiter + junit-jupiter-params + 5.0.0 + + + junit + junit + 4.12 + compile + + + + com.squareup.retrofit2 + retrofit + 2.11.0 + + + com.squareup.okhttp3 + logging-interceptor + 4.12.0 + + + org.example + api-testing-automation-framework + 1.0-SNAPSHOT + + + + com.squareup.retrofit2 + converter-gson + 2.11.0 + + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + 1.6.2 + + + + org.apache.logging.log4j + log4j-core + 2.19.0 + + + org.apache.logging.log4j + log4j-api + 2.19.0 + + + + \ No newline at end of file diff --git a/src/main/kotlin/api/BookerApi.kt b/src/main/kotlin/api/BookerApi.kt new file mode 100644 index 0000000..3fa771f --- /dev/null +++ b/src/main/kotlin/api/BookerApi.kt @@ -0,0 +1,37 @@ +package api + +import okhttp3.OkHttpClient +import java.util.concurrent.TimeUnit +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + +class BookerApi { + private val baseUrl = "https://restful-booker.herokuapp.com/" + + private val loggingInterceptor = HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.BODY + } + + private fun buildOkHttpClient(timeoutSeconds: Long = 30L): OkHttpClient { + val builder = OkHttpClient.Builder() + + builder + .addInterceptor(loggingInterceptor) + .connectTimeout(timeoutSeconds, TimeUnit.SECONDS) + .writeTimeout(timeoutSeconds, TimeUnit.SECONDS) + .readTimeout(timeoutSeconds, TimeUnit.SECONDS) + + return builder.build() + } + + fun invoke(): RestfulBookerApi { + val okHttpClient = buildOkHttpClient() + return Retrofit.Builder() + .client(okHttpClient) + .baseUrl(baseUrl) + .addConverterFactory(GsonConverterFactory.create()) + .build() + .create(RestfulBookerApi::class.java) + } +} \ No newline at end of file diff --git a/src/main/kotlin/api/RestfulBookerApi.kt b/src/main/kotlin/api/RestfulBookerApi.kt new file mode 100644 index 0000000..738e267 --- /dev/null +++ b/src/main/kotlin/api/RestfulBookerApi.kt @@ -0,0 +1,61 @@ +package api + +import model.* +import retrofit2.Response +import retrofit2.http.* + +interface RestfulBookerApi { + + @Headers("Content-Type:application/json") + @POST("auth") + suspend fun createAuthToken( + @Field("username") username: String, + @Field("password") password: String, + ): Response + + @GET("booking") + suspend fun getBookingIds( + @Field("firstname") firstname: String? = null, + @Field("lastname") lastname: String? = null, + @Field("checkin") checkin: String? = null, + @Field("checkout") checkout: String? = null, + ): Response> + + @GET("booking/{id}") + suspend fun getSingleBooking( + @Path("id") id: String + ): Response + + @Headers("Content-Type:application/json") + @POST("booking") + suspend fun createBooking( + @Body booking: BookingRequest, + ): Response + + @Headers("Content-Type:application/json", "Accept: application/json") + @PUT("booking/{id}") + suspend fun updateBooking( + @Path("id") id: String, + @Body booking: BookingRequest, + @Header("Authorization") token: String + ): Response + + @Headers("Content-Type:application/json", "Accept: application/json") + @PATCH("booking/{id}") + suspend fun partialUpdateBooking( + @Path("id") id: String, + @Header("Authorization") token: String, + @Field("firstname") firstname: String? = null, + @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 + + @DELETE("booking/{id}") + suspend fun deleteBooking( + @Path("id") id: String + ): Response +} \ No newline at end of file diff --git a/src/main/kotlin/model/AuthResponse.kt b/src/main/kotlin/model/AuthResponse.kt new file mode 100644 index 0000000..d8e0ac1 --- /dev/null +++ b/src/main/kotlin/model/AuthResponse.kt @@ -0,0 +1,5 @@ +package model + +class AuthResponse { + var token: String? = null +} diff --git a/src/main/kotlin/model/BookingIdResponse.kt b/src/main/kotlin/model/BookingIdResponse.kt new file mode 100644 index 0000000..b1a2070 --- /dev/null +++ b/src/main/kotlin/model/BookingIdResponse.kt @@ -0,0 +1,5 @@ +package model + +class BookingIdResponse { + var bookingid = 0 +} diff --git a/src/main/kotlin/model/BookingRequest.kt b/src/main/kotlin/model/BookingRequest.kt new file mode 100644 index 0000000..9a5da1e --- /dev/null +++ b/src/main/kotlin/model/BookingRequest.kt @@ -0,0 +1,12 @@ +package model + +data class BookingRequest ( + var firstname: String? = null, + var lastname: String? = null, + var totalprice: Int = 0, + var depositpaid: Boolean = false, + var bookingdates: Bookingdates? = null, + var additionalneeds: String? = null, +) + + diff --git a/src/main/kotlin/model/BookingResponse.kt b/src/main/kotlin/model/BookingResponse.kt new file mode 100644 index 0000000..10082c5 --- /dev/null +++ b/src/main/kotlin/model/BookingResponse.kt @@ -0,0 +1,11 @@ +package model + + +data class BookingResponse ( + var firstname: String? = null, + var lastname: String? = null, + var totalprice: Int = 0, + var depositpaid: Boolean = false, + var bookingdates: Bookingdates? = null, + var additionalneeds: String? = null +) \ No newline at end of file diff --git a/src/main/kotlin/model/Bookingdates.kt b/src/main/kotlin/model/Bookingdates.kt new file mode 100644 index 0000000..6a1c1e7 --- /dev/null +++ b/src/main/kotlin/model/Bookingdates.kt @@ -0,0 +1,6 @@ +package model + +data class Bookingdates ( + var checkin: String? = null, + var checkout: String? = null, +) \ No newline at end of file diff --git a/src/main/kotlin/model/CreateBookingResponse.kt b/src/main/kotlin/model/CreateBookingResponse.kt new file mode 100644 index 0000000..e280a85 --- /dev/null +++ b/src/main/kotlin/model/CreateBookingResponse.kt @@ -0,0 +1,6 @@ +package model + +class CreateBookingResponse { + var bookingid = 0 + var booking: BookingResponse? = null +} \ No newline at end of file diff --git a/src/test/kotlin/NetworkTests.kt b/src/test/kotlin/NetworkTests.kt new file mode 100644 index 0000000..f487b47 --- /dev/null +++ b/src/test/kotlin/NetworkTests.kt @@ -0,0 +1,20 @@ +import kotlinx.coroutines.runBlocking +import retrofit2.Response +import java.io.IOException + +abstract class NetworkTests { + + fun responseUnwrap( + call: suspend () -> Response + ): T { + + val response = runBlocking { call.invoke() } + if (response.isSuccessful) { + return response.body()!! + } else { + val error = response.errorBody()?.string() + + throw IOException(error ?: "Unable to handle end point") + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/Tests.kt b/src/test/kotlin/Tests.kt new file mode 100644 index 0000000..e76947f --- /dev/null +++ b/src/test/kotlin/Tests.kt @@ -0,0 +1,105 @@ +import api.BookerApi +import api.RestfulBookerApi +import io.restassured.RestAssured.given +import kotlinx.coroutines.runBlocking +import model.BookingRequest +import model.Bookingdates +import org.apache.logging.log4j.LogManager +import org.junit.FixMethodOrder +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import org.junit.runners.MethodSorters +import utils.FileReader + +@FixMethodOrder(MethodSorters.DEFAULT) +class Tests : NetworkTests(){ + + companion object { + private lateinit var bookerApi: RestfulBookerApi + private lateinit var fileReader: FileReader + private val logger = LogManager.getLogger(Tests::class.java) + + @BeforeAll + @JvmStatic + internal fun beforeAll() { + bookerApi = BookerApi().invoke() + } + + @AfterAll + @JvmStatic + internal fun afterAll() { + + } + } + + + /** + * 1. Generate 3 new bookings --> Log below scenarios to a log file + * o All available booking ID's + * o Above added 3 new booking details + */ + @Test() + fun testScenarioOne() { + // Given + val bookingRequestOne = fileReader.readJsonFileFromResources("test1") + val bookingRequestTwo = fileReader.readJsonFileFromResources("test2") + val bookingRequestThree = BookingRequest( + firstname = "Mark", + lastname = "Wahlberg", + totalprice = 750, + depositpaid = true, + bookingdates = Bookingdates( + checkin = "2025-01-01", + checkout = "2025-01-10" + ), + additionalneeds = "Breakfast" + ) + + // When + val createBookingOneResponse = responseUnwrap { bookerApi.createBooking(bookingRequestOne) } + val createBookingTwoResponse = responseUnwrap { bookerApi.createBooking(bookingRequestTwo) } + val createBookingThreeResponse = responseUnwrap { bookerApi.createBooking(bookingRequestThree) } + + // Then + val bookingIds = responseUnwrap { bookerApi.getBookingIds() } + logger.trace("Available booking IDs: ${bookingIds.joinToString()}") + bookingIds.forEach { + val currentBookingResponse = responseUnwrap { bookerApi.getSingleBooking("$it") } + logger.trace(currentBookingResponse) + } + } + + /** + * 2. Modify total price for test1 to 1000 and test2 to 1500. Log this data to same log file. + * Test data : + * • Test1 with total price 500 who deposit is paid with random check in and checkout dates and having preference of lunch as additional need. + * + */ + @Test() + fun testScenarioTwo() { + // Given + + // When + + // Then + } + + @Test() + fun testScenarioThree() { + // Given + + // When + + // Then + } + + @Test() + fun testScenarioFour() { + // Given + + // When + + // Then + } +} \ No newline at end of file diff --git a/src/test/kotlin/utils/FileReader.kt b/src/test/kotlin/utils/FileReader.kt new file mode 100644 index 0000000..bb64a91 --- /dev/null +++ b/src/test/kotlin/utils/FileReader.kt @@ -0,0 +1,21 @@ +package utils + +import com.google.gson.Gson +import java.io.BufferedReader +import java.lang.reflect.ParameterizedType + +class FileReader { + + private val gson by lazy { Gson() } + + fun readJsonFileFromResources(fileName: String): T { + val iStream = this::class.java.getResourceAsStream("$fileName.json") + ?: throw IllegalStateException("Unable to read the file requested") + val data = iStream.bufferedReader().use(BufferedReader::readText) + val genericType = ((javaClass.genericSuperclass as? ParameterizedType) + ?.actualTypeArguments?.getOrNull(0) as? Class) + ?: throw IllegalStateException("Can not find class from generic argument") + + return gson.fromJson(data, genericType) + } +} \ No newline at end of file diff --git a/src/test/resources/test1.json b/src/test/resources/test1.json new file mode 100644 index 0000000..8f86452 --- /dev/null +++ b/src/test/resources/test1.json @@ -0,0 +1,11 @@ +{ + "firstname" : "Jim", + "lastname" : "Brown", + "totalprice" : 500, + "depositpaid" : true, + "bookingdates" : { + "checkin" : "2025-01-01", + "checkout" : "2025-01-10" + }, + "additionalneeds" : "Lunch" +} \ No newline at end of file diff --git a/src/test/resources/test2.json b/src/test/resources/test2.json new file mode 100644 index 0000000..8f86452 --- /dev/null +++ b/src/test/resources/test2.json @@ -0,0 +1,11 @@ +{ + "firstname" : "Jim", + "lastname" : "Brown", + "totalprice" : 500, + "depositpaid" : true, + "bookingdates" : { + "checkin" : "2025-01-01", + "checkout" : "2025-01-10" + }, + "additionalneeds" : "Lunch" +} \ No newline at end of file