Merge branch 'tests'
This commit is contained in:
commit
f09219b032
44
api/pom.xml
44
api/pom.xml
@ -129,6 +129,11 @@
|
|||||||
<artifactId>ktorm-support-mysql</artifactId>
|
<artifactId>ktorm-support-mysql</artifactId>
|
||||||
<version>${ktorm_version}</version>
|
<version>${ktorm_version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>me.liuwj.ktorm</groupId>
|
||||||
|
<artifactId>ktorm-jackson</artifactId>
|
||||||
|
<version>${ktorm_version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.hekeki</groupId>
|
<groupId>com.github.hekeki</groupId>
|
||||||
<artifactId>huckleberry</artifactId>
|
<artifactId>huckleberry</artifactId>
|
||||||
@ -161,6 +166,36 @@
|
|||||||
<version>1.0.2</version>
|
<version>1.0.2</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</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>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<sourceDirectory>${project.basedir}/src</sourceDirectory>
|
<sourceDirectory>${project.basedir}/src</sourceDirectory>
|
||||||
@ -174,12 +209,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<configuration>
|
<version>3.0.0-M4</version>
|
||||||
<includes>
|
|
||||||
<include>Test*</include>
|
|
||||||
<include>*Test</include>
|
|
||||||
</includes>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
@ -210,7 +240,7 @@
|
|||||||
<configuration>
|
<configuration>
|
||||||
<experimentalCoroutines>enable</experimentalCoroutines>
|
<experimentalCoroutines>enable</experimentalCoroutines>
|
||||||
<sourceDirs>
|
<sourceDirs>
|
||||||
<sourceDir>${project.basedir}/src/test</sourceDir>
|
<sourceDir>${project.basedir}/test</sourceDir>
|
||||||
</sourceDirs>
|
</sourceDirs>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
|
|||||||
@ -1,9 +1,46 @@
|
|||||||
CREATE TABLE `Users`
|
create table Users
|
||||||
(
|
(
|
||||||
`id` int PRIMARY KEY AUTO_INCREMENT,
|
id int auto_increment primary key,
|
||||||
`username` varchar(50) UNIQUE NOT NULL,
|
username varchar(50) not null,
|
||||||
`email` varchar(255) UNIQUE NOT NULL,
|
email varchar(255) not null,
|
||||||
`password` varchar(255) NOT NULL,
|
password varchar(255) not null,
|
||||||
`created_at` datetime NOT NULL,
|
created_at datetime not null,
|
||||||
`last_login` datetime
|
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);
|
||||||
|
|||||||
@ -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;
|
|
||||||
@ -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;
|
|
||||||
@ -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`;
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
ALTER TABLE `Notes`
|
|
||||||
DROP COLUMN `last_viewed`;
|
|
||||||
|
|
||||||
ALTER TABLE `Notes`
|
|
||||||
ADD COLUMN `updated_at` datetime;
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE `Chapters`
|
|
||||||
ADD COLUMN `title` varchar(50);
|
|
||||||
@ -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;
|
|
||||||
@ -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;
|
|
||||||
@ -12,4 +12,6 @@
|
|||||||
<logger name="org.eclipse.jetty" level="INFO"/>
|
<logger name="org.eclipse.jetty" level="INFO"/>
|
||||||
<logger name="io.netty" level="INFO"/>
|
<logger name="io.netty" level="INFO"/>
|
||||||
<logger name="org.flywaydb.core" level="INFO"/>
|
<logger name="org.flywaydb.core" level="INFO"/>
|
||||||
|
<logger name="org.testcontainers" level="INFO"/>
|
||||||
|
<logger name="com.github.dockerjava" level="INFO"/>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
21
api/src/Dependencies.kt
Normal file
21
api/src/Dependencies.kt
Normal 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>()) }
|
||||||
|
}
|
||||||
@ -20,15 +20,13 @@ import org.slf4j.Logger
|
|||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import javax.sql.DataSource
|
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() {
|
fun main() {
|
||||||
|
|
||||||
|
val kodein = Kodein{
|
||||||
|
import(mainModule)
|
||||||
|
}
|
||||||
|
|
||||||
val config by kodein.instance<Config>()
|
val config by kodein.instance<Config>()
|
||||||
val logger by kodein.instance<Logger>()
|
val logger by kodein.instance<Logger>()
|
||||||
logger.info("Running application with configuration $config")
|
logger.info("Running application with configuration $config")
|
||||||
@ -43,7 +41,7 @@ fun serve(kodein: Kodein) {
|
|||||||
val logger by kodein.instance<Logger>()
|
val logger by kodein.instance<Logger>()
|
||||||
val env = applicationEngineEnvironment {
|
val env = applicationEngineEnvironment {
|
||||||
module {
|
module {
|
||||||
module()
|
module(kodein)
|
||||||
}
|
}
|
||||||
log = logger
|
log = logger
|
||||||
connector {
|
connector {
|
||||||
@ -55,7 +53,7 @@ fun serve(kodein: Kodein) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun Application.module() {
|
fun Application.module(kodein: Kodein) {
|
||||||
loadFeatures(kodein)
|
loadFeatures(kodein)
|
||||||
|
|
||||||
routing {
|
routing {
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
package be.vandewalleh.auth
|
package be.vandewalleh.auth
|
||||||
|
|
||||||
import be.vandewalleh.kodein
|
|
||||||
import io.ktor.application.*
|
import io.ktor.application.*
|
||||||
import io.ktor.auth.*
|
import io.ktor.auth.*
|
||||||
import io.ktor.auth.jwt.*
|
import io.ktor.auth.jwt.*
|
||||||
|
import org.kodein.di.Kodein
|
||||||
import org.kodein.di.generic.instance
|
import org.kodein.di.generic.instance
|
||||||
|
|
||||||
fun Application.authenticationModule() {
|
fun Application.authenticationModule(kodein: Kodein) {
|
||||||
install(Authentication) {
|
install(Authentication) {
|
||||||
jwt {
|
jwt {
|
||||||
val simpleJwt by kodein.instance<SimpleJWT>(tag = "auth")
|
val simpleJwt by kodein.instance<SimpleJWT>(tag = "auth")
|
||||||
|
|||||||
@ -12,4 +12,4 @@ interface User : Entity<User> {
|
|||||||
var password: String
|
var password: String
|
||||||
var createdAt: LocalDateTime
|
var createdAt: LocalDateTime
|
||||||
var lastLogin: LocalDateTime?
|
var lastLogin: LocalDateTime?
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,16 @@
|
|||||||
package be.vandewalleh.extensions
|
package be.vandewalleh.extensions
|
||||||
|
|
||||||
import be.vandewalleh.auth.UserDbIdPrincipal
|
import be.vandewalleh.auth.UserDbIdPrincipal
|
||||||
import be.vandewalleh.kodein
|
|
||||||
import be.vandewalleh.services.FullNoteCreateDTO
|
import be.vandewalleh.services.FullNoteCreateDTO
|
||||||
import be.vandewalleh.services.FullNotePatchDTO
|
import be.vandewalleh.services.FullNotePatchDTO
|
||||||
import be.vandewalleh.services.UserService
|
|
||||||
import io.ktor.application.*
|
import io.ktor.application.*
|
||||||
import io.ktor.auth.*
|
import io.ktor.auth.*
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import io.ktor.request.*
|
import io.ktor.request.*
|
||||||
import io.ktor.response.*
|
import io.ktor.response.*
|
||||||
import org.kodein.di.generic.instance
|
|
||||||
|
|
||||||
suspend fun ApplicationCall.respondStatus(status: HttpStatusCode) {
|
suspend fun ApplicationCall.respondStatus(status: HttpStatusCode) {
|
||||||
respond(status, status.description)
|
respond(status, """{"msg": "${status.description}"}""")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -3,9 +3,12 @@ package be.vandewalleh.features
|
|||||||
import io.ktor.application.*
|
import io.ktor.application.*
|
||||||
import io.ktor.features.*
|
import io.ktor.features.*
|
||||||
import io.ktor.jackson.*
|
import io.ktor.jackson.*
|
||||||
|
import me.liuwj.ktorm.jackson.*
|
||||||
|
|
||||||
fun Application.contentNegotiationFeature() {
|
fun Application.contentNegotiationFeature() {
|
||||||
install(ContentNegotiation) {
|
install(ContentNegotiation) {
|
||||||
jackson {}
|
jackson {
|
||||||
|
registerModule(KtormModule())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,6 @@ fun Application.loadFeatures(kodein: Kodein) {
|
|||||||
corsFeature()
|
corsFeature()
|
||||||
}
|
}
|
||||||
contentNegotiationFeature()
|
contentNegotiationFeature()
|
||||||
authenticationModule()
|
authenticationModule(kodein)
|
||||||
handleErrors()
|
handleErrors()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
package be.vandewalleh.routing
|
package be.vandewalleh.routing
|
||||||
|
|
||||||
|
import be.vandewalleh.entities.User
|
||||||
import be.vandewalleh.extensions.respondStatus
|
import be.vandewalleh.extensions.respondStatus
|
||||||
import be.vandewalleh.extensions.userId
|
import be.vandewalleh.extensions.userId
|
||||||
import be.vandewalleh.services.UserDto
|
|
||||||
import be.vandewalleh.services.UserService
|
import be.vandewalleh.services.UserService
|
||||||
import io.ktor.application.*
|
import io.ktor.application.*
|
||||||
import io.ktor.auth.*
|
import io.ktor.auth.*
|
||||||
@ -13,49 +13,52 @@ import io.ktor.routing.*
|
|||||||
import org.kodein.di.Kodein
|
import org.kodein.di.Kodein
|
||||||
import org.kodein.di.generic.instance
|
import org.kodein.di.generic.instance
|
||||||
import org.mindrot.jbcrypt.BCrypt
|
import org.mindrot.jbcrypt.BCrypt
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
fun Routing.user(kodein: Kodein) {
|
fun Routing.user(kodein: Kodein) {
|
||||||
val userService by kodein.instance<UserService>()
|
val userService by kodein.instance<UserService>()
|
||||||
|
|
||||||
|
post("/user/test") {
|
||||||
|
val user = call.receive<User>()
|
||||||
|
call.respond(user)
|
||||||
|
}
|
||||||
|
|
||||||
route("/user") {
|
route("/user") {
|
||||||
post {
|
post {
|
||||||
val user = call.receive<UserDto>()
|
val user = call.receive<User>()
|
||||||
|
|
||||||
if (userService.userExists(user.username, user.email))
|
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())
|
val hashedPassword = BCrypt.hashpw(user.password, BCrypt.gensalt())
|
||||||
|
|
||||||
userService.createUser(
|
userService.createUser(user.username, user.email, hashedPassword)
|
||||||
UserDto(user.username, user.email, hashedPassword)
|
|
||||||
)
|
|
||||||
|
|
||||||
call.respondStatus(HttpStatusCode.Created)
|
call.respondStatus(HttpStatusCode.Created)
|
||||||
}
|
}
|
||||||
|
|
||||||
authenticate {
|
authenticate {
|
||||||
put {
|
put {
|
||||||
val user = call.receive<UserDto>()
|
val user = call.receive<User>()
|
||||||
|
|
||||||
if (userService.userExists(user.username, user.email))
|
if (userService.userExists(user.username, user.email))
|
||||||
return@put call.respond(HttpStatusCode.Conflict)
|
return@put call.respond(HttpStatusCode.Conflict)
|
||||||
|
|
||||||
val hashedPassword = BCrypt.hashpw(user.password, BCrypt.gensalt())
|
val hashedPassword = BCrypt.hashpw(user.password, BCrypt.gensalt())
|
||||||
|
|
||||||
userService.updateUser(
|
userService.updateUser(call.userId(), user.username, user.email, hashedPassword)
|
||||||
call.userId(),
|
|
||||||
UserDto(user.username, user.email, hashedPassword)
|
|
||||||
)
|
|
||||||
|
|
||||||
call.respondStatus(HttpStatusCode.OK)
|
call.respondStatus(HttpStatusCode.OK)
|
||||||
}
|
}
|
||||||
|
|
||||||
delete {
|
delete {
|
||||||
userService.deleteUser(call.userId())
|
val status = if (userService.deleteUser(call.userId()))
|
||||||
call.respondStatus(HttpStatusCode.OK)
|
HttpStatusCode.OK
|
||||||
|
else
|
||||||
|
HttpStatusCode.NotFound
|
||||||
|
call.respondStatus(status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import me.liuwj.ktorm.entity.*
|
|||||||
import org.kodein.di.Kodein
|
import org.kodein.di.Kodein
|
||||||
import org.kodein.di.KodeinAware
|
import org.kodein.di.KodeinAware
|
||||||
import org.kodein.di.generic.instance
|
import org.kodein.di.generic.instance
|
||||||
|
import java.sql.SQLIntegrityConstraintViolationException
|
||||||
import java.time.LocalDateTime
|
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)
|
return db.from(Users)
|
||||||
.select(Users.email, Users.password, Users.id)
|
.select(Users.email, Users.password, Users.id)
|
||||||
.where { Users.username eq username }
|
.where { Users.username eq username }
|
||||||
.map { row ->
|
.map { row ->
|
||||||
UserSchema(
|
Users.createEntity(row)
|
||||||
row[Users.id]!!,
|
|
||||||
username,
|
|
||||||
row[Users.email]!!,
|
|
||||||
row[Users.password]!!
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
.firstOrNull()
|
.firstOrNull()
|
||||||
}
|
}
|
||||||
@ -59,11 +55,11 @@ class UserService(override val kodein: Kodein) : KodeinAware {
|
|||||||
.firstOrNull() != null
|
.firstOrNull() != null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getUserInfo(id: Int): UserInfoDto? {
|
fun getUserInfo(id: Int): User? {
|
||||||
return db.from(Users)
|
return db.from(Users)
|
||||||
.select(Users.email, Users.username)
|
.select(Users.email, Users.username)
|
||||||
.where { Users.id eq id }
|
.where { Users.id eq id }
|
||||||
.map { UserInfoDto(it[Users.username]!!, it[Users.email]!!) }
|
.map { Users.createEntity(it) }
|
||||||
.firstOrNull()
|
.firstOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,25 +67,30 @@ class UserService(override val kodein: Kodein) : KodeinAware {
|
|||||||
* create a new user
|
* create a new user
|
||||||
* password should already be hashed
|
* password should already be hashed
|
||||||
*/
|
*/
|
||||||
fun createUser(user: UserDto) {
|
fun createUser(username: String, email: String, hashedPassword: String): User? {
|
||||||
db.useTransaction {
|
try {
|
||||||
val newUser = User {
|
db.useTransaction {
|
||||||
this.username = user.username
|
val newUser = User {
|
||||||
this.email = user.email
|
this.username = username
|
||||||
this.password = user.password
|
this.email = email
|
||||||
this.createdAt = LocalDateTime.now()
|
this.password = hashedPassword
|
||||||
}
|
this.createdAt = LocalDateTime.now()
|
||||||
|
}
|
||||||
|
|
||||||
db.sequenceOf(Users).add(newUser)
|
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.useTransaction {
|
||||||
db.update(Users) {
|
db.update(Users) {
|
||||||
it.username to user.username
|
it.username to username
|
||||||
it.email to user.email
|
it.email to email
|
||||||
it.password to user.password
|
it.password to hashedPassword
|
||||||
where {
|
where {
|
||||||
it.id eq userId
|
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.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)
|
|
||||||
|
|||||||
@ -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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -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))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
142
api/test/routing/UserControllerKtTest.kt
Normal file
142
api/test/routing/UserControllerKtTest.kt
Normal 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"}"""
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
69
api/test/services/UserServiceTest.kt
Normal file
69
api/test/services/UserServiceTest.kt
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
19
api/test/utils/Assertions.kt
Normal file
19
api/test/utils/Assertions.kt
Normal 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)
|
||||||
23
api/test/utils/JsonAssertExtensions.kt
Normal file
23
api/test/utils/JsonAssertExtensions.kt
Normal 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)
|
||||||
|
}
|
||||||
52
api/test/utils/KtorTestingExtensions.kt
Normal file
52
api/test/utils/KtorTestingExtensions.kt
Normal 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
|
||||||
18
api/test/utils/TestContainers.kt
Normal file
18
api/test/utils/TestContainers.kt
Normal 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)
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user