Merge branch 'tests'

This commit is contained in:
Hubert Van De Walle 2020-06-14 23:10:03 +02:00
commit f09219b032
27 changed files with 493 additions and 260 deletions

View File

@ -129,6 +129,11 @@
<artifactId>ktorm-support-mysql</artifactId>
<version>${ktorm_version}</version>
</dependency>
<dependency>
<groupId>me.liuwj.ktorm</groupId>
<artifactId>ktorm-jackson</artifactId>
<version>${ktorm_version}</version>
</dependency>
<dependency>
<groupId>com.github.hekeki</groupId>
<artifactId>huckleberry</artifactId>
@ -161,6 +166,36 @@
<version>1.0.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ktor</groupId>
<artifactId>ktor-server-tests</artifactId>
<version>${ktor_version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mariadb</artifactId>
<version>1.14.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.amshove.kluent</groupId>
<artifactId>kluent</artifactId>
<version>1.61</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.skyscreamer</groupId>
<artifactId>jsonassert</artifactId>
<version>1.5.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.mockk</groupId>
<artifactId>mockk</artifactId>
<version>1.10.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>${project.basedir}/src</sourceDirectory>
@ -174,12 +209,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>Test*</include>
<include>*Test</include>
</includes>
</configuration>
<version>3.0.0-M4</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
@ -210,7 +240,7 @@
<configuration>
<experimentalCoroutines>enable</experimentalCoroutines>
<sourceDirs>
<sourceDir>${project.basedir}/src/test</sourceDir>
<sourceDir>${project.basedir}/test</sourceDir>
</sourceDirs>
</configuration>
</execution>

View File

@ -1,9 +1,46 @@
CREATE TABLE `Users`
create table Users
(
`id` int PRIMARY KEY AUTO_INCREMENT,
`username` varchar(50) UNIQUE NOT NULL,
`email` varchar(255) UNIQUE NOT NULL,
`password` varchar(255) NOT NULL,
`created_at` datetime NOT NULL,
`last_login` datetime
id int auto_increment primary key,
username varchar(50) not null,
email varchar(255) not null,
password varchar(255) not null,
created_at datetime not null,
last_login datetime null,
constraint email unique (email),
constraint username unique (username)
);
create table Notes
(
uuid binary(16) not null primary key,
title varchar(50) not null,
user_id int not null,
updated_at datetime null,
constraint Notes_fk_user foreign key (user_id) references Users (id) on delete cascade
);
create table Chapters
(
id int auto_increment primary key,
number int not null,
title varchar(50) not null,
content text not null,
note_uuid binary(16) not null,
constraint Chapters_fk_note foreign key (note_uuid) references Notes (uuid) on delete cascade
);
create index note_uuid on Chapters (note_uuid);
create index user_id on Notes (user_id);
create table Tags
(
id int auto_increment primary key,
name varchar(50) not null,
note_uuid binary(16) not null,
constraint Tags_fk_note foreign key (note_uuid) references Notes (uuid) on delete cascade
);
create index note_uuid on Tags (note_uuid);

View File

@ -1,11 +0,0 @@
CREATE TABLE `Notes`
(
`id` int PRIMARY KEY AUTO_INCREMENT,
`title` varchar(50) NOT NULL,
`content` text NOT NULL,
`user_id` int NOT NULL,
`last_viewed` datetime
);
ALTER TABLE `Notes`
ADD FOREIGN KEY (`user_id`) REFERENCES `Users` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT;

View File

@ -1,9 +0,0 @@
CREATE TABLE `Tags`
(
`id` int PRIMARY KEY AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`note_id` int NOT NULL
);
ALTER TABLE `Tags`
ADD FOREIGN KEY (`note_id`) REFERENCES `Notes` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT;

View File

@ -1,13 +0,0 @@
CREATE TABLE `Chapters`
(
`id` int PRIMARY KEY AUTO_INCREMENT,
`number` int NOT NULL,
`content` text NOT NULL,
`note_id` int NOT NULL
);
ALTER TABLE `Chapters`
ADD FOREIGN KEY (`note_id`) REFERENCES `Notes` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT;
ALTER TABLE `Notes`
DROP COLUMN `content`;

View File

@ -1,5 +0,0 @@
ALTER TABLE `Notes`
DROP COLUMN `last_viewed`;
ALTER TABLE `Notes`
ADD COLUMN `updated_at` datetime;

View File

@ -1,2 +0,0 @@
ALTER TABLE `Chapters`
ADD COLUMN `title` varchar(50);

View File

@ -1,19 +0,0 @@
-- ON DELETE -> CASCADE
ALTER TABLE `Notes`
DROP CONSTRAINT `Notes_ibfk_1`;
ALTER TABLE `Notes`
ADD FOREIGN KEY (`user_id`) REFERENCES `Users` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT;
ALTER TABLE `Chapters`
DROP CONSTRAINT `Chapters_ibfk_1`;
ALTER TABLE `Chapters`
ADD FOREIGN KEY (`note_id`) REFERENCES `Notes` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT;
ALTER TABLE `Tags`
DROP CONSTRAINT `Tags_ibfk_1`;
ALTER TABLE `Tags`
ADD FOREIGN KEY (`note_id`) REFERENCES `Notes` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT;

View File

@ -1,37 +0,0 @@
-- no need to migrate existing data yet
drop table if exists Chapters;
drop table if exists Tags;
drop table if exists Notes;
CREATE TABLE `Notes`
(
`uuid` binary(16) PRIMARY KEY,
`title` varchar(50) NOT NULL,
`user_id` int NOT NULL,
`updated_at` datetime
);
ALTER TABLE `Notes`
ADD FOREIGN KEY (`user_id`) REFERENCES `Users` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT;
CREATE TABLE `Tags`
(
`id` int PRIMARY KEY AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`note_uuid` binary(16) NOT NULL
);
ALTER TABLE `Tags`
ADD FOREIGN KEY (`note_uuid`) REFERENCES `Notes` (`uuid`) ON DELETE CASCADE ON UPDATE RESTRICT;
CREATE TABLE `Chapters`
(
`id` int PRIMARY KEY AUTO_INCREMENT,
`number` int NOT NULL,
`title` varchar(50) NOT NULL,
`content` text NOT NULL,
`note_uuid` binary(16) NOT NULL
);
ALTER TABLE `Chapters`
ADD FOREIGN KEY (`note_uuid`) REFERENCES `Notes` (`uuid`) ON DELETE CASCADE ON UPDATE RESTRICT;

View File

@ -12,4 +12,6 @@
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="io.netty" level="INFO"/>
<logger name="org.flywaydb.core" level="INFO"/>
<logger name="org.testcontainers" level="INFO"/>
<logger name="com.github.dockerjava" level="INFO"/>
</configuration>

21
api/src/Dependencies.kt Normal file
View File

@ -0,0 +1,21 @@
package be.vandewalleh
import be.vandewalleh.features.configurationModule
import be.vandewalleh.migrations.Migration
import be.vandewalleh.services.serviceModule
import me.liuwj.ktorm.database.*
import org.kodein.di.Kodein
import org.kodein.di.generic.bind
import org.kodein.di.generic.instance
import org.kodein.di.generic.singleton
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import javax.sql.DataSource
val mainModule = Kodein.Module("main") {
import(serviceModule)
import(configurationModule)
bind<Logger>() with singleton { LoggerFactory.getLogger("Application") }
bind<Migration>() with singleton { Migration(this.kodein) }
bind<Database>() with singleton { Database.connect(this.instance<DataSource>()) }
}

View File

@ -20,15 +20,13 @@ import org.slf4j.Logger
import org.slf4j.LoggerFactory
import javax.sql.DataSource
val kodein = Kodein {
import(serviceModule)
import(configurationModule)
bind<Logger>() with singleton { LoggerFactory.getLogger("Application") }
bind<Migration>() with singleton { Migration(this.kodein) }
bind<Database>() with singleton { Database.connect(this.instance<DataSource>()) }
}
fun main() {
val kodein = Kodein{
import(mainModule)
}
val config by kodein.instance<Config>()
val logger by kodein.instance<Logger>()
logger.info("Running application with configuration $config")
@ -43,7 +41,7 @@ fun serve(kodein: Kodein) {
val logger by kodein.instance<Logger>()
val env = applicationEngineEnvironment {
module {
module()
module(kodein)
}
log = logger
connector {
@ -55,7 +53,7 @@ fun serve(kodein: Kodein) {
}
fun Application.module() {
fun Application.module(kodein: Kodein) {
loadFeatures(kodein)
routing {

View File

@ -1,12 +1,12 @@
package be.vandewalleh.auth
import be.vandewalleh.kodein
import io.ktor.application.*
import io.ktor.auth.*
import io.ktor.auth.jwt.*
import org.kodein.di.Kodein
import org.kodein.di.generic.instance
fun Application.authenticationModule() {
fun Application.authenticationModule(kodein: Kodein) {
install(Authentication) {
jwt {
val simpleJwt by kodein.instance<SimpleJWT>(tag = "auth")

View File

@ -1,19 +1,16 @@
package be.vandewalleh.extensions
import be.vandewalleh.auth.UserDbIdPrincipal
import be.vandewalleh.kodein
import be.vandewalleh.services.FullNoteCreateDTO
import be.vandewalleh.services.FullNotePatchDTO
import be.vandewalleh.services.UserService
import io.ktor.application.*
import io.ktor.auth.*
import io.ktor.http.*
import io.ktor.request.*
import io.ktor.response.*
import org.kodein.di.generic.instance
suspend fun ApplicationCall.respondStatus(status: HttpStatusCode) {
respond(status, status.description)
respond(status, """{"msg": "${status.description}"}""")
}
/**

View File

@ -3,9 +3,12 @@ package be.vandewalleh.features
import io.ktor.application.*
import io.ktor.features.*
import io.ktor.jackson.*
import me.liuwj.ktorm.jackson.*
fun Application.contentNegotiationFeature() {
install(ContentNegotiation) {
jackson {}
jackson {
registerModule(KtormModule())
}
}
}

View File

@ -11,6 +11,6 @@ fun Application.loadFeatures(kodein: Kodein) {
corsFeature()
}
contentNegotiationFeature()
authenticationModule()
authenticationModule(kodein)
handleErrors()
}

View File

@ -1,8 +1,8 @@
package be.vandewalleh.routing
import be.vandewalleh.entities.User
import be.vandewalleh.extensions.respondStatus
import be.vandewalleh.extensions.userId
import be.vandewalleh.services.UserDto
import be.vandewalleh.services.UserService
import io.ktor.application.*
import io.ktor.auth.*
@ -13,49 +13,52 @@ import io.ktor.routing.*
import org.kodein.di.Kodein
import org.kodein.di.generic.instance
import org.mindrot.jbcrypt.BCrypt
import java.time.LocalDateTime
fun Routing.user(kodein: Kodein) {
val userService by kodein.instance<UserService>()
post("/user/test") {
val user = call.receive<User>()
call.respond(user)
}
route("/user") {
post {
val user = call.receive<UserDto>()
val user = call.receive<User>()
if (userService.userExists(user.username, user.email))
return@post call.respond(HttpStatusCode.Conflict)
return@post call.respondStatus(HttpStatusCode.Conflict)
val hashedPassword = BCrypt.hashpw(user.password, BCrypt.gensalt())
userService.createUser(
UserDto(user.username, user.email, hashedPassword)
)
userService.createUser(user.username, user.email, hashedPassword)
call.respondStatus(HttpStatusCode.Created)
}
authenticate {
put {
val user = call.receive<UserDto>()
val user = call.receive<User>()
if (userService.userExists(user.username, user.email))
return@put call.respond(HttpStatusCode.Conflict)
val hashedPassword = BCrypt.hashpw(user.password, BCrypt.gensalt())
userService.updateUser(
call.userId(),
UserDto(user.username, user.email, hashedPassword)
)
userService.updateUser(call.userId(), user.username, user.email, hashedPassword)
call.respondStatus(HttpStatusCode.OK)
}
delete {
userService.deleteUser(call.userId())
call.respondStatus(HttpStatusCode.OK)
val status = if (userService.deleteUser(call.userId()))
HttpStatusCode.OK
else
HttpStatusCode.NotFound
call.respondStatus(status)
}
}
}
}

View File

@ -8,6 +8,7 @@ import me.liuwj.ktorm.entity.*
import org.kodein.di.Kodein
import org.kodein.di.KodeinAware
import org.kodein.di.generic.instance
import java.sql.SQLIntegrityConstraintViolationException
import java.time.LocalDateTime
/**
@ -28,19 +29,14 @@ class UserService(override val kodein: Kodein) : KodeinAware {
}
/**
* returns a user email and password from it's email if found or null
* returns a user email and password from it's username if found or null
*/
fun getFromUsername(username: String): UserSchema? {
fun getFromUsername(username: String): User? {
return db.from(Users)
.select(Users.email, Users.password, Users.id)
.where { Users.username eq username }
.map { row ->
UserSchema(
row[Users.id]!!,
username,
row[Users.email]!!,
row[Users.password]!!
)
Users.createEntity(row)
}
.firstOrNull()
}
@ -59,11 +55,11 @@ class UserService(override val kodein: Kodein) : KodeinAware {
.firstOrNull() != null
}
fun getUserInfo(id: Int): UserInfoDto? {
fun getUserInfo(id: Int): User? {
return db.from(Users)
.select(Users.email, Users.username)
.where { Users.id eq id }
.map { UserInfoDto(it[Users.username]!!, it[Users.email]!!) }
.map { Users.createEntity(it) }
.firstOrNull()
}
@ -71,25 +67,30 @@ class UserService(override val kodein: Kodein) : KodeinAware {
* create a new user
* password should already be hashed
*/
fun createUser(user: UserDto) {
fun createUser(username: String, email: String, hashedPassword: String): User? {
try {
db.useTransaction {
val newUser = User {
this.username = user.username
this.email = user.email
this.password = user.password
this.username = username
this.email = email
this.password = hashedPassword
this.createdAt = LocalDateTime.now()
}
db.sequenceOf(Users).add(newUser)
return newUser
}
} catch (e: SQLIntegrityConstraintViolationException) {
return null
}
}
fun updateUser(userId: Int, user: UserDto) {
fun updateUser(userId: Int, username: String, email: String, hashedPassword: String) {
db.useTransaction {
db.update(Users) {
it.username to user.username
it.email to user.email
it.password to user.password
it.username to username
it.email to email
it.password to hashedPassword
where {
it.id eq userId
}
@ -97,13 +98,14 @@ class UserService(override val kodein: Kodein) : KodeinAware {
}
}
fun deleteUser(userId: Int) {
fun deleteUser(userId: Int): Boolean {
db.useTransaction {
db.delete(Users) { it.id eq userId }
return when (db.delete(Users) { it.id eq userId }) {
1 -> true
0 -> false
else -> error("??")
}
}
}
}
data class UserSchema(val id: Int, val username: String, val email: String, val password: String)
data class UserDto(val username: String, val email: String, val password: String)
data class UserInfoDto(val username: String, val email: String)

View File

@ -1,37 +0,0 @@
import be.vandewalleh.services.ChapterDTO
import be.vandewalleh.services.FullNoteCreateDTO
import be.vandewalleh.services.NotesService
import com.github.javafaker.Faker
import org.junit.jupiter.api.Test
import org.kodein.di.generic.instance
class FakeDataTest {
val notesService by kodein.instance<NotesService>()
@Test
fun addNotes() {
val faker = Faker()
val title = faker.hobbit().quote()
val tags = listOf(
faker.beer().name(),
faker.beer().yeast()
)
val chapters = listOf(
ChapterDTO(
faker.animal().name(),
faker.lorem().paragraph()
),
ChapterDTO(
faker.animal().name(),
faker.lorem().paragraph()
)
)
val note = FullNoteCreateDTO(title, tags, chapters)
notesService.createNote(1, note)
}
}

View File

@ -1,50 +0,0 @@
import be.vandewalleh.services.NotesService
import be.vandewalleh.services.UserService
import com.hekeki.huckleberry.Benchmark
import com.hekeki.huckleberry.BenchmarkRunner
import com.hekeki.huckleberry.BenchmarkTest
import com.hekeki.huckleberry.TimeUnit
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import me.liuwj.ktorm.database.*
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
import org.kodein.di.Kodein
import org.kodein.di.generic.bind
import org.kodein.di.generic.instance
import org.kodein.di.generic.singleton
val hikariConfig = HikariConfig().apply {
jdbcUrl = "jdbc:mariadb://localhost:3306/notes"
username = "notes"
password = "notes"
}
val dataSource = HikariDataSource(hikariConfig)
val db = Database.Companion.connect(dataSource)
val kodein = Kodein {
bind<Database>() with singleton { db }
bind<UserService>() with singleton { UserService(this.kodein) }
bind<NotesService>() with singleton { NotesService(this.kodein) }
}
val notesService by kodein.instance<NotesService>()
@Benchmark(threads = 1, iterations = 30, warmup = true, warmupIterations = 1000, timeUnit = TimeUnit.MILLIS)
class RetrieveNotesBenchmarkTest : BenchmarkTest {
override fun execute() {
notesService.getNotes(15)
}
@Test
fun compute() {
val benchmarkResult = BenchmarkRunner(RetrieveNotesBenchmarkTest::class.java).run()
assertTrue(benchmarkResult.median(2, TimeUnit.MILLIS))
assertTrue(benchmarkResult.maxTime(4, TimeUnit.MILLIS))
}
}

View File

@ -0,0 +1,142 @@
package routing
import be.vandewalleh.auth.SimpleJWT
import be.vandewalleh.entities.User
import be.vandewalleh.mainModule
import be.vandewalleh.module
import be.vandewalleh.services.UserService
import io.ktor.http.*
import io.ktor.server.testing.*
import io.mockk.every
import io.mockk.mockk
import org.amshove.kluent.*
import org.junit.jupiter.api.*
import org.kodein.di.Kodein
import org.kodein.di.generic.bind
import org.kodein.di.generic.instance
import utils.*
import java.time.LocalDateTime
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class UserControllerKtTest {
private val userService = mockk<UserService>()
init {
// new user
every { userService.userExists("new", "new@test.com") } returns false
every { userService.createUser("new", "new@test.com", any()) } returns User {
this.createdAt = LocalDateTime.now()
this.username = "new"
this.email = "new@test.com"
}
// existing user
every { userService.userExists("existing", "existing@test.com") } returns true
every { userService.createUser("existing", "existing@test.com", any()) } returns null
every { userService.getUserId("existing@test.com") } returns 1
every { userService.deleteUser(1) } returns true andThen false
// modified user
every { userService.userExists("modified", "modified@test.com") } returns true
every {
userService.userExists(
and(not("modified"), not("existing")),
and(not("modified@test.com"), not("existing@test.com"))
)
} returns false
every { userService.userExists(1) } returns true
every { userService.createUser("modified", "modified@test.com", any()) } returns null
every { userService.getUserId("modified@test.com") } returns 1
every { userService.updateUser(1, "ThisIsMyNewName", "ThisIsMyNewName@mail.com", any()) } returns Unit
}
private val kodein = Kodein {
import(mainModule, allowOverride = true)
bind<UserService>(overrides = true) with instance(userService)
}
private val testEngine = TestApplicationEngine().apply {
start()
application.module(kodein)
}
@Nested
inner class CreateUser {
@Test
fun `create a new user`() {
val res = testEngine.post("/user") {
json {
it["username"] = "new"
it["password"] = "test"
it["email"] = "new@test.com"
}
}
res.status() `should be equal to` HttpStatusCode.Created
res.content `should be equal to json` """{msg:"Created"}"""
}
@Test
fun `create an existing user`() {
val res = testEngine.post("/user") {
json {
it["username"] = "existing"
it["email"] = "existing@test.com"
it["password"] = "test"
}
}
res.status() `should be equal to` HttpStatusCode.Conflict
res.content `should be equal to json` """{msg:"Conflict"}"""
}
}
@Nested
inner class DeleteUser {
@Test
fun `delete an existing user`() {
val authJwt by kodein.instance<SimpleJWT>("auth")
val token = authJwt.sign(1)
val res = testEngine.delete("/user") {
addHeader(HttpHeaders.Authorization, "Bearer $token")
}
res.status() `should be equal to` HttpStatusCode.OK
res.content `should be equal to json` """{msg:"OK"}"""
// try again
val res2 = testEngine.delete("/user") {
setToken(token)
}
res2.status() `should be equal to` HttpStatusCode.NotFound
res2.content `should be equal to json` """{msg:"Not Found"}"""
}
}
@Nested
inner class ModifyUser {
@Test
fun `modify a user`() {
val authJwt by kodein.instance<SimpleJWT>("auth")
val token = authJwt.sign(1)
val res = testEngine.put("/user") {
setToken(token)
json {
it["username"] = "ThisIsMyNewName"
it["email"] = "ThisIsMyNewName@mail.com"
}
}
res.status() `should be equal to` HttpStatusCode.OK
res.content `should be equal to json` """{msg:"OK"}"""
}
}
}

View File

@ -0,0 +1,69 @@
package services
import be.vandewalleh.mainModule
import be.vandewalleh.migrations.Migration
import be.vandewalleh.services.UserService
import org.amshove.kluent.*
import org.junit.jupiter.api.*
import org.kodein.di.Kodein
import org.kodein.di.generic.bind
import org.kodein.di.generic.instance
import org.kodein.di.generic.singleton
import utils.KMariadbContainer
import utils.testContainerDataSource
import javax.sql.DataSource
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation::class)
class UserServiceTest {
private val mariadb = KMariadbContainer().apply { start() }
private val kodein = Kodein {
import(mainModule, allowOverride = true)
bind<DataSource>(overrides = true) with singleton { testContainerDataSource(mariadb) }
}
private val migration by kodein.instance<Migration>()
init {
migration.migrate()
}
private val userService by kodein.instance<UserService>()
@Test
@Order(1)
fun `test create user`() {
val username = "hubert"
val email = "a@a"
val password = "password"
println(userService.createUser(username, email, password))
val id = userService.getUserId(email)
id `should not be` null
userService.getUserInfo(id!!)!!.let {
it.username `should be equal to` username
it.email `should be equal to` email
}
}
@Test
@Order(2)
fun `test create same user`() {
userService.createUser(username = "hubert", hashedPassword = "password", email = "a@a") `should be` null
}
@Test
@Order(3)
fun `test delete user`() {
val email = "a@a"
val id = userService.getUserId(email)!!
userService.deleteUser(id)
userService.getUserId(email) `should be` null
userService.getUserInfo(id) `should be` null
}
}

View File

@ -0,0 +1,19 @@
package utils
import org.skyscreamer.jsonassert.JSONAssert
infix fun String?.shouldBeEqualToJson(expected: String?) = JSONAssert.assertEquals(expected, this, false)
infix fun String?.`should be equal to json`(expected: String?) = shouldBeEqualToJson(expected)
infix fun String?.shouldStrictlyBeEqualToJson(expected: String?) = JSONAssert.assertEquals(expected, this, true)
infix fun String?.`should strictly be equal to json`(expected: String?) = shouldStrictlyBeEqualToJson(expected)
infix fun String?.shouldNotStrictlyBeEqualToJson(expected: String?) = JSONAssert.assertNotEquals(expected, this, true)
infix fun String?.`should not strictly be equal to json`(expected: String?) = shouldNotStrictlyBeEqualToJson(expected)
infix fun String?.shouldNotBeEqualToJson(expected: String?) = JSONAssert.assertNotEquals(expected, this, false)
infix fun String?.`should not be equal to json`(expected: String?) = shouldNotBeEqualToJson(expected)

View File

@ -0,0 +1,23 @@
package utils
import org.json.JSONObject
operator fun JSONObject.set(name: String, value: String) {
this.put(name, value)
}
operator fun JSONObject.set(name: String, value: Double) {
this.put(name, value)
}
operator fun JSONObject.set(name: String, value: Long) {
this.put(name, value)
}
operator fun JSONObject.set(name: String, value: Int) {
this.put(name, value)
}
operator fun JSONObject.set(name: String, value: Boolean) {
this.put(name, value)
}

View File

@ -0,0 +1,52 @@
package utils
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpMethod
import io.ktor.server.testing.*
import org.json.JSONObject
fun TestApplicationRequest.json(block: (JSONObject) -> Unit) {
addHeader(HttpHeaders.ContentType, "application/json")
setBody(JSONObject().apply(block).toString())
}
fun TestApplicationRequest.setToken(token: String) {
addHeader(HttpHeaders.Authorization, "Bearer $token")
}
fun TestApplicationEngine.post(
uri: String,
setup: TestApplicationRequest.() -> Unit = {}
): TestApplicationResponse = handleRequest {
this.uri = uri
this.method = HttpMethod.Post
setup()
}.response
fun TestApplicationEngine.get(
uri: String,
setup: TestApplicationRequest.() -> Unit = {}
): TestApplicationResponse = handleRequest {
this.uri = uri
this.method = HttpMethod.Get
setup()
}.response
fun TestApplicationEngine.delete(
uri: String,
setup: TestApplicationRequest.() -> Unit = {}
): TestApplicationResponse = handleRequest {
this.uri = uri
this.method = HttpMethod.Delete
setup()
}.response
fun TestApplicationEngine.put(
uri: String,
setup: TestApplicationRequest.() -> Unit = {}
): TestApplicationResponse = handleRequest {
this.uri = uri
this.method = HttpMethod.Put
setup()
}.response

View File

@ -0,0 +1,18 @@
package utils
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import org.testcontainers.containers.MariaDBContainer
import javax.sql.DataSource
class KMariadbContainer : MariaDBContainer<KMariadbContainer>()
fun testContainerDataSource(container: KMariadbContainer): DataSource {
val hikariConfig = HikariConfig().apply {
jdbcUrl = container.jdbcUrl
username = container.username
password = container.password
}
return HikariDataSource(hikariConfig)
}