Initial commit

- Retrofit used to call API
This commit is contained in:
2024-07-18 17:04:56 +01:00
commit 982b4e8d5d
20 changed files with 534 additions and 0 deletions

38
.gitignore vendored Normal file
View File

@@ -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

3
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

7
.idea/encodings.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/kotlin" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>

View File

@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="ReplaceUntilWithRangeUntil" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
</profile>
</component>

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

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.9.0" />
</component>
</project>

14
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_20" default="true" project-jdk-name="20" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

149
pom.xml Normal file
View File

@@ -0,0 +1,149 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
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>
<artifactId>api-testing-automation-framework</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>consoleApp</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<kotlin.code.style>official</kotlin.code.style>
<kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
</properties>
<repositories>
<repository>
<id>mavenCentral</id>
<url>https://repo1.maven.org/maven2/</url>
</repository>
</repositories>
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>1.9.0</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.22.2</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<configuration>
<mainClass>MainKt</mainClass>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit5</artifactId>
<version>1.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>1.9.0</version>
</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 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.squareup.retrofit2/retrofit -->
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>logging-interceptor</artifactId>
<version>4.12.0</version>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>api-testing-automation-framework</artifactId>
<version>1.0-SNAPSHOT</version>
</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>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.19.0</version>
</dependency>
</dependencies>
</project>

View File

@@ -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)
}
}

View File

@@ -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<AuthResponse>
@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<List<BookingIdResponse>>
@GET("booking/{id}")
suspend fun getSingleBooking(
@Path("id") id: String
): Response<BookingResponse>
@Headers("Content-Type:application/json")
@POST("booking")
suspend fun createBooking(
@Body booking: BookingRequest,
): Response<CreateBookingResponse>
@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<BookingResponse>
@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<BookingResponse>
@DELETE("booking/{id}")
suspend fun deleteBooking(
@Path("id") id: String
): Response<Any>
}

View File

@@ -0,0 +1,5 @@
package model
class AuthResponse {
var token: String? = null
}

View File

@@ -0,0 +1,5 @@
package model
class BookingIdResponse {
var bookingid = 0
}

View File

@@ -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,
)

View File

@@ -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
)

View File

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

View File

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

View File

@@ -0,0 +1,20 @@
import kotlinx.coroutines.runBlocking
import retrofit2.Response
import java.io.IOException
abstract class NetworkTests {
fun <T : Any> responseUnwrap(
call: suspend () -> Response<T>
): 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")
}
}
}

105
src/test/kotlin/Tests.kt Normal file
View File

@@ -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<BookingRequest>("test1")
val bookingRequestTwo = fileReader.readJsonFileFromResources<BookingRequest>("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
}
}

View File

@@ -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 <T : Any?> 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<T>)
?: throw IllegalStateException("Can not find class from generic argument")
return gson.fromJson(data, genericType)
}
}

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"
}

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"
}