From 0c9909af3d87d52ec577ccdf01197bc393bfe9a5 Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Wed, 22 Jun 2022 23:13:01 +0200 Subject: [PATCH 01/33] rename files folder to public folder. --- CopyScriptJsMain.ps1 | 2 +- Dockerfile | 6 +++--- Dockerfile_dev | 6 +++--- budget-binder-server/{files => public}/favicon.ico | Bin budget-binder-server/{files => public}/openapi.json | 0 .../hsfl/budgetBinder/server/routes/BaseRoutes.kt | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) rename budget-binder-server/{files => public}/favicon.ico (100%) rename budget-binder-server/{files => public}/openapi.json (100%) diff --git a/CopyScriptJsMain.ps1 b/CopyScriptJsMain.ps1 index fd8d0534..0f12a448 100644 --- a/CopyScriptJsMain.ps1 +++ b/CopyScriptJsMain.ps1 @@ -1 +1 @@ -Copy-Item -Force -Path ".\budget-binder-multiplatform-app\build\distributions\*" -Destination ".\budget-binder-server\files\" \ No newline at end of file +Copy-Item -Force -Path ".\budget-binder-multiplatform-app\build\distributions\*" -Destination ".\budget-binder-server\public\" diff --git a/Dockerfile b/Dockerfile index 183c920b..1cd757c0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,12 +4,12 @@ WORKDIR /home/gradle/src RUN gradle :budget-binder-multiplatform-app:jsBrowserDistribution :budget-binder-server:shadowJar --no-daemon FROM openjdk:17 -RUN mkdir -p /app/data && mkdir -p /app/files +RUN mkdir -p /app/data && mkdir -p /app/public WORKDIR /app VOLUME /app/data -COPY --from=build /home/gradle/src/budget-binder-server/files /app/files/ -COPY --from=build /home/gradle/src/budget-binder-multiplatform-app/build/distributions/ /app/files/ +COPY --from=build /home/gradle/src/budget-binder-server/files /app/public/ +COPY --from=build /home/gradle/src/budget-binder-multiplatform-app/build/distributions/ /app/public/ COPY --from=build /home/gradle/src/budget-binder-server/build/libs/*.jar /app/ktor-docker-server.jar ENV \ diff --git a/Dockerfile_dev b/Dockerfile_dev index a3759418..b5b8f2f6 100644 --- a/Dockerfile_dev +++ b/Dockerfile_dev @@ -4,12 +4,12 @@ WORKDIR /home/gradle/src RUN gradle :budget-binder-multiplatform-app:jsBrowserDistribution :budget-binder-server:shadowJar --no-daemon FROM openjdk:17 -RUN mkdir -p /app/data && mkdir -p /app/files +RUN mkdir -p /app/data && mkdir -p /app/public WORKDIR /app VOLUME /app/data -COPY --from=build /home/gradle/src/budget-binder-server/files /app/files/ -COPY --from=build /home/gradle/src/budget-binder-multiplatform-app/build/distributions/ /app/files/ +COPY --from=build /home/gradle/src/budget-binder-server/files /app/public/ +COPY --from=build /home/gradle/src/budget-binder-multiplatform-app/build/distributions/ /app/public/ COPY --from=build /home/gradle/src/budget-binder-server/build/libs/*.jar /app/ktor-docker-server.jar ENV \ diff --git a/budget-binder-server/files/favicon.ico b/budget-binder-server/public/favicon.ico similarity index 100% rename from budget-binder-server/files/favicon.ico rename to budget-binder-server/public/favicon.ico diff --git a/budget-binder-server/files/openapi.json b/budget-binder-server/public/openapi.json similarity index 100% rename from budget-binder-server/files/openapi.json rename to budget-binder-server/public/openapi.json diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/routes/BaseRoutes.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/routes/BaseRoutes.kt index 20ca3721..a5b8e79c 100644 --- a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/routes/BaseRoutes.kt +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/routes/BaseRoutes.kt @@ -10,7 +10,7 @@ import java.io.File fun Application.baseRoutes() { routing { static("/") { - staticRootFolder = File("files") + staticRootFolder = File("public") files(".") default("index.html") } From 743a4010199765763fb84988c852eea69ad45a75 Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Wed, 22 Jun 2022 23:14:03 +0200 Subject: [PATCH 02/33] use the script functions instead of writing everything as a string. --- .../hsfl/budgetBinder/server/routes/BaseRoutes.kt | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/routes/BaseRoutes.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/routes/BaseRoutes.kt index a5b8e79c..56461be4 100644 --- a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/routes/BaseRoutes.kt +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/routes/BaseRoutes.kt @@ -29,18 +29,21 @@ fun Application.baseRoutes() { div { id = "swagger-ui" } - unsafe { - +""" - - - """.trimIndent() + """.trimIndent() + } } } } From 5249e1d29036ac0934431c0ddcc79ebe904218e4 Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Wed, 22 Jun 2022 23:14:25 +0200 Subject: [PATCH 03/33] rename DataBase and dataBase to Database and database. --- budget-binder-server/data/config_sample.yaml | 2 +- .../hsfl/budgetBinder/server/config/Config.kt | 4 ++-- .../server/config/ConfigIntermediate.kt | 22 +++++++++---------- .../de/hsfl/budgetBinder/server/mainModule.kt | 10 ++++----- .../hsfl/budgetBinder/server/TestHelpers.kt | 2 +- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/budget-binder-server/data/config_sample.yaml b/budget-binder-server/data/config_sample.yaml index e736fd7c..f11e1fc8 100644 --- a/budget-binder-server/data/config_sample.yaml +++ b/budget-binder-server/data/config_sample.yaml @@ -8,7 +8,7 @@ server: keyStorePassword: null keyStorePath: null noForwardedHeaderSupport: false -dataBase: +database: dbType: SQLITE sqlitePath: null serverAddress: null diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/config/Config.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/config/Config.kt index 9b9b0c24..f03b1ff1 100644 --- a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/config/Config.kt +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/config/Config.kt @@ -1,7 +1,7 @@ package de.hsfl.budgetBinder.server.config -data class Config(val server: Server, val dataBase: DataBase, val jwt: JWT) { +data class Config(val server: Server, val database: Database, val jwt: JWT) { data class Server( val dev: Boolean, val ssl: Boolean, @@ -20,7 +20,7 @@ data class Config(val server: Server, val dataBase: DataBase, val jwt: JWT) { POSTGRES, } - data class DataBase( + data class Database( val dbType: DBType, val sqlitePath: String, val serverAddress: String, diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/config/ConfigIntermediate.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/config/ConfigIntermediate.kt index 941313b0..efaf9e53 100644 --- a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/config/ConfigIntermediate.kt +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/config/ConfigIntermediate.kt @@ -5,7 +5,7 @@ import com.sksamuel.hoplite.addFileSource import com.sksamuel.hoplite.yaml.YamlPropertySource import java.io.File -data class ConfigIntermediate(val server: Server?, val dataBase: DataBase, val jwt: JWT) { +data class ConfigIntermediate(val server: Server?, val database: Database, val jwt: JWT) { data class Server( val dev: Boolean?, val ssl: Boolean?, @@ -18,7 +18,7 @@ data class ConfigIntermediate(val server: Server?, val dataBase: DataBase, val j val noForwardedHeaderSupport: Boolean? ) - data class DataBase( + data class Database( val dbType: Config.DBType, val sqlitePath: String?, val serverAddress: String?, @@ -39,7 +39,7 @@ data class ConfigIntermediate(val server: Server?, val dataBase: DataBase, val j ) fun toConfig(): Config { - val dbType = dataBase.dbType + val dbType = database.dbType val sqlitePath: String val dbServerAddress: String @@ -48,7 +48,7 @@ data class ConfigIntermediate(val server: Server?, val dataBase: DataBase, val j val dbUser: String val dbPassword: String if (dbType == Config.DBType.SQLITE) { - sqlitePath = dataBase.sqlitePath ?: (System.getProperty("user.dir") + "/data/data.db") + sqlitePath = database.sqlitePath ?: (System.getProperty("user.dir") + "/data/data.db") dbServerAddress = "" dbServerPort = "" dbName = "" @@ -56,11 +56,11 @@ data class ConfigIntermediate(val server: Server?, val dataBase: DataBase, val j dbPassword = "" } else { sqlitePath = "" - dbServerAddress = dataBase.serverAddress ?: error("No dbServerAddress specified") - dbServerPort = dataBase.serverPort ?: error("No dbServerPort specified") - dbName = dataBase.name ?: error("No dbDatabaseName specified") - dbUser = dataBase.user ?: error("No dbUser specified") - dbPassword = dataBase.password ?: error("No dbPassword specified") + dbServerAddress = database.serverAddress ?: error("No dbServerAddress specified") + dbServerPort = database.serverPort ?: error("No dbServerPort specified") + dbName = database.name ?: error("No dbDatabaseName specified") + dbUser = database.user ?: error("No dbUser specified") + dbPassword = database.password ?: error("No dbPassword specified") } val dev = server?.dev ?: false @@ -94,7 +94,7 @@ data class ConfigIntermediate(val server: Server?, val dataBase: DataBase, val j val jwtAudience = jwt.audience ?: "http://0.0.0.0:8080/" return Config( - dataBase = Config.DataBase( + database = Config.Database( dbType, sqlitePath, dbServerAddress, @@ -145,7 +145,7 @@ private fun getConfigIntermediateFromEnv(): ConfigIntermediate { else -> error("No Database Type given") } - val dataBase = ConfigIntermediate.DataBase( + val dataBase = ConfigIntermediate.Database( dbType, System.getenv("SQLITE_PATH"), System.getenv("DB_SERVER"), diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/mainModule.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/mainModule.kt index ae2aa2da..2d4f42ad 100644 --- a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/mainModule.kt +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/mainModule.kt @@ -39,9 +39,9 @@ import org.slf4j.event.Level import java.sql.DriverManager fun Application.mainModule(config: Config) { - val url: String = when (config.dataBase.dbType) { + val url: String = when (config.database.dbType) { Config.DBType.SQLITE -> { - val url = "jdbc:sqlite:${config.dataBase.sqlitePath}" + val url = "jdbc:sqlite:${config.database.sqlitePath}" /* * The url is used in the tests to not create or alter the normal database. * the connection must be held because exposed closes the connection to the db @@ -52,11 +52,11 @@ fun Application.mainModule(config: Config) { } url } - Config.DBType.MYSQL -> "jdbc:mysql://${config.dataBase.serverAddress}:${config.dataBase.serverPort}/${config.dataBase.name}" - Config.DBType.POSTGRES -> "jdbc:postgresql://${config.dataBase.serverAddress}:${config.dataBase.serverPort}/${config.dataBase.name}" + Config.DBType.MYSQL -> "jdbc:mysql://${config.database.serverAddress}:${config.database.serverPort}/${config.database.name}" + Config.DBType.POSTGRES -> "jdbc:postgresql://${config.database.serverAddress}:${config.database.serverPort}/${config.database.name}" } - Database.connect(url, user = config.dataBase.user, password = config.dataBase.password) + Database.connect(url, user = config.database.user, password = config.database.password) transaction { SchemaUtils.create(Users, Categories, Entries) diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestHelpers.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestHelpers.kt index 9c7b8458..925ada9b 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestHelpers.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestHelpers.kt @@ -14,7 +14,7 @@ fun customTestApplication(block: suspend ApplicationTestBuilder.(client: HttpCli testApplication { application { val configString = """ - dataBase: + database: dbType: SQLITE sqlitePath: file:test?mode=memory&cache=shared jwt: From 18ebbfcd57218577a3b9178ff9fe6c6009bd761c Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Wed, 22 Jun 2022 23:19:24 +0200 Subject: [PATCH 04/33] use the challenge functions instead of doing it in StatusPages. --- .../de/hsfl/budgetBinder/server/mainModule.kt | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/mainModule.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/mainModule.kt index 2d4f42ad..3ed35050 100644 --- a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/mainModule.kt +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/mainModule.kt @@ -20,7 +20,6 @@ import io.ktor.server.auth.* import io.ktor.server.auth.jwt.* import io.ktor.http.* import io.ktor.serialization.kotlinx.json.* -import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.plugins.callloging.CallLogging import io.ktor.server.plugins.contentnegotiation.ContentNegotiation @@ -86,6 +85,18 @@ fun Application.mainModule(config: Config) { val userService: UserService by closestDI().instance() userService.findUserByEmailAndPassword(it.name, it.password) } + + challenge { + call.respond( + HttpStatusCode.Unauthorized, + APIResponse( + ErrorModel( + "Your username and/or password do not match.", + HttpStatusCode.Unauthorized.value + ) + ) + ) + } } jwt("auth-jwt") { @@ -99,6 +110,19 @@ fun Application.mainModule(config: Config) { val userService: UserService by closestDI().instance() userService.getUserPrincipalByIDAndTokenVersion(id, tokenVersion) } + + challenge { defaultScheme, realm -> + call.response.headers.append(HttpHeaders.WWWAuthenticate, "$defaultScheme realm=$realm") + call.respond( + HttpStatusCode.Unauthorized, + APIResponse( + ErrorModel( + "Your accessToken is absent or does not match.", + HttpStatusCode.Unauthorized.value + ) + ) + ) + } } } @@ -131,25 +155,6 @@ fun Application.mainModule(config: Config) { } } } - status(HttpStatusCode.Unauthorized) { call, status -> - when (call.request.uri) { - "/login" -> call.respond( - status, - APIResponse(ErrorModel("Your username and/or password do not match.", status.value)) - ) - else -> { - val jwtService: JWTService by this@mainModule.closestDI().instance() - call.response.headers.append( - HttpHeaders.WWWAuthenticate, - "Bearer realm=\"${jwtService.getRealm()}\"" - ) - call.respond( - status, - APIResponse(ErrorModel("Your accessToken is absent or does not match.", status.value)) - ) - } - } - } status(HttpStatusCode.MethodNotAllowed) { call, status -> call.respond( status, From 7a933b5fdbcea44fd88f4096655628b4ff659859 Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Wed, 22 Jun 2022 23:44:53 +0200 Subject: [PATCH 05/33] put all Plugin-Configuration into its own files --- .../de/hsfl/budgetBinder/server/mainModule.kt | 167 ++---------------- .../hsfl/budgetBinder/server/plugins/Auth.kt | 64 +++++++ .../server/plugins/ContentNegotiation.kt | 14 ++ .../de/hsfl/budgetBinder/server/plugins/DI.kt | 25 +++ .../budgetBinder/server/plugins/Database.kt | 41 +++++ .../server/plugins/ForwardHeaders.kt | 14 ++ .../budgetBinder/server/plugins/Logging.kt | 12 ++ .../server/plugins/StatusPages.kt | 42 +++++ 8 files changed, 222 insertions(+), 157 deletions(-) create mode 100644 budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/Auth.kt create mode 100644 budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/ContentNegotiation.kt create mode 100644 budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/DI.kt create mode 100644 budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/Database.kt create mode 100644 budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/ForwardHeaders.kt create mode 100644 budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/Logging.kt create mode 100644 budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/StatusPages.kt diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/mainModule.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/mainModule.kt index 3ed35050..08aff2f8 100644 --- a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/mainModule.kt +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/mainModule.kt @@ -1,169 +1,22 @@ package de.hsfl.budgetBinder.server -import de.hsfl.budgetBinder.common.APIResponse -import de.hsfl.budgetBinder.common.ErrorModel import de.hsfl.budgetBinder.server.config.Config -import de.hsfl.budgetBinder.server.models.Categories -import de.hsfl.budgetBinder.server.models.Entries -import de.hsfl.budgetBinder.server.models.Users -import de.hsfl.budgetBinder.server.utils.UnauthorizedException +import de.hsfl.budgetBinder.server.plugins.* import de.hsfl.budgetBinder.server.routes.* -import de.hsfl.budgetBinder.server.services.* -import de.hsfl.budgetBinder.server.services.implementations.CategoryServiceImpl -import de.hsfl.budgetBinder.server.services.implementations.EntryServiceImpl -import de.hsfl.budgetBinder.server.services.implementations.UserServiceImpl -import de.hsfl.budgetBinder.server.services.interfaces.CategoryService -import de.hsfl.budgetBinder.server.services.interfaces.EntryService -import de.hsfl.budgetBinder.server.services.interfaces.UserService import io.ktor.server.application.* -import io.ktor.server.auth.* -import io.ktor.server.auth.jwt.* -import io.ktor.http.* -import io.ktor.serialization.kotlinx.json.* -import io.ktor.server.response.* -import io.ktor.server.plugins.callloging.CallLogging -import io.ktor.server.plugins.contentnegotiation.ContentNegotiation -import io.ktor.server.plugins.forwardedheaders.* -import io.ktor.server.plugins.statuspages.* -import kotlinx.serialization.json.Json -import org.jetbrains.exposed.sql.Database -import org.jetbrains.exposed.sql.SchemaUtils -import org.jetbrains.exposed.sql.transactions.transaction -import org.kodein.di.bindEagerSingleton -import org.kodein.di.bindSingleton -import org.kodein.di.instance -import org.kodein.di.ktor.closestDI -import org.kodein.di.ktor.di -import org.slf4j.event.Level -import java.sql.DriverManager fun Application.mainModule(config: Config) { - val url: String = when (config.database.dbType) { - Config.DBType.SQLITE -> { - val url = "jdbc:sqlite:${config.database.sqlitePath}" - /* - * The url is used in the tests to not create or alter the normal database. - * the connection must be held because exposed closes the connection to the db - * after every transaction and if no connection is alive the memory database will be deleted - * */ - if (url == "jdbc:sqlite:file:test?mode=memory&cache=shared") { - DriverManager.getConnection(url) - } - url - } - Config.DBType.MYSQL -> "jdbc:mysql://${config.database.serverAddress}:${config.database.serverPort}/${config.database.name}" - Config.DBType.POSTGRES -> "jdbc:postgresql://${config.database.serverAddress}:${config.database.serverPort}/${config.database.name}" - } - Database.connect(url, user = config.database.user, password = config.database.password) + // install all Plugin Modules + configureDI(config) + configureDatabase() + configureLogging() + configureForwardHeaders() + configureAuth() + configureContentNegotiation() + configureStatusPages() - transaction { - SchemaUtils.create(Users, Categories, Entries) - } - - di { - bindEagerSingleton { config } - bindSingleton { UserServiceImpl() } - bindSingleton { EntryServiceImpl() } - bindSingleton { CategoryServiceImpl() } - bindSingleton { JWTService(instance()) } - } - - install(CallLogging) { - level = Level.INFO - disableDefaultColors() - } - - if (config.server.forwardedHeaderSupport) - install(XForwardedHeaders) - - install(Authentication) { - form("auth-form") { - userParamName = "username" - passwordParamName = "password" - validate { - val userService: UserService by closestDI().instance() - userService.findUserByEmailAndPassword(it.name, it.password) - } - - challenge { - call.respond( - HttpStatusCode.Unauthorized, - APIResponse( - ErrorModel( - "Your username and/or password do not match.", - HttpStatusCode.Unauthorized.value - ) - ) - ) - } - } - - jwt("auth-jwt") { - val jwtService: JWTService by this@mainModule.closestDI().instance() - realm = jwtService.getRealm() - verifier(jwtService.getAccessTokenVerifier()) - - validate { - val id = it.payload.getClaim("userid").asInt() - val tokenVersion = it.payload.getClaim("token_version").asInt() - val userService: UserService by closestDI().instance() - userService.getUserPrincipalByIDAndTokenVersion(id, tokenVersion) - } - - challenge { defaultScheme, realm -> - call.response.headers.append(HttpHeaders.WWWAuthenticate, "$defaultScheme realm=$realm") - call.respond( - HttpStatusCode.Unauthorized, - APIResponse( - ErrorModel( - "Your accessToken is absent or does not match.", - HttpStatusCode.Unauthorized.value - ) - ) - ) - } - } - } - - install(ContentNegotiation) { - json(Json { - ignoreUnknownKeys = true - }) - } - - install(StatusPages) { - exception { call, cause -> - when (cause) { - is UnauthorizedException -> { - call.respond( - HttpStatusCode.Unauthorized, - APIResponse(ErrorModel(cause.message, HttpStatusCode.Unauthorized.value)) - ) - } - else -> { - call.respond( - HttpStatusCode.InternalServerError, - APIResponse( - ErrorModel( - "An Internal-Server-Error occurred. Please contact your Administrator or see the Server-Logs.", - HttpStatusCode.InternalServerError.value - ) - ) - ) - throw cause - } - } - } - status(HttpStatusCode.MethodNotAllowed) { call, status -> - call.respond( - status, - APIResponse(ErrorModel("The used HTTP-Method is not allowed on this Endpoint.", status.value)) - ) - } - } - - // install all Modules + // install all Routing Modules baseRoutes() userRoutes() authRoutes() diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/Auth.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/Auth.kt new file mode 100644 index 00000000..068c5709 --- /dev/null +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/Auth.kt @@ -0,0 +1,64 @@ +package de.hsfl.budgetBinder.server.plugins + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.ErrorModel +import de.hsfl.budgetBinder.server.services.JWTService +import de.hsfl.budgetBinder.server.services.interfaces.UserService +import io.ktor.http.* +import io.ktor.server.application.* +import io.ktor.server.auth.* +import io.ktor.server.auth.jwt.* +import io.ktor.server.response.* +import org.kodein.di.instance +import org.kodein.di.ktor.closestDI + +fun Application.configureAuth() { + install(Authentication) { + form("auth-form") { + userParamName = "username" + passwordParamName = "password" + validate { + val userService: UserService by closestDI().instance() + userService.findUserByEmailAndPassword(it.name, it.password) + } + + challenge { + call.respond( + HttpStatusCode.Unauthorized, + APIResponse( + ErrorModel( + "Your username and/or password do not match.", + HttpStatusCode.Unauthorized.value + ) + ) + ) + } + } + + jwt("auth-jwt") { + val jwtService: JWTService by this@configureAuth.closestDI().instance() + realm = jwtService.getRealm() + verifier(jwtService.getAccessTokenVerifier()) + + validate { + val id = it.payload.getClaim("userid").asInt() + val tokenVersion = it.payload.getClaim("token_version").asInt() + val userService: UserService by closestDI().instance() + userService.getUserPrincipalByIDAndTokenVersion(id, tokenVersion) + } + + challenge { defaultScheme, realm -> + call.response.headers.append(HttpHeaders.WWWAuthenticate, "$defaultScheme realm=$realm") + call.respond( + HttpStatusCode.Unauthorized, + APIResponse( + ErrorModel( + "Your accessToken is absent or does not match.", + HttpStatusCode.Unauthorized.value + ) + ) + ) + } + } + } +} diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/ContentNegotiation.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/ContentNegotiation.kt new file mode 100644 index 00000000..a57c53d8 --- /dev/null +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/ContentNegotiation.kt @@ -0,0 +1,14 @@ +package de.hsfl.budgetBinder.server.plugins + +import io.ktor.serialization.kotlinx.json.* +import io.ktor.server.application.* +import io.ktor.server.plugins.contentnegotiation.* +import kotlinx.serialization.json.Json + +fun Application.configureContentNegotiation() { + install(ContentNegotiation) { + json(Json { + ignoreUnknownKeys = true + }) + } +} diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/DI.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/DI.kt new file mode 100644 index 00000000..e7b556ab --- /dev/null +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/DI.kt @@ -0,0 +1,25 @@ +package de.hsfl.budgetBinder.server.plugins + +import de.hsfl.budgetBinder.server.config.Config +import de.hsfl.budgetBinder.server.services.JWTService +import de.hsfl.budgetBinder.server.services.implementations.CategoryServiceImpl +import de.hsfl.budgetBinder.server.services.implementations.EntryServiceImpl +import de.hsfl.budgetBinder.server.services.implementations.UserServiceImpl +import de.hsfl.budgetBinder.server.services.interfaces.CategoryService +import de.hsfl.budgetBinder.server.services.interfaces.EntryService +import de.hsfl.budgetBinder.server.services.interfaces.UserService +import io.ktor.server.application.* +import org.kodein.di.bindEagerSingleton +import org.kodein.di.bindSingleton +import org.kodein.di.instance +import org.kodein.di.ktor.di + +fun Application.configureDI(config: Config) { + di { + bindEagerSingleton { config } + bindSingleton { UserServiceImpl() } + bindSingleton { EntryServiceImpl() } + bindSingleton { CategoryServiceImpl() } + bindSingleton { JWTService(instance()) } + } +} diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/Database.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/Database.kt new file mode 100644 index 00000000..c39d1696 --- /dev/null +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/Database.kt @@ -0,0 +1,41 @@ +package de.hsfl.budgetBinder.server.plugins + +import de.hsfl.budgetBinder.server.config.Config +import de.hsfl.budgetBinder.server.models.Categories +import de.hsfl.budgetBinder.server.models.Entries +import de.hsfl.budgetBinder.server.models.Users +import io.ktor.server.application.* +import org.jetbrains.exposed.sql.Database +import org.jetbrains.exposed.sql.SchemaUtils +import org.jetbrains.exposed.sql.transactions.transaction +import org.kodein.di.instance +import org.kodein.di.ktor.closestDI +import java.sql.DriverManager + +fun Application.configureDatabase() { + + val config: Config by closestDI().instance() + + val url: String = when (config.database.dbType) { + Config.DBType.SQLITE -> { + val url = "jdbc:sqlite:${config.database.sqlitePath}" + /* + * The url is used in the tests to not create or alter the normal database. + * the connection must be held because exposed closes the connection to the db + * after every transaction and if no connection is alive the memory database will be deleted + * */ + if (url == "jdbc:sqlite:file:test?mode=memory&cache=shared") { + DriverManager.getConnection(url) + } + url + } + Config.DBType.MYSQL -> "jdbc:mysql://${config.database.serverAddress}:${config.database.serverPort}/${config.database.name}" + Config.DBType.POSTGRES -> "jdbc:postgresql://${config.database.serverAddress}:${config.database.serverPort}/${config.database.name}" + } + + Database.connect(url, user = config.database.user, password = config.database.password) + + transaction { + SchemaUtils.create(Users, Categories, Entries) + } +} diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/ForwardHeaders.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/ForwardHeaders.kt new file mode 100644 index 00000000..9d833f43 --- /dev/null +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/ForwardHeaders.kt @@ -0,0 +1,14 @@ +package de.hsfl.budgetBinder.server.plugins + +import de.hsfl.budgetBinder.server.config.Config +import io.ktor.server.application.* +import io.ktor.server.plugins.forwardedheaders.* +import org.kodein.di.instance +import org.kodein.di.ktor.closestDI + +fun Application.configureForwardHeaders() { + val config: Config by closestDI().instance() + + if (config.server.forwardedHeaderSupport) + install(XForwardedHeaders) +} diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/Logging.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/Logging.kt new file mode 100644 index 00000000..be8c3615 --- /dev/null +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/Logging.kt @@ -0,0 +1,12 @@ +package de.hsfl.budgetBinder.server.plugins + +import io.ktor.server.application.* +import io.ktor.server.plugins.callloging.* +import org.slf4j.event.Level + +fun Application.configureLogging() { + install(CallLogging) { + level = Level.INFO + disableDefaultColors() + } +} diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/StatusPages.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/StatusPages.kt new file mode 100644 index 00000000..04f60ef2 --- /dev/null +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/StatusPages.kt @@ -0,0 +1,42 @@ +package de.hsfl.budgetBinder.server.plugins + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.ErrorModel +import de.hsfl.budgetBinder.server.utils.UnauthorizedException +import io.ktor.http.* +import io.ktor.server.application.* +import io.ktor.server.plugins.statuspages.* +import io.ktor.server.response.* + +fun Application.configureStatusPages() { + install(StatusPages) { + exception { call, cause -> + when (cause) { + is UnauthorizedException -> { + call.respond( + HttpStatusCode.Unauthorized, + APIResponse(ErrorModel(cause.message, HttpStatusCode.Unauthorized.value)) + ) + } + else -> { + call.respond( + HttpStatusCode.InternalServerError, + APIResponse( + ErrorModel( + "An Internal-Server-Error occurred. Please contact your Administrator or see the Server-Logs.", + HttpStatusCode.InternalServerError.value + ) + ) + ) + throw cause + } + } + } + status(HttpStatusCode.MethodNotAllowed) { call, status -> + call.respond( + status, + APIResponse(ErrorModel("The used HTTP-Method is not allowed on this Endpoint.", status.value)) + ) + } + } +} From 9e2a2e5f6f05cef8051e373f58acb5fb252f9c60 Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Thu, 23 Jun 2022 00:03:53 +0200 Subject: [PATCH 06/33] restructured Config and ConfigIntermediate.kt we don't need configString anymore, we now pass a ConfigIntermediate to Config.create(ConfigIntermediate) and get a Config. --- .../hsfl/budgetBinder/server/config/Config.kt | 96 +++++++ .../server/config/ConfigIntermediate.kt | 239 +++++------------- .../de/hsfl/budgetBinder/server/main.kt | 4 +- .../hsfl/budgetBinder/server/TestHelpers.kt | 25 +- 4 files changed, 181 insertions(+), 183 deletions(-) diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/config/Config.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/config/Config.kt index f03b1ff1..2dea4d5b 100644 --- a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/config/Config.kt +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/config/Config.kt @@ -1,5 +1,7 @@ package de.hsfl.budgetBinder.server.config +import java.io.File + data class Config(val server: Server, val database: Database, val jwt: JWT) { data class Server( @@ -39,4 +41,98 @@ data class Config(val server: Server, val database: Database, val jwt: JWT) { val issuer: String, val audience: String ) + + companion object { + fun create(configFile: File? = null): Config { + return configFile?.let { + createFromIntermediate(ConfigIntermediate.createFromFile(it)) + } ?: createFromIntermediate(ConfigIntermediate.createFromEnv()) + } + + fun createFromIntermediate(intermediate: ConfigIntermediate): Config { + val dbType = intermediate.database.dbType + + val sqlitePath: String + val dbServerAddress: String + val dbServerPort: String + val dbName: String + val dbUser: String + val dbPassword: String + if (dbType == DBType.SQLITE) { + sqlitePath = intermediate.database.sqlitePath ?: (System.getProperty("user.dir") + "/data/data.db") + dbServerAddress = "" + dbServerPort = "" + dbName = "" + dbUser = "" + dbPassword = "" + } else { + sqlitePath = "" + dbServerAddress = intermediate.database.serverAddress ?: error("No dbServerAddress specified") + dbServerPort = intermediate.database.serverPort ?: error("No dbServerPort specified") + dbName = intermediate.database.name ?: error("No dbDatabaseName specified") + dbUser = intermediate.database.user ?: error("No dbUser specified") + dbPassword = intermediate.database.password ?: error("No dbPassword specified") + } + + val dev = intermediate.server?.dev ?: false + val ssl = intermediate.server?.ssl ?: false + + val host = intermediate.server?.host ?: "0.0.0.0" + val port = intermediate.server?.port ?: 8080 + val sslHost = intermediate.server?.sslHost ?: "0.0.0.0" + val sslPort = intermediate.server?.sslPort ?: 8443 + + val keyStorePassword: String + val keyStorePath: String + if (ssl) { + keyStorePassword = intermediate.server?.keyStorePassword + ?: if (dev) "budget-binder-server" else error("No KeystorePassword provided") + keyStorePath = intermediate.server?.keyStorePath + ?: if (dev) "data/dev_keystore.jks" else error("No KeystorePath provided") + } else { + keyStorePassword = "" + keyStorePath = "" + } + + val forwardedHeaderSupport = !(intermediate.server?.noForwardedHeaderSupport ?: false) + + val jwtAccessSecret = intermediate.jwt.accessSecret + val jwtRefreshSecret = intermediate.jwt.refreshSecret + val jwtAccessMinutes = intermediate.jwt.accessMinutes ?: 15 + val jwtRefreshDays = intermediate.jwt.refreshDays ?: 7 + val jwtRealm = intermediate.jwt.realm ?: "budget-binder-server" + val jwtIssuer = intermediate.jwt.issuer ?: "http://0.0.0.0:8080/" + val jwtAudience = intermediate.jwt.audience ?: "http://0.0.0.0:8080/" + + return Config( + database = Database( + dbType, + sqlitePath, + dbServerAddress, + dbServerPort, + dbName, + dbUser, + dbPassword + ), server = Server( + dev, + ssl, + host, + port, + sslHost, + sslPort, + keyStorePassword, + keyStorePath, + forwardedHeaderSupport + ), jwt = JWT( + jwtAccessSecret, + jwtRefreshSecret, + jwtAccessMinutes, + jwtRefreshDays, + jwtRealm, + jwtIssuer, + jwtAudience + ) + ) + } + } } diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/config/ConfigIntermediate.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/config/ConfigIntermediate.kt index efaf9e53..d8f5fe13 100644 --- a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/config/ConfigIntermediate.kt +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/config/ConfigIntermediate.kt @@ -2,195 +2,94 @@ package de.hsfl.budgetBinder.server.config import com.sksamuel.hoplite.ConfigLoaderBuilder import com.sksamuel.hoplite.addFileSource -import com.sksamuel.hoplite.yaml.YamlPropertySource import java.io.File -data class ConfigIntermediate(val server: Server?, val database: Database, val jwt: JWT) { +data class ConfigIntermediate(val server: Server? = null, val database: Database, val jwt: JWT) { data class Server( - val dev: Boolean?, - val ssl: Boolean?, - val host: String?, - val port: Int?, - val sslHost: String?, - val sslPort: Int?, - val keyStorePassword: String?, - val keyStorePath: String?, - val noForwardedHeaderSupport: Boolean? + val dev: Boolean? = null, + val ssl: Boolean? = null, + val host: String? = null, + val port: Int? = null, + val sslHost: String? = null, + val sslPort: Int? = null, + val keyStorePassword: String? = null, + val keyStorePath: String? = null, + val noForwardedHeaderSupport: Boolean? = null ) data class Database( val dbType: Config.DBType, - val sqlitePath: String?, - val serverAddress: String?, - val serverPort: String?, - val name: String?, - val user: String?, - val password: String? + val sqlitePath: String? = null, + val serverAddress: String? = null, + val serverPort: String? = null, + val name: String? = null, + val user: String? = null, + val password: String? = null ) data class JWT( val accessSecret: String, val refreshSecret: String, - val accessMinutes: Int?, - val refreshDays: Int?, - val realm: String?, - val issuer: String?, - val audience: String? + val accessMinutes: Int? = null, + val refreshDays: Int? = null, + val realm: String? = null, + val issuer: String? = null, + val audience: String? = null ) - fun toConfig(): Config { - val dbType = database.dbType - - val sqlitePath: String - val dbServerAddress: String - val dbServerPort: String - val dbName: String - val dbUser: String - val dbPassword: String - if (dbType == Config.DBType.SQLITE) { - sqlitePath = database.sqlitePath ?: (System.getProperty("user.dir") + "/data/data.db") - dbServerAddress = "" - dbServerPort = "" - dbName = "" - dbUser = "" - dbPassword = "" - } else { - sqlitePath = "" - dbServerAddress = database.serverAddress ?: error("No dbServerAddress specified") - dbServerPort = database.serverPort ?: error("No dbServerPort specified") - dbName = database.name ?: error("No dbDatabaseName specified") - dbUser = database.user ?: error("No dbUser specified") - dbPassword = database.password ?: error("No dbPassword specified") - } - - val dev = server?.dev ?: false - val ssl = server?.ssl ?: false - - val host = server?.host ?: "0.0.0.0" - val port = server?.port ?: 8080 - val sslHost = server?.sslHost ?: "0.0.0.0" - val sslPort = server?.sslPort ?: 8443 - - val keyStorePassword: String - val keyStorePath: String - if (ssl) { - keyStorePassword = server?.keyStorePassword - ?: if (dev) "budget-binder-server" else error("No KeystorePassword provided") - keyStorePath = server?.keyStorePath - ?: if (dev) "data/dev_keystore.jks" else error("No KeystorePath provided") - } else { - keyStorePassword = "" - keyStorePath = "" - } - - val forwardedHeaderSupport = !(server?.noForwardedHeaderSupport ?: false) + companion object { + fun createFromEnv(): ConfigIntermediate { + val server = Server( + System.getenv("DEV") != null, + System.getenv("SSL") != null, + System.getenv("HOST"), + System.getenv("PORT")?.toIntOrNull(), + System.getenv("SSL_HOST"), + System.getenv("SSL_PORT")?.toIntOrNull(), + System.getenv("KEYSTORE_PASSWORD"), + System.getenv("KEYSTORE_PATH"), + System.getenv("NO_FORWARDED_HEADER") != null + ) - val jwtAccessSecret = jwt.accessSecret - val jwtRefreshSecret = jwt.refreshSecret - val jwtAccessMinutes = jwt.accessMinutes ?: 15 - val jwtRefreshDays = jwt.refreshDays ?: 7 - val jwtRealm = jwt.realm ?: "budget-binder-server" - val jwtIssuer = jwt.issuer ?: "http://0.0.0.0:8080/" - val jwtAudience = jwt.audience ?: "http://0.0.0.0:8080/" + val dbType = when (System.getenv("DB_TYPE")) { + "SQLITE" -> Config.DBType.SQLITE + "MYSQL" -> Config.DBType.MYSQL + "POSTGRES" -> Config.DBType.POSTGRES + else -> error("No Database Type given") + } - return Config( - database = Config.Database( + val dataBase = Database( dbType, - sqlitePath, - dbServerAddress, - dbServerPort, - dbName, - dbUser, - dbPassword - ), server = Config.Server( - dev, - ssl, - host, - port, - sslHost, - sslPort, - keyStorePassword, - keyStorePath, - forwardedHeaderSupport - ), jwt = Config.JWT( - jwtAccessSecret, - jwtRefreshSecret, - jwtAccessMinutes, - jwtRefreshDays, - jwtRealm, - jwtIssuer, - jwtAudience + System.getenv("SQLITE_PATH"), + System.getenv("DB_SERVER"), + System.getenv("DB_PORT"), + System.getenv("DB_DATABASE_NAME"), + System.getenv("DB_USER"), + System.getenv("DB_PASSWORD") ) - ) - } -} - -private fun getConfigIntermediateFromEnv(): ConfigIntermediate { - val server = ConfigIntermediate.Server( - System.getenv("DEV") != null, - System.getenv("SSL") != null, - System.getenv("HOST"), - System.getenv("PORT")?.toIntOrNull(), - System.getenv("SSL_HOST"), - System.getenv("SSL_PORT")?.toIntOrNull(), - System.getenv("KEYSTORE_PASSWORD"), - System.getenv("KEYSTORE_PATH"), - System.getenv("NO_FORWARDED_HEADER") != null - ) - - val dbType = when (System.getenv("DB_TYPE")) { - "SQLITE" -> Config.DBType.SQLITE - "MYSQL" -> Config.DBType.MYSQL - "POSTGRES" -> Config.DBType.POSTGRES - else -> error("No Database Type given") - } - val dataBase = ConfigIntermediate.Database( - dbType, - System.getenv("SQLITE_PATH"), - System.getenv("DB_SERVER"), - System.getenv("DB_PORT"), - System.getenv("DB_DATABASE_NAME"), - System.getenv("DB_USER"), - System.getenv("DB_PASSWORD") - ) - - val accessSecret = System.getenv("JWT_ACCESS_SECRET") ?: error("No AccessTokenSecret given") - val refreshSecret = System.getenv("JWT_REFRESH_SECRET") ?: error("No RefreshTokenSecret given") - - val jwt = ConfigIntermediate.JWT( - accessSecret, - refreshSecret, - System.getenv("JWT_ACCESS_MINUTES")?.toIntOrNull(), - System.getenv("JWT_REFRESH_DAYS")?.toIntOrNull(), - System.getenv("JWT_REALM"), - System.getenv("JWT_ISSUER"), - System.getenv("JWT_AUDIENCE") - ) - - return ConfigIntermediate(server, dataBase, jwt) -} - -private fun getConfigIntermediateFromFile(configFile: File): ConfigIntermediate { - return ConfigLoaderBuilder - .default() - .addFileSource(configFile) - .build() - .loadConfigOrThrow() -} + val accessSecret = System.getenv("JWT_ACCESS_SECRET") ?: error("No AccessTokenSecret given") + val refreshSecret = System.getenv("JWT_REFRESH_SECRET") ?: error("No RefreshTokenSecret given") + + val jwt = JWT( + accessSecret, + refreshSecret, + System.getenv("JWT_ACCESS_MINUTES")?.toIntOrNull(), + System.getenv("JWT_REFRESH_DAYS")?.toIntOrNull(), + System.getenv("JWT_REALM"), + System.getenv("JWT_ISSUER"), + System.getenv("JWT_AUDIENCE") + ) -private fun getConfigIntermediateFromString(configString: String): ConfigIntermediate { - return ConfigLoaderBuilder - .default() - .addSource(YamlPropertySource(configString)) - .build() - .loadConfigOrThrow() -} + return ConfigIntermediate(server, dataBase, jwt) + } -fun getServerConfig(configFile: File? = null, configString: String? = null): Config { - return configString?.let { - getConfigIntermediateFromString(it).toConfig() - } ?: configFile?.let { - getConfigIntermediateFromFile(it).toConfig() - } ?: getConfigIntermediateFromEnv().toConfig() + fun createFromFile(configFile: File): ConfigIntermediate { + return ConfigLoaderBuilder + .default() + .addFileSource(configFile) + .build() + .loadConfigOrThrow() + } + } } diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/main.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/main.kt index a00bf74d..9cd9fcc4 100644 --- a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/main.kt +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/main.kt @@ -3,7 +3,7 @@ package de.hsfl.budgetBinder.server import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.parameters.options.option import com.github.ajalt.clikt.parameters.types.file -import de.hsfl.budgetBinder.server.config.getServerConfig +import de.hsfl.budgetBinder.server.config.Config import io.ktor.network.tls.certificates.* import io.ktor.server.engine.* import io.ktor.server.netty.* @@ -22,7 +22,7 @@ class ServerMain : CliktCommand() { ) override fun run(): Unit = runBlocking { - val config = getServerConfig(configFile = configFile) + val config = Config.create(configFile = configFile) val keyStore = when { config.server.dev && config.server.ssl -> { diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestHelpers.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestHelpers.kt index 925ada9b..9077f93e 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestHelpers.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestHelpers.kt @@ -2,7 +2,8 @@ package de.hsfl.budgetBinder.server import de.hsfl.budgetBinder.common.APIResponse import de.hsfl.budgetBinder.common.ErrorModel -import de.hsfl.budgetBinder.server.config.getServerConfig +import de.hsfl.budgetBinder.server.config.Config +import de.hsfl.budgetBinder.server.config.ConfigIntermediate import io.ktor.client.* import io.ktor.serialization.kotlinx.json.* import io.ktor.client.plugins.contentnegotiation.* @@ -13,16 +14,18 @@ import java.time.format.DateTimeFormatter fun customTestApplication(block: suspend ApplicationTestBuilder.(client: HttpClient) -> Unit) { testApplication { application { - val configString = """ - database: - dbType: SQLITE - sqlitePath: file:test?mode=memory&cache=shared - jwt: - accessSecret: testSecret - refreshSecret: testSecret2 - accessMinutes: 10 - """.trimIndent() - mainModule(getServerConfig(configString = configString)) + val intermediate = ConfigIntermediate( + database = ConfigIntermediate.Database( + dbType = Config.DBType.SQLITE, + sqlitePath = "file:test?mode=memory&cache=shared" + ), + jwt = ConfigIntermediate.JWT( + accessSecret = "testSecret", + refreshSecret = "testSecret2", + accessMinutes = 10 + ) + ) + mainModule(Config.createFromIntermediate(intermediate)) } val client = createClient { install(ContentNegotiation) { From 64e985b3adf48a6edbb2cc3998e48559b7597008 Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Thu, 23 Jun 2022 00:07:36 +0200 Subject: [PATCH 07/33] don't need to create own getter. --- .../de/hsfl/budgetBinder/server/plugins/Auth.kt | 2 +- .../de/hsfl/budgetBinder/server/routes/AuthRoutes.kt | 2 +- .../hsfl/budgetBinder/server/services/JWTService.kt | 12 ++---------- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/Auth.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/Auth.kt index 068c5709..40941061 100644 --- a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/Auth.kt +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/Auth.kt @@ -38,7 +38,7 @@ fun Application.configureAuth() { jwt("auth-jwt") { val jwtService: JWTService by this@configureAuth.closestDI().instance() realm = jwtService.getRealm() - verifier(jwtService.getAccessTokenVerifier()) + verifier(jwtService.accessTokenVerifier) validate { val id = it.payload.getClaim("userid").asInt() diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/routes/AuthRoutes.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/routes/AuthRoutes.kt index 1bab721a..22bc3216 100644 --- a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/routes/AuthRoutes.kt +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/routes/AuthRoutes.kt @@ -60,7 +60,7 @@ fun Route.refreshCookie() { val userService: UserService by closestDI().instance() val response = call.request.cookies["jwt"]?.let { tokenToCheck -> - val token = jwtService.getRefreshTokenVerifier().verify(tokenToCheck) + val token = jwtService.refreshTokenVerifier.verify(tokenToCheck) val id = token.getClaim("userid").asInt() val tokenVersion = token.getClaim("token_version").asInt() userService.getUserPrincipalByIDAndTokenVersion(id, tokenVersion)?.let { userPrincipal -> diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/services/JWTService.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/services/JWTService.kt index 5257d96d..158edb68 100644 --- a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/services/JWTService.kt +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/services/JWTService.kt @@ -13,7 +13,7 @@ class JWTService(private val config: Config) { private val accessTokenValidationTime = 1000 * 60 * config.jwt.accessMinutes private val refreshTokenValidationTime = 1000 * 60 * 60 * 24 * config.jwt.refreshDays - private val accessTokenVerifier = JWT + val accessTokenVerifier: JWTVerifier = JWT .require(Algorithm.HMAC256(config.jwt.accessSecret)) .withAudience(config.jwt.audience) .withIssuer(config.jwt.issuer) @@ -21,7 +21,7 @@ class JWTService(private val config: Config) { .withClaimPresence("token_version") .build() - private val refreshTokenVerifier = JWT + val refreshTokenVerifier: JWTVerifier = JWT .require(Algorithm.HMAC256(config.jwt.refreshSecret)) .withAudience(config.jwt.audience) .withIssuer(config.jwt.issuer) @@ -29,14 +29,6 @@ class JWTService(private val config: Config) { .withClaimPresence("token_version") .build() - fun getAccessTokenVerifier(): JWTVerifier { - return accessTokenVerifier - } - - fun getRefreshTokenVerifier(): JWTVerifier { - return refreshTokenVerifier - } - fun getRealm(): String { return config.jwt.realm } From e917e8e80f0c6521df00d5620316602628d69a1e Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Thu, 23 Jun 2022 00:17:15 +0200 Subject: [PATCH 08/33] add a linux/macos script version --- CopyScriptJsMain.sh | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 CopyScriptJsMain.sh diff --git a/CopyScriptJsMain.sh b/CopyScriptJsMain.sh new file mode 100644 index 00000000..13642823 --- /dev/null +++ b/CopyScriptJsMain.sh @@ -0,0 +1,2 @@ +#!/bin/bash +cp -r "./budget-binder-multiplatform-app/build/distributions/*" "./budget-binder-server/public/" From b6981062ef29adb2ff3e7e5ce13b5e2f8578342a Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Thu, 23 Jun 2022 00:18:00 +0200 Subject: [PATCH 09/33] added linebreaks --- build.gradle.kts | 2 +- settings.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 95c1c864..a4819cf5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -23,4 +23,4 @@ buildscript { // Keep 'com.android.tools.lint:lint' @30.0.3 classpath("com.android.tools.lint:lint:30.0.3") } -} \ No newline at end of file +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 57128a59..89ce27f9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,4 +10,4 @@ rootProject.name = "budget-binder" include(":budget-binder-common") include(":budget-binder-multiplatform-app") -include(":budget-binder-server") \ No newline at end of file +include(":budget-binder-server") From e4e8823ef15d2087f8542dc67a975381b496d10a Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Thu, 23 Jun 2022 00:20:55 +0200 Subject: [PATCH 10/33] put the Versions in a gradle.properties --- budget-binder-server/build.gradle.kts | 6 +++--- budget-binder-server/gradle.properties | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 budget-binder-server/gradle.properties diff --git a/budget-binder-server/build.gradle.kts b/budget-binder-server/build.gradle.kts index beb767ce..c072bc0a 100644 --- a/budget-binder-server/build.gradle.kts +++ b/budget-binder-server/build.gradle.kts @@ -1,5 +1,8 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +val ktorVersion: String by project +val exposedVersion: String by project + plugins { application kotlin("jvm") @@ -25,7 +28,6 @@ dependencies { implementation(project(":budget-binder-common")) implementation(kotlin("stdlib")) - val ktorVersion = "2.0.2" implementation("io.ktor:ktor-network-tls-certificates-jvm:$ktorVersion") implementation("io.ktor:ktor-server-core-jvm:$ktorVersion") implementation("io.ktor:ktor-server-auth-jvm:$ktorVersion") @@ -45,10 +47,8 @@ dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.2") - implementation("ch.qos.logback:logback-classic:1.2.11") - val exposedVersion = "0.38.2" implementation("org.jetbrains.exposed:exposed-core:$exposedVersion") implementation("org.jetbrains.exposed:exposed-dao:$exposedVersion") implementation("org.jetbrains.exposed:exposed-jdbc:$exposedVersion") diff --git a/budget-binder-server/gradle.properties b/budget-binder-server/gradle.properties new file mode 100644 index 00000000..956362bc --- /dev/null +++ b/budget-binder-server/gradle.properties @@ -0,0 +1,2 @@ +ktorVersion=2.0.2 +exposedVersion=0.38.2 From 4849860ce15ff55181cac3e0d947d22a8ab2fabc Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Thu, 23 Jun 2022 10:41:03 +0200 Subject: [PATCH 11/33] append line breaks. --- .../kotlin/de/hsfl/budgetBinder/common/APIResponse.kt | 2 +- .../commonMain/kotlin/de/hsfl/budgetBinder/common/AuthToken.kt | 2 +- .../commonMain/kotlin/de/hsfl/budgetBinder/common/Category.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/budget-binder-common/src/commonMain/kotlin/de/hsfl/budgetBinder/common/APIResponse.kt b/budget-binder-common/src/commonMain/kotlin/de/hsfl/budgetBinder/common/APIResponse.kt index 7ce6f150..02fecd0b 100644 --- a/budget-binder-common/src/commonMain/kotlin/de/hsfl/budgetBinder/common/APIResponse.kt +++ b/budget-binder-common/src/commonMain/kotlin/de/hsfl/budgetBinder/common/APIResponse.kt @@ -7,4 +7,4 @@ data class APIResponse( val error: ErrorModel? = null, val data: T? = null, val success: Boolean = false -) \ No newline at end of file +) diff --git a/budget-binder-common/src/commonMain/kotlin/de/hsfl/budgetBinder/common/AuthToken.kt b/budget-binder-common/src/commonMain/kotlin/de/hsfl/budgetBinder/common/AuthToken.kt index 15873dae..06c15495 100644 --- a/budget-binder-common/src/commonMain/kotlin/de/hsfl/budgetBinder/common/AuthToken.kt +++ b/budget-binder-common/src/commonMain/kotlin/de/hsfl/budgetBinder/common/AuthToken.kt @@ -3,4 +3,4 @@ package de.hsfl.budgetBinder.common import kotlinx.serialization.Serializable @Serializable -data class AuthToken(val token: String) \ No newline at end of file +data class AuthToken(val token: String) diff --git a/budget-binder-common/src/commonMain/kotlin/de/hsfl/budgetBinder/common/Category.kt b/budget-binder-common/src/commonMain/kotlin/de/hsfl/budgetBinder/common/Category.kt index 876c2d74..eec288eb 100644 --- a/budget-binder-common/src/commonMain/kotlin/de/hsfl/budgetBinder/common/Category.kt +++ b/budget-binder-common/src/commonMain/kotlin/de/hsfl/budgetBinder/common/Category.kt @@ -72,4 +72,4 @@ data class Category( val image: Image? = null, val budget: Float? = null ) -} \ No newline at end of file +} From cc06b6dca83980c986d2ff672fe4b53af27b1bd2 Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Thu, 23 Jun 2022 10:41:31 +0200 Subject: [PATCH 12/33] edit the config to not ignore keys and always send default values. --- .../hsfl/budgetBinder/server/plugins/ContentNegotiation.kt | 4 +++- .../test/kotlin/de/hsfl/budgetBinder/server/TestHelpers.kt | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/ContentNegotiation.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/ContentNegotiation.kt index a57c53d8..6fa9c11f 100644 --- a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/ContentNegotiation.kt +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/plugins/ContentNegotiation.kt @@ -8,7 +8,9 @@ import kotlinx.serialization.json.Json fun Application.configureContentNegotiation() { install(ContentNegotiation) { json(Json { - ignoreUnknownKeys = true + // ignoreUnknownKeys = true + encodeDefaults = true + prettyPrint = true }) } } diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestHelpers.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestHelpers.kt index 9077f93e..04fabac0 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestHelpers.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestHelpers.kt @@ -8,6 +8,7 @@ import io.ktor.client.* import io.ktor.serialization.kotlinx.json.* import io.ktor.client.plugins.contentnegotiation.* import io.ktor.server.testing.* +import kotlinx.serialization.json.Json import java.time.LocalDateTime import java.time.format.DateTimeFormatter @@ -29,7 +30,10 @@ fun customTestApplication(block: suspend ApplicationTestBuilder.(client: HttpCli } val client = createClient { install(ContentNegotiation) { - json() + json(Json { + encodeDefaults = true + prettyPrint = true + }) } } block(client) From 0f1ae1e447f1a134fd7b6faf3db4074c49971dfa Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Thu, 23 Jun 2022 11:29:31 +0200 Subject: [PATCH 13/33] made all helpers to extension-functions Then I don't need to provide client as parameter --- .../budgetBinder/server/ApplicationTest.kt | 28 +++---- .../budgetBinder/server/CategoryEntryTest.kt | 74 ++++++++----------- .../hsfl/budgetBinder/server/CategoryTest.kt | 72 ++++++++---------- .../de/hsfl/budgetBinder/server/EntryTest.kt | 74 ++++++++----------- .../hsfl/budgetBinder/server/TestHelpers.kt | 2 +- .../hsfl/budgetBinder/server/TestRequests.kt | 26 +++---- 6 files changed, 118 insertions(+), 158 deletions(-) diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/ApplicationTest.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/ApplicationTest.kt index a18e1311..be738220 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/ApplicationTest.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/ApplicationTest.kt @@ -17,7 +17,7 @@ import kotlin.test.* class ApplicationTest { @BeforeTest fun registerTestUser() = customTestApplication { client -> - registerUser(client) + client.registerUser() } @AfterTest @@ -93,7 +93,7 @@ class ApplicationTest { assertEquals(shouldResponse, responseBody) } - loginUser(client) { response -> + client.loginUser() { response -> val setCookieHeader = response.headers[HttpHeaders.SetCookie] assertNotNull(setCookieHeader) val cookie = HttpCookie.parse(setCookieHeader) @@ -121,7 +121,7 @@ class ApplicationTest { assertEquals(shouldResponse, responseBody) } - checkMeSuccess(client) + client.checkMeSuccess() client.get("/refresh_token").let { response -> assertEquals(HttpStatusCode.OK, response.status) @@ -137,7 +137,7 @@ class ApplicationTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Get, "/logout") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/logout") { response -> assertEquals(HttpStatusCode.OK, response.status) val logoutResponse: APIResponse = response.body() val shouldResponse = wrapSuccess(AuthToken("")) @@ -152,9 +152,9 @@ class ApplicationTest { assertEquals("", cookie[0].value) } - checkMeSuccess(client) + client.checkMeSuccess() TestUser.accessToken = null - checkMeFailure(client) + client.checkMeFailure() } @Test @@ -174,8 +174,8 @@ class ApplicationTest { } install(HttpCookies) } - loginUser(client) - checkMeSuccess(client) + client.loginUser() + client.checkMeSuccess() val tokenVersion = transaction { val tokenVersion = UserEntity.all().first().tokenVersion @@ -183,7 +183,7 @@ class ApplicationTest { tokenVersion } - sendAuthenticatedRequest(client, HttpMethod.Get, "/logout?all=true") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/logout?all=true") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse = wrapSuccess(AuthToken("")) @@ -195,7 +195,7 @@ class ApplicationTest { assertNotEquals(tokenVersion, newTokenVersion) } - checkMeFailure(client) + client.checkMeFailure() client.get("/refresh_token").let { response -> assertEquals(HttpStatusCode.Unauthorized, response.status) @@ -208,11 +208,11 @@ class ApplicationTest { @Test fun testUserEndpoints() = customTestApplicationWithLogin { client -> - checkMeSuccess(client) + client.checkMeSuccess() val userId = transaction { UserEntity.all().first().id.value } - sendAuthenticatedRequest(client, HttpMethod.Patch, "/me") { response -> + client.sendAuthenticatedRequest(HttpMethod.Patch, "/me") { response -> assertEquals(HttpStatusCode.OK, response.status) val user: APIResponse = response.body() @@ -232,7 +232,7 @@ class ApplicationTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequestWithBody(client, HttpMethod.Patch, "/me", patchedUser) { response -> + client. sendAuthenticatedRequestWithBody(HttpMethod.Patch, "/me", patchedUser) { response -> assertEquals(HttpStatusCode.OK, response.status) val user: APIResponse = response.body() @@ -285,7 +285,7 @@ class ApplicationTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Delete, "/me") { response -> + client.sendAuthenticatedRequest(HttpMethod.Delete, "/me") { response -> assertEquals(HttpStatusCode.OK, response.status) val user: APIResponse = response.body() diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/CategoryEntryTest.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/CategoryEntryTest.kt index c5ad1eac..e9105a96 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/CategoryEntryTest.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/CategoryEntryTest.kt @@ -19,7 +19,7 @@ class CategoryEntryTest { @BeforeTest fun before() = customTestApplication { client -> - registerUser(client) + client.registerUser() val userEntity = transaction { UserEntity.all().first() } val now = LocalDateTime.now() @@ -127,14 +127,14 @@ class CategoryEntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Get, "/categories/test/entries") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/test/entries") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse> = response.body() val shouldResponse: APIResponse> = wrapFailure("The ID you provided is not a number.") assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Get, "/categories/5000/entries") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/5000/entries") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse> = response.body() val shouldResponse: APIResponse> = wrapFailure("Your category was not found.") @@ -152,35 +152,35 @@ class CategoryEntryTest { Entry(entryId + 4, "Monthly Pay", 3000f, true, null), ) - sendAuthenticatedRequest(client, HttpMethod.Get, "/categories/${categoryId - 1}/entries") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/${categoryId - 1}/entries") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse> = response.body() val shouldResponse: APIResponse> = wrapFailure("Your category was not found.") assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Get, "/categories/$categoryId/entries") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/$categoryId/entries") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse> = response.body() val shouldResponse = wrapSuccess(listOf(entryList[0])) assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Get, "/categories/${categoryId + 1}/entries") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/${categoryId + 1}/entries") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse> = response.body() val shouldResponse = wrapSuccess(listOf(entryList[1], entryList[2], entryList[3])) assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Get, "/categories/null/entries") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/null/entries") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse> = response.body() val shouldResponse = wrapSuccess(listOf(entryList[4])) assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Get, "/entries?current=true") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/entries?current=true") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse> = response.body() val shouldResponse = wrapSuccess(listOf(entryList[1], entryList[2], entryList[4])) @@ -203,15 +203,14 @@ class CategoryEntryTest { Entry(entryId + 4, "Monthly Pay", 3000f, true, null), ) - sendAuthenticatedRequest(client, HttpMethod.Get, "/categories/${categoryId}/entries?current=true") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/${categoryId}/entries?current=true") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse> = response.body() val shouldResponse: APIResponse> = wrapSuccess(emptyList()) assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest( - client, + client.sendAuthenticatedRequest( HttpMethod.Get, "/categories/${categoryId}/entries?period=${formatToPeriod(now.minusMonths(1))}" ) { response -> @@ -221,8 +220,7 @@ class CategoryEntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest( - client, + client.sendAuthenticatedRequest( HttpMethod.Get, "/categories/${categoryId}/entries?period=${formatToPeriod(now.minusMonths(2))}" ) { response -> @@ -232,8 +230,7 @@ class CategoryEntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest( - client, + client.sendAuthenticatedRequest( HttpMethod.Get, "/categories/${categoryId}/entries?period=${formatToPeriod(now.minusMonths(3))}" ) { response -> @@ -243,8 +240,7 @@ class CategoryEntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest( - client, + client.sendAuthenticatedRequest( HttpMethod.Get, "/categories/${categoryId + 1}/entries?current=true" ) { response -> @@ -254,8 +250,7 @@ class CategoryEntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest( - client, + client.sendAuthenticatedRequest( HttpMethod.Get, "/categories/${categoryId + 1}/entries?period=${formatToPeriod(now.minusMonths(1))}" ) { response -> @@ -265,8 +260,7 @@ class CategoryEntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest( - client, + client.sendAuthenticatedRequest( HttpMethod.Get, "/categories/${categoryId + 1}/entries?period=${formatToPeriod(now.minusMonths(2))}" ) { response -> @@ -276,8 +270,7 @@ class CategoryEntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest( - client, + client.sendAuthenticatedRequest( HttpMethod.Get, "/categories/${categoryId + 1}/entries?period=${formatToPeriod(now.minusMonths(3))}" ) { response -> @@ -293,8 +286,7 @@ class CategoryEntryTest { fun createEntryWithCategory() = customTestApplicationWithLogin { client -> val categoryId = transaction { CategoryEntity.all().first().id.value + 1 } - sendAuthenticatedRequestWithBody( - client, + client.sendAuthenticatedRequestWithBody( HttpMethod.Post, "/entries", Entry.In("Second Phone", -50f, true, 5000) ) { response -> @@ -314,8 +306,7 @@ class CategoryEntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequestWithBody( - client, + client.sendAuthenticatedRequestWithBody( HttpMethod.Post, "/entries", Entry.In("Second Phone", -50f, true, categoryId) ) { response -> @@ -342,8 +333,7 @@ class CategoryEntryTest { val categoryId = transaction { CategoryEntity.all().first().id.value + 1 } val entryId = transaction { EntryEntity.all().last().id.value } - sendAuthenticatedRequestWithBody( - client, + client.sendAuthenticatedRequestWithBody( HttpMethod.Patch, "/entries/$entryId", Entry.Patch(category = Entry.Category(categoryId - 1)) ) { response -> @@ -353,8 +343,7 @@ class CategoryEntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequestWithBody( - client, + client.sendAuthenticatedRequestWithBody( HttpMethod.Patch, "/entries/$entryId", Entry.Patch(category = Entry.Category(categoryId)) ) { response -> @@ -368,8 +357,7 @@ class CategoryEntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequestWithBody( - client, + client.sendAuthenticatedRequestWithBody( HttpMethod.Patch, "/entries/$entryId", Entry.Patch(category = Entry.Category(5000)) ) { response -> @@ -390,8 +378,7 @@ class CategoryEntryTest { val categoryId = transaction { CategoryEntity.all().first().id.value + 1 } val entryId = transaction { EntryEntity.all().first().id.value + 1 } - sendAuthenticatedRequestWithBody( - client, + client.sendAuthenticatedRequestWithBody( HttpMethod.Patch, "/categories/$categoryId", Category.Patch(budget = 200f) ) { response -> @@ -479,8 +466,7 @@ class CategoryEntryTest { val entryId = transaction { EntryEntity.all().last().id.value - 1 } - sendAuthenticatedRequestWithBody( - client, + client.sendAuthenticatedRequestWithBody( HttpMethod.Patch, "/categories/$categoryId", Category.Patch(budget = 200f) ) { response -> @@ -560,8 +546,7 @@ class CategoryEntryTest { val entryId = transaction { EntryEntity.all().last().id.value - 1 } - sendAuthenticatedRequestWithBody( - client, + client.sendAuthenticatedRequestWithBody( HttpMethod.Patch, "/categories/$categoryId", Category.Patch(budget = 200f) ) { response -> @@ -649,8 +634,7 @@ class CategoryEntryTest { val categoryId = transaction { CategoryEntity.all().last().id.value } val entryId = transaction { EntryEntity.all().last().id.value - 1 } - sendAuthenticatedRequestWithBody( - client, + client.sendAuthenticatedRequestWithBody( HttpMethod.Patch, "/categories/$categoryId", Category.Patch(budget = 200f) ) { response -> @@ -697,7 +681,7 @@ class CategoryEntryTest { val categoryId = transaction { CategoryEntity.all().first().id.value + 1 } val entryId = transaction { EntryEntity.all().first().id.value + 1 } - sendAuthenticatedRequest(client, HttpMethod.Delete, "/categories/$categoryId") { response -> + client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/$categoryId") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() @@ -779,7 +763,7 @@ class CategoryEntryTest { val entryId = transaction { EntryEntity.all().last().id.value - 1 } - sendAuthenticatedRequest(client, HttpMethod.Delete, "/categories/$categoryId") { response -> + client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/$categoryId") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() @@ -849,7 +833,7 @@ class CategoryEntryTest { val entryId = transaction { EntryEntity.all().last().id.value - 1 } - sendAuthenticatedRequest(client, HttpMethod.Delete, "/categories/$categoryId") { response -> + client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/$categoryId") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() @@ -926,7 +910,7 @@ class CategoryEntryTest { val categoryId = transaction { CategoryEntity.all().last().id.value } val entryId = transaction { EntryEntity.all().last().id.value - 1 } - sendAuthenticatedRequest(client, HttpMethod.Delete, "/categories/$categoryId") { response -> + client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/$categoryId") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/CategoryTest.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/CategoryTest.kt index 0acab05e..021aebdf 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/CategoryTest.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/CategoryTest.kt @@ -15,7 +15,7 @@ class CategoryTest { @BeforeTest fun before() = customTestApplication { client -> - registerUser(client) + client.registerUser() val userEntity = transaction { UserEntity.all().first() } val now = LocalDateTime.now() @@ -101,7 +101,7 @@ class CategoryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Post, "/categories") { response -> + client.sendAuthenticatedRequest(HttpMethod.Post, "/categories") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() @@ -109,8 +109,7 @@ class CategoryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequestWithBody( - client, + client.sendAuthenticatedRequestWithBody( HttpMethod.Post, "/categories", Category.In("Test", TestCategories.color, TestCategories.image, 50f) ) { response -> @@ -152,7 +151,7 @@ class CategoryTest { Category(id + 4, "Hobbies", TestCategories.color, TestCategories.image, 150f), ) - sendAuthenticatedRequest(client, HttpMethod.Get, "/categories") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse> = response.body() assert(responseBody.success) @@ -161,7 +160,7 @@ class CategoryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Get, "/categories?current=true") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories?current=true") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse> = response.body() assert(responseBody.success) @@ -172,7 +171,7 @@ class CategoryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Get, "/categories?period=508346") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories?period=508346") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse> = response.body() val shouldResponse = wrapFailure>("period has not the right pattern") @@ -181,7 +180,7 @@ class CategoryTest { val now = LocalDateTime.now() - sendAuthenticatedRequest(client, HttpMethod.Get, "/categories?period=${formatToPeriod(now)}") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories?period=${formatToPeriod(now)}") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse> = response.body() assert(responseBody.success) @@ -192,8 +191,7 @@ class CategoryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest( - client, + client.sendAuthenticatedRequest( HttpMethod.Get, "/categories?period=${formatToPeriod(now.minusMonths(2))}" ) { response -> @@ -207,8 +205,7 @@ class CategoryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest( - client, + client.sendAuthenticatedRequest( HttpMethod.Get, "/categories?period=${formatToPeriod(now.minusMonths(3))}" ) { response -> @@ -222,8 +219,7 @@ class CategoryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest( - client, + client.sendAuthenticatedRequest( HttpMethod.Get, "/categories?period=${formatToPeriod(now.minusMonths(4))}" ) { response -> @@ -237,8 +233,7 @@ class CategoryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest( - client, + client.sendAuthenticatedRequest( HttpMethod.Get, "/categories?period=${formatToPeriod(now.minusMonths(5))}" ) { response -> @@ -252,8 +247,7 @@ class CategoryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest( - client, + client.sendAuthenticatedRequest( HttpMethod.Get, "/categories?period=${formatToPeriod(now.minusMonths(6))}" ) { response -> @@ -276,21 +270,21 @@ class CategoryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Get, "/categories/test") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/test") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Get, "/categories/null") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/null") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Get, "/categories/5000") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/5000") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("Your category was not found.") @@ -299,14 +293,14 @@ class CategoryTest { val id = transaction { CategoryEntity.all().first().id.value } - sendAuthenticatedRequest(client, HttpMethod.Get, "/categories/${id - 1}") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/${id - 1}") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("Your category was not found.") assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Get, "/categories/${id}") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/${id}") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse = wrapSuccess(Category(id, "test", TestCategories.color, TestCategories.image, 10f)) @@ -324,21 +318,21 @@ class CategoryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Patch, "/categories/test") { response -> + client.sendAuthenticatedRequest(HttpMethod.Patch, "/categories/test") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Patch, "/categories/null") { response -> + client.sendAuthenticatedRequest(HttpMethod.Patch, "/categories/null") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Patch, "/categories/5000") { response -> + client.sendAuthenticatedRequest(HttpMethod.Patch, "/categories/5000") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("Your category was not found.") @@ -347,15 +341,14 @@ class CategoryTest { val id = transaction { CategoryEntity.all().first().id.value } - sendAuthenticatedRequest(client, HttpMethod.Patch, "/categories/${id}") { response -> + client.sendAuthenticatedRequest(HttpMethod.Patch, "/categories/${id}") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("The object you provided it not in the right format.") assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequestWithBody( - client, + client.sendAuthenticatedRequestWithBody( HttpMethod.Patch, "/categories/${id}", Category.Patch(name = "patchedTest") @@ -366,8 +359,7 @@ class CategoryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequestWithBody( - client, + client.sendAuthenticatedRequestWithBody( HttpMethod.Patch, "/categories/${id - 1}", Category.Patch(name = "patchedTest") @@ -378,8 +370,7 @@ class CategoryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequestWithBody( - client, + client.sendAuthenticatedRequestWithBody( HttpMethod.Patch, "/categories/${id + 4}", Category.Patch(name = "Fishing") @@ -398,8 +389,7 @@ class CategoryTest { } } - sendAuthenticatedRequestWithBody( - client, + client.sendAuthenticatedRequestWithBody( HttpMethod.Patch, "/categories/${id + 4}", Category.Patch(name = "Fishing", budget = 100f) @@ -429,21 +419,21 @@ class CategoryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Delete, "/categories/test") { response -> + client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/test") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Delete, "/categories/null") { response -> + client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/null") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Delete, "/categories/5000") { response -> + client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/5000") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("Your category was not found.") @@ -452,21 +442,21 @@ class CategoryTest { val id = transaction { CategoryEntity.all().first().id.value } - sendAuthenticatedRequest(client, HttpMethod.Delete, "/categories/${id - 1}") { response -> + client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/${id - 1}") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("Your category was not found.") assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Delete, "/categories/${id}") { response -> + client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/${id}") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("you can't delete an old category.") assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Delete, "/categories/${id + 4}") { response -> + client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/${id + 4}") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/EntryTest.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/EntryTest.kt index 6c2801ce..d1b67f2a 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/EntryTest.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/EntryTest.kt @@ -16,7 +16,7 @@ class EntryTest { @BeforeTest fun before() = customTestApplication { client -> - registerUser(client) + client.registerUser() val userEntity = transaction { UserEntity.all().first() } val now = LocalDateTime.now() @@ -131,15 +131,14 @@ class EntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Post, "/entries") { response -> + client.sendAuthenticatedRequest(HttpMethod.Post, "/entries") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("The object you provided it not in the right format.") assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequestWithBody( - client, + client.sendAuthenticatedRequestWithBody( HttpMethod.Post, "/entries", Entry.In("Bafög", 700f, true, null) ) { response -> @@ -182,7 +181,7 @@ class EntryTest { Entry(id + 6, "new Phone", -50f, true, null), ) - sendAuthenticatedRequest(client, HttpMethod.Get, "/entries") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/entries") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse> = response.body() assert(responseBody.success) @@ -191,7 +190,7 @@ class EntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Get, "/entries?current=true") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/entries?current=true") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse> = response.body() assert(responseBody.success) @@ -202,7 +201,7 @@ class EntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Get, "/entries?period=50-8346") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/entries?period=50-8346") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse> = response.body() val shouldResponse: APIResponse> = wrapFailure("period has not the right pattern") @@ -211,7 +210,7 @@ class EntryTest { val now = LocalDateTime.now() - sendAuthenticatedRequest(client, HttpMethod.Get, "/entries?period=${formatToPeriod(now)}") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/entries?period=${formatToPeriod(now)}") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse> = response.body() assert(responseBody.success) @@ -222,8 +221,7 @@ class EntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest( - client, + client.sendAuthenticatedRequest( HttpMethod.Get, "/entries?period=${formatToPeriod(now.minusMonths(1))}" ) { response -> @@ -237,8 +235,7 @@ class EntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest( - client, + client.sendAuthenticatedRequest( HttpMethod.Get, "/entries?period=${formatToPeriod(now.minusMonths(2))}" ) { response -> @@ -252,8 +249,7 @@ class EntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest( - client, + client.sendAuthenticatedRequest( HttpMethod.Get, "/entries?period=${formatToPeriod(now.minusMonths(3))}" ) { response -> @@ -267,8 +263,7 @@ class EntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest( - client, + client.sendAuthenticatedRequest( HttpMethod.Get, "/entries?period=${formatToPeriod(now.minusMonths(4))}" ) { response -> @@ -292,14 +287,14 @@ class EntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Get, "/entries/test") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/entries/test") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Get, "/entries/5000") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/entries/5000") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("Your entry was not found.") @@ -308,7 +303,7 @@ class EntryTest { val id = transaction { EntryEntity.all().first().id.value } - sendAuthenticatedRequest(client, HttpMethod.Get, "/entries/$id") { response -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/entries/$id") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse = wrapSuccess(Entry(id, "Monthly Job Pay", 3000f, true, null)) @@ -326,14 +321,14 @@ class EntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Patch, "/entries/test") { response -> + client.sendAuthenticatedRequest(HttpMethod.Patch, "/entries/test") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Patch, "/entries/5000") { response -> + client.sendAuthenticatedRequest(HttpMethod.Patch, "/entries/5000") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("Your entry was not found.") @@ -342,15 +337,14 @@ class EntryTest { val id = transaction { EntryEntity.all().first().id.value } - sendAuthenticatedRequest(client, HttpMethod.Patch, "/entries/$id") { response -> + client.sendAuthenticatedRequest(HttpMethod.Patch, "/entries/$id") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("The object you provided it not in the right format.") assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequestWithBody( - client, + client.sendAuthenticatedRequestWithBody( HttpMethod.Patch, "/entries/$id", Entry.Patch(name = "Pay") ) { response -> @@ -360,8 +354,7 @@ class EntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequestWithBody( - client, + client.sendAuthenticatedRequestWithBody( HttpMethod.Patch, "/entries/${id + 5}", Entry.Patch(name = "Ikea Shopping") ) { response -> @@ -371,8 +364,7 @@ class EntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequestWithBody( - client, + client.sendAuthenticatedRequestWithBody( HttpMethod.Patch, "/entries/${id + 4}", Entry.Patch(amount = -1700f) ) { response -> @@ -382,8 +374,7 @@ class EntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequestWithBody( - client, + client.sendAuthenticatedRequestWithBody( HttpMethod.Patch, "/entries/${id + 5}", Entry.Patch(name = "Ikea", repeat = true) ) { response -> @@ -393,8 +384,7 @@ class EntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequestWithBody( - client, + client.sendAuthenticatedRequestWithBody( HttpMethod.Patch, "/entries/${id + 6}", Entry.Patch(repeat = false) ) { response -> @@ -404,8 +394,7 @@ class EntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequestWithBody( - client, + client.sendAuthenticatedRequestWithBody( HttpMethod.Patch, "/entries/${id + 3}", Entry.Patch(repeat = false) ) { response -> @@ -424,8 +413,7 @@ class EntryTest { } } - sendAuthenticatedRequestWithBody( - client, + client.sendAuthenticatedRequestWithBody( HttpMethod.Patch, "/entries/${id + 1}", Entry.Patch(amount = 3700f) ) { response -> @@ -455,14 +443,14 @@ class EntryTest { assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Delete, "/entries/test") { response -> + client.sendAuthenticatedRequest(HttpMethod.Delete, "/entries/test") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Delete, "/entries/5000") { response -> + client.sendAuthenticatedRequest(HttpMethod.Delete, "/entries/5000") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("Your entry was not found.") @@ -471,14 +459,14 @@ class EntryTest { val id = transaction { EntryEntity.all().first().id.value } - sendAuthenticatedRequest(client, HttpMethod.Delete, "/entries/$id") { response -> + client.sendAuthenticatedRequest(HttpMethod.Delete, "/entries/$id") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("you can't delete this Entry") assertEquals(shouldResponse, responseBody) } - sendAuthenticatedRequest(client, HttpMethod.Delete, "/entries/${id + 1}") { response -> + client.sendAuthenticatedRequest(HttpMethod.Delete, "/entries/${id + 1}") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse = wrapSuccess(Entry(id + 1, "Monthly Job Pay", 3500f, true, null)) @@ -490,7 +478,7 @@ class EntryTest { } } - sendAuthenticatedRequest(client, HttpMethod.Delete, "/entries/${id + 6}") { response -> + client.sendAuthenticatedRequest(HttpMethod.Delete, "/entries/${id + 6}") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse = wrapSuccess(Entry(id + 6, "new Phone", -50f, true, null)) @@ -502,7 +490,7 @@ class EntryTest { } } - sendAuthenticatedRequest(client, HttpMethod.Delete, "/entries/${id + 4}") { response -> + client.sendAuthenticatedRequest(HttpMethod.Delete, "/entries/${id + 4}") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse = wrapSuccess(Entry(id + 4, "Bike", -1500f, false, null)) @@ -514,7 +502,7 @@ class EntryTest { } } - sendAuthenticatedRequest(client, HttpMethod.Delete, "/entries/${id + 5}") { response -> + client.sendAuthenticatedRequest(HttpMethod.Delete, "/entries/${id + 5}") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() val shouldResponse = wrapSuccess(Entry(id + 5, "Ikea", -200f, false, null)) diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestHelpers.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestHelpers.kt index 04fabac0..1fe5fed6 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestHelpers.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestHelpers.kt @@ -42,7 +42,7 @@ fun customTestApplication(block: suspend ApplicationTestBuilder.(client: HttpCli fun customTestApplicationWithLogin(block: suspend ApplicationTestBuilder.(client: HttpClient) -> Unit) = customTestApplication { client -> - loginUser(client) + client.loginUser() block(client) } diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestRequests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestRequests.kt index 170ca677..3aca6ce8 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestRequests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestRequests.kt @@ -13,8 +13,8 @@ import org.jetbrains.exposed.sql.transactions.transaction import kotlin.test.assertEquals import kotlin.test.assertNotNull -suspend fun registerUser(client: HttpClient) { - val response = client.post("/register") { +suspend fun HttpClient.registerUser() { + val response = this.post("/register") { contentType(ContentType.Application.Json) setBody(TestUser.userIn) } @@ -36,8 +36,8 @@ suspend fun registerUser(client: HttpClient) { assertEquals(shouldResponse, responseBody) } -suspend fun loginUser(client: HttpClient, block: (response: HttpResponse) -> Unit = {}) { - val response = client.post("/login") { +suspend fun HttpClient.loginUser(block: (response: HttpResponse) -> Unit = {}) { + val response = this.post("/login") { contentType(ContentType.Application.FormUrlEncoded) setBody( listOf( @@ -54,29 +54,27 @@ suspend fun loginUser(client: HttpClient, block: (response: HttpResponse) -> Uni block(response) } -suspend inline fun sendAuthenticatedRequest( - client: HttpClient, +suspend inline fun HttpClient.sendAuthenticatedRequest( sendMethod: HttpMethod, path: String, block: (response: HttpResponse) -> Unit ) { block( - client.request(path) { + this.request(path) { method = sendMethod header(HttpHeaders.Authorization, "Bearer ${TestUser.accessToken ?: ""}") } ) } -suspend inline fun sendAuthenticatedRequestWithBody( - client: HttpClient, +suspend inline fun HttpClient.sendAuthenticatedRequestWithBody( sendMethod: HttpMethod, path: String, body: T, block: (response: HttpResponse) -> Unit ) { block( - client.request(path) { + this.request(path) { method = sendMethod header(HttpHeaders.Authorization, "Bearer ${TestUser.accessToken ?: ""}") header(HttpHeaders.ContentType, ContentType.Application.Json) @@ -85,8 +83,8 @@ suspend inline fun sendAuthenticatedRequestWithBody( ) } -suspend fun checkMeSuccess(client: HttpClient) { - sendAuthenticatedRequest(client, HttpMethod.Get, "/me") { response -> +suspend fun HttpClient.checkMeSuccess() { + this.sendAuthenticatedRequest(HttpMethod.Get, "/me") { response -> assertEquals(HttpStatusCode.OK, response.status) val responseBody: APIResponse = response.body() @@ -97,8 +95,8 @@ suspend fun checkMeSuccess(client: HttpClient) { } } -suspend fun checkMeFailure(client: HttpClient) { - sendAuthenticatedRequest(client, HttpMethod.Get, "/me") { response -> +suspend fun HttpClient.checkMeFailure() { + this.sendAuthenticatedRequest(HttpMethod.Get, "/me") { response -> assertEquals(HttpStatusCode.Unauthorized, response.status) val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("Your accessToken is absent or does not match.", 401) From d0b3953259bffc57964bf05b6e0f0d933b85a1b1 Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Thu, 23 Jun 2022 11:52:09 +0200 Subject: [PATCH 14/33] added CustomCookieStorage it is the same implementation I wrote for the multiplatform app. --- .../de/hsfl/budgetBinder/server/TestModels.kt | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestModels.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestModels.kt index 533de973..8b1218f8 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestModels.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestModels.kt @@ -2,6 +2,13 @@ package de.hsfl.budgetBinder.server import de.hsfl.budgetBinder.common.Category import de.hsfl.budgetBinder.common.User +import io.ktor.client.plugins.cookies.* +import io.ktor.http.* +import io.ktor.util.* +import io.ktor.util.date.* +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import kotlin.math.min object TestUser { const val email = "test@test.com" @@ -24,3 +31,108 @@ object TestCategories { val image = Category.Image.DEFAULT } + +class CustomCookieStorage : CookiesStorage { + private val container: MutableList = mutableListOf() + private var oldestCookie: Long = 0L + private val mutex = Mutex() + + override suspend fun get(requestUrl: Url): List = mutex.withLock { + val date = GMTDate() + if (date.timestamp >= oldestCookie) cleanup(date.timestamp) + + return@withLock container.filter { isCookieForUrl(it, requestUrl) } + } + + override suspend fun addCookie(requestUrl: Url, cookie: Cookie): Unit = mutex.withLock { + with(cookie) { + if (name.isBlank()) return@withLock + } + + val newCookie = cookie.fillDefaults(requestUrl) + + container.removeAll { areSameCookies(it, newCookie) } + container.add(newCookie) + + cookie.expires?.timestamp?.let { expires -> + if (oldestCookie > expires) { + oldestCookie = expires + } + } + cleanup(GMTDate().timestamp) + } + + private fun areSameCookies(cookie: Cookie, other: Cookie): Boolean { + val domain = cookie.domain?.toLowerCasePreservingASCIIRules()?.trimStart('.') + ?: error("Domain field should have the default value") + + val otherDomain = other.domain?.toLowerCasePreservingASCIIRules()?.trimStart('.') + ?: error("Domain field should have the default value") + + val path = cookie.path?.let { + if (it.endsWith('/')) it else "$it/" + } ?: error("Path field should have the default value") + + val otherPath = other.path?.let { + if (it.endsWith('/')) it else "$it/" + } ?: error("Path field should have the default value") + + return domain == otherDomain && path == otherPath && cookie.name == other.name + } + + private fun isCookieForUrl(cookie: Cookie, requestUrl: Url): Boolean { + val domain = cookie.domain?.toLowerCasePreservingASCIIRules()?.trimStart('.') + ?: error("Domain field should have the default value") + + val path = cookie.path?.let { + if (it.endsWith('/')) it else "$it/" + } ?: error("Path field should have the default value") + + val host = requestUrl.host.toLowerCasePreservingASCIIRules() + val requestPath = let { + val pathInRequest = requestUrl.encodedPath + if (pathInRequest.endsWith('/')) pathInRequest else "$pathInRequest/" + } + + if (host != domain && (hostIsIp(host) || !host.endsWith(".$domain"))) { + return false + } + + if (path != "/" && + requestPath != path && + !requestPath.startsWith(path) + ) return false + + return !(cookie.secure && !requestUrl.protocol.isSecure()) + } + + private fun Cookie.fillDefaults(requestUrl: Url): Cookie { + var result = this + + if (result.path?.startsWith("/") != true) { + result = result.copy(path = requestUrl.encodedPath) + } + + if (result.domain.isNullOrBlank()) { + result = result.copy(domain = requestUrl.host) + } + + return result + } + + private fun cleanup(timestamp: Long) { + container.removeAll { cookie -> + val expires = cookie.expires?.timestamp ?: return@removeAll false + expires < timestamp + } + + val newOldest = container.fold(Long.MAX_VALUE) { acc, cookie -> + cookie.expires?.timestamp?.let { min(acc, it) } ?: acc + } + + oldestCookie = newOldest + } + + override fun close() { + } +} From 86364fb1417ceb0b2decaa96c5baa5e5d57360bf Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Thu, 23 Jun 2022 13:14:31 +0200 Subject: [PATCH 15/33] start to restructure tests --- .../server/LogoutAndRefreshTests.kt | 144 ++++++++++++++++++ .../budgetBinder/server/UnauthorizedTests.kt | 102 +++++++++++++ 2 files changed, 246 insertions(+) create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LogoutAndRefreshTests.kt create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UnauthorizedTests.kt diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LogoutAndRefreshTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LogoutAndRefreshTests.kt new file mode 100644 index 00000000..028e4b0a --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LogoutAndRefreshTests.kt @@ -0,0 +1,144 @@ +package de.hsfl.budgetBinder.server + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.AuthToken +import de.hsfl.budgetBinder.server.models.UserEntity +import io.ktor.client.* +import io.ktor.client.call.* +import io.ktor.client.plugins.contentnegotiation.* +import io.ktor.client.plugins.cookies.* +import io.ktor.client.request.* +import io.ktor.http.* +import io.ktor.serialization.kotlinx.json.* +import org.jetbrains.exposed.sql.transactions.transaction +import java.net.HttpCookie +import kotlin.test.* + +class LogoutAndRefreshTests { + + private suspend fun HttpClient.logoutUser(all: Boolean) { + this.sendAuthenticatedRequest(HttpMethod.Get, "/logout?all=$all") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapSuccess(AuthToken("")) + assertEquals(shouldResponse, responseBody) + + val setCookieHeader = response.headers[HttpHeaders.SetCookie] + assertNotNull(setCookieHeader) + val cookie = HttpCookie.parse(setCookieHeader) + assertNotNull(cookie) + assertEquals(1, cookie.size) + assertEquals("jwt", cookie[0].name) + assertEquals("", cookie[0].value) + } + } + + @BeforeTest + fun registerTestUser() = customTestApplication { client -> + client.registerUser() + } + + @AfterTest + fun deleteTestUser() = transaction { + UserEntity.all().forEach { it.delete() } + } + + @Test + fun testGetMeAfterLogout() = customTestApplicationWithLogin { client -> + client.checkMeSuccess() + client.logoutUser(false) + client.checkMeSuccess() + TestUser.accessToken = "" + client.checkMeFailure() + } + + @Test + fun testGetMeAfterLogoutAll() = customTestApplicationWithLogin { client -> + client.checkMeSuccess() + client.logoutUser(true) + client.checkMeFailure() + } + + @Test + fun testRefreshTokenAfterLogout() = customTestApplication { + var client = createClient { + install(ContentNegotiation) { + json() + } + install(HttpCookies) { + storage = CustomCookieStorage() + } + } + client.loginUser() + client.checkMeSuccess() + val cookies = client.cookies("http://localhost/refresh_token") + assertEquals(1, cookies.size) + client.logoutUser(false) + + client.get("refresh_token").let { + assertEquals(HttpStatusCode.Unauthorized, it.status) + val responseBody: APIResponse = it.body() + val shouldResponse: APIResponse = wrapFailure("Your refreshToken is absent.", 401) + assertEquals(shouldResponse, responseBody) + } + + assertEquals(0, client.cookies("http://localhost/refresh_token").size) + + client = createClient { + install(ContentNegotiation) { + json() + } + install(HttpCookies) { + storage = ConstantCookiesStorage(cookies[0]) + } + } + client.get("refresh_token").let { + assertEquals(HttpStatusCode.OK, it.status) + val responseBody: APIResponse = it.body() + assert(responseBody.success) + assertNull(responseBody.error) + assertNotNull(responseBody.data) + } + } + + @Test + fun testRefreshTokenAfterLogoutAll() = customTestApplication { + var client = createClient { + install(ContentNegotiation) { + json() + } + install(HttpCookies) { + storage = CustomCookieStorage() + } + } + client.loginUser() + client.checkMeSuccess() + val cookies = client.cookies("http://localhost/refresh_token") + assertEquals(1, cookies.size) + client.logoutUser(true) + assertEquals(0, client.cookies("http://localhost/refresh_token").size) + + client.get("refresh_token").let { + assertEquals(HttpStatusCode.Unauthorized, it.status) + val responseBody: APIResponse = it.body() + val shouldResponse: APIResponse = wrapFailure("Your refreshToken is absent.", 401) + assertEquals(shouldResponse, responseBody) + } + + client = createClient { + install(ContentNegotiation) { + json() + } + install(HttpCookies) { + storage = ConstantCookiesStorage(cookies[0]) + } + } + + client.get("refresh_token").let { + assertEquals(HttpStatusCode.Unauthorized, it.status) + val responseBody: APIResponse = it.body() + val shouldResponse: APIResponse = wrapFailure("Your refreshToken does not match.", 401) + assertEquals(shouldResponse, responseBody) + } + } +} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UnauthorizedTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UnauthorizedTests.kt new file mode 100644 index 00000000..673d943c --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UnauthorizedTests.kt @@ -0,0 +1,102 @@ +package de.hsfl.budgetBinder.server + +import de.hsfl.budgetBinder.common.* +import io.ktor.client.call.* +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.http.* +import kotlin.test.* + +class UnauthorizedTests { + private suspend inline fun HttpResponse.checkUnauthorized(errorMsg: String = "Your accessToken is absent or does not match.") { + assertEquals(HttpStatusCode.Unauthorized, this.status) + val responseBody: APIResponse = this.body() + val shouldResponse: APIResponse = wrapFailure(errorMsg, 401) + assertEquals(shouldResponse, responseBody) + } + + @Test + fun testLogin() = customTestApplication { client -> + client.post("/login").checkUnauthorized("Your username and/or password do not match.") + } + + @Test + fun testLogout() = customTestApplication { client -> + client.get("/logout").checkUnauthorized() + } + + @Test + fun testRefreshToken() = customTestApplication { client -> + client.get("refresh_token").checkUnauthorized("Your refreshToken is absent.") + } + + @Test + fun testGetMe() = customTestApplication { client -> + client.get("/me").checkUnauthorized() + } + + @Test + fun testPatchMe() = customTestApplication { client -> + client.patch("/me").checkUnauthorized() + } + + @Test + fun testDeleteMe() = customTestApplication { client -> + client.delete("/me").checkUnauthorized() + } + + @Test + fun testGetCategories() = customTestApplication { client -> + client.get("/categories").checkUnauthorized>() + } + + @Test + fun testPostCategory() = customTestApplication { client -> + client.post("/categories").checkUnauthorized() + } + + @Test + fun testGetCategoryByID() = customTestApplication { client -> + client.get("/categories/1").checkUnauthorized() + } + + @Test + fun testPatchCategoryByID() = customTestApplication { client -> + client.patch("/categories/1").checkUnauthorized() + } + + @Test + fun testDeleteCategoryByID() = customTestApplication { client -> + client.delete("/categories/1").checkUnauthorized() + } + + @Test + fun testGetEntriesByCategoryID() = customTestApplication { client -> + client.get("/categories/1/entries").checkUnauthorized>() + } + + @Test + fun testGetEntries() = customTestApplication { client -> + client.get("/entries").checkUnauthorized>() + } + + @Test + fun testPostEntry() = customTestApplication { client -> + client.post("/entries").checkUnauthorized() + } + + @Test + fun testGetEntryByID() = customTestApplication { client -> + client.get("/entries/1").checkUnauthorized() + } + + @Test + fun testPatchEntryByID() = customTestApplication { client -> + client.patch("/entries/1").checkUnauthorized() + } + + @Test + fun testDeleteEntryByID() = customTestApplication { client -> + client.delete("/entries/1").checkUnauthorized() + } +} From 69ec923414af9047ed07cc1901b701ab8440d93c Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Thu, 23 Jun 2022 13:22:11 +0200 Subject: [PATCH 16/33] add BaseRoutesTests --- .../budgetBinder/server/BaseRoutesTests.kt | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/BaseRoutesTests.kt diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/BaseRoutesTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/BaseRoutesTests.kt new file mode 100644 index 00000000..9eca6ffd --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/BaseRoutesTests.kt @@ -0,0 +1,32 @@ +package de.hsfl.budgetBinder.server + +import io.ktor.client.request.* +import io.ktor.http.* +import kotlin.test.* + +class BaseRoutesTests { + + @Test + fun testGetOpenApi() = customTestApplication { client -> + client.get("/openapi.json").let { response -> + assertEquals(HttpStatusCode.OK, response.status) + assertEquals(ContentType.Application.Json, response.contentType()) + } + } + + @Test + fun testFavicon() = customTestApplication { client -> + client.get("/favicon.ico").let { response -> + assertEquals(HttpStatusCode.OK, response.status) + assertEquals(ContentType.Image.XIcon, response.contentType()) + } + } + + @Test + fun testDocs() = customTestApplication { client -> + client.get("/docs").let { response -> + assertEquals(HttpStatusCode.OK, response.status) + assertEquals(ContentType.Text.Html.withCharset(Charsets.UTF_8), response.contentType()) + } + } +} From feff8c308e7427687e1241953d5e6589f64a7cb2 Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Thu, 23 Jun 2022 13:26:26 +0200 Subject: [PATCH 17/33] added utils package --- .../test/kotlin/de/hsfl/budgetBinder/server/ApplicationTest.kt | 1 + .../test/kotlin/de/hsfl/budgetBinder/server/BaseRoutesTests.kt | 1 + .../kotlin/de/hsfl/budgetBinder/server/CategoryEntryTest.kt | 1 + .../test/kotlin/de/hsfl/budgetBinder/server/CategoryTest.kt | 1 + .../src/test/kotlin/de/hsfl/budgetBinder/server/EntryTest.kt | 1 + .../de/hsfl/budgetBinder/server/LogoutAndRefreshTests.kt | 1 + .../kotlin/de/hsfl/budgetBinder/server/UnauthorizedTests.kt | 2 ++ .../de/hsfl/budgetBinder/server/{ => utils}/TestHelpers.kt | 3 ++- .../de/hsfl/budgetBinder/server/{ => utils}/TestModels.kt | 2 +- .../de/hsfl/budgetBinder/server/{ => utils}/TestRequests.kt | 2 +- 10 files changed, 12 insertions(+), 3 deletions(-) rename budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/{ => utils}/TestHelpers.kt (95%) rename budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/{ => utils}/TestModels.kt (99%) rename budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/{ => utils}/TestRequests.kt (98%) diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/ApplicationTest.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/ApplicationTest.kt index be738220..3538ab8d 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/ApplicationTest.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/ApplicationTest.kt @@ -4,6 +4,7 @@ import de.hsfl.budgetBinder.common.APIResponse import de.hsfl.budgetBinder.common.AuthToken import de.hsfl.budgetBinder.common.User import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* import io.ktor.client.call.* import io.ktor.client.plugins.contentnegotiation.* import io.ktor.client.plugins.cookies.* diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/BaseRoutesTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/BaseRoutesTests.kt index 9eca6ffd..5e25431e 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/BaseRoutesTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/BaseRoutesTests.kt @@ -1,5 +1,6 @@ package de.hsfl.budgetBinder.server +import de.hsfl.budgetBinder.server.utils.customTestApplication import io.ktor.client.request.* import io.ktor.http.* import kotlin.test.* diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/CategoryEntryTest.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/CategoryEntryTest.kt index e9105a96..9775388c 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/CategoryEntryTest.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/CategoryEntryTest.kt @@ -7,6 +7,7 @@ import de.hsfl.budgetBinder.server.models.CategoryEntity import de.hsfl.budgetBinder.server.models.Entries import de.hsfl.budgetBinder.server.models.EntryEntity import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* import io.ktor.client.call.* import io.ktor.client.request.* import io.ktor.http.* diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/CategoryTest.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/CategoryTest.kt index 021aebdf..f41db437 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/CategoryTest.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/CategoryTest.kt @@ -4,6 +4,7 @@ import de.hsfl.budgetBinder.common.APIResponse import de.hsfl.budgetBinder.common.Category import de.hsfl.budgetBinder.server.models.CategoryEntity import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* import io.ktor.client.call.* import io.ktor.client.request.* import io.ktor.http.* diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/EntryTest.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/EntryTest.kt index d1b67f2a..52ebde71 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/EntryTest.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/EntryTest.kt @@ -4,6 +4,7 @@ import de.hsfl.budgetBinder.common.APIResponse import de.hsfl.budgetBinder.common.Entry import de.hsfl.budgetBinder.server.models.EntryEntity import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* import io.ktor.client.call.* import io.ktor.client.request.* import io.ktor.http.* diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LogoutAndRefreshTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LogoutAndRefreshTests.kt index 028e4b0a..4d4309bd 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LogoutAndRefreshTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LogoutAndRefreshTests.kt @@ -3,6 +3,7 @@ package de.hsfl.budgetBinder.server import de.hsfl.budgetBinder.common.APIResponse import de.hsfl.budgetBinder.common.AuthToken import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* import io.ktor.client.* import io.ktor.client.call.* import io.ktor.client.plugins.contentnegotiation.* diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UnauthorizedTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UnauthorizedTests.kt index 673d943c..4a22c706 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UnauthorizedTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UnauthorizedTests.kt @@ -1,6 +1,8 @@ package de.hsfl.budgetBinder.server import de.hsfl.budgetBinder.common.* +import de.hsfl.budgetBinder.server.utils.customTestApplication +import de.hsfl.budgetBinder.server.utils.wrapFailure import io.ktor.client.call.* import io.ktor.client.request.* import io.ktor.client.statement.* diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestHelpers.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/utils/TestHelpers.kt similarity index 95% rename from budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestHelpers.kt rename to budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/utils/TestHelpers.kt index 1fe5fed6..fd1fd8ae 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestHelpers.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/utils/TestHelpers.kt @@ -1,9 +1,10 @@ -package de.hsfl.budgetBinder.server +package de.hsfl.budgetBinder.server.utils import de.hsfl.budgetBinder.common.APIResponse import de.hsfl.budgetBinder.common.ErrorModel import de.hsfl.budgetBinder.server.config.Config import de.hsfl.budgetBinder.server.config.ConfigIntermediate +import de.hsfl.budgetBinder.server.mainModule import io.ktor.client.* import io.ktor.serialization.kotlinx.json.* import io.ktor.client.plugins.contentnegotiation.* diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestModels.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/utils/TestModels.kt similarity index 99% rename from budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestModels.kt rename to budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/utils/TestModels.kt index 8b1218f8..1e22f846 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestModels.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/utils/TestModels.kt @@ -1,4 +1,4 @@ -package de.hsfl.budgetBinder.server +package de.hsfl.budgetBinder.server.utils import de.hsfl.budgetBinder.common.Category import de.hsfl.budgetBinder.common.User diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestRequests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/utils/TestRequests.kt similarity index 98% rename from budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestRequests.kt rename to budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/utils/TestRequests.kt index 3aca6ce8..07705974 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/TestRequests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/utils/TestRequests.kt @@ -1,4 +1,4 @@ -package de.hsfl.budgetBinder.server +package de.hsfl.budgetBinder.server.utils import de.hsfl.budgetBinder.common.APIResponse import de.hsfl.budgetBinder.common.AuthToken From 1680ce4b0fe4511f9630e0287e2b2147d4ddb610 Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Thu, 23 Jun 2022 13:35:47 +0200 Subject: [PATCH 18/33] add Login Tests and move some things around --- .../de/hsfl/budgetBinder/server/LoginTests.kt | 69 +++++++++++++++++++ .../server/LogoutAndRefreshTests.kt | 10 +++ .../budgetBinder/server/UnauthorizedTests.kt | 14 +--- 3 files changed, 81 insertions(+), 12 deletions(-) create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LoginTests.kt diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LoginTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LoginTests.kt new file mode 100644 index 00000000..9392836f --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LoginTests.kt @@ -0,0 +1,69 @@ +package de.hsfl.budgetBinder.server + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.AuthToken +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.customTestApplication +import de.hsfl.budgetBinder.server.utils.loginUser +import de.hsfl.budgetBinder.server.utils.registerUser +import de.hsfl.budgetBinder.server.utils.wrapFailure +import io.ktor.client.call.* +import io.ktor.client.request.* +import io.ktor.http.* +import org.jetbrains.exposed.sql.transactions.transaction +import java.net.HttpCookie +import kotlin.test.* + +class LoginTests { + + @BeforeTest + fun registerTestUser() = customTestApplication { client -> + client.registerUser() + } + + @AfterTest + fun deleteTestUser() = transaction { + UserEntity.all().forEach { it.delete() } + } + + @Test + fun testLoginUnauthorized() = customTestApplication { client -> + client.post("/login").let { + assertEquals(HttpStatusCode.Unauthorized, it.status) + val responseBody: APIResponse = it.body() + val shouldResponse: APIResponse = wrapFailure("Your username and/or password do not match.", 401) + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testLoginFalseData() = customTestApplication { client -> + client.post("/login") { + header(HttpHeaders.ContentType, ContentType.Application.FormUrlEncoded) + setBody( + listOf( + "username" to "falseTest@test.com", + "password" to "falsetest" + ).formUrlEncode() + ) + }.let { response -> + assertEquals(HttpStatusCode.Unauthorized, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("Your username and/or password do not match.", 401) + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testLoginSuccessFull() = customTestApplication { client -> + client.loginUser { response -> + val setCookieHeader = response.headers[HttpHeaders.SetCookie] + assertNotNull(setCookieHeader) + val cookie = HttpCookie.parse(setCookieHeader) + assertNotNull(cookie) + assertEquals(1, cookie.size) + assertEquals("jwt", cookie[0].name) + assertNotEquals("", cookie[0].value) + } + } +} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LogoutAndRefreshTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LogoutAndRefreshTests.kt index 4d4309bd..d6ea46b8 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LogoutAndRefreshTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LogoutAndRefreshTests.kt @@ -44,6 +44,16 @@ class LogoutAndRefreshTests { UserEntity.all().forEach { it.delete() } } + @Test + fun testRefreshTokenUnauthorized() = customTestApplication { client -> + client.get("refresh_token").let { + assertEquals(HttpStatusCode.Unauthorized, it.status) + val responseBody: APIResponse = it.body() + val shouldResponse: APIResponse = wrapFailure("Your refreshToken is absent.", 401) + assertEquals(shouldResponse, responseBody) + } + } + @Test fun testGetMeAfterLogout() = customTestApplicationWithLogin { client -> client.checkMeSuccess() diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UnauthorizedTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UnauthorizedTests.kt index 4a22c706..5512ccac 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UnauthorizedTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UnauthorizedTests.kt @@ -10,28 +10,18 @@ import io.ktor.http.* import kotlin.test.* class UnauthorizedTests { - private suspend inline fun HttpResponse.checkUnauthorized(errorMsg: String = "Your accessToken is absent or does not match.") { + private suspend inline fun HttpResponse.checkUnauthorized() { assertEquals(HttpStatusCode.Unauthorized, this.status) val responseBody: APIResponse = this.body() - val shouldResponse: APIResponse = wrapFailure(errorMsg, 401) + val shouldResponse: APIResponse = wrapFailure("Your accessToken is absent or does not match.", 401) assertEquals(shouldResponse, responseBody) } - @Test - fun testLogin() = customTestApplication { client -> - client.post("/login").checkUnauthorized("Your username and/or password do not match.") - } - @Test fun testLogout() = customTestApplication { client -> client.get("/logout").checkUnauthorized() } - @Test - fun testRefreshToken() = customTestApplication { client -> - client.get("refresh_token").checkUnauthorized("Your refreshToken is absent.") - } - @Test fun testGetMe() = customTestApplication { client -> client.get("/me").checkUnauthorized() From 625a04676a87c98ac455b3bd0adbfdbb17005cd7 Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Thu, 23 Jun 2022 14:16:26 +0200 Subject: [PATCH 19/33] add more tests and removed old ApplicationTest --- .../budgetBinder/server/ApplicationTest.kt | 325 ------------------ .../de/hsfl/budgetBinder/server/LoginTests.kt | 6 +- .../server/LogoutAndRefreshTests.kt | 73 +++- .../hsfl/budgetBinder/server/RegisterTests.kt | 45 +++ .../de/hsfl/budgetBinder/server/UserTests.kt | 130 +++++++ 5 files changed, 236 insertions(+), 343 deletions(-) delete mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/ApplicationTest.kt create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/RegisterTests.kt create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UserTests.kt diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/ApplicationTest.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/ApplicationTest.kt deleted file mode 100644 index 3538ab8d..00000000 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/ApplicationTest.kt +++ /dev/null @@ -1,325 +0,0 @@ -package de.hsfl.budgetBinder.server - -import de.hsfl.budgetBinder.common.APIResponse -import de.hsfl.budgetBinder.common.AuthToken -import de.hsfl.budgetBinder.common.User -import de.hsfl.budgetBinder.server.models.UserEntity -import de.hsfl.budgetBinder.server.utils.* -import io.ktor.client.call.* -import io.ktor.client.plugins.contentnegotiation.* -import io.ktor.client.plugins.cookies.* -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.serialization.kotlinx.json.* -import org.jetbrains.exposed.sql.transactions.transaction -import java.net.HttpCookie -import kotlin.test.* - -class ApplicationTest { - @BeforeTest - fun registerTestUser() = customTestApplication { client -> - client.registerUser() - } - - @AfterTest - fun deleteTestUser() = transaction { - UserEntity.all().forEach { - it.delete() - } - } - - @Test - fun testRoot() = customTestApplication { client -> - client.get("/docs").let { response -> - assertEquals(HttpStatusCode.OK, response.status) - assertEquals(ContentType.Text.Html.withCharset(Charsets.UTF_8), response.contentType()) - } - - client.get("/favicon.ico").let { response -> - assertEquals(HttpStatusCode.OK, response.status) - assertEquals(ContentType.Image.XIcon, response.contentType()) - } - - client.get("/openapi.json").let { response -> - assertEquals(HttpStatusCode.OK, response.status) - assertEquals(ContentType.Application.Json, response.contentType()) - } - } - - @Test - fun testRegisterLoginAndLogout() = customTestApplication { - val client = createClient { - install(ContentNegotiation) { - json() - } - install(HttpCookies) - } - - client.post("/register").let { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("The object you provided it not in the right format.") - assertEquals(shouldResponse, responseBody) - } - - client.post("/register") { - contentType(ContentType.Application.Json) - setBody(TestUser.userIn) - }.let { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Email already assigned. Please choose another.") - assertEquals(shouldResponse, responseBody) - } - - client.post("/login").let { response -> - assertEquals(HttpStatusCode.Unauthorized, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your username and/or password do not match.", 401) - assertEquals(shouldResponse, responseBody) - } - - client.post("/login") { - header(HttpHeaders.ContentType, ContentType.Application.FormUrlEncoded) - setBody( - listOf( - "username" to "falseTest@test.com", - "password" to "falsetest" - ).formUrlEncode() - ) - }.let { response -> - assertEquals(HttpStatusCode.Unauthorized, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your username and/or password do not match.", 401) - assertEquals(shouldResponse, responseBody) - } - - client.loginUser() { response -> - val setCookieHeader = response.headers[HttpHeaders.SetCookie] - assertNotNull(setCookieHeader) - val cookie = HttpCookie.parse(setCookieHeader) - assertNotNull(cookie) - assertEquals(1, cookie.size) - assertEquals("jwt", cookie[0].name) - assertNotEquals("", cookie[0].value) - } - - client.get("/me").let { response -> - assertEquals(HttpStatusCode.Unauthorized, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your accessToken is absent or does not match.", 401) - assertEquals(shouldResponse, responseBody) - } - - client.get("/me") { - val bearer = - "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyaWQiOjF9.OsLv52jTx-f-vcuQEJ6FJ-kTJ_DYm3XqVpjLwagQtM0" - header(HttpHeaders.Authorization, "Bearer $bearer") - }.let { response -> - assertEquals(HttpStatusCode.Unauthorized, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your accessToken is absent or does not match.", 401) - assertEquals(shouldResponse, responseBody) - } - - client.checkMeSuccess() - - client.get("/refresh_token").let { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - assert(responseBody.success) - assertNotNull(responseBody.data) - } - - client.get("/logout").let { response -> - assertEquals(HttpStatusCode.Unauthorized, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your accessToken is absent or does not match.", 401) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Get, "/logout") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val logoutResponse: APIResponse = response.body() - val shouldResponse = wrapSuccess(AuthToken("")) - assertEquals(shouldResponse, logoutResponse) - - val setCookieHeader = response.headers[HttpHeaders.SetCookie] - assertNotNull(setCookieHeader) - val cookie = HttpCookie.parse(setCookieHeader) - assertNotNull(cookie) - assertEquals(1, cookie.size) - assertEquals("jwt", cookie[0].name) - assertEquals("", cookie[0].value) - } - - client.checkMeSuccess() - TestUser.accessToken = null - client.checkMeFailure() - } - - @Test - fun testRefreshTokenWithoutCookie() = customTestApplication { client -> - val response = client.get("/refresh_token") - assertEquals(HttpStatusCode.Unauthorized, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your refreshToken is absent.", 401) - assertEquals(shouldResponse, responseBody) - } - - @Test - fun testLogoutAll() = customTestApplication { - val client = createClient { - install(ContentNegotiation) { - json() - } - install(HttpCookies) - } - client.loginUser() - client.checkMeSuccess() - - val tokenVersion = transaction { - val tokenVersion = UserEntity.all().first().tokenVersion - assertEquals(1, tokenVersion) - tokenVersion - } - - client.sendAuthenticatedRequest(HttpMethod.Get, "/logout?all=true") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse = wrapSuccess(AuthToken("")) - assertEquals(shouldResponse, responseBody) - } - - transaction { - val newTokenVersion = UserEntity.all().first().tokenVersion - assertNotEquals(tokenVersion, newTokenVersion) - } - - client.checkMeFailure() - - client.get("/refresh_token").let { response -> - assertEquals(HttpStatusCode.Unauthorized, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = - wrapFailure("Your refreshToken does not match.", 401) - assertEquals(shouldResponse, responseBody) - } - } - - @Test - fun testUserEndpoints() = customTestApplicationWithLogin { client -> - client.checkMeSuccess() - - val userId = transaction { UserEntity.all().first().id.value } - - client.sendAuthenticatedRequest(HttpMethod.Patch, "/me") { response -> - assertEquals(HttpStatusCode.OK, response.status) - - val user: APIResponse = response.body() - val shouldUser: APIResponse = wrapFailure("The object you provided it not in the right format.") - assertEquals(shouldUser, user) - } - - val patchedUser = User.Patch("changedTest", "changedSurname", "newPassword") - - client.patch("/me") { - header(HttpHeaders.ContentType, ContentType.Application.Json) - setBody(patchedUser) - }.let { response -> - assertEquals(HttpStatusCode.Unauthorized, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your accessToken is absent or does not match.", 401) - assertEquals(shouldResponse, responseBody) - } - - client. sendAuthenticatedRequestWithBody(HttpMethod.Patch, "/me", patchedUser) { response -> - assertEquals(HttpStatusCode.OK, response.status) - - val user: APIResponse = response.body() - val shouldUser = wrapSuccess(User(userId, "changedTest", "changedSurname", TestUser.email)) - assertEquals(shouldUser, user) - - transaction { - val userEntity = UserEntity[userId] - assertEquals("changedTest", userEntity.firstName) - assertEquals("changedSurname", userEntity.name) - assertEquals("changedSurname", userEntity.name) - } - } - - client.post("/login") { - header(HttpHeaders.ContentType, ContentType.Application.FormUrlEncoded) - setBody( - listOf( - "username" to TestUser.email, - "password" to TestUser.password - ).formUrlEncode() - ) - }.let { response -> - assertEquals(HttpStatusCode.Unauthorized, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your username and/or password do not match.", 401) - assertEquals(shouldResponse, responseBody) - } - - client.post("/login") { - header(HttpHeaders.ContentType, ContentType.Application.FormUrlEncoded) - setBody( - listOf( - "username" to TestUser.email, - "password" to "newPassword" - ).formUrlEncode() - ) - }.let { response -> - assertEquals(HttpStatusCode.OK, response.status) - val token: APIResponse = response.body() - assert(token.success) - assertNotNull(token.data) - TestUser.accessToken = token.data!!.token - } - - client.delete("/me").let { response -> - assertEquals(HttpStatusCode.Unauthorized, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your accessToken is absent or does not match.", 401) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Delete, "/me") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val user: APIResponse = response.body() - - val shouldUser = wrapSuccess(User(userId, "changedTest", "changedSurname", TestUser.email)) - assertEquals(shouldUser, user) - - transaction { - assertNull(UserEntity.findById(userId)) - } - } - - client.get("/me") { - header(HttpHeaders.Authorization, "Bearer ${TestUser.accessToken ?: ""}") - }.let { response -> - assertEquals(HttpStatusCode.Unauthorized, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your accessToken is absent or does not match.", 401) - assertEquals(shouldResponse, responseBody) - } - - client.post("/login") { - header(HttpHeaders.ContentType, ContentType.Application.FormUrlEncoded) - setBody( - listOf( - "username" to TestUser.email, - "password" to "newPassword" - ).formUrlEncode() - ) - }.let { response -> - assertEquals(HttpStatusCode.Unauthorized, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your username and/or password do not match.", 401) - assertEquals(shouldResponse, responseBody) - } - } -} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LoginTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LoginTests.kt index 9392836f..0ebff1e3 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LoginTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LoginTests.kt @@ -28,9 +28,9 @@ class LoginTests { @Test fun testLoginUnauthorized() = customTestApplication { client -> - client.post("/login").let { - assertEquals(HttpStatusCode.Unauthorized, it.status) - val responseBody: APIResponse = it.body() + client.post("/login").let { response -> + assertEquals(HttpStatusCode.Unauthorized, response.status) + val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("Your username and/or password do not match.", 401) assertEquals(shouldResponse, responseBody) } diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LogoutAndRefreshTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LogoutAndRefreshTests.kt index d6ea46b8..3e94b186 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LogoutAndRefreshTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LogoutAndRefreshTests.kt @@ -46,14 +46,44 @@ class LogoutAndRefreshTests { @Test fun testRefreshTokenUnauthorized() = customTestApplication { client -> - client.get("refresh_token").let { - assertEquals(HttpStatusCode.Unauthorized, it.status) - val responseBody: APIResponse = it.body() + client.get("refresh_token").let { response -> + assertEquals(HttpStatusCode.Unauthorized, response.status) + val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("Your refreshToken is absent.", 401) assertEquals(shouldResponse, responseBody) } } + @Test + fun testRefreshToken() = customTestApplication { + val client = createClient { + install(ContentNegotiation) { + json() + } + install(HttpCookies) { + storage = CustomCookieStorage() + } + } + + client.loginUser() + + client.get("refresh_token").let { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + assert(responseBody.success) + assertNull(responseBody.error) + assertNotNull(responseBody.data) + + val setCookieHeader = response.headers[HttpHeaders.SetCookie] + assertNotNull(setCookieHeader) + val cookie = HttpCookie.parse(setCookieHeader) + assertNotNull(cookie) + assertEquals(1, cookie.size) + assertEquals("jwt", cookie[0].name) + assertNotEquals("", cookie[0].value) + } + } + @Test fun testGetMeAfterLogout() = customTestApplicationWithLogin { client -> client.checkMeSuccess() @@ -66,7 +96,20 @@ class LogoutAndRefreshTests { @Test fun testGetMeAfterLogoutAll() = customTestApplicationWithLogin { client -> client.checkMeSuccess() + + val tokenVersion = transaction { + val tokenVersion = UserEntity.all().first().tokenVersion + assertEquals(1, tokenVersion) + tokenVersion + } + client.logoutUser(true) + + transaction { + val newTokenVersion = UserEntity.all().first().tokenVersion + assertNotEquals(tokenVersion, newTokenVersion) + } + client.checkMeFailure() } @@ -86,9 +129,9 @@ class LogoutAndRefreshTests { assertEquals(1, cookies.size) client.logoutUser(false) - client.get("refresh_token").let { - assertEquals(HttpStatusCode.Unauthorized, it.status) - val responseBody: APIResponse = it.body() + client.get("refresh_token").let { response -> + assertEquals(HttpStatusCode.Unauthorized, response.status) + val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("Your refreshToken is absent.", 401) assertEquals(shouldResponse, responseBody) } @@ -103,9 +146,9 @@ class LogoutAndRefreshTests { storage = ConstantCookiesStorage(cookies[0]) } } - client.get("refresh_token").let { - assertEquals(HttpStatusCode.OK, it.status) - val responseBody: APIResponse = it.body() + client.get("refresh_token").let { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() assert(responseBody.success) assertNull(responseBody.error) assertNotNull(responseBody.data) @@ -129,9 +172,9 @@ class LogoutAndRefreshTests { client.logoutUser(true) assertEquals(0, client.cookies("http://localhost/refresh_token").size) - client.get("refresh_token").let { - assertEquals(HttpStatusCode.Unauthorized, it.status) - val responseBody: APIResponse = it.body() + client.get("refresh_token").let { response -> + assertEquals(HttpStatusCode.Unauthorized, response.status) + val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("Your refreshToken is absent.", 401) assertEquals(shouldResponse, responseBody) } @@ -145,9 +188,9 @@ class LogoutAndRefreshTests { } } - client.get("refresh_token").let { - assertEquals(HttpStatusCode.Unauthorized, it.status) - val responseBody: APIResponse = it.body() + client.get("refresh_token").let { response -> + assertEquals(HttpStatusCode.Unauthorized, response.status) + val responseBody: APIResponse = response.body() val shouldResponse: APIResponse = wrapFailure("Your refreshToken does not match.", 401) assertEquals(shouldResponse, responseBody) } diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/RegisterTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/RegisterTests.kt new file mode 100644 index 00000000..9359ee6d --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/RegisterTests.kt @@ -0,0 +1,45 @@ +package de.hsfl.budgetBinder.server + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.User +import de.hsfl.budgetBinder.server.utils.TestUser +import de.hsfl.budgetBinder.server.utils.customTestApplication +import de.hsfl.budgetBinder.server.utils.registerUser +import de.hsfl.budgetBinder.server.utils.wrapFailure +import io.ktor.client.call.* +import io.ktor.client.request.* +import io.ktor.http.* +import org.junit.Test +import kotlin.test.assertEquals + +class RegisterTests { + + @Test + fun testRegisterWithoutBody() = customTestApplication { client -> + client.post("/register").let { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("The object you provided it not in the right format.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testRegisterUser() = customTestApplication { client -> + client.registerUser() + } + + @Test + fun testRegisterSameUser() = customTestApplication { client -> + client.registerUser() + client.post("/register") { + contentType(ContentType.Application.Json) + setBody(TestUser.userIn) + }.let { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("Email already assigned. Please choose another.") + assertEquals(shouldResponse, responseBody) + } + } +} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UserTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UserTests.kt new file mode 100644 index 00000000..12edc21c --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UserTests.kt @@ -0,0 +1,130 @@ +package de.hsfl.budgetBinder.server + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.AuthToken +import de.hsfl.budgetBinder.common.User +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* +import io.ktor.client.call.* +import io.ktor.client.request.* +import io.ktor.http.* +import org.jetbrains.exposed.sql.transactions.transaction +import kotlin.test.* + +class UserTests { + + @BeforeTest + fun registerTestUser() = customTestApplication { client -> + client.registerUser() + } + + @AfterTest + fun deleteTestUser() = transaction { + UserEntity.all().forEach { it.delete() } + } + + @Test + fun testGetMe() = customTestApplicationWithLogin { client -> + client.checkMeSuccess() + } + + @Test + fun testPatchMeWithoutBody() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Patch, "/me") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val user: APIResponse = response.body() + val shouldUser: APIResponse = wrapFailure("The object you provided it not in the right format.") + assertEquals(shouldUser, user) + } + } + + @Test + fun testPatchMe() = customTestApplicationWithLogin { client -> + val userId = transaction { UserEntity.all().first().id.value } + val patchedUser = User.Patch("changedTest", "changedSurname") + + client.sendAuthenticatedRequestWithBody(HttpMethod.Patch, "/me", patchedUser) { response -> + assertEquals(HttpStatusCode.OK, response.status) + + val user: APIResponse = response.body() + val shouldUser = wrapSuccess(User(userId, "changedTest", "changedSurname", TestUser.email)) + assertEquals(shouldUser, user) + + transaction { + val userEntity = UserEntity[userId] + assertEquals("changedTest", userEntity.firstName) + assertEquals("changedSurname", userEntity.name) + } + } + } + + @Test + fun testChangePassword() = customTestApplicationWithLogin { client -> + val userId = transaction { UserEntity.all().first().id.value } + val userPassword = transaction { UserEntity.all().first().passwordHash } + + val patchedUser = User.Patch(password = "newPassword") + + client.sendAuthenticatedRequestWithBody(HttpMethod.Patch, "/me", patchedUser) { response -> + assertEquals(HttpStatusCode.OK, response.status) + + val user: APIResponse = response.body() + val shouldUser = wrapSuccess(TestUser.getTestUser(userId)) + assertEquals(shouldUser, user) + + transaction { + val userEntity = UserEntity[userId] + assertNotEquals(userPassword, userEntity.passwordHash) + } + } + + client.checkMeSuccess() + + client.post("/login") { + header(HttpHeaders.ContentType, ContentType.Application.FormUrlEncoded) + setBody( + listOf( + "username" to TestUser.email, + "password" to TestUser.password + ).formUrlEncode() + ) + }.let { response -> + assertEquals(HttpStatusCode.Unauthorized, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("Your username and/or password do not match.", 401) + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testDeleteMe() = customTestApplicationWithLogin { client -> + val userId = transaction { UserEntity.all().first().id.value } + client.delete("/me").let { response -> + assertEquals(HttpStatusCode.OK, response.status) + val user: APIResponse = response.body() + val shouldUser = wrapSuccess(TestUser.getTestUser(userId)) + assertEquals(shouldUser, user) + + transaction { + assertNull(UserEntity.findById(userId)) + } + } + + client.checkMeFailure() + + client.post("/login") { + header(HttpHeaders.ContentType, ContentType.Application.FormUrlEncoded) + setBody( + listOf( + "username" to TestUser.email, + "password" to TestUser.password + ).formUrlEncode() + ) + }.let { response -> + assertEquals(HttpStatusCode.Unauthorized, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("Your username and/or password do not match.", 401) + assertEquals(shouldResponse, responseBody) + } + } +} From 8119efeb4a91508445f76dc10b4fd2dada6f6053 Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Thu, 23 Jun 2022 15:05:59 +0200 Subject: [PATCH 20/33] update tests --- .../kotlin/de/hsfl/budgetBinder/server/RegisterTests.kt | 9 ++++++++- .../test/kotlin/de/hsfl/budgetBinder/server/UserTests.kt | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/RegisterTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/RegisterTests.kt index 9359ee6d..fffa5bca 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/RegisterTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/RegisterTests.kt @@ -2,6 +2,7 @@ package de.hsfl.budgetBinder.server import de.hsfl.budgetBinder.common.APIResponse import de.hsfl.budgetBinder.common.User +import de.hsfl.budgetBinder.server.models.UserEntity import de.hsfl.budgetBinder.server.utils.TestUser import de.hsfl.budgetBinder.server.utils.customTestApplication import de.hsfl.budgetBinder.server.utils.registerUser @@ -9,11 +10,17 @@ import de.hsfl.budgetBinder.server.utils.wrapFailure import io.ktor.client.call.* import io.ktor.client.request.* import io.ktor.http.* -import org.junit.Test +import org.jetbrains.exposed.sql.transactions.transaction +import kotlin.test.* import kotlin.test.assertEquals class RegisterTests { + @AfterTest + fun deleteTestUser() = transaction { + UserEntity.all().forEach { it.delete() } + } + @Test fun testRegisterWithoutBody() = customTestApplication { client -> client.post("/register").let { response -> diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UserTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UserTests.kt index 12edc21c..b92f2c0c 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UserTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UserTests.kt @@ -99,7 +99,7 @@ class UserTests { @Test fun testDeleteMe() = customTestApplicationWithLogin { client -> val userId = transaction { UserEntity.all().first().id.value } - client.delete("/me").let { response -> + client.sendAuthenticatedRequest(HttpMethod.Delete, "/me") { response -> assertEquals(HttpStatusCode.OK, response.status) val user: APIResponse = response.body() val shouldUser = wrapSuccess(TestUser.getTestUser(userId)) From e6230f82ecb08fb1503ac54b9676e9953c596610 Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Thu, 23 Jun 2022 15:06:36 +0200 Subject: [PATCH 21/33] extract all category Tests in own package and different files --- .../hsfl/budgetBinder/server/CategoryTest.kt | 473 ------------------ .../categories/CategoryFalseValueTests.kt | 103 ++++ .../server/categories/CreateCategoryTests.kt | 48 ++ .../server/categories/DeleteCategoryTests.kt | 119 +++++ .../server/categories/GetCategoriesTests.kt | 215 ++++++++ .../server/categories/GetCategoryByIDTests.kt | 75 +++ .../server/categories/PatchCategoryTests.kt | 165 ++++++ 7 files changed, 725 insertions(+), 473 deletions(-) delete mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/CategoryTest.kt create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/CategoryFalseValueTests.kt create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/CreateCategoryTests.kt create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/DeleteCategoryTests.kt create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/GetCategoriesTests.kt create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/GetCategoryByIDTests.kt create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/PatchCategoryTests.kt diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/CategoryTest.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/CategoryTest.kt deleted file mode 100644 index f41db437..00000000 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/CategoryTest.kt +++ /dev/null @@ -1,473 +0,0 @@ -package de.hsfl.budgetBinder.server - -import de.hsfl.budgetBinder.common.APIResponse -import de.hsfl.budgetBinder.common.Category -import de.hsfl.budgetBinder.server.models.CategoryEntity -import de.hsfl.budgetBinder.server.models.UserEntity -import de.hsfl.budgetBinder.server.utils.* -import io.ktor.client.call.* -import io.ktor.client.request.* -import io.ktor.http.* -import org.jetbrains.exposed.sql.transactions.transaction -import java.time.LocalDateTime -import kotlin.test.* - -class CategoryTest { - - @BeforeTest - fun before() = customTestApplication { client -> - client.registerUser() - - val userEntity = transaction { UserEntity.all().first() } - val now = LocalDateTime.now() - - transaction { - CategoryEntity.new { - name = "test" - color = TestCategories.color - image = TestCategories.image - budget = 10f - created = now.minusMonths(5) - ended = now.minusMonths(3) - child = null - user = userEntity - } - - val internet = CategoryEntity.new { - name = "Internet" - color = TestCategories.color - image = TestCategories.image - budget = 50f - created = now.minusMonths(3) - ended = now.minusMonths(2) - child = null - user = userEntity - } - - val internetPhone = CategoryEntity.new { - name = "Internet-Phone" - color = TestCategories.color - image = TestCategories.image - budget = 100f - created = now.minusMonths(2) - ended = null - child = null - user = userEntity - } - - internet.child = internetPhone.id - - CategoryEntity.new { - name = "Insurance" - color = TestCategories.color - image = TestCategories.image - budget = 100f - created = now.minusMonths(3) - ended = null - child = null - user = userEntity - } - - CategoryEntity.new { - name = "Hobbies" - color = TestCategories.color - image = TestCategories.image - budget = 150f - created = now.minusMonths(2) - ended = null - child = null - user = userEntity - } - } - } - - - @AfterTest - fun after() = transaction { - UserEntity.all().forEach { - it.delete() - } - CategoryEntity.all().forEach { - it.delete() - } - } - - - @Test - fun testCreateCategory() = customTestApplicationWithLogin { client -> - client.get("/categories").let { response -> - assertEquals(HttpStatusCode.Unauthorized, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your accessToken is absent or does not match.", 401) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Post, "/categories") { response -> - assertEquals(HttpStatusCode.OK, response.status) - - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("The object you provided it not in the right format.") - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequestWithBody( - HttpMethod.Post, "/categories", - Category.In("Test", TestCategories.color, TestCategories.image, 50f) - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - - val responseBody: APIResponse = response.body() - val id = transaction { - val categoryEntity = CategoryEntity.all().last() - - assertEquals("Test", categoryEntity.name) - assertEquals(TestCategories.color, categoryEntity.color) - assertEquals(TestCategories.image, categoryEntity.image) - assertEquals(50f, categoryEntity.budget) - - categoryEntity.id.value - } - val shouldResponse = wrapSuccess(Category(id, "Test", TestCategories.color, TestCategories.image, 50f)) - assertEquals(shouldResponse, responseBody) - } - } - - - @Test - fun testGetCategories() = customTestApplicationWithLogin { client -> - client.get("/categories").let { response -> - assertEquals(HttpStatusCode.Unauthorized, response.status) - val responseBody: APIResponse> = response.body() - val shouldResponse: APIResponse> = wrapFailure("Your accessToken is absent or does not match.", 401) - assertEquals(shouldResponse, responseBody) - } - - val id = transaction { CategoryEntity.all().first().id.value } - - val categoryList = listOf( - Category(id, "test", TestCategories.color, TestCategories.image, 10f), - Category(id + 1, "Internet-Phone", TestCategories.color, TestCategories.image, 50f), - Category(id + 2, "Internet-Phone", TestCategories.color, TestCategories.image, 100f), - Category(id + 3, "Insurance", TestCategories.color, TestCategories.image, 100f), - Category(id + 4, "Hobbies", TestCategories.color, TestCategories.image, 150f), - ) - - client.sendAuthenticatedRequest(HttpMethod.Get, "/categories") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - assert(responseBody.success) - assertEquals(5, responseBody.data!!.size) - val shouldResponse = wrapSuccess(categoryList) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Get, "/categories?current=true") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - assert(responseBody.success) - assertEquals(3, responseBody.data!!.size) - - val currentList = listOf(categoryList[2], categoryList[3], categoryList[4]) - val shouldResponse = wrapSuccess(currentList) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Get, "/categories?period=508346") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - val shouldResponse = wrapFailure>("period has not the right pattern") - assertEquals(shouldResponse, responseBody) - } - - val now = LocalDateTime.now() - - client.sendAuthenticatedRequest(HttpMethod.Get, "/categories?period=${formatToPeriod(now)}") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - assert(responseBody.success) - assertEquals(3, responseBody.data!!.size) - - val currentList = listOf(categoryList[2], categoryList[3], categoryList[4]) - val shouldResponse = wrapSuccess(currentList) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest( - HttpMethod.Get, - "/categories?period=${formatToPeriod(now.minusMonths(2))}" - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - assert(responseBody.success) - assertEquals(3, responseBody.data!!.size) - - val currentList = listOf(categoryList[2], categoryList[3], categoryList[4]) - val shouldResponse = wrapSuccess(currentList) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest( - HttpMethod.Get, - "/categories?period=${formatToPeriod(now.minusMonths(3))}" - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - assert(responseBody.success) - assertEquals(2, responseBody.data!!.size) - - val currentList = listOf(categoryList[1], categoryList[3]) - val shouldResponse = wrapSuccess(currentList) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest( - HttpMethod.Get, - "/categories?period=${formatToPeriod(now.minusMonths(4))}" - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - assert(responseBody.success) - assertEquals(1, responseBody.data!!.size) - - val currentList = listOf(categoryList[0]) - val shouldResponse = wrapSuccess(currentList) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest( - HttpMethod.Get, - "/categories?period=${formatToPeriod(now.minusMonths(5))}" - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - assert(responseBody.success) - assertEquals(1, responseBody.data!!.size) - - val currentList = listOf(categoryList[0]) - val shouldResponse = wrapSuccess(currentList) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest( - HttpMethod.Get, - "/categories?period=${formatToPeriod(now.minusMonths(6))}" - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - assert(responseBody.success) - assertEquals(0, responseBody.data!!.size) - val shouldResponse: APIResponse> = wrapSuccess(emptyList()) - assertEquals(shouldResponse, responseBody) - } - } - - - @Test - fun testGetCategoryById() = customTestApplicationWithLogin { client -> - client.get("/categories/1").let { response -> - assertEquals(HttpStatusCode.Unauthorized, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your accessToken is absent or does not match.", 401) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/test") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/null") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/5000") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your category was not found.") - assertEquals(shouldResponse, responseBody) - } - - val id = transaction { CategoryEntity.all().first().id.value } - - client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/${id - 1}") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your category was not found.") - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/${id}") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse = wrapSuccess(Category(id, "test", TestCategories.color, TestCategories.image, 10f)) - assertEquals(shouldResponse, responseBody) - } - } - - - @Test - fun testPatchCategory() = customTestApplicationWithLogin { client -> - client.patch("/categories/1").let { response -> - assertEquals(HttpStatusCode.Unauthorized, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your accessToken is absent or does not match.", 401) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Patch, "/categories/test") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Patch, "/categories/null") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Patch, "/categories/5000") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your category was not found.") - assertEquals(shouldResponse, responseBody) - } - - val id = transaction { CategoryEntity.all().first().id.value } - - client.sendAuthenticatedRequest(HttpMethod.Patch, "/categories/${id}") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("The object you provided it not in the right format.") - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequestWithBody( - HttpMethod.Patch, - "/categories/${id}", - Category.Patch(name = "patchedTest") - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("you can't change an old category.") - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequestWithBody( - HttpMethod.Patch, - "/categories/${id - 1}", - Category.Patch(name = "patchedTest") - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your category was not found.") - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequestWithBody( - HttpMethod.Patch, - "/categories/${id + 4}", - Category.Patch(name = "Fishing") - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse = - wrapSuccess(Category(id + 4, "Fishing", TestCategories.color, TestCategories.image, 150f)) - assertEquals(shouldResponse, responseBody) - - transaction { - val categoryEntity = CategoryEntity[id + 4] - assertEquals("Fishing", categoryEntity.name) - assertNull(categoryEntity.ended) - assertNull(categoryEntity.child) - } - } - - client.sendAuthenticatedRequestWithBody( - HttpMethod.Patch, - "/categories/${id + 4}", - Category.Patch(name = "Fishing", budget = 100f) - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse = - wrapSuccess(Category(id + 4, "Fishing", TestCategories.color, TestCategories.image, 100f)) - assertEquals(shouldResponse, responseBody) - - transaction { - val newCategoryEntity = CategoryEntity[id + 4] - assertEquals("Fishing", newCategoryEntity.name) - assertNull(newCategoryEntity.ended) - assertNull(newCategoryEntity.child) - } - } - } - - - @Test - fun testDeleteCategory() = customTestApplicationWithLogin { client -> - client.get("categories/1").let { response -> - assertEquals(HttpStatusCode.Unauthorized, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your accessToken is absent or does not match.", 401) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/test") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/null") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/5000") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your category was not found.") - assertEquals(shouldResponse, responseBody) - } - - val id = transaction { CategoryEntity.all().first().id.value } - - client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/${id - 1}") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your category was not found.") - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/${id}") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("you can't delete an old category.") - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/${id + 4}") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = - wrapSuccess(Category(id + 4, "Hobbies", TestCategories.color, TestCategories.image, 150f)) - assertEquals(shouldResponse, responseBody) - - transaction { - val categoryEntity = CategoryEntity.findById(id + 4) - assertNull(categoryEntity) - } - } - } -} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/CategoryFalseValueTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/CategoryFalseValueTests.kt new file mode 100644 index 00000000..191491d9 --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/CategoryFalseValueTests.kt @@ -0,0 +1,103 @@ +package de.hsfl.budgetBinder.server.categories + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.Category +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* +import io.ktor.client.call.* +import io.ktor.http.* +import org.jetbrains.exposed.sql.transactions.transaction +import kotlin.test.* + +class CategoryFalseValueTests { + @BeforeTest + fun before() = customTestApplication { client -> + client.registerUser() + } + + @AfterTest + fun after() = transaction { + UserEntity.all().forEach { it.delete() } + } + + @Test + fun testCreateCategoryFalseBody() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Post, "/categories") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = + wrapFailure("The object you provided it not in the right format.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testGetCategoriesByPeriod() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories?period=508346") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + val shouldResponse = wrapFailure>("period has not the right pattern") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testGetCategoryByIDString() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/test") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testGetCategoryByIDNull() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/null") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testPatchCategoryByIDString() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Patch, "/categories/test") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testPatchCategoryByIDNull() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Patch, "/categories/null") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testDeleteCategoryByIDString() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/test") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testDeleteCategoryByIDNull() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/null") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") + assertEquals(shouldResponse, responseBody) + } + } +} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/CreateCategoryTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/CreateCategoryTests.kt new file mode 100644 index 00000000..b63b2077 --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/CreateCategoryTests.kt @@ -0,0 +1,48 @@ +package de.hsfl.budgetBinder.server.categories + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.Category +import de.hsfl.budgetBinder.server.models.CategoryEntity +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* +import io.ktor.client.call.* +import io.ktor.http.* +import org.jetbrains.exposed.sql.transactions.transaction +import kotlin.test.* + +class CreateCategoryTests { + @BeforeTest + fun before() = customTestApplication { client -> + client.registerUser() + } + + @AfterTest + fun after() = transaction { + UserEntity.all().forEach { it.delete() } + CategoryEntity.all().forEach { it.delete() } + } + + @Test + fun testCreateCategory() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequestWithBody( + HttpMethod.Post, "/categories", + Category.In("Test", TestCategories.color, TestCategories.image, 50f) + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + + val responseBody: APIResponse = response.body() + val id = transaction { + val categoryEntity = CategoryEntity.all().last() + + assertEquals("Test", categoryEntity.name) + assertEquals(TestCategories.color, categoryEntity.color) + assertEquals(TestCategories.image, categoryEntity.image) + assertEquals(50f, categoryEntity.budget) + + categoryEntity.id.value + } + val shouldResponse = wrapSuccess(Category(id, "Test", TestCategories.color, TestCategories.image, 50f)) + assertEquals(shouldResponse, responseBody) + } + } +} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/DeleteCategoryTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/DeleteCategoryTests.kt new file mode 100644 index 00000000..67faac96 --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/DeleteCategoryTests.kt @@ -0,0 +1,119 @@ +package de.hsfl.budgetBinder.server.categories + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.Category +import de.hsfl.budgetBinder.server.models.CategoryEntity +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* +import io.ktor.client.call.* +import io.ktor.http.* +import org.jetbrains.exposed.sql.transactions.transaction +import java.time.LocalDateTime +import kotlin.test.* + +class DeleteCategoryTests { + + @BeforeTest + fun before() = customTestApplication { client -> + client.registerUser() + + val userEntity = transaction { UserEntity.all().first() } + val now = LocalDateTime.now() + + transaction { + val internet = CategoryEntity.new { + name = "Internet" + color = TestCategories.color + image = TestCategories.image + budget = 50f + created = now.minusMonths(3) + ended = now.minusMonths(2) + child = null + user = userEntity + } + + val internetPhone = CategoryEntity.new { + name = "Internet-Phone" + color = TestCategories.color + image = TestCategories.image + budget = 100f + created = now.minusMonths(2) + ended = null + child = null + user = userEntity + } + + internet.child = internetPhone.id + + CategoryEntity.new { + name = "Hobbies" + color = TestCategories.color + image = TestCategories.image + budget = 150f + created = now.minusMonths(2) + ended = null + child = null + user = userEntity + } + } + } + + @AfterTest + fun after() = transaction { + UserEntity.all().forEach { it.delete() } + CategoryEntity.all().forEach { it.delete() } + } + + + @Test + fun testDeleteCategoryByIDTooHigh() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Patch, "/categories/5000") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("Your category was not found.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testDeleteCategoryByIDTooLow() = customTestApplicationWithLogin { client -> + val id = transaction { CategoryEntity.all().first().id.value } + + client.sendAuthenticatedRequest(HttpMethod.Patch, "/categories/${id - 1}") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("Your category was not found.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testDeleteCategoryOld() = customTestApplicationWithLogin { client -> + val id = transaction { CategoryEntity.all().first().id.value } + + client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/$id") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("you can't delete an old category.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testDeleteCategory() = customTestApplicationWithLogin { client -> + val id = transaction { CategoryEntity.all().first().id.value + 2 } + + client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/$id") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = + wrapSuccess(Category(id, "Hobbies", TestCategories.color, TestCategories.image, 150f)) + assertEquals(shouldResponse, responseBody) + + transaction { + val categoryEntity = CategoryEntity.findById(id) + assertNull(categoryEntity) + } + } + } +} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/GetCategoriesTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/GetCategoriesTests.kt new file mode 100644 index 00000000..f88e2c06 --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/GetCategoriesTests.kt @@ -0,0 +1,215 @@ +package de.hsfl.budgetBinder.server.categories + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.Category +import de.hsfl.budgetBinder.server.models.CategoryEntity +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* +import io.ktor.client.call.* +import io.ktor.http.* +import org.jetbrains.exposed.sql.transactions.transaction +import java.time.LocalDateTime +import kotlin.test.* + +class GetCategoriesTests { + private fun getCategoryListFromID(id: Int): List { + return listOf( + Category(id, "test", TestCategories.color, TestCategories.image, 10f), + Category(id + 1, "Internet-Phone", TestCategories.color, TestCategories.image, 50f), + Category(id + 2, "Internet-Phone", TestCategories.color, TestCategories.image, 100f), + Category(id + 3, "Insurance", TestCategories.color, TestCategories.image, 100f), + Category(id + 4, "Hobbies", TestCategories.color, TestCategories.image, 150f), + ) + } + + @BeforeTest + fun before() = customTestApplication { client -> + client.registerUser() + + val userEntity = transaction { UserEntity.all().first() } + val now = LocalDateTime.now() + + transaction { + CategoryEntity.new { + name = "test" + color = TestCategories.color + image = TestCategories.image + budget = 10f + created = now.minusMonths(5) + ended = now.minusMonths(3) + child = null + user = userEntity + } + + val internet = CategoryEntity.new { + name = "Internet" + color = TestCategories.color + image = TestCategories.image + budget = 50f + created = now.minusMonths(3) + ended = now.minusMonths(2) + child = null + user = userEntity + } + + val internetPhone = CategoryEntity.new { + name = "Internet-Phone" + color = TestCategories.color + image = TestCategories.image + budget = 100f + created = now.minusMonths(2) + ended = null + child = null + user = userEntity + } + + internet.child = internetPhone.id + + CategoryEntity.new { + name = "Insurance" + color = TestCategories.color + image = TestCategories.image + budget = 100f + created = now.minusMonths(3) + ended = null + child = null + user = userEntity + } + + CategoryEntity.new { + name = "Hobbies" + color = TestCategories.color + image = TestCategories.image + budget = 150f + created = now.minusMonths(2) + ended = null + child = null + user = userEntity + } + } + } + + @AfterTest + fun after() = transaction { + UserEntity.all().forEach { it.delete() } + CategoryEntity.all().forEach { it.delete() } + } + + @Test + fun testGetAllCategories() = customTestApplicationWithLogin { client -> + val id = transaction { CategoryEntity.all().first().id.value } + val categoryList = getCategoryListFromID(id) + + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + assert(responseBody.success) + assertEquals(5, responseBody.data!!.size) + val shouldResponse = wrapSuccess(categoryList) + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testGetCurrentCategories() = customTestApplicationWithLogin { client -> + val id = transaction { CategoryEntity.all().first().id.value } + val categoryList = getCategoryListFromID(id) + + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories?current=true") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + assert(responseBody.success) + assertEquals(3, responseBody.data!!.size) + + val currentList = listOf(categoryList[2], categoryList[3], categoryList[4]) + val shouldResponse = wrapSuccess(currentList) + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testGetCategoriesByPeriod() = customTestApplicationWithLogin { client -> + val now = LocalDateTime.now() + val id = transaction { CategoryEntity.all().first().id.value } + val categoryList = getCategoryListFromID(id) + + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories?period=${formatToPeriod(now)}") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + assert(responseBody.success) + assertEquals(3, responseBody.data!!.size) + + val currentList = listOf(categoryList[2], categoryList[3], categoryList[4]) + val shouldResponse = wrapSuccess(currentList) + assertEquals(shouldResponse, responseBody) + } + + client.sendAuthenticatedRequest( + HttpMethod.Get, + "/categories?period=${formatToPeriod(now.minusMonths(2))}" + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + assert(responseBody.success) + assertEquals(3, responseBody.data!!.size) + + val currentList = listOf(categoryList[2], categoryList[3], categoryList[4]) + val shouldResponse = wrapSuccess(currentList) + assertEquals(shouldResponse, responseBody) + } + + client.sendAuthenticatedRequest( + HttpMethod.Get, + "/categories?period=${formatToPeriod(now.minusMonths(3))}" + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + assert(responseBody.success) + assertEquals(2, responseBody.data!!.size) + + val currentList = listOf(categoryList[1], categoryList[3]) + val shouldResponse = wrapSuccess(currentList) + assertEquals(shouldResponse, responseBody) + } + + client.sendAuthenticatedRequest( + HttpMethod.Get, + "/categories?period=${formatToPeriod(now.minusMonths(4))}" + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + assert(responseBody.success) + assertEquals(1, responseBody.data!!.size) + + val currentList = listOf(categoryList[0]) + val shouldResponse = wrapSuccess(currentList) + assertEquals(shouldResponse, responseBody) + } + + client.sendAuthenticatedRequest( + HttpMethod.Get, + "/categories?period=${formatToPeriod(now.minusMonths(5))}" + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + assert(responseBody.success) + assertEquals(1, responseBody.data!!.size) + + val currentList = listOf(categoryList[0]) + val shouldResponse = wrapSuccess(currentList) + assertEquals(shouldResponse, responseBody) + } + + client.sendAuthenticatedRequest( + HttpMethod.Get, + "/categories?period=${formatToPeriod(now.minusMonths(6))}" + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + assert(responseBody.success) + assertEquals(0, responseBody.data!!.size) + val shouldResponse: APIResponse> = wrapSuccess(emptyList()) + assertEquals(shouldResponse, responseBody) + } + } +} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/GetCategoryByIDTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/GetCategoryByIDTests.kt new file mode 100644 index 00000000..7f60cda3 --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/GetCategoryByIDTests.kt @@ -0,0 +1,75 @@ +package de.hsfl.budgetBinder.server.categories + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.Category +import de.hsfl.budgetBinder.server.models.CategoryEntity +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* +import io.ktor.client.call.* +import io.ktor.http.* +import org.jetbrains.exposed.sql.transactions.transaction +import java.time.LocalDateTime +import kotlin.test.* + +class GetCategoryByIDTests { + @BeforeTest + fun before() = customTestApplication { client -> + client.registerUser() + + val userEntity = transaction { UserEntity.all().first() } + val now = LocalDateTime.now() + + transaction { + CategoryEntity.new { + name = "test" + color = TestCategories.color + image = TestCategories.image + budget = 10f + created = now.minusMonths(5) + ended = now.minusMonths(3) + child = null + user = userEntity + } + } + } + + @AfterTest + fun after() = transaction { + UserEntity.all().forEach { it.delete() } + CategoryEntity.all().forEach { it.delete() } + } + + @Test + fun testGetCategoryByIDTooHigh() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/5000") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("Your category was not found.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testGetCategoryByIDTooLow() = customTestApplicationWithLogin { client -> + val id = transaction { CategoryEntity.all().first().id.value } + + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/${id - 1}") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("Your category was not found.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testGetCategoryByID() = customTestApplicationWithLogin { client -> + val id = transaction { CategoryEntity.all().first().id.value } + + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/${id}") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess(Category(id, "test", TestCategories.color, TestCategories.image, 10f)) + assertEquals(shouldResponse, responseBody) + } + } +} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/PatchCategoryTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/PatchCategoryTests.kt new file mode 100644 index 00000000..7d4d1016 --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/PatchCategoryTests.kt @@ -0,0 +1,165 @@ +package de.hsfl.budgetBinder.server.categories + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.Category +import de.hsfl.budgetBinder.server.models.CategoryEntity +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* +import io.ktor.client.call.* +import io.ktor.http.* +import org.jetbrains.exposed.sql.transactions.transaction +import java.time.LocalDateTime +import kotlin.test.* + +class PatchCategoryTests { + + @BeforeTest + fun before() = customTestApplication { client -> + client.registerUser() + + val userEntity = transaction { UserEntity.all().first() } + val now = LocalDateTime.now() + + transaction { + val internet = CategoryEntity.new { + name = "Internet" + color = TestCategories.color + image = TestCategories.image + budget = 50f + created = now.minusMonths(3) + ended = now.minusMonths(2) + child = null + user = userEntity + } + + val internetPhone = CategoryEntity.new { + name = "Internet-Phone" + color = TestCategories.color + image = TestCategories.image + budget = 100f + created = now.minusMonths(2) + ended = null + child = null + user = userEntity + } + + internet.child = internetPhone.id + + CategoryEntity.new { + name = "Hobbies" + color = TestCategories.color + image = TestCategories.image + budget = 150f + created = now.minusMonths(2) + ended = null + child = null + user = userEntity + } + } + } + + @AfterTest + fun after() = transaction { + UserEntity.all().forEach { it.delete() } + CategoryEntity.all().forEach { it.delete() } + } + + @Test + fun testPatchCategoryFalseBody() = customTestApplicationWithLogin { client -> + val id = transaction { CategoryEntity.all().first().id.value } + + client.sendAuthenticatedRequest(HttpMethod.Patch, "/categories/$id") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = + wrapFailure("The object you provided it not in the right format.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testPatchCategoryByIDTooHigh() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Patch, "/categories/5000") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("Your category was not found.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testPatchCategoryByIDTooLow() = customTestApplicationWithLogin { client -> + val id = transaction { CategoryEntity.all().first().id.value } + + client.sendAuthenticatedRequest(HttpMethod.Patch, "/categories/${id - 1}") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("Your category was not found.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testPatchCategoryOld() = customTestApplicationWithLogin { client -> + val id = transaction { CategoryEntity.all().first().id.value } + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, + "/categories/${id}", + Category.Patch(name = "patchedTest") + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("you can't change an old category.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testChangeCategory() = customTestApplicationWithLogin { client -> + val id = transaction { CategoryEntity.all().first().id.value + 2 } + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, + "/categories/$id", + Category.Patch(name = "Fishing") + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = + wrapSuccess(Category(id, "Fishing", TestCategories.color, TestCategories.image, 150f)) + assertEquals(shouldResponse, responseBody) + + transaction { + val categoryEntity = CategoryEntity[id] + assertEquals("Fishing", categoryEntity.name) + assertNull(categoryEntity.ended) + assertNull(categoryEntity.child) + } + } + } + + @Test + fun testChangeCategoryBudget() = customTestApplicationWithLogin { client -> + val id = transaction { CategoryEntity.all().first().id.value + 2 } + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, + "/categories/$id", + Category.Patch(name = "Fishing", budget = 100f) + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = + wrapSuccess(Category(id, "Fishing", TestCategories.color, TestCategories.image, 100f)) + assertEquals(shouldResponse, responseBody) + + transaction { + val newCategoryEntity = CategoryEntity[id] + assertEquals("Fishing", newCategoryEntity.name) + assertNull(newCategoryEntity.ended) + assertNull(newCategoryEntity.child) + } + } + } +} From 225ed219c2c29628432909e2e10b2a0e8118a93e Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Thu, 23 Jun 2022 15:23:15 +0200 Subject: [PATCH 22/33] start to extract all Entry Tests --- .../de/hsfl/budgetBinder/server/EntryTest.kt | 156 ------------ .../server/entries/CreateEntryTests.kt | 47 ++++ .../server/entries/DeleteEntryTests.kt | 28 +++ .../server/entries/EntryFalseValueTests.kt | 43 ++++ .../server/entries/GetEntriesTests.kt | 234 ++++++++++++++++++ .../server/entries/GetEntryByIDTests.kt | 41 +++ .../server/entries/PatchEntryTests.kt | 22 ++ 7 files changed, 415 insertions(+), 156 deletions(-) create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/CreateEntryTests.kt create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/DeleteEntryTests.kt create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/EntryFalseValueTests.kt create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/GetEntriesTests.kt create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/GetEntryByIDTests.kt create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/PatchEntryTests.kt diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/EntryTest.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/EntryTest.kt index 52ebde71..4b33ade8 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/EntryTest.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/EntryTest.kt @@ -123,162 +123,6 @@ class EntryTest { } - @Test - fun testCreateEntry() = customTestApplicationWithLogin { client -> - client.get("/entries").let { response -> - assertEquals(HttpStatusCode.Unauthorized, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your accessToken is absent or does not match.", 401) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Post, "/entries") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("The object you provided it not in the right format.") - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequestWithBody( - HttpMethod.Post, "/entries", - Entry.In("Bafög", 700f, true, null) - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - - val id = transaction { - EntryEntity.all().last().let { - assertEquals("Bafög", it.name) - assertEquals(700f, it.amount) - assert(it.repeat) - assertNull(it.category) - it.id.value - } - } - val shouldResponse = wrapSuccess(Entry(id, "Bafög", 700f, true, null)) - assertEquals(shouldResponse, responseBody) - } - } - - - @Test - fun testGetEntries() = customTestApplicationWithLogin { client -> - client.get("/entries").let { response -> - assertEquals(HttpStatusCode.Unauthorized, response.status) - val responseBody: APIResponse> = response.body() - val shouldResponse: APIResponse> = wrapFailure("Your accessToken is absent or does not match.", 401) - assertEquals(shouldResponse, responseBody) - } - - val id = transaction { EntryEntity.all().first().id.value } - - val entryList = listOf( - Entry(id, "Monthly Job Pay", 3000f, true, null), - Entry(id + 1, "Monthly Job Pay", 3500f, true, null), - Entry(id + 2, "Phone", -50f, true, null), - Entry(id + 3, "Internet", -50f, true, null), - Entry(id + 4, "Bike", -1500f, false, null), - Entry(id + 5, "Ikea", -200f, false, null), - Entry(id + 6, "new Phone", -50f, true, null), - ) - - client.sendAuthenticatedRequest(HttpMethod.Get, "/entries") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - assert(responseBody.success) - assertEquals(7, responseBody.data!!.size) - val shouldResponse = wrapSuccess(entryList) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Get, "/entries?current=true") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - assert(responseBody.success) - assertEquals(4, responseBody.data!!.size) - - val currentList = listOf(entryList[1], entryList[3], entryList[5], entryList[6]) - val shouldResponse = wrapSuccess(currentList) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Get, "/entries?period=50-8346") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - val shouldResponse: APIResponse> = wrapFailure("period has not the right pattern") - assertEquals(shouldResponse, responseBody) - } - - val now = LocalDateTime.now() - - client.sendAuthenticatedRequest(HttpMethod.Get, "/entries?period=${formatToPeriod(now)}") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - assert(responseBody.success) - assertEquals(4, responseBody.data!!.size) - - val currentList = listOf(entryList[1], entryList[3], entryList[5], entryList[6]) - val shouldResponse = wrapSuccess(currentList) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest( - HttpMethod.Get, - "/entries?period=${formatToPeriod(now.minusMonths(1))}" - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - assert(responseBody.success) - assertEquals(3, responseBody.data!!.size) - - val currentList = listOf(entryList[1], entryList[3], entryList[4]) - val shouldResponse = wrapSuccess(currentList) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest( - HttpMethod.Get, - "/entries?period=${formatToPeriod(now.minusMonths(2))}" - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - assert(responseBody.success) - assertEquals(3, responseBody.data!!.size) - - val currentList = listOf(entryList[1], entryList[2], entryList[3]) - val shouldResponse = wrapSuccess(currentList) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest( - HttpMethod.Get, - "/entries?period=${formatToPeriod(now.minusMonths(3))}" - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - assert(responseBody.success) - assertEquals(2, responseBody.data!!.size) - - val currentList = listOf(entryList[0], entryList[2]) - val shouldResponse = wrapSuccess(currentList) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest( - HttpMethod.Get, - "/entries?period=${formatToPeriod(now.minusMonths(4))}" - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - assert(responseBody.success) - assertEquals(0, responseBody.data!!.size) - - val shouldResponse: APIResponse> = wrapSuccess(emptyList()) - assertEquals(shouldResponse, responseBody) - } - } - - @Test fun testGetEntryById() = customTestApplicationWithLogin { client -> client.get("/entries/1").let { response -> diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/CreateEntryTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/CreateEntryTests.kt new file mode 100644 index 00000000..27c10459 --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/CreateEntryTests.kt @@ -0,0 +1,47 @@ +package de.hsfl.budgetBinder.server.entries + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.Entry +import de.hsfl.budgetBinder.server.models.EntryEntity +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* +import io.ktor.client.call.* +import io.ktor.http.* +import org.jetbrains.exposed.sql.transactions.transaction +import kotlin.test.* + +class CreateEntryTests { + + @BeforeTest + fun before() = customTestApplication { client -> + client.registerUser() + } + + @AfterTest + fun after() = transaction { + UserEntity.all().forEach { it.delete() } + } + + @Test + fun testCreateEntry() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequestWithBody( + HttpMethod.Post, "/entries", + Entry.In("Bafög", 700f, true, null) + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + + val id = transaction { + EntryEntity.all().last().let { + assertEquals("Bafög", it.name) + assertEquals(700f, it.amount) + assert(it.repeat) + assertNull(it.category) + it.id.value + } + } + val shouldResponse = wrapSuccess(Entry(id, "Bafög", 700f, true, null)) + assertEquals(shouldResponse, responseBody) + } + } +} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/DeleteEntryTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/DeleteEntryTests.kt new file mode 100644 index 00000000..d1998df7 --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/DeleteEntryTests.kt @@ -0,0 +1,28 @@ +package de.hsfl.budgetBinder.server.entries + +import de.hsfl.budgetBinder.server.models.EntryEntity +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.customTestApplication +import de.hsfl.budgetBinder.server.utils.customTestApplicationWithLogin +import de.hsfl.budgetBinder.server.utils.registerUser +import org.jetbrains.exposed.sql.transactions.transaction +import kotlin.test.* + +class DeleteEntryTests { + + @BeforeTest + fun before() = customTestApplication { client -> + client.registerUser() + } + + @AfterTest + fun after() = transaction { + EntryEntity.all().forEach { it.delete() } + UserEntity.all().forEach { it.delete() } + } + + @Test + fun test() = customTestApplicationWithLogin { client -> + + } +} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/EntryFalseValueTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/EntryFalseValueTests.kt new file mode 100644 index 00000000..2fb5673f --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/EntryFalseValueTests.kt @@ -0,0 +1,43 @@ +package de.hsfl.budgetBinder.server.entries + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.Entry +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* +import io.ktor.client.call.* +import io.ktor.http.* +import org.jetbrains.exposed.sql.transactions.transaction +import kotlin.test.* + +class EntryFalseValueTests { + + @BeforeTest + fun before() = customTestApplication { client -> + client.registerUser() + } + + @AfterTest + fun after() = transaction { + UserEntity.all().forEach { it.delete() } + } + + @Test + fun testCreateEntryFalseBody() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Post, "/entries") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("The object you provided it not in the right format.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testGetEntriesByPeriod() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/entries?period=50-8346") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + val shouldResponse: APIResponse> = wrapFailure("period has not the right pattern") + assertEquals(shouldResponse, responseBody) + } + } +} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/GetEntriesTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/GetEntriesTests.kt new file mode 100644 index 00000000..6c4d668f --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/GetEntriesTests.kt @@ -0,0 +1,234 @@ +package de.hsfl.budgetBinder.server.entries + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.Entry +import de.hsfl.budgetBinder.server.models.EntryEntity +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* +import io.ktor.client.call.* +import io.ktor.http.* +import org.jetbrains.exposed.sql.transactions.transaction +import java.time.LocalDateTime +import kotlin.test.* + +class GetEntriesTests { + + private fun getEntryListFromID(id: Int): List { + return listOf( + Entry(id, "Monthly Job Pay", 3000f, true, null), + Entry(id + 1, "Monthly Job Pay", 3500f, true, null), + Entry(id + 2, "Phone", -50f, true, null), + Entry(id + 3, "Internet", -50f, true, null), + Entry(id + 4, "Bike", -1500f, false, null), + Entry(id + 5, "Ikea", -200f, false, null), + Entry(id + 6, "new Phone", -50f, true, null), + ) + } + + @BeforeTest + fun before() = customTestApplication { client -> + client.registerUser() + + val userEntity = transaction { UserEntity.all().first() } + val now = LocalDateTime.now() + + transaction { + val oldPay = EntryEntity.new { + name = "Monthly Pay" + amount = 3000f + repeat = true + created = now.minusMonths(3) + ended = now.minusMonths(2) + child = null + + user = userEntity + category = null + } + + val newPay = EntryEntity.new { + name = "Monthly Job Pay" + amount = 3500f + repeat = true + created = now.minusMonths(2) + ended = null + child = null + + user = userEntity + category = null + } + + oldPay.child = newPay.id + + EntryEntity.new { + name = "Phone" + amount = -50f + repeat = true + created = now.minusMonths(3) + ended = now.minusMonths(1) + child = null + + user = userEntity + category = null + } + + EntryEntity.new { + name = "Internet" + amount = -50f + repeat = true + created = now.minusMonths(2) + ended = null + child = null + + user = userEntity + category = null + } + + EntryEntity.new { + name = "Bike" + amount = -1500f + repeat = false + created = now.minusMonths(1) + ended = null + child = null + + user = userEntity + category = null + } + + EntryEntity.new { + name = "Ikea" + amount = -200f + repeat = false + created = now + ended = null + child = null + + user = userEntity + category = null + } + + EntryEntity.new { + name = "new Phone" + amount = -50f + repeat = true + created = now + ended = null + child = null + + user = userEntity + category = null + } + } + } + + @AfterTest + fun after() = transaction { + EntryEntity.all().forEach { it.delete() } + UserEntity.all().forEach { it.delete() } + } + + @Test + fun testGetAllCategories() = customTestApplicationWithLogin { client -> + val id = transaction { EntryEntity.all().first().id.value } + val entryList = getEntryListFromID(id) + + client.sendAuthenticatedRequest(HttpMethod.Get, "/entries") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + assert(responseBody.success) + assertEquals(7, responseBody.data!!.size) + val shouldResponse = wrapSuccess(entryList) + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testGetCurrentEntries() = customTestApplicationWithLogin { client -> + val id = transaction { EntryEntity.all().first().id.value } + val entryList = getEntryListFromID(id) + + client.sendAuthenticatedRequest(HttpMethod.Get, "/entries?current=true") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + assert(responseBody.success) + assertEquals(4, responseBody.data!!.size) + + val currentList = listOf(entryList[1], entryList[3], entryList[5], entryList[6]) + val shouldResponse = wrapSuccess(currentList) + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testGetEntriesByPeriod() = customTestApplicationWithLogin { client -> + val id = transaction { EntryEntity.all().first().id.value } + val entryList = getEntryListFromID(id) + val now = LocalDateTime.now() + + client.sendAuthenticatedRequest(HttpMethod.Get, "/entries?period=${formatToPeriod(now)}") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + assert(responseBody.success) + assertEquals(4, responseBody.data!!.size) + + val currentList = listOf(entryList[1], entryList[3], entryList[5], entryList[6]) + val shouldResponse = wrapSuccess(currentList) + assertEquals(shouldResponse, responseBody) + } + + client.sendAuthenticatedRequest( + HttpMethod.Get, + "/entries?period=${formatToPeriod(now.minusMonths(1))}" + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + assert(responseBody.success) + assertEquals(3, responseBody.data!!.size) + + val currentList = listOf(entryList[1], entryList[3], entryList[4]) + val shouldResponse = wrapSuccess(currentList) + assertEquals(shouldResponse, responseBody) + } + + client.sendAuthenticatedRequest( + HttpMethod.Get, + "/entries?period=${formatToPeriod(now.minusMonths(2))}" + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + assert(responseBody.success) + assertEquals(3, responseBody.data!!.size) + + val currentList = listOf(entryList[1], entryList[2], entryList[3]) + val shouldResponse = wrapSuccess(currentList) + assertEquals(shouldResponse, responseBody) + } + + client.sendAuthenticatedRequest( + HttpMethod.Get, + "/entries?period=${formatToPeriod(now.minusMonths(3))}" + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + assert(responseBody.success) + assertEquals(2, responseBody.data!!.size) + + val currentList = listOf(entryList[0], entryList[2]) + val shouldResponse = wrapSuccess(currentList) + assertEquals(shouldResponse, responseBody) + } + + client.sendAuthenticatedRequest( + HttpMethod.Get, + "/entries?period=${formatToPeriod(now.minusMonths(4))}" + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + assert(responseBody.success) + assertEquals(0, responseBody.data!!.size) + + val shouldResponse: APIResponse> = wrapSuccess(emptyList()) + assertEquals(shouldResponse, responseBody) + } + } +} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/GetEntryByIDTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/GetEntryByIDTests.kt new file mode 100644 index 00000000..045880da --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/GetEntryByIDTests.kt @@ -0,0 +1,41 @@ +package de.hsfl.budgetBinder.server.entries + +import de.hsfl.budgetBinder.server.models.EntryEntity +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.customTestApplication +import de.hsfl.budgetBinder.server.utils.registerUser +import org.jetbrains.exposed.sql.transactions.transaction +import java.time.LocalDateTime +import kotlin.test.* + +class GetEntryByIDTests { + + @BeforeTest + fun before() = customTestApplication { client -> + client.registerUser() + + val userEntity = transaction { UserEntity.all().first() } + val now = LocalDateTime.now() + + transaction { + EntryEntity.new { + name = "Monthly Pay" + amount = 3000f + repeat = true + created = now.minusMonths(3) + ended = now.minusMonths(2) + child = null + + user = userEntity + category = null + } + } + } + + @AfterTest + fun after() = transaction { + UserEntity.all().forEach { it.delete() } + } + + +} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/PatchEntryTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/PatchEntryTests.kt new file mode 100644 index 00000000..bd74332b --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/PatchEntryTests.kt @@ -0,0 +1,22 @@ +package de.hsfl.budgetBinder.server.entries + +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.customTestApplication +import de.hsfl.budgetBinder.server.utils.registerUser +import org.jetbrains.exposed.sql.transactions.transaction +import kotlin.test.* + +class PatchEntryTests { + + @BeforeTest + fun before() = customTestApplication { client -> + client.registerUser() + } + + @AfterTest + fun after() = transaction { + UserEntity.all().forEach { it.delete() } + } + + +} From e11bcec1305ad5b5eb1cf6f0dd963df63d2d5920 Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Thu, 23 Jun 2022 19:41:02 +0200 Subject: [PATCH 23/33] moved some tests around --- .../categories/CategoryFalseValueTests.kt | 30 +++++++++++++++++++ .../server/categories/DeleteCategoryTests.kt | 23 -------------- .../server/categories/GetCategoryByIDTests.kt | 22 -------------- .../server/categories/PatchCategoryTests.kt | 22 -------------- 4 files changed, 30 insertions(+), 67 deletions(-) diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/CategoryFalseValueTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/CategoryFalseValueTests.kt index 191491d9..488c04d7 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/CategoryFalseValueTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/CategoryFalseValueTests.kt @@ -61,6 +61,16 @@ class CategoryFalseValueTests { } } + @Test + fun testGetCategoryByIDNotFound() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/5000") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("Your category was not found.") + assertEquals(shouldResponse, responseBody) + } + } + @Test fun testPatchCategoryByIDString() = customTestApplicationWithLogin { client -> client.sendAuthenticatedRequest(HttpMethod.Patch, "/categories/test") { response -> @@ -81,6 +91,16 @@ class CategoryFalseValueTests { } } + @Test + fun testPatchCategoryByIDNotFound() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Patch, "/categories/5000") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("Your category was not found.") + assertEquals(shouldResponse, responseBody) + } + } + @Test fun testDeleteCategoryByIDString() = customTestApplicationWithLogin { client -> client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/test") { response -> @@ -100,4 +120,14 @@ class CategoryFalseValueTests { assertEquals(shouldResponse, responseBody) } } + + @Test + fun testDeleteCategoryByIDNotFound() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Patch, "/categories/5000") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("Your category was not found.") + assertEquals(shouldResponse, responseBody) + } + } } diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/DeleteCategoryTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/DeleteCategoryTests.kt index 67faac96..91238886 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/DeleteCategoryTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/DeleteCategoryTests.kt @@ -64,29 +64,6 @@ class DeleteCategoryTests { CategoryEntity.all().forEach { it.delete() } } - - @Test - fun testDeleteCategoryByIDTooHigh() = customTestApplicationWithLogin { client -> - client.sendAuthenticatedRequest(HttpMethod.Patch, "/categories/5000") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your category was not found.") - assertEquals(shouldResponse, responseBody) - } - } - - @Test - fun testDeleteCategoryByIDTooLow() = customTestApplicationWithLogin { client -> - val id = transaction { CategoryEntity.all().first().id.value } - - client.sendAuthenticatedRequest(HttpMethod.Patch, "/categories/${id - 1}") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your category was not found.") - assertEquals(shouldResponse, responseBody) - } - } - @Test fun testDeleteCategoryOld() = customTestApplicationWithLogin { client -> val id = transaction { CategoryEntity.all().first().id.value } diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/GetCategoryByIDTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/GetCategoryByIDTests.kt index 7f60cda3..1fa16c65 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/GetCategoryByIDTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/GetCategoryByIDTests.kt @@ -39,28 +39,6 @@ class GetCategoryByIDTests { CategoryEntity.all().forEach { it.delete() } } - @Test - fun testGetCategoryByIDTooHigh() = customTestApplicationWithLogin { client -> - client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/5000") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your category was not found.") - assertEquals(shouldResponse, responseBody) - } - } - - @Test - fun testGetCategoryByIDTooLow() = customTestApplicationWithLogin { client -> - val id = transaction { CategoryEntity.all().first().id.value } - - client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/${id - 1}") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your category was not found.") - assertEquals(shouldResponse, responseBody) - } - } - @Test fun testGetCategoryByID() = customTestApplicationWithLogin { client -> val id = transaction { CategoryEntity.all().first().id.value } diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/PatchCategoryTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/PatchCategoryTests.kt index 7d4d1016..3ab20311 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/PatchCategoryTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/PatchCategoryTests.kt @@ -77,28 +77,6 @@ class PatchCategoryTests { } } - @Test - fun testPatchCategoryByIDTooHigh() = customTestApplicationWithLogin { client -> - client.sendAuthenticatedRequest(HttpMethod.Patch, "/categories/5000") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your category was not found.") - assertEquals(shouldResponse, responseBody) - } - } - - @Test - fun testPatchCategoryByIDTooLow() = customTestApplicationWithLogin { client -> - val id = transaction { CategoryEntity.all().first().id.value } - - client.sendAuthenticatedRequest(HttpMethod.Patch, "/categories/${id - 1}") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your category was not found.") - assertEquals(shouldResponse, responseBody) - } - } - @Test fun testPatchCategoryOld() = customTestApplicationWithLogin { client -> val id = transaction { CategoryEntity.all().first().id.value } From 7d69442335be5fdefbfa9c528f3fb708f760cdc2 Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Thu, 23 Jun 2022 20:32:59 +0200 Subject: [PATCH 24/33] finished extracting Entry Tests --- .../de/hsfl/budgetBinder/server/EntryTest.kt | 362 ------------------ .../server/categories/DeleteCategoryTests.kt | 1 - .../server/categories/PatchCategoryTests.kt | 5 +- .../server/entries/CreateEntryTests.kt | 2 +- .../server/entries/DeleteEntryTests.kt | 128 ++++++- .../server/entries/EntryFalseValueTests.kt | 61 ++- .../server/entries/GetEntriesTests.kt | 1 - .../server/entries/GetEntryByIDTests.kt | 46 ++- .../server/entries/PatchEntryTests.kt | 221 ++++++++++- 9 files changed, 446 insertions(+), 381 deletions(-) delete mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/EntryTest.kt diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/EntryTest.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/EntryTest.kt deleted file mode 100644 index 4b33ade8..00000000 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/EntryTest.kt +++ /dev/null @@ -1,362 +0,0 @@ -package de.hsfl.budgetBinder.server - -import de.hsfl.budgetBinder.common.APIResponse -import de.hsfl.budgetBinder.common.Entry -import de.hsfl.budgetBinder.server.models.EntryEntity -import de.hsfl.budgetBinder.server.models.UserEntity -import de.hsfl.budgetBinder.server.utils.* -import io.ktor.client.call.* -import io.ktor.client.request.* -import io.ktor.http.* -import org.jetbrains.exposed.sql.transactions.transaction -import java.time.LocalDateTime -import kotlin.test.* - -class EntryTest { - - @BeforeTest - fun before() = customTestApplication { client -> - - client.registerUser() - - val userEntity = transaction { UserEntity.all().first() } - val now = LocalDateTime.now() - - transaction { - val oldPay = EntryEntity.new { - name = "Monthly Pay" - amount = 3000f - repeat = true - created = now.minusMonths(3) - ended = now.minusMonths(2) - child = null - - user = userEntity - category = null - } - - val newPay = EntryEntity.new { - name = "Monthly Job Pay" - amount = 3500f - repeat = true - created = now.minusMonths(2) - ended = null - child = null - - user = userEntity - category = null - } - - oldPay.child = newPay.id - - EntryEntity.new { - name = "Phone" - amount = -50f - repeat = true - created = now.minusMonths(3) - ended = now.minusMonths(1) - child = null - - user = userEntity - category = null - } - - EntryEntity.new { - name = "Internet" - amount = -50f - repeat = true - created = now.minusMonths(2) - ended = null - child = null - - user = userEntity - category = null - } - - EntryEntity.new { - name = "Bike" - amount = -1500f - repeat = false - created = now.minusMonths(1) - ended = null - child = null - - user = userEntity - category = null - } - - EntryEntity.new { - name = "Ikea" - amount = -200f - repeat = false - created = now - ended = null - child = null - - user = userEntity - category = null - } - - EntryEntity.new { - name = "new Phone" - amount = -50f - repeat = true - created = now - ended = null - child = null - - user = userEntity - category = null - } - } - } - - - @AfterTest - fun after() = transaction { - EntryEntity.all().forEach { - it.delete() - } - UserEntity.all().forEach { - it.delete() - } - } - - - @Test - fun testGetEntryById() = customTestApplicationWithLogin { client -> - client.get("/entries/1").let { response -> - assertEquals(HttpStatusCode.Unauthorized, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your accessToken is absent or does not match.", 401) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Get, "/entries/test") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Get, "/entries/5000") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your entry was not found.") - assertEquals(shouldResponse, responseBody) - } - - val id = transaction { EntryEntity.all().first().id.value } - - client.sendAuthenticatedRequest(HttpMethod.Get, "/entries/$id") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse = wrapSuccess(Entry(id, "Monthly Job Pay", 3000f, true, null)) - assertEquals(shouldResponse, responseBody) - } - } - - - @Test - fun testPatchEntry() = customTestApplicationWithLogin { client -> - client.patch("/entries/1").let { response -> - assertEquals(HttpStatusCode.Unauthorized, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your accessToken is absent or does not match.", 401) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Patch, "/entries/test") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Patch, "/entries/5000") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your entry was not found.") - assertEquals(shouldResponse, responseBody) - } - - val id = transaction { EntryEntity.all().first().id.value } - - client.sendAuthenticatedRequest(HttpMethod.Patch, "/entries/$id") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("The object you provided it not in the right format.") - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequestWithBody( - HttpMethod.Patch, "/entries/$id", - Entry.Patch(name = "Pay") - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("you can't change this Entry") - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequestWithBody( - HttpMethod.Patch, "/entries/${id + 5}", - Entry.Patch(name = "Ikea Shopping") - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse = wrapSuccess(Entry(id + 5, "Ikea Shopping", -200f, false, null)) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequestWithBody( - HttpMethod.Patch, "/entries/${id + 4}", - Entry.Patch(amount = -1700f) - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse = wrapSuccess(Entry(id + 4, "Bike", -1700f, false, null)) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequestWithBody( - HttpMethod.Patch, "/entries/${id + 5}", - Entry.Patch(name = "Ikea", repeat = true) - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse = wrapSuccess(Entry(id + 5, "Ikea", -200f, true, null)) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequestWithBody( - HttpMethod.Patch, "/entries/${id + 6}", - Entry.Patch(repeat = false) - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse = wrapSuccess(Entry(id + 6, "new Phone", -50f, false, null)) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequestWithBody( - HttpMethod.Patch, "/entries/${id + 3}", - Entry.Patch(repeat = false) - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse = wrapSuccess(Entry(id + 7, "Internet", -50f, false, null)) - assertEquals(shouldResponse, responseBody) - - transaction { - val oldEntry = EntryEntity[id + 3] - val newEntry = EntryEntity[id + 7] - assertNotNull(oldEntry.ended) - assertEquals(newEntry.id, oldEntry.child) - assertNull(newEntry.ended) - assertNull(newEntry.child) - } - } - - client.sendAuthenticatedRequestWithBody( - HttpMethod.Patch, "/entries/${id + 1}", - Entry.Patch(amount = 3700f) - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse = wrapSuccess(Entry(id + 8, "Monthly Job Pay", 3700f, true, null)) - assertEquals(shouldResponse, responseBody) - - transaction { - val oldEntry = EntryEntity[id + 1] - val newEntry = EntryEntity[id + 8] - assertNotNull(oldEntry.ended) - assertEquals(newEntry.id, oldEntry.child) - assertNull(newEntry.ended) - assertNull(newEntry.child) - } - } - } - - - @Test - fun testDeleteEntry() = customTestApplicationWithLogin { client -> - client.delete("/entries/1").let { response -> - assertEquals(HttpStatusCode.Unauthorized, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your accessToken is absent or does not match.", 401) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Delete, "/entries/test") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Delete, "/entries/5000") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("Your entry was not found.") - assertEquals(shouldResponse, responseBody) - } - - val id = transaction { EntryEntity.all().first().id.value } - - client.sendAuthenticatedRequest(HttpMethod.Delete, "/entries/$id") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("you can't delete this Entry") - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Delete, "/entries/${id + 1}") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse = wrapSuccess(Entry(id + 1, "Monthly Job Pay", 3500f, true, null)) - assertEquals(shouldResponse, responseBody) - - transaction { - val entry = EntryEntity[id + 1] - assertNotNull(entry.ended) - } - } - - client.sendAuthenticatedRequest(HttpMethod.Delete, "/entries/${id + 6}") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse = wrapSuccess(Entry(id + 6, "new Phone", -50f, true, null)) - assertEquals(shouldResponse, responseBody) - - transaction { - val entry = EntryEntity.findById(id + 6) - assertNull(entry) - } - } - - client.sendAuthenticatedRequest(HttpMethod.Delete, "/entries/${id + 4}") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse = wrapSuccess(Entry(id + 4, "Bike", -1500f, false, null)) - assertEquals(shouldResponse, responseBody) - - transaction { - val entry = EntryEntity.findById(id + 4) - assertNull(entry) - } - } - - client.sendAuthenticatedRequest(HttpMethod.Delete, "/entries/${id + 5}") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse = wrapSuccess(Entry(id + 5, "Ikea", -200f, false, null)) - assertEquals(shouldResponse, responseBody) - - transaction { - val entry = EntryEntity.findById(id + 5) - assertNull(entry) - } - } - } -} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/DeleteCategoryTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/DeleteCategoryTests.kt index 91238886..722d5de1 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/DeleteCategoryTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/DeleteCategoryTests.kt @@ -12,7 +12,6 @@ import java.time.LocalDateTime import kotlin.test.* class DeleteCategoryTests { - @BeforeTest fun before() = customTestApplication { client -> client.registerUser() diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/PatchCategoryTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/PatchCategoryTests.kt index 3ab20311..dd01fcdb 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/PatchCategoryTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categories/PatchCategoryTests.kt @@ -12,7 +12,6 @@ import java.time.LocalDateTime import kotlin.test.* class PatchCategoryTests { - @BeforeTest fun before() = customTestApplication { client -> client.registerUser() @@ -94,7 +93,7 @@ class PatchCategoryTests { } @Test - fun testChangeCategory() = customTestApplicationWithLogin { client -> + fun testPatchCategory() = customTestApplicationWithLogin { client -> val id = transaction { CategoryEntity.all().first().id.value + 2 } client.sendAuthenticatedRequestWithBody( @@ -118,7 +117,7 @@ class PatchCategoryTests { } @Test - fun testChangeCategoryBudget() = customTestApplicationWithLogin { client -> + fun testPatchCategoryBudget() = customTestApplicationWithLogin { client -> val id = transaction { CategoryEntity.all().first().id.value + 2 } client.sendAuthenticatedRequestWithBody( diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/CreateEntryTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/CreateEntryTests.kt index 27c10459..96d97f5a 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/CreateEntryTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/CreateEntryTests.kt @@ -11,7 +11,6 @@ import org.jetbrains.exposed.sql.transactions.transaction import kotlin.test.* class CreateEntryTests { - @BeforeTest fun before() = customTestApplication { client -> client.registerUser() @@ -19,6 +18,7 @@ class CreateEntryTests { @AfterTest fun after() = transaction { + EntryEntity.all().forEach { it.delete() } UserEntity.all().forEach { it.delete() } } diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/DeleteEntryTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/DeleteEntryTests.kt index d1998df7..3f38e475 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/DeleteEntryTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/DeleteEntryTests.kt @@ -1,18 +1,75 @@ package de.hsfl.budgetBinder.server.entries +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.Entry import de.hsfl.budgetBinder.server.models.EntryEntity import de.hsfl.budgetBinder.server.models.UserEntity -import de.hsfl.budgetBinder.server.utils.customTestApplication -import de.hsfl.budgetBinder.server.utils.customTestApplicationWithLogin -import de.hsfl.budgetBinder.server.utils.registerUser +import de.hsfl.budgetBinder.server.utils.* +import io.ktor.client.call.* +import io.ktor.http.* import org.jetbrains.exposed.sql.transactions.transaction +import java.time.LocalDateTime import kotlin.test.* class DeleteEntryTests { - @BeforeTest fun before() = customTestApplication { client -> client.registerUser() + + val userEntity = transaction { UserEntity.all().first() } + val now = LocalDateTime.now() + + transaction { + val oldPay = EntryEntity.new { + name = "Monthly Pay" + amount = 3000f + repeat = true + created = now.minusMonths(3) + ended = now.minusMonths(2) + child = null + + user = userEntity + category = null + } + + val newPay = EntryEntity.new { + name = "Monthly Job Pay" + amount = 3500f + repeat = true + created = now.minusMonths(2) + ended = null + child = null + + user = userEntity + category = null + } + + oldPay.child = newPay.id + + EntryEntity.new { + name = "Bike" + amount = -1500f + repeat = false + created = now.minusMonths(1) + ended = null + child = null + + user = userEntity + category = null + } + + EntryEntity.new { + name = "new Phone" + amount = -50f + repeat = true + created = now + ended = null + child = null + + user = userEntity + category = null + } + } } @AfterTest @@ -22,7 +79,68 @@ class DeleteEntryTests { } @Test - fun test() = customTestApplicationWithLogin { client -> + fun testDeleteEntryOld() = customTestApplicationWithLogin { client -> + val id = transaction { EntryEntity.all().first().id.value } + + client.sendAuthenticatedRequest(HttpMethod.Delete, "/entries/$id") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("you can't delete this Entry") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testDeleteEntryNotRepeat() = customTestApplicationWithLogin { client -> + val id = transaction { EntryEntity.all().first().id.value + 2 } + + client.sendAuthenticatedRequest(HttpMethod.Delete, "/entries/$id") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess(Entry(id, "Bike", -1500f, false, null)) + assertEquals(shouldResponse, responseBody) + + transaction { + val entry = EntryEntity.findById(id) + assertNull(entry) + } + } + } + + @Test + fun testDeleteEntryRepeatNew() = customTestApplicationWithLogin { client -> + println(transaction { EntryEntity.all().toList().map { it.toDto() } }) + + + val id = transaction { EntryEntity.all().first().id.value + 3 } + + client.sendAuthenticatedRequest(HttpMethod.Delete, "/entries/$id") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess(Entry(id, "new Phone", -50f, true, null)) + assertEquals(shouldResponse, responseBody) + + transaction { + val entry = EntryEntity.findById(id) + assertNull(entry) + } + } + } + + @Test + fun testDeleteEntryRepeatOld() = customTestApplicationWithLogin { client -> + val id = transaction { EntryEntity.all().first().id.value + 1 } + + client.sendAuthenticatedRequest(HttpMethod.Delete, "/entries/$id") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess(Entry(id, "Monthly Job Pay", 3500f, true, null)) + assertEquals(shouldResponse, responseBody) + transaction { + val entry = EntryEntity[id] + assertNotNull(entry.ended) + } + } } } diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/EntryFalseValueTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/EntryFalseValueTests.kt index 2fb5673f..ba2d4707 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/EntryFalseValueTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/EntryFalseValueTests.kt @@ -10,7 +10,6 @@ import org.jetbrains.exposed.sql.transactions.transaction import kotlin.test.* class EntryFalseValueTests { - @BeforeTest fun before() = customTestApplication { client -> client.registerUser() @@ -40,4 +39,64 @@ class EntryFalseValueTests { assertEquals(shouldResponse, responseBody) } } + + @Test + fun testGetEntryByIDString() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/entries/test") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testGetEntryByIDNotFound() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/entries/5000") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("Your entry was not found.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testPatchEntryByIDString() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Patch, "/entries/test") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testPatchEntryByIDNotFound() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Patch, "/entries/5000") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("Your entry was not found.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testDeleteEntryByIDString() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Delete, "/entries/test") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("The ID you provided is not a number.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testDeleteEntryByIDNotFound() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Delete, "/entries/5000") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("Your entry was not found.") + assertEquals(shouldResponse, responseBody) + } + } } diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/GetEntriesTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/GetEntriesTests.kt index 6c4d668f..525c4383 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/GetEntriesTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/GetEntriesTests.kt @@ -12,7 +12,6 @@ import java.time.LocalDateTime import kotlin.test.* class GetEntriesTests { - private fun getEntryListFromID(id: Int): List { return listOf( Entry(id, "Monthly Job Pay", 3000f, true, null), diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/GetEntryByIDTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/GetEntryByIDTests.kt index 045880da..6ef72784 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/GetEntryByIDTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/GetEntryByIDTests.kt @@ -1,15 +1,17 @@ package de.hsfl.budgetBinder.server.entries +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.Entry import de.hsfl.budgetBinder.server.models.EntryEntity import de.hsfl.budgetBinder.server.models.UserEntity -import de.hsfl.budgetBinder.server.utils.customTestApplication -import de.hsfl.budgetBinder.server.utils.registerUser +import de.hsfl.budgetBinder.server.utils.* +import io.ktor.client.call.* +import io.ktor.http.* import org.jetbrains.exposed.sql.transactions.transaction import java.time.LocalDateTime import kotlin.test.* class GetEntryByIDTests { - @BeforeTest fun before() = customTestApplication { client -> client.registerUser() @@ -18,24 +20,60 @@ class GetEntryByIDTests { val now = LocalDateTime.now() transaction { - EntryEntity.new { + val oldPay = EntryEntity.new { name = "Monthly Pay" amount = 3000f repeat = true created = now.minusMonths(3) ended = now.minusMonths(2) child = null + user = userEntity + category = null + } + + val newPay = EntryEntity.new { + name = "Monthly Job Pay" + amount = 3500f + repeat = true + created = now.minusMonths(2) + ended = null + child = null user = userEntity category = null } + + oldPay.child = newPay.id } } @AfterTest fun after() = transaction { + EntryEntity.all().forEach { it.delete() } UserEntity.all().forEach { it.delete() } } + @Test + fun testGetEntryByIDNew() = customTestApplicationWithLogin { client -> + val id = transaction { EntryEntity.all().first().id.value + 1 } + + client.sendAuthenticatedRequest(HttpMethod.Get, "/entries/$id") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess(Entry(id, "Monthly Job Pay", 3500f, true, null)) + assertEquals(shouldResponse, responseBody) + } + } + @Test + fun testGetEntryByIDOld() = customTestApplicationWithLogin { client -> + val id = transaction { EntryEntity.all().first().id.value } + + client.sendAuthenticatedRequest(HttpMethod.Get, "/entries/$id") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess(Entry(id, "Monthly Job Pay", 3000f, true, null)) + assertEquals(shouldResponse, responseBody) + } + } } diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/PatchEntryTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/PatchEntryTests.kt index bd74332b..c07b9538 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/PatchEntryTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/entries/PatchEntryTests.kt @@ -1,22 +1,237 @@ package de.hsfl.budgetBinder.server.entries +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.Entry +import de.hsfl.budgetBinder.server.models.EntryEntity import de.hsfl.budgetBinder.server.models.UserEntity -import de.hsfl.budgetBinder.server.utils.customTestApplication -import de.hsfl.budgetBinder.server.utils.registerUser +import de.hsfl.budgetBinder.server.utils.* +import io.ktor.client.call.* +import io.ktor.http.* import org.jetbrains.exposed.sql.transactions.transaction +import java.time.LocalDateTime import kotlin.test.* class PatchEntryTests { - @BeforeTest fun before() = customTestApplication { client -> client.registerUser() + + val userEntity = transaction { UserEntity.all().first() } + val now = LocalDateTime.now() + + transaction { + val oldPay = EntryEntity.new { + name = "Monthly Pay" + amount = 3000f + repeat = true + created = now.minusMonths(3) + ended = now.minusMonths(2) + child = null + + user = userEntity + category = null + } + + val newPay = EntryEntity.new { + name = "Monthly Job Pay" + amount = 3500f + repeat = true + created = now.minusMonths(2) + ended = null + child = null + + user = userEntity + category = null + } + + oldPay.child = newPay.id + + EntryEntity.new { + name = "Bike" + amount = -1500f + repeat = false + created = now.minusMonths(1) + ended = null + child = null + + user = userEntity + category = null + } + + EntryEntity.new { + name = "new Phone" + amount = -50f + repeat = true + created = now + ended = null + child = null + + user = userEntity + category = null + } + } } @AfterTest fun after() = transaction { + EntryEntity.all().forEach { it.delete() } UserEntity.all().forEach { it.delete() } } + @Test + fun testPatchEntryFalseBody() = customTestApplicationWithLogin { client -> + val id = transaction { EntryEntity.all().first().id.value } + + client.sendAuthenticatedRequest(HttpMethod.Patch, "/entries/$id") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("The object you provided it not in the right format.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testPatchEntryOld() = customTestApplicationWithLogin { client -> + val id = transaction { EntryEntity.all().first().id.value } + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, "/entries/$id", + Entry.Patch(name = "Pay") + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("you can't change this Entry") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testPatchEntryName() = customTestApplicationWithLogin { client -> + val id = transaction { EntryEntity.all().first().id.value + 1 } + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, "/entries/$id", + Entry.Patch(name = "Pay") + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess(Entry(id, "Pay", 3500f, true, null)) + assertEquals(shouldResponse, responseBody) + + transaction { + val entry = EntryEntity[id] + assertEquals("Pay", entry.name) + } + } + } + @Test + fun testPatchEntryAmountNotRepeat() = customTestApplicationWithLogin { client -> + val id = transaction { EntryEntity.all().first().id.value + 2 } + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, "/entries/$id", + Entry.Patch(amount = -1700f) + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess(Entry(id, "Bike", -1700f, false, null)) + assertEquals(shouldResponse, responseBody) + + transaction { + val entry = EntryEntity[id] + assertEquals(-1700f, entry.amount) + } + } + } + + @Test + fun testPatchEntryAmountRepeatNew() = customTestApplicationWithLogin { client -> + val id = transaction { EntryEntity.all().first().id.value + 3 } + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, "/entries/$id", + Entry.Patch(amount = -60f) + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess(Entry(id, "new Phone", -60f, true, null)) + assertEquals(shouldResponse, responseBody) + + transaction { + val entry = EntryEntity[id] + assertEquals(-60f, entry.amount) + } + } + } + + @Test + fun testPatchEntryAmountRepeatOld() = customTestApplicationWithLogin { client -> + val id = transaction { EntryEntity.all().first().id.value + 1 } + val newId = transaction { EntryEntity.all().last().id.value + 1 } + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, "/entries/$id", + Entry.Patch(amount = 3700f) + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess(Entry(newId, "Monthly Job Pay", 3700f, true, null)) + assertEquals(shouldResponse, responseBody) + + transaction { + val oldEntry = EntryEntity[id] + val newEntry = EntryEntity[newId] + assertNotNull(oldEntry.ended) + assertEquals(newEntry.id, oldEntry.child) + assertNull(newEntry.ended) + assertNull(newEntry.child) + } + } + } + + @Test + fun testPatchEntryRepeatToFalseNew() = customTestApplicationWithLogin { client -> + val id = transaction { EntryEntity.all().first().id.value + 3 } + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, "/entries/$id", + Entry.Patch(repeat = false) + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess(Entry(id, "new Phone", -50f, false, null)) + assertEquals(shouldResponse, responseBody) + + transaction { + val entry = EntryEntity[id] + assert(!entry.repeat) + } + } + } + + @Test + fun testPatchEntryRepeatToFalseOld() = customTestApplicationWithLogin { client -> + val id = transaction { EntryEntity.all().first().id.value + 1 } + val newId = transaction { EntryEntity.all().last().id.value + 1 } + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, "/entries/$id", + Entry.Patch(repeat = false) + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess(Entry(newId, "Monthly Job Pay", 3500f, false, null)) + assertEquals(shouldResponse, responseBody) + + transaction { + val oldEntry = EntryEntity[id] + val newEntry = EntryEntity[newId] + assertNotNull(oldEntry.ended) + assertEquals(newEntry.id, oldEntry.child) + assertNull(newEntry.ended) + assertNull(newEntry.child) + } + } + } } From 996a8eaf6ac1d65d91e74fde538011ed191ec3d5 Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Fri, 24 Jun 2022 00:01:41 +0200 Subject: [PATCH 25/33] added new Logic For Entries When the Category of an Entry is changed we handle it now differently --- .../implementations/EntryServiceImpl.kt | 44 ++++- .../alterEntryCategory/ToNewCategoryTests.kt | 153 +++++++++++++++++ .../alterEntryCategory/ToNullCategoryTests.kt | 161 ++++++++++++++++++ .../alterEntryCategory/ToOldCategoryTests.kt | 100 +++++++++++ .../ToVeryOldCategoryTests.kt | 161 ++++++++++++++++++ 5 files changed, 614 insertions(+), 5 deletions(-) create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterEntryCategory/ToNewCategoryTests.kt create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterEntryCategory/ToNullCategoryTests.kt create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterEntryCategory/ToOldCategoryTests.kt create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterEntryCategory/ToVeryOldCategoryTests.kt diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/services/implementations/EntryServiceImpl.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/services/implementations/EntryServiceImpl.kt index 8aa95465..10f56236 100644 --- a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/services/implementations/EntryServiceImpl.kt +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/services/implementations/EntryServiceImpl.kt @@ -49,12 +49,41 @@ class EntryServiceImpl : EntryService { }.toDto() } - private fun createOrChangeEntry(oldEntry: EntryEntity, repeat: Boolean?, amount: Float?): EntryEntity { + private fun createOrChangeEntry( + oldEntry: EntryEntity, + repeat: Boolean?, + amount: Float?, + category: Entry.Category?, + categoryEntity: CategoryEntity? + ): EntryEntity? { val now = LocalDateTime.now() val period = LocalDateTime.of(now.year, now.month.value, 1, 0, 0) if (!oldEntry.repeat || (repeat != false && amount == null) || oldEntry.created > period) { - return oldEntry + if (category == null) { + return oldEntry + } + val entryPeriod = LocalDateTime.of(oldEntry.created.year, oldEntry.created.month.value, 1, 0, 0) + + if (entryPeriod == period) { + return oldEntry + } + + if (categoryEntity == null) { + if (!oldEntry.repeat) { + return oldEntry + } + } else { + val categoryPeriod = + LocalDateTime.of(categoryEntity.created.year, categoryEntity.created.month.value, 1, 0, 0) + + if (!oldEntry.repeat && categoryPeriod > entryPeriod) { + return null + } + if (!oldEntry.repeat) { + return oldEntry + } + } } val newEntry = oldEntry.createChild() @@ -64,9 +93,10 @@ class EntryServiceImpl : EntryService { return newEntry } + override fun changeEntry(userId: Int, entryId: Int, entry: Entry.Patch): Entry? = transaction { - var entryEntity = EntryEntity[entryId] - if (entryEntity.ended != null) { + var entryEntity: EntryEntity? = EntryEntity[entryId] + if (entryEntity!!.ended != null) { return@transaction null } @@ -75,7 +105,11 @@ class EntryServiceImpl : EntryService { if (categoryEntity?.ended != null) { return@transaction null } - entryEntity = createOrChangeEntry(entryEntity, entry.repeat, entry.amount) + entryEntity = createOrChangeEntry(entryEntity, entry.repeat, entry.amount, entry.category, categoryEntity) + + if (entryEntity == null) { + return@transaction null + } entry.name?.let { entryEntity.name = it } entry.amount?.let { entryEntity.amount = it } diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterEntryCategory/ToNewCategoryTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterEntryCategory/ToNewCategoryTests.kt new file mode 100644 index 00000000..fe3e0b89 --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterEntryCategory/ToNewCategoryTests.kt @@ -0,0 +1,153 @@ +package de.hsfl.budgetBinder.server.alterEntryCategory + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.Entry +import de.hsfl.budgetBinder.server.models.CategoryEntity +import de.hsfl.budgetBinder.server.models.EntryEntity +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* +import io.ktor.client.call.* +import io.ktor.http.* +import org.jetbrains.exposed.sql.transactions.transaction +import java.time.LocalDateTime +import kotlin.test.* + +class ToNewCategoryTests { + + private fun createEntry(isRepeated: Boolean, time: LocalDateTime): Int = transaction { + EntryEntity.new { + name = "Aldi" + amount = -200f + repeat = isRepeated + created = time + ended = null + child = null + user = UserEntity.all().first() + category = null + }.id.value + } + + @BeforeTest + fun before() = customTestApplication { client -> + client.registerUser() + + transaction { + CategoryEntity.new { + name = "Shopping" + color = TestCategories.color + image = TestCategories.image + budget = 300f + created = LocalDateTime.now() + ended = null + child = null + user = UserEntity.all().first() + } + } + } + + @AfterTest + fun after() = transaction { + EntryEntity.all().forEach { it.delete() } + CategoryEntity.all().forEach { it.delete() } + UserEntity.all().forEach { it.delete() } + } + + @Test + fun testOldEntryNoRepeat() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = createEntry(false, LocalDateTime.now().minusMonths(1)) + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, + "/entries/$entryId", + Entry.Patch(category = Entry.Category(categoryId)) + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("you can't change this Entry") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testOldEntryRepeat() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = createEntry(true, LocalDateTime.now().minusMonths(1)) + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, + "/entries/$entryId", + Entry.Patch(category = Entry.Category(categoryId)) + ) { response -> + val newEntryId = transaction { EntryEntity.all().last().id.value } + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess(Entry(newEntryId, "Aldi", -200f, true, categoryId)) + assertEquals(shouldResponse, responseBody) + + transaction { + val entryEntity = EntryEntity[entryId] + val newEntryEntity = EntryEntity[newEntryId] + + assertNotNull(entryEntity.ended) + assertEquals(newEntryEntity.id, entryEntity.child) + assert(entryEntity.repeat) + assert(newEntryEntity.repeat) + assertNull(entryEntity.category) + assertEquals(categoryId, newEntryEntity.category?.value) + } + } + } + + @Test + fun testNewEntryNoRepeat() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = createEntry(false, LocalDateTime.now()) + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, + "/entries/$entryId", + Entry.Patch(category = Entry.Category(categoryId)) + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess(Entry(entryId, "Aldi", -200f, false, categoryId)) + assertEquals(shouldResponse, responseBody) + + transaction { + val entryEntity = EntryEntity[entryId] + + assertNull(entryEntity.ended) + assertNull(entryEntity.child) + assert(!entryEntity.repeat) + assertEquals(categoryId, entryEntity.category?.value) + } + } + } + + @Test + fun testNewEntryRepeat() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = createEntry(true, LocalDateTime.now()) + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, + "/entries/$entryId", + Entry.Patch(category = Entry.Category(categoryId)) + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess(Entry(entryId, "Aldi", -200f, true, categoryId)) + assertEquals(shouldResponse, responseBody) + + transaction { + val entryEntity = EntryEntity[entryId] + + assertNull(entryEntity.ended) + assertNull(entryEntity.child) + assert(entryEntity.repeat) + assertEquals(categoryId, entryEntity.category?.value) + } + } + } +} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterEntryCategory/ToNullCategoryTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterEntryCategory/ToNullCategoryTests.kt new file mode 100644 index 00000000..ace9bba4 --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterEntryCategory/ToNullCategoryTests.kt @@ -0,0 +1,161 @@ +package de.hsfl.budgetBinder.server.alterEntryCategory + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.Entry +import de.hsfl.budgetBinder.server.models.CategoryEntity +import de.hsfl.budgetBinder.server.models.EntryEntity +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* +import io.ktor.client.call.* +import io.ktor.http.* +import org.jetbrains.exposed.sql.transactions.transaction +import java.time.LocalDateTime +import kotlin.test.* + +class ToNullCategoryTests { + private fun createEntry(isRepeated: Boolean, time: LocalDateTime, categoryId: Int): Int = transaction { + EntryEntity.new { + name = "Aldi" + amount = -200f + repeat = isRepeated + created = time + ended = null + child = null + user = UserEntity.all().first() + category = CategoryEntity[categoryId].id + }.id.value + } + + @BeforeTest + fun before() = customTestApplication { client -> + client.registerUser() + + transaction { + CategoryEntity.new { + name = "Shopping" + color = TestCategories.color + image = TestCategories.image + budget = 300f + created = LocalDateTime.now().minusMonths(2) + ended = null + child = null + user = UserEntity.all().first() + } + } + } + + @AfterTest + fun after() = transaction { + EntryEntity.all().forEach { it.delete() } + CategoryEntity.all().forEach { it.delete() } + UserEntity.all().forEach { it.delete() } + } + + @Test + fun testOldEntryNoRepeat() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = createEntry(false, LocalDateTime.now().minusMonths(1), categoryId) + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, + "/entries/$entryId", + Entry.Patch(category = Entry.Category(null)) + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess(Entry(entryId, "Aldi", -200f, false, null)) + assertEquals(shouldResponse, responseBody) + + transaction { + val entryEntity = EntryEntity[entryId] + + assertNull(entryEntity.ended) + assertNull(entryEntity.child) + assert(!entryEntity.repeat) + assertNull(entryEntity.category) + } + } + } + + @Test + fun testOldEntryRepeat() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = createEntry(true, LocalDateTime.now().minusMonths(1), categoryId) + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, + "/entries/$entryId", + Entry.Patch(category = Entry.Category(null)) + ) { response -> + val newEntryId = transaction { EntryEntity.all().last().id.value } + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess(Entry(newEntryId, "Aldi", -200f, true, null)) + assertEquals(shouldResponse, responseBody) + + transaction { + val entryEntity = EntryEntity[entryId] + val newEntryEntity = EntryEntity[newEntryId] + + assertNotNull(entryEntity.ended) + assertEquals(newEntryEntity.id, entryEntity.child) + assert(entryEntity.repeat) + assert(newEntryEntity.repeat) + assertEquals(categoryId, entryEntity.category?.value) + assertNull(newEntryEntity.category) + } + } + } + + @Test + fun testNewEntryNoRepeat() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = createEntry(false, LocalDateTime.now(), categoryId) + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, + "/entries/$entryId", + Entry.Patch(category = Entry.Category(null)) + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess(Entry(entryId, "Aldi", -200f, false, null)) + assertEquals(shouldResponse, responseBody) + + transaction { + val entryEntity = EntryEntity[entryId] + + assertNull(entryEntity.ended) + assertNull(entryEntity.child) + assert(!entryEntity.repeat) + assertNull(entryEntity.category) + } + } + } + + @Test + fun testNewEntryRepeat() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = createEntry(true, LocalDateTime.now(), categoryId) + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, + "/entries/$entryId", + Entry.Patch(category = Entry.Category(null)) + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess(Entry(entryId, "Aldi", -200f, true, null)) + assertEquals(shouldResponse, responseBody) + + transaction { + val entryEntity = EntryEntity[entryId] + + assertNull(entryEntity.ended) + assertNull(entryEntity.child) + assert(entryEntity.repeat) + assertNull(entryEntity.category) + } + } + } +} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterEntryCategory/ToOldCategoryTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterEntryCategory/ToOldCategoryTests.kt new file mode 100644 index 00000000..3ecaee40 --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterEntryCategory/ToOldCategoryTests.kt @@ -0,0 +1,100 @@ +package de.hsfl.budgetBinder.server.alterEntryCategory + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.Entry +import de.hsfl.budgetBinder.server.models.CategoryEntity +import de.hsfl.budgetBinder.server.models.EntryEntity +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* +import io.ktor.client.call.* +import io.ktor.http.* +import org.jetbrains.exposed.sql.transactions.transaction +import java.time.LocalDateTime +import kotlin.test.* + +class ToOldCategoryTests { + private fun createEntry(isRepeated: Boolean): Int = transaction { + EntryEntity.new { + name = "Aldi" + amount = -200f + repeat = isRepeated + created = LocalDateTime.now().minusMonths(2) + ended = null + child = null + user = UserEntity.all().first() + category = null + }.id.value + } + + @BeforeTest + fun before() = customTestApplication { client -> + client.registerUser() + + transaction { + CategoryEntity.new { + name = "Shopping" + color = TestCategories.color + image = TestCategories.image + budget = 300f + created = LocalDateTime.now().minusMonths(1) + ended = null + child = null + user = UserEntity.all().first() + } + } + } + + @AfterTest + fun after() = transaction { + EntryEntity.all().forEach { it.delete() } + CategoryEntity.all().forEach { it.delete() } + UserEntity.all().forEach { it.delete() } + } + + @Test + fun testOldEntryNoRepeat() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = createEntry(false) + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, + "/entries/$entryId", + Entry.Patch(category = Entry.Category(categoryId)) + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse: APIResponse = wrapFailure("you can't change this Entry") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testOldEntryRepeat() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = createEntry(true) + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, + "/entries/$entryId", + Entry.Patch(category = Entry.Category(categoryId)) + ) { response -> + val newEntryId = transaction { EntryEntity.all().last().id.value } + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess(Entry(newEntryId, "Aldi", -200f, true, categoryId)) + assertEquals(shouldResponse, responseBody) + + transaction { + val entryEntity = EntryEntity[entryId] + val newEntryEntity = EntryEntity[newEntryId] + + assertNotNull(entryEntity.ended) + assertEquals(newEntryEntity.id, entryEntity.child) + assert(entryEntity.repeat) + assert(newEntryEntity.repeat) + assertNull(entryEntity.category) + assertEquals(categoryId, newEntryEntity.category?.value) + } + } + } +} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterEntryCategory/ToVeryOldCategoryTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterEntryCategory/ToVeryOldCategoryTests.kt new file mode 100644 index 00000000..73e6b7c7 --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterEntryCategory/ToVeryOldCategoryTests.kt @@ -0,0 +1,161 @@ +package de.hsfl.budgetBinder.server.alterEntryCategory + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.Entry +import de.hsfl.budgetBinder.server.models.CategoryEntity +import de.hsfl.budgetBinder.server.models.EntryEntity +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* +import io.ktor.client.call.* +import io.ktor.http.* +import org.jetbrains.exposed.sql.transactions.transaction +import java.time.LocalDateTime +import kotlin.test.* + +class ToVeryOldCategoryTests { + private fun createEntry(isRepeated: Boolean, time: LocalDateTime): Int = transaction { + EntryEntity.new { + name = "Aldi" + amount = -200f + repeat = isRepeated + created = time + ended = null + child = null + user = UserEntity.all().first() + category = null + }.id.value + } + + @BeforeTest + fun before() = customTestApplication { client -> + client.registerUser() + + transaction { + CategoryEntity.new { + name = "Shopping" + color = TestCategories.color + image = TestCategories.image + budget = 300f + created = LocalDateTime.now().minusMonths(2) + ended = null + child = null + user = UserEntity.all().first() + } + } + } + + @AfterTest + fun after() = transaction { + EntryEntity.all().forEach { it.delete() } + CategoryEntity.all().forEach { it.delete() } + UserEntity.all().forEach { it.delete() } + } + + @Test + fun testOldEntryNoRepeat() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = createEntry(false, LocalDateTime.now().minusMonths(1)) + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, + "/entries/$entryId", + Entry.Patch(category = Entry.Category(categoryId)) + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess(Entry(entryId, "Aldi", -200f, false, categoryId)) + assertEquals(shouldResponse, responseBody) + + transaction { + val entryEntity = EntryEntity[entryId] + + assertNull(entryEntity.ended) + assertNull(entryEntity.child) + assert(!entryEntity.repeat) + assertEquals(categoryId, entryEntity.category?.value) + } + } + } + + @Test + fun testOldEntryRepeat() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = createEntry(true, LocalDateTime.now().minusMonths(1)) + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, + "/entries/$entryId", + Entry.Patch(category = Entry.Category(categoryId)) + ) { response -> + val newEntryId = transaction { EntryEntity.all().last().id.value } + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess(Entry(newEntryId, "Aldi", -200f, true, categoryId)) + assertEquals(shouldResponse, responseBody) + + transaction { + val entryEntity = EntryEntity[entryId] + val newEntryEntity = EntryEntity[newEntryId] + + assertNotNull(entryEntity.ended) + assertEquals(newEntryEntity.id, entryEntity.child) + assert(entryEntity.repeat) + assert(newEntryEntity.repeat) + assertNull(entryEntity.category) + assertEquals(categoryId, newEntryEntity.category?.value) + } + } + } + + @Test + fun testNewEntryNoRepeat() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = createEntry(false, LocalDateTime.now()) + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, + "/entries/$entryId", + Entry.Patch(category = Entry.Category(categoryId)) + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess(Entry(entryId, "Aldi", -200f, false, categoryId)) + assertEquals(shouldResponse, responseBody) + + transaction { + val entryEntity = EntryEntity[entryId] + + assertNull(entryEntity.ended) + assertNull(entryEntity.child) + assert(!entryEntity.repeat) + assertEquals(categoryId, entryEntity.category?.value) + } + } + } + + @Test + fun testNewEntryRepeat() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = createEntry(true, LocalDateTime.now()) + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, + "/entries/$entryId", + Entry.Patch(category = Entry.Category(categoryId)) + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess(Entry(entryId, "Aldi", -200f, true, categoryId)) + assertEquals(shouldResponse, responseBody) + + transaction { + val entryEntity = EntryEntity[entryId] + + assertNull(entryEntity.ended) + assertNull(entryEntity.child) + assert(entryEntity.repeat) + assertEquals(categoryId, entryEntity.category?.value) + } + } + } +} From 4cb3d6fc4602e57df7b2e5ed1daa960860d17c31 Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Fri, 24 Jun 2022 00:02:27 +0200 Subject: [PATCH 26/33] new Tests for altering Categories with Entries in them --- .../NewCategoryHasNewEntriesTests.kt | 150 ++++++++++++ .../OldCategoryHasOldAndNewEntriesTests.kt | 222 ++++++++++++++++++ .../OldCategoryHasOldEntriesTests.kt | 173 ++++++++++++++ .../OldCategoryHasOnlyNewEntriesTests.kt | 150 ++++++++++++ 4 files changed, 695 insertions(+) create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterCategoriesWithEntries/NewCategoryHasNewEntriesTests.kt create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterCategoriesWithEntries/OldCategoryHasOldAndNewEntriesTests.kt create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterCategoriesWithEntries/OldCategoryHasOldEntriesTests.kt create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterCategoriesWithEntries/OldCategoryHasOnlyNewEntriesTests.kt diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterCategoriesWithEntries/NewCategoryHasNewEntriesTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterCategoriesWithEntries/NewCategoryHasNewEntriesTests.kt new file mode 100644 index 00000000..05353518 --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterCategoriesWithEntries/NewCategoryHasNewEntriesTests.kt @@ -0,0 +1,150 @@ +package de.hsfl.budgetBinder.server.alterCategoriesWithEntries + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.Category +import de.hsfl.budgetBinder.server.models.CategoryEntity +import de.hsfl.budgetBinder.server.models.EntryEntity +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* +import io.ktor.client.call.* +import io.ktor.http.* +import org.jetbrains.exposed.sql.transactions.transaction +import java.time.LocalDateTime +import kotlin.test.* + +class NewCategoryHasNewEntriesTests { + @BeforeTest + fun before() = customTestApplication { client -> + client.registerUser() + + val userEntity = transaction { UserEntity.all().first() } + val now = LocalDateTime.now() + + transaction { + val shoppingCategory = CategoryEntity.new { + name = "Shopping" + color = TestCategories.color + image = TestCategories.image + budget = 300f + created = now + ended = null + child = null + user = userEntity + } + + EntryEntity.new { + name = "Aldi" + amount = -200f + repeat = true + created = now + ended = null + child = null + user = userEntity + category = shoppingCategory.id + } + + EntryEntity.new { + name = "Ikea" + amount = -200f + repeat = false + created = now + ended = null + child = null + user = userEntity + category = shoppingCategory.id + } + } + } + + @AfterTest + fun after() = transaction { + EntryEntity.all().forEach { it.delete() } + CategoryEntity.all().forEach { it.delete() } + UserEntity.all().forEach { it.delete() } + } + + @Test + fun testPatchCategory() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = transaction { EntryEntity.all().first().id.value } + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, "/categories/$categoryId", + Category.Patch(budget = 400f) + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess( + Category( + categoryId, + "Shopping", + TestCategories.color, + TestCategories.image, + 400f + ) + ) + assertEquals(shouldResponse, responseBody) + + transaction { + val categoryEntity = CategoryEntity[categoryId] + assertNull(categoryEntity.ended) + assertNull(categoryEntity.child) + assertEquals(400f, categoryEntity.budget) + + val mobileEntry = EntryEntity[entryId] + val mobileOneEntry = EntryEntity[entryId + 1] + + assertNull(mobileEntry.child) + assertNull(mobileEntry.ended) + assert(mobileEntry.repeat) + + assertEquals(categoryEntity.id, mobileEntry.category) + assertEquals(categoryEntity.id, mobileOneEntry.category) + + assertNull(mobileOneEntry.child) + assertNull(mobileOneEntry.ended) + assert(!mobileOneEntry.repeat) + } + } + } + + @Test + fun testDeleteCategory() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = transaction { EntryEntity.all().first().id.value } + + client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/$categoryId") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess( + Category( + categoryId, + "Shopping", + TestCategories.color, + TestCategories.image, + 300f + ) + ) + assertEquals(shouldResponse, responseBody) + + transaction { + val categoryEntity = CategoryEntity.findById(categoryId) + assertNull(categoryEntity) + + val entryEntity1 = EntryEntity[entryId] + val entryEntity2 = EntryEntity[entryId + 1] + + assertNull(entryEntity1.child) + assertNull(entryEntity1.ended) + assert(entryEntity1.repeat) + + assertNull(entryEntity1.category) + assertNull(entryEntity2.category) + + assertNull(entryEntity2.child) + assertNull(entryEntity2.ended) + assert(!entryEntity2.repeat) + } + } + } +} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterCategoriesWithEntries/OldCategoryHasOldAndNewEntriesTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterCategoriesWithEntries/OldCategoryHasOldAndNewEntriesTests.kt new file mode 100644 index 00000000..2288d41e --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterCategoriesWithEntries/OldCategoryHasOldAndNewEntriesTests.kt @@ -0,0 +1,222 @@ +package de.hsfl.budgetBinder.server.alterCategoriesWithEntries + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.Category +import de.hsfl.budgetBinder.server.models.CategoryEntity +import de.hsfl.budgetBinder.server.models.EntryEntity +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* +import io.ktor.client.call.* +import io.ktor.http.* +import org.jetbrains.exposed.sql.transactions.transaction +import java.time.LocalDateTime +import kotlin.test.* + +class OldCategoryHasOldAndNewEntriesTests { + @BeforeTest + fun before() = customTestApplication { client -> + client.registerUser() + + val userEntity = transaction { UserEntity.all().first() } + val now = LocalDateTime.now() + + transaction { + val shoppingCategory = CategoryEntity.new { + name = "Shopping" + color = TestCategories.color + image = TestCategories.image + budget = 300f + created = now.minusMonths(1) + ended = null + child = null + user = userEntity + } + + EntryEntity.new { + name = "Aldi Old" + amount = -200f + repeat = true + created = now.minusMonths(1) + ended = null + child = null + user = userEntity + category = shoppingCategory.id + } + + EntryEntity.new { + name = "Ikea Old" + amount = -200f + repeat = false + created = now.minusMonths(1) + ended = null + child = null + user = userEntity + category = shoppingCategory.id + } + + EntryEntity.new { + name = "Aldi New" + amount = -200f + repeat = true + created = now + ended = null + child = null + user = userEntity + category = shoppingCategory.id + } + + EntryEntity.new { + name = "Ikea New" + amount = -200f + repeat = false + created = now + ended = null + child = null + user = userEntity + category = shoppingCategory.id + } + } + } + + @AfterTest + fun after() = transaction { + EntryEntity.all().forEach { it.delete() } + CategoryEntity.all().forEach { it.delete() } + UserEntity.all().forEach { it.delete() } + } + + @Test + fun testPatchCategory() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = transaction { EntryEntity.all().first().id.value } + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, "/categories/$categoryId", + Category.Patch(budget = 400f) + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + + val newCategoryId = transaction { CategoryEntity.all().last().id.value } + val newEntryId = transaction { EntryEntity.all().last().id.value } + + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess( + Category( + newCategoryId, + "Shopping", + TestCategories.color, + TestCategories.image, + 400f + ) + ) + assertEquals(shouldResponse, responseBody) + + transaction { + val categoryEntity = CategoryEntity[categoryId] + val newCategoryEntity = CategoryEntity[newCategoryId] + + val entryEntity1 = EntryEntity[entryId] + val entryEntity2 = EntryEntity[entryId + 1] + val entryEntity3 = EntryEntity[entryId + 2] + val entryEntity4 = EntryEntity[entryId + 3] + + val newEntryEntity1 = EntryEntity[newEntryId] + + assertNotNull(categoryEntity.ended) + assertNull(newCategoryEntity.ended) + assertNull(newCategoryEntity.child) + assertEquals(newCategoryEntity.id, categoryEntity.child) + assertEquals(300f, categoryEntity.budget) + assertEquals(400f, newCategoryEntity.budget) + + assertNotNull(entryEntity1.ended) + assertEquals(newEntryEntity1.id, entryEntity1.child) + assert(entryEntity1.repeat) + assert(newEntryEntity1.repeat) + + assertNull(newEntryEntity1.ended) + assertNull(newEntryEntity1.child) + + assertNull(entryEntity2.child) + assertNull(entryEntity2.ended) + assert(!entryEntity2.repeat) + + assertNull(entryEntity3.child) + assertNull(entryEntity3.ended) + assert(entryEntity3.repeat) + + assertNull(entryEntity4.child) + assertNull(entryEntity4.ended) + assert(!entryEntity4.repeat) + + assertEquals(categoryEntity.id, entryEntity1.category) + assertEquals(categoryEntity.id, entryEntity2.category) + assertEquals(newCategoryEntity.id, entryEntity3.category) + assertEquals(newCategoryEntity.id, entryEntity4.category) + assertEquals(newCategoryEntity.id, newEntryEntity1.category) + } + } + } + + @Test + fun testDeleteCategory() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = transaction { EntryEntity.all().first().id.value } + + client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/$categoryId") { response -> + val newEntryId = transaction { EntryEntity.all().last().id.value } + + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess( + Category( + categoryId, + "Shopping", + TestCategories.color, + TestCategories.image, + 300f + ) + ) + assertEquals(shouldResponse, responseBody) + + transaction { + val categoryEntity = CategoryEntity[categoryId] + + val entryEntity1 = EntryEntity[entryId] + val entryEntity2 = EntryEntity[entryId + 1] + val entryEntity3 = EntryEntity[entryId + 2] + val entryEntity4 = EntryEntity[entryId + 3] + + val newEntryEntity1 = EntryEntity[newEntryId] + + assertNotNull(categoryEntity.ended) + assertNull(categoryEntity.child) + + assertNotNull(entryEntity1.ended) + assertEquals(newEntryEntity1.id, entryEntity1.child) + assert(entryEntity1.repeat) + assert(newEntryEntity1.repeat) + + assertNull(newEntryEntity1.ended) + assertNull(newEntryEntity1.child) + + assertNull(entryEntity2.child) + assertNull(entryEntity2.ended) + assert(!entryEntity2.repeat) + + assertNull(entryEntity3.child) + assertNull(entryEntity3.ended) + assert(entryEntity3.repeat) + + assertNull(entryEntity4.child) + assertNull(entryEntity4.ended) + assert(!entryEntity4.repeat) + + assertEquals(categoryEntity.id, entryEntity1.category) + assertEquals(categoryEntity.id, entryEntity2.category) + assertNull(entryEntity3.category) + assertNull(entryEntity4.category) + assertNull(newEntryEntity1.category) + } + } + } +} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterCategoriesWithEntries/OldCategoryHasOldEntriesTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterCategoriesWithEntries/OldCategoryHasOldEntriesTests.kt new file mode 100644 index 00000000..d1b25aa9 --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterCategoriesWithEntries/OldCategoryHasOldEntriesTests.kt @@ -0,0 +1,173 @@ +package de.hsfl.budgetBinder.server.alterCategoriesWithEntries + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.Category +import de.hsfl.budgetBinder.server.models.CategoryEntity +import de.hsfl.budgetBinder.server.models.EntryEntity +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* +import io.ktor.client.call.* +import io.ktor.http.* +import org.jetbrains.exposed.sql.transactions.transaction +import java.time.LocalDateTime +import kotlin.test.* + +class OldCategoryHasOldEntriesTests { + @BeforeTest + fun before() = customTestApplication { client -> + client.registerUser() + + val userEntity = transaction { UserEntity.all().first() } + val now = LocalDateTime.now() + + transaction { + val shoppingCategory = CategoryEntity.new { + name = "Shopping" + color = TestCategories.color + image = TestCategories.image + budget = 300f + created = now.minusMonths(1) + ended = null + child = null + user = userEntity + } + + EntryEntity.new { + name = "Aldi" + amount = -200f + repeat = true + created = now.minusMonths(1) + ended = null + child = null + user = userEntity + category = shoppingCategory.id + } + + EntryEntity.new { + name = "Ikea" + amount = -200f + repeat = false + created = now.minusMonths(1) + ended = null + child = null + user = userEntity + category = shoppingCategory.id + } + } + } + + @AfterTest + fun after() = transaction { + EntryEntity.all().forEach { it.delete() } + CategoryEntity.all().forEach { it.delete() } + UserEntity.all().forEach { it.delete() } + } + + @Test + fun testPatchCategory() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = transaction { EntryEntity.all().first().id.value } + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, "/categories/$categoryId", + Category.Patch(budget = 400f) + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + + val newCategoryId = transaction { CategoryEntity.all().last().id.value } + val newEntryId = transaction { EntryEntity.all().last().id.value } + + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess( + Category( + newCategoryId, + "Shopping", + TestCategories.color, + TestCategories.image, + 400f + ) + ) + assertEquals(shouldResponse, responseBody) + + transaction { + val categoryEntity = CategoryEntity[categoryId] + val newCategoryEntity = CategoryEntity[newCategoryId] + assertNotNull(categoryEntity.ended) + assertNull(newCategoryEntity.ended) + assertNull(newCategoryEntity.child) + assertEquals(newCategoryEntity.id, categoryEntity.child) + assertEquals(300f, categoryEntity.budget) + assertEquals(400f, newCategoryEntity.budget) + + val entryEntity1 = EntryEntity[entryId] + val newEntryEntity1 = EntryEntity[newEntryId] + val entryEntity2 = EntryEntity[entryId + 1] + + assertNotNull(entryEntity1.ended) + assertEquals(newEntryEntity1.id, entryEntity1.child) + assert(entryEntity1.repeat) + assert(newEntryEntity1.repeat) + + assertNull(newEntryEntity1.ended) + assertNull(newEntryEntity1.child) + + assertEquals(categoryEntity.id, entryEntity1.category) + assertEquals(newCategoryEntity.id, newEntryEntity1.category) + assertEquals(categoryEntity.id, entryEntity2.category) + + assertNull(entryEntity2.child) + assertNull(entryEntity2.ended) + assert(!entryEntity2.repeat) + } + } + } + + @Test + fun testDeleteCategory() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = transaction { EntryEntity.all().first().id.value } + + client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/$categoryId") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess( + Category( + categoryId, + "Shopping", + TestCategories.color, + TestCategories.image, + 300f + ) + ) + assertEquals(shouldResponse, responseBody) + + val newEntryId = transaction { EntryEntity.all().last().id.value } + + transaction { + val categoryEntity = CategoryEntity[categoryId] + assertNotNull(categoryEntity.ended) + assertNull(categoryEntity.child) + + val entryEntity1 = EntryEntity[entryId] + val newEntryEntity1 = EntryEntity[newEntryId] + val entryEntity2 = EntryEntity[entryId + 1] + + assertNotNull(entryEntity1.ended) + assertEquals(newEntryEntity1.id, entryEntity1.child) + assert(entryEntity1.repeat) + assert(newEntryEntity1.repeat) + + assertNull(newEntryEntity1.ended) + assertNull(newEntryEntity1.child) + + assertEquals(categoryEntity.id, entryEntity1.category) + assertNull(newEntryEntity1.category) + assertEquals(categoryEntity.id, entryEntity2.category) + + assertNull(entryEntity2.child) + assertNull(entryEntity2.ended) + assert(!entryEntity2.repeat) + } + } + } +} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterCategoriesWithEntries/OldCategoryHasOnlyNewEntriesTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterCategoriesWithEntries/OldCategoryHasOnlyNewEntriesTests.kt new file mode 100644 index 00000000..0c69dc55 --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/alterCategoriesWithEntries/OldCategoryHasOnlyNewEntriesTests.kt @@ -0,0 +1,150 @@ +package de.hsfl.budgetBinder.server.alterCategoriesWithEntries + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.Category +import de.hsfl.budgetBinder.server.models.CategoryEntity +import de.hsfl.budgetBinder.server.models.EntryEntity +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* +import io.ktor.client.call.* +import io.ktor.http.* +import org.jetbrains.exposed.sql.transactions.transaction +import java.time.LocalDateTime +import kotlin.test.* + +class OldCategoryHasOnlyNewEntriesTests { + @BeforeTest + fun before() = customTestApplication { client -> + client.registerUser() + + val userEntity = transaction { UserEntity.all().first() } + val now = LocalDateTime.now() + + transaction { + val shoppingCategory = CategoryEntity.new { + name = "Shopping" + color = TestCategories.color + image = TestCategories.image + budget = 300f + created = now.minusMonths(1) + ended = null + child = null + user = userEntity + } + + EntryEntity.new { + name = "Aldi" + amount = -200f + repeat = true + created = now + ended = null + child = null + user = userEntity + category = shoppingCategory.id + } + + EntryEntity.new { + name = "Ikea" + amount = -200f + repeat = false + created = now + ended = null + child = null + user = userEntity + category = shoppingCategory.id + } + } + } + + @AfterTest + fun after() = transaction { + EntryEntity.all().forEach { it.delete() } + CategoryEntity.all().forEach { it.delete() } + UserEntity.all().forEach { it.delete() } + } + + @Test + fun testPatchCategory() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = transaction { EntryEntity.all().first().id.value } + + client.sendAuthenticatedRequestWithBody( + HttpMethod.Patch, "/categories/$categoryId", + Category.Patch(budget = 400f) + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess( + Category( + categoryId, + "Shopping", + TestCategories.color, + TestCategories.image, + 400f + ) + ) + assertEquals(shouldResponse, responseBody) + + transaction { + val categoryEntity = CategoryEntity[categoryId] + assertNull(categoryEntity.ended) + assertNull(categoryEntity.child) + assertEquals(400f, categoryEntity.budget) + + val entryEntity1 = EntryEntity[entryId] + val entryEntity2 = EntryEntity[entryId + 1] + + assertNull(entryEntity1.child) + assertNull(entryEntity1.ended) + assert(entryEntity1.repeat) + + assertEquals(categoryEntity.id, entryEntity1.category) + assertEquals(categoryEntity.id, entryEntity2.category) + + assertNull(entryEntity2.child) + assertNull(entryEntity2.ended) + assert(!entryEntity2.repeat) + } + } + } + + @Test + fun testDeleteCategory() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = transaction { EntryEntity.all().first().id.value } + + client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/$categoryId") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + val shouldResponse = wrapSuccess( + Category( + categoryId, + "Shopping", + TestCategories.color, + TestCategories.image, + 300f + ) + ) + assertEquals(shouldResponse, responseBody) + + transaction { + val categoryEntity = CategoryEntity.findById(categoryId) + assertNull(categoryEntity) + + val mobileEntry = EntryEntity[entryId] + val mobileOneEntry = EntryEntity[entryId + 1] + + assertNull(mobileEntry.child) + assertNull(mobileEntry.ended) + assert(mobileEntry.repeat) + + assertNull(mobileEntry.category) + assertNull(mobileOneEntry.category) + + assertNull(mobileOneEntry.child) + assertNull(mobileOneEntry.ended) + assert(!mobileOneEntry.repeat) + } + } + } +} From d13a857afdf6a2e64e20ea63ef25832971f8b590 Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Fri, 24 Jun 2022 10:07:37 +0200 Subject: [PATCH 27/33] extracted the rest entry with Category Tests --- .../budgetBinder/server/CategoryEntryTest.kt | 946 ------------------ .../CreateEntryWithCategoryTests.kt | 87 ++ .../GetEntriesByCategoryTests.kt | 177 ++++ .../GetEntriesByCategoryWithPeriodTests.kt | 238 +++++ 4 files changed, 502 insertions(+), 946 deletions(-) delete mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/CategoryEntryTest.kt create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categoriesWithEntries/CreateEntryWithCategoryTests.kt create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categoriesWithEntries/GetEntriesByCategoryTests.kt create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categoriesWithEntries/GetEntriesByCategoryWithPeriodTests.kt diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/CategoryEntryTest.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/CategoryEntryTest.kt deleted file mode 100644 index 9775388c..00000000 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/CategoryEntryTest.kt +++ /dev/null @@ -1,946 +0,0 @@ -package de.hsfl.budgetBinder.server - -import de.hsfl.budgetBinder.common.APIResponse -import de.hsfl.budgetBinder.common.Category -import de.hsfl.budgetBinder.common.Entry -import de.hsfl.budgetBinder.server.models.CategoryEntity -import de.hsfl.budgetBinder.server.models.Entries -import de.hsfl.budgetBinder.server.models.EntryEntity -import de.hsfl.budgetBinder.server.models.UserEntity -import de.hsfl.budgetBinder.server.utils.* -import io.ktor.client.call.* -import io.ktor.client.request.* -import io.ktor.http.* -import org.jetbrains.exposed.sql.deleteWhere -import org.jetbrains.exposed.sql.transactions.transaction -import java.time.LocalDateTime -import kotlin.test.* - -class CategoryEntryTest { - - @BeforeTest - fun before() = customTestApplication { client -> - client.registerUser() - - val userEntity = transaction { UserEntity.all().first() } - val now = LocalDateTime.now() - - transaction { - val internetCategory = CategoryEntity.new { - name = "Internet" - color = TestCategories.color - image = TestCategories.image - budget = 50f - created = now.minusMonths(3) - ended = now.minusMonths(2) - child = null - user = userEntity - } - - val internetPhoneCategory = CategoryEntity.new { - name = "Internet-Phone" - color = TestCategories.color - image = TestCategories.image - budget = 100f - created = now.minusMonths(2) - ended = null - child = null - user = userEntity - } - - internetCategory.child = internetPhoneCategory.id - - val internetEntry = EntryEntity.new { - name = "Internet" - amount = -50f - repeat = true - created = now.minusMonths(3) - ended = now.minusMonths(2) - child = null - user = userEntity - category = internetCategory.id - } - - EntryEntity.new { - name = "Internet" - amount = -50f - repeat = true - created = now.minusMonths(2) - ended = null - child = null - user = userEntity - category = internetPhoneCategory.id - }.let { internetEntry.child = it.id } - - EntryEntity.new { - name = "Phone" - amount = -50f - repeat = true - created = now.minusMonths(2) - ended = null - child = null - user = userEntity - category = internetPhoneCategory.id - } - - EntryEntity.new { - name = "Phone one Time" - amount = -250f - repeat = false - created = now.minusMonths(2) - ended = null - child = null - user = userEntity - category = internetPhoneCategory.id - } - - EntryEntity.new { - name = "Monthly Pay" - amount = 3000f - repeat = true - created = now.minusMonths(3) - ended = null - child = null - user = userEntity - category = null - } - } - } - - - @AfterTest - fun after() = transaction { - EntryEntity.all().forEach { it.delete() } - UserEntity.all().forEach { - it.delete() - } - CategoryEntity.all().forEach { it.delete() } - } - - - @Test - fun testGetEntriesByCategory() = customTestApplicationWithLogin { client -> - client.get("/categories/1/entries").let { response -> - assertEquals(HttpStatusCode.Unauthorized, response.status) - val responseBody: APIResponse> = response.body() - val shouldResponse: APIResponse> = - wrapFailure("Your accessToken is absent or does not match.", 401) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/test/entries") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - val shouldResponse: APIResponse> = wrapFailure("The ID you provided is not a number.") - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/5000/entries") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - val shouldResponse: APIResponse> = wrapFailure("Your category was not found.") - assertEquals(shouldResponse, responseBody) - } - - val categoryId = transaction { CategoryEntity.all().first().id.value } - val entryId = transaction { EntryEntity.all().first().id.value } - - val entryList = listOf( - Entry(entryId, "Internet", -50f, true, categoryId), - Entry(entryId + 1, "Internet", -50f, true, categoryId + 1), - Entry(entryId + 2, "Phone", -50f, true, categoryId + 1), - Entry(entryId + 3, "Phone one Time", -250f, false, categoryId + 1), - Entry(entryId + 4, "Monthly Pay", 3000f, true, null), - ) - - client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/${categoryId - 1}/entries") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - val shouldResponse: APIResponse> = wrapFailure("Your category was not found.") - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/$categoryId/entries") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - val shouldResponse = wrapSuccess(listOf(entryList[0])) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/${categoryId + 1}/entries") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - val shouldResponse = wrapSuccess(listOf(entryList[1], entryList[2], entryList[3])) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/null/entries") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - val shouldResponse = wrapSuccess(listOf(entryList[4])) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest(HttpMethod.Get, "/entries?current=true") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - val shouldResponse = wrapSuccess(listOf(entryList[1], entryList[2], entryList[4])) - assertEquals(shouldResponse, responseBody) - } - } - - @Test - fun testGetEntriesByCategoryWithPeriod() = customTestApplicationWithLogin { client -> - val categoryId = transaction { CategoryEntity.all().first().id.value } - val entryId = transaction { EntryEntity.all().first().id.value } - - val now = LocalDateTime.now() - - val entryList = listOf( - Entry(entryId, "Internet", -50f, true, categoryId), - Entry(entryId + 1, "Internet", -50f, true, categoryId + 1), - Entry(entryId + 2, "Phone", -50f, true, categoryId + 1), - Entry(entryId + 3, "Phone one Time", -250f, false, categoryId + 1), - Entry(entryId + 4, "Monthly Pay", 3000f, true, null), - ) - - client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/${categoryId}/entries?current=true") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - val shouldResponse: APIResponse> = wrapSuccess(emptyList()) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest( - HttpMethod.Get, - "/categories/${categoryId}/entries?period=${formatToPeriod(now.minusMonths(1))}" - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - val shouldResponse: APIResponse> = wrapSuccess(emptyList()) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest( - HttpMethod.Get, - "/categories/${categoryId}/entries?period=${formatToPeriod(now.minusMonths(2))}" - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - val shouldResponse: APIResponse> = wrapSuccess(emptyList()) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest( - HttpMethod.Get, - "/categories/${categoryId}/entries?period=${formatToPeriod(now.minusMonths(3))}" - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - val shouldResponse = wrapSuccess(listOf(entryList[0])) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest( - HttpMethod.Get, - "/categories/${categoryId + 1}/entries?current=true" - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - val shouldResponse = wrapSuccess(listOf(entryList[1], entryList[2])) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest( - HttpMethod.Get, - "/categories/${categoryId + 1}/entries?period=${formatToPeriod(now.minusMonths(1))}" - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - val shouldResponse = wrapSuccess(listOf(entryList[1], entryList[2])) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest( - HttpMethod.Get, - "/categories/${categoryId + 1}/entries?period=${formatToPeriod(now.minusMonths(2))}" - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - val shouldResponse = wrapSuccess(listOf(entryList[1], entryList[2], entryList[3])) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequest( - HttpMethod.Get, - "/categories/${categoryId + 1}/entries?period=${formatToPeriod(now.minusMonths(3))}" - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse> = response.body() - val shouldResponse: APIResponse> = wrapSuccess(emptyList()) - assertEquals(shouldResponse, responseBody) - } - } - - - @Test - fun createEntryWithCategory() = customTestApplicationWithLogin { client -> - val categoryId = transaction { CategoryEntity.all().first().id.value + 1 } - - client.sendAuthenticatedRequestWithBody( - HttpMethod.Post, "/entries", - Entry.In("Second Phone", -50f, true, 5000) - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - - val id = transaction { - EntryEntity.all().last().let { - assertEquals("Second Phone", it.name) - assertEquals(-50f, it.amount) - assert(it.repeat) - assertNull(it.category) - it.id.value - } - } - val shouldResponse = wrapSuccess(Entry(id, "Second Phone", -50f, true, null)) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequestWithBody( - HttpMethod.Post, "/entries", - Entry.In("Second Phone", -50f, true, categoryId) - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - - val id = transaction { - EntryEntity.all().last().let { - assertEquals("Second Phone", it.name) - assertEquals(-50f, it.amount) - assert(it.repeat) - assertEquals(categoryId, it.category?.value) - it.id.value - } - } - val shouldResponse = wrapSuccess(Entry(id, "Second Phone", -50f, true, categoryId)) - assertEquals(shouldResponse, responseBody) - } - - } - - @Test - fun testChangeCategoryInEntry() = customTestApplicationWithLogin { client -> - val categoryId = transaction { CategoryEntity.all().first().id.value + 1 } - val entryId = transaction { EntryEntity.all().last().id.value } - - client.sendAuthenticatedRequestWithBody( - HttpMethod.Patch, "/entries/$entryId", - Entry.Patch(category = Entry.Category(categoryId - 1)) - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - val shouldResponse: APIResponse = wrapFailure("you can't change this Entry") - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequestWithBody( - HttpMethod.Patch, "/entries/$entryId", - Entry.Patch(category = Entry.Category(categoryId)) - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - - transaction { - assertEquals(categoryId, EntryEntity[entryId].category?.value) - } - val shouldResponse = wrapSuccess(Entry(entryId, "Monthly Pay", 3000f, true, categoryId)) - assertEquals(shouldResponse, responseBody) - } - - client.sendAuthenticatedRequestWithBody( - HttpMethod.Patch, "/entries/$entryId", - Entry.Patch(category = Entry.Category(5000)) - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - - transaction { - assertNull(EntryEntity[entryId].category) - } - val shouldResponse = wrapSuccess(Entry(entryId, "Monthly Pay", 3000f, true, null)) - assertEquals(shouldResponse, responseBody) - } - } - - - @Test - fun testChangeOldCategoryHasOldEntries() = customTestApplicationWithLogin { client -> - val categoryId = transaction { CategoryEntity.all().first().id.value + 1 } - val entryId = transaction { EntryEntity.all().first().id.value + 1 } - - client.sendAuthenticatedRequestWithBody( - HttpMethod.Patch, "/categories/$categoryId", - Category.Patch(budget = 200f) - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - - val id = transaction { - val oldCategory = CategoryEntity[categoryId] - assertNotNull(oldCategory.ended) - assertNotNull(oldCategory.child) - val newCategory = CategoryEntity[oldCategory.child!!] - - assertEquals(100f, oldCategory.budget) - assertEquals(200f, newCategory.budget) - - val oldInternetEntry = EntryEntity[entryId] - val oldPhoneEntry = EntryEntity[entryId + 1] - val oldPhoneOneTimeEntry = EntryEntity[entryId + 2] - - assertNotNull(oldInternetEntry.child) - assertNotNull(oldInternetEntry.ended) - val newInternetEntry = EntryEntity[oldInternetEntry.child!!] - - assertNotNull(oldPhoneEntry.child) - assertNotNull(oldPhoneEntry.ended) - val newPhoneEntry = EntryEntity[oldPhoneEntry.child!!] - - assertEquals(oldInternetEntry.name, newInternetEntry.name) - assertEquals(oldInternetEntry.repeat, newInternetEntry.repeat) - assertNotEquals(oldInternetEntry.category, newInternetEntry.category) - assertEquals(newInternetEntry.id, oldInternetEntry.child) - assertNull(newInternetEntry.child) - assertNull(newInternetEntry.ended) - - assertEquals(oldPhoneEntry.name, newPhoneEntry.name) - assertEquals(oldPhoneEntry.repeat, newPhoneEntry.repeat) - assertNotEquals(oldPhoneEntry.category, newPhoneEntry.category) - assertEquals(oldPhoneEntry.child, newPhoneEntry.id) - assertNull(newPhoneEntry.child) - assertNull(newPhoneEntry.ended) - - assert(!oldPhoneOneTimeEntry.repeat) - assertNull(oldPhoneOneTimeEntry.child) - assertNull(oldPhoneOneTimeEntry.ended) - - newCategory.id.value - } - - val shouldResponse = - wrapSuccess(Category(id, "Internet-Phone", TestCategories.color, TestCategories.image, 200f)) - assertEquals(shouldResponse, responseBody) - } - } - - - @Test - fun testChangeOldCategoryHasNewEntries() = customTestApplicationWithLogin { client -> - val categoryId = transaction { CategoryEntity.all().first().id.value + 1 } - transaction { - val userEntity = UserEntity.all().first() - val categoryEntity = CategoryEntity[categoryId] - - EntryEntity.new { - name = "Mobile" - amount = -50f - repeat = true - created = LocalDateTime.now() - ended = null - child = null - user = userEntity - category = categoryEntity.id - }.id.value - - EntryEntity.new { - name = "Mobile One" - amount = -250f - repeat = false - created = LocalDateTime.now() - ended = null - child = null - user = userEntity - category = categoryEntity.id - } - } - - val entryId = transaction { EntryEntity.all().last().id.value - 1 } - - client.sendAuthenticatedRequestWithBody( - HttpMethod.Patch, "/categories/$categoryId", - Category.Patch(budget = 200f) - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - - val id = transaction { - val oldCategory = CategoryEntity[categoryId] - assertNotNull(oldCategory.ended) - assertNotNull(oldCategory.child) - val newCategory = CategoryEntity[oldCategory.child!!] - - assertEquals(100f, oldCategory.budget) - assertEquals(200f, newCategory.budget) - - val mobileEntry = EntryEntity[entryId] - val mobileOneEntry = EntryEntity[entryId + 1] - - assertNull(mobileEntry.child) - assertNull(mobileEntry.ended) - assert(mobileEntry.repeat) - assertEquals(newCategory.id, mobileEntry.category) - - assertNull(mobileOneEntry.child) - assertNull(mobileOneEntry.ended) - assert(!mobileOneEntry.repeat) - assertEquals(newCategory.id, mobileOneEntry.category) - - newCategory.id.value - } - - val shouldResponse = wrapSuccess( - Category( - id, - "Internet-Phone", - TestCategories.color, - TestCategories.image, - 200f - ) - ) - assertEquals(shouldResponse, responseBody) - } - } - - - @Test - fun testChangeOldCategoryHasOnlyNewEntries() = customTestApplicationWithLogin { client -> - val categoryId = transaction { CategoryEntity.all().first().id.value + 1 } - transaction { - val userEntity = UserEntity.all().first() - val categoryEntity = CategoryEntity[categoryId] - - Entries.deleteWhere { Entries.category eq categoryEntity.id } - - EntryEntity.new { - name = "Mobile" - amount = -50f - repeat = true - created = LocalDateTime.now() - ended = null - child = null - user = userEntity - category = categoryEntity.id - }.id.value - - EntryEntity.new { - name = "Mobile One" - amount = -250f - repeat = false - created = LocalDateTime.now() - ended = null - child = null - user = userEntity - category = categoryEntity.id - } - } - - val entryId = transaction { EntryEntity.all().last().id.value - 1 } - - client.sendAuthenticatedRequestWithBody( - HttpMethod.Patch, "/categories/$categoryId", - Category.Patch(budget = 200f) - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - - val id = transaction { - val categoryEntity = CategoryEntity[categoryId] - assertNull(categoryEntity.ended) - assertNull(categoryEntity.child) - - assertEquals(200f, categoryEntity.budget) - - val mobileEntry = EntryEntity[entryId] - val mobileOneEntry = EntryEntity[entryId + 1] - - assertNull(mobileEntry.child) - assertNull(mobileEntry.ended) - assert(mobileEntry.repeat) - - assertEquals(categoryEntity.id, mobileEntry.category) - assertEquals(categoryEntity.id, mobileOneEntry.category) - - assertNull(mobileOneEntry.child) - assertNull(mobileOneEntry.ended) - assert(!mobileOneEntry.repeat) - - categoryEntity.id.value - } - - val shouldResponse = wrapSuccess( - Category( - id, - "Internet-Phone", - TestCategories.color, - TestCategories.image, - 200f - ) - ) - assertEquals(shouldResponse, responseBody) - } - } - - - @Test - fun testChangeNewCategoryHasNewEntries() = customTestApplicationWithLogin { client -> - transaction { - val userEntity = UserEntity.all().first() - val now = LocalDateTime.now() - - val categoryEntity = CategoryEntity.new { - name = "Mobile" - color = TestCategories.color - image = TestCategories.image - budget = 50f - created = now - ended = null - child = null - user = userEntity - } - - EntryEntity.new { - name = "Mobile" - amount = -50f - repeat = true - created = LocalDateTime.now() - ended = null - child = null - user = userEntity - category = categoryEntity.id - }.id.value - - EntryEntity.new { - name = "Mobile One" - amount = -250f - repeat = false - created = now - ended = null - child = null - user = userEntity - category = categoryEntity.id - } - } - - val categoryId = transaction { CategoryEntity.all().last().id.value } - val entryId = transaction { EntryEntity.all().last().id.value - 1 } - - client.sendAuthenticatedRequestWithBody( - HttpMethod.Patch, "/categories/$categoryId", - Category.Patch(budget = 200f) - ) { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - - transaction { - val categoryEntity = CategoryEntity[categoryId] - assertNull(categoryEntity.ended) - assertNull(categoryEntity.child) - assertEquals(200f, categoryEntity.budget) - - val mobileEntry = EntryEntity[entryId] - val mobileOneEntry = EntryEntity[entryId + 1] - - assertNull(mobileEntry.child) - assertNull(mobileEntry.ended) - assert(mobileEntry.repeat) - - assertEquals(categoryEntity.id, mobileEntry.category) - assertEquals(categoryEntity.id, mobileOneEntry.category) - - assertNull(mobileOneEntry.child) - assertNull(mobileOneEntry.ended) - assert(!mobileOneEntry.repeat) - } - - val shouldResponse = wrapSuccess( - Category( - categoryId, - "Mobile", - TestCategories.color, - TestCategories.image, - 200f - ) - ) - assertEquals(shouldResponse, responseBody) - } - } - - - @Test - fun testDeleteOldCategoryHasOldEntries() = customTestApplicationWithLogin { client -> - val categoryId = transaction { CategoryEntity.all().first().id.value + 1 } - val entryId = transaction { EntryEntity.all().first().id.value + 1 } - - client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/$categoryId") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - - transaction { - val categoryEntity = CategoryEntity.findById(categoryId) - assertNotNull(categoryEntity) - assertNotNull(categoryEntity.ended) - assertNull(categoryEntity.child) - - val oldInternetEntry = EntryEntity[entryId] - val oldPhoneEntry = EntryEntity[entryId + 1] - val oldPhoneOneTimeEntry = EntryEntity[entryId + 2] - assertNull(oldPhoneOneTimeEntry.child) - assertNull(oldPhoneOneTimeEntry.ended) - - assertNotNull(oldInternetEntry.child) - assertNotNull(oldInternetEntry.ended) - val newInternetEntry = EntryEntity[oldInternetEntry.child!!] - assertNull(newInternetEntry.child) - assertNull(newInternetEntry.ended) - - assertNotNull(oldPhoneEntry.child) - assertNotNull(oldPhoneEntry.ended) - val newPhoneEntry = EntryEntity[oldPhoneEntry.child!!] - assertNull(newPhoneEntry.child) - assertNull(newPhoneEntry.ended) - - assertEquals(categoryEntity.id, oldInternetEntry.category) - assertEquals(categoryEntity.id, oldPhoneEntry.category) - assertNull(newInternetEntry.category) - assertNull(newPhoneEntry.category) - - assertEquals(categoryEntity.id, oldPhoneOneTimeEntry.category) - } - - val shouldResponse = wrapSuccess( - Category( - categoryId, - "Internet-Phone", - TestCategories.color, - TestCategories.image, - 100f - ) - ) - assertEquals(shouldResponse, responseBody) - } - } - - - @Test - fun testDeleteOldCategoryHasNewEntries() = customTestApplicationWithLogin { client -> - val categoryId = transaction { CategoryEntity.all().first().id.value + 1 } - transaction { - val userEntity = UserEntity.all().first() - val categoryEntity = CategoryEntity[categoryId] - - EntryEntity.new { - name = "Mobile" - amount = -50f - repeat = true - created = LocalDateTime.now() - ended = null - child = null - user = userEntity - category = categoryEntity.id - }.id.value - - EntryEntity.new { - name = "Mobile One" - amount = -250f - repeat = false - created = LocalDateTime.now() - ended = null - child = null - user = userEntity - category = categoryEntity.id - } - } - - val entryId = transaction { EntryEntity.all().last().id.value - 1 } - - client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/$categoryId") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - - transaction { - val categoryEntity = CategoryEntity.findById(categoryId) - assertNotNull(categoryEntity) - assertNotNull(categoryEntity.ended) - assertNull(categoryEntity.child) - - val mobileEntry = EntryEntity[entryId] - val mobileOneEntry = EntryEntity[entryId + 1] - - assertNull(mobileEntry.child) - assertNull(mobileEntry.ended) - assertNull(mobileOneEntry.child) - assertNull(mobileOneEntry.ended) - - assertNull(mobileEntry.category) - assertNull(mobileOneEntry.category) - } - - val shouldResponse = wrapSuccess( - Category( - categoryId, - "Internet-Phone", - TestCategories.color, - TestCategories.image, - 100f - ) - ) - assertEquals(shouldResponse, responseBody) - } - } - - - @Test - fun testDeleteOldCategoryHasOnlyNewEntries() = customTestApplicationWithLogin { client -> - val categoryId = transaction { CategoryEntity.all().first().id.value + 1 } - transaction { - val userEntity = UserEntity.all().first() - val categoryEntity = CategoryEntity[categoryId] - - Entries.deleteWhere { Entries.category eq categoryEntity.id } - - EntryEntity.new { - name = "Mobile" - amount = -50f - repeat = true - created = LocalDateTime.now() - ended = null - child = null - user = userEntity - category = categoryEntity.id - }.id.value - - EntryEntity.new { - name = "Mobile One" - amount = -250f - repeat = false - created = LocalDateTime.now() - ended = null - child = null - user = userEntity - category = categoryEntity.id - } - } - - val entryId = transaction { EntryEntity.all().last().id.value - 1 } - - client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/$categoryId") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - - transaction { - val categoryEntity = CategoryEntity.findById(categoryId) - assertNull(categoryEntity) - - val mobileEntry = EntryEntity[entryId] - val mobileOneEntry = EntryEntity[entryId + 1] - - assertNull(mobileEntry.child) - assertNull(mobileEntry.ended) - assertNull(mobileOneEntry.child) - assertNull(mobileOneEntry.ended) - - assertNull(mobileEntry.category) - assertNull(mobileOneEntry.category) - } - - val shouldResponse = wrapSuccess( - Category( - categoryId, - "Internet-Phone", - TestCategories.color, - TestCategories.image, - 100f - ) - ) - assertEquals(shouldResponse, responseBody) - } - } - - - @Test - fun testDeleteNewCategoryHasNewEntries() = customTestApplicationWithLogin { client -> - transaction { - val userEntity = UserEntity.all().first() - val now = LocalDateTime.now() - - val categoryEntity = CategoryEntity.new { - name = "Mobile" - color = TestCategories.color - image = TestCategories.image - budget = 50f - created = now - ended = null - child = null - user = userEntity - } - - EntryEntity.new { - name = "Mobile" - amount = -50f - repeat = true - created = LocalDateTime.now() - ended = null - child = null - user = userEntity - category = categoryEntity.id - }.id.value - - EntryEntity.new { - name = "Mobile One" - amount = -250f - repeat = false - created = now - ended = null - child = null - user = userEntity - category = categoryEntity.id - } - } - - val categoryId = transaction { CategoryEntity.all().last().id.value } - val entryId = transaction { EntryEntity.all().last().id.value - 1 } - - client.sendAuthenticatedRequest(HttpMethod.Delete, "/categories/$categoryId") { response -> - assertEquals(HttpStatusCode.OK, response.status) - val responseBody: APIResponse = response.body() - - transaction { - val categoryEntity = CategoryEntity.findById(categoryId) - assertNull(categoryEntity) - - val mobileEntry = EntryEntity[entryId] - val mobileOneEntry = EntryEntity[entryId + 1] - - assertNull(mobileEntry.child) - assertNull(mobileEntry.ended) - assertNull(mobileOneEntry.child) - assertNull(mobileOneEntry.ended) - - assertNull(mobileEntry.category) - assertNull(mobileOneEntry.category) - } - - val shouldResponse = wrapSuccess( - Category( - categoryId, - "Mobile", - TestCategories.color, - TestCategories.image, - 50f - ) - ) - assertEquals(shouldResponse, responseBody) - } - } -} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categoriesWithEntries/CreateEntryWithCategoryTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categoriesWithEntries/CreateEntryWithCategoryTests.kt new file mode 100644 index 00000000..e395dc6f --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categoriesWithEntries/CreateEntryWithCategoryTests.kt @@ -0,0 +1,87 @@ +package de.hsfl.budgetBinder.server.categoriesWithEntries + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.Entry +import de.hsfl.budgetBinder.server.models.CategoryEntity +import de.hsfl.budgetBinder.server.models.EntryEntity +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* +import io.ktor.client.call.* +import io.ktor.http.* +import org.jetbrains.exposed.sql.transactions.transaction +import java.time.LocalDateTime +import kotlin.test.* + +class CreateEntryWithCategoryTests { + @BeforeTest + fun before() = customTestApplication { client -> + client.registerUser() + + transaction { + CategoryEntity.new { + name = "Shopping" + color = TestCategories.color + image = TestCategories.image + budget = 300f + created = LocalDateTime.now() + ended = null + child = null + user = UserEntity.all().first() + } + } + } + + @AfterTest + fun after() = transaction { + EntryEntity.all().forEach { it.delete() } + CategoryEntity.all().forEach { it.delete() } + UserEntity.all().forEach { it.delete() } + } + + @Test + fun testCreateEntryWithCategoryNotFound() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequestWithBody( + HttpMethod.Post, "/entries", + Entry.In("Second Phone", -50f, true, 5000) + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + + val id = transaction { + EntryEntity.all().last().let { + assertEquals("Second Phone", it.name) + assertEquals(-50f, it.amount) + assert(it.repeat) + assertNull(it.category) + it.id.value + } + } + val shouldResponse = wrapSuccess(Entry(id, "Second Phone", -50f, true, null)) + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testCreateEntryWithCategory() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + client.sendAuthenticatedRequestWithBody( + HttpMethod.Post, "/entries", + Entry.In("Second Phone", -50f, true, categoryId) + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse = response.body() + + val id = transaction { + EntryEntity.all().last().let { + assertEquals("Second Phone", it.name) + assertEquals(-50f, it.amount) + assert(it.repeat) + assertEquals(categoryId, it.category?.value) + it.id.value + } + } + val shouldResponse = wrapSuccess(Entry(id, "Second Phone", -50f, true, categoryId)) + assertEquals(shouldResponse, responseBody) + } + } +} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categoriesWithEntries/GetEntriesByCategoryTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categoriesWithEntries/GetEntriesByCategoryTests.kt new file mode 100644 index 00000000..d50a13c8 --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categoriesWithEntries/GetEntriesByCategoryTests.kt @@ -0,0 +1,177 @@ +package de.hsfl.budgetBinder.server.categoriesWithEntries + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.Entry +import de.hsfl.budgetBinder.server.models.CategoryEntity +import de.hsfl.budgetBinder.server.models.EntryEntity +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* +import io.ktor.client.call.* +import io.ktor.http.* +import org.jetbrains.exposed.sql.transactions.transaction +import java.time.LocalDateTime +import kotlin.test.* + +class GetEntriesByCategoryTests { + + private fun getEntryListFromID(entryId: Int, categoryId: Int): List { + return listOf( + Entry(entryId, "Internet", -50f, true, categoryId), + Entry(entryId + 1, "Internet", -50f, true, categoryId + 1), + Entry(entryId + 2, "Phone", -50f, true, categoryId + 1), + Entry(entryId + 3, "Phone one Time", -250f, false, categoryId + 1), + Entry(entryId + 4, "Monthly Pay", 3000f, true, null), + ) + } + + @BeforeTest + fun before() = customTestApplication { client -> + client.registerUser() + + val userEntity = transaction { UserEntity.all().first() } + val now = LocalDateTime.now() + + transaction { + val internetCategory = CategoryEntity.new { + name = "Internet" + color = TestCategories.color + image = TestCategories.image + budget = 50f + created = now.minusMonths(3) + ended = now.minusMonths(2) + child = null + user = userEntity + } + + val internetPhoneCategory = CategoryEntity.new { + name = "Internet-Phone" + color = TestCategories.color + image = TestCategories.image + budget = 100f + created = now.minusMonths(2) + ended = null + child = null + user = userEntity + } + + internetCategory.child = internetPhoneCategory.id + + val internetEntry = EntryEntity.new { + name = "Internet" + amount = -50f + repeat = true + created = now.minusMonths(3) + ended = now.minusMonths(2) + child = null + user = userEntity + category = internetCategory.id + } + + EntryEntity.new { + name = "Internet" + amount = -50f + repeat = true + created = now.minusMonths(2) + ended = null + child = null + user = userEntity + category = internetPhoneCategory.id + }.let { internetEntry.child = it.id } + + EntryEntity.new { + name = "Phone" + amount = -50f + repeat = true + created = now.minusMonths(2) + ended = null + child = null + user = userEntity + category = internetPhoneCategory.id + } + + EntryEntity.new { + name = "Phone one Time" + amount = -250f + repeat = false + created = now.minusMonths(2) + ended = null + child = null + user = userEntity + category = internetPhoneCategory.id + } + + EntryEntity.new { + name = "Monthly Pay" + amount = 3000f + repeat = true + created = now.minusMonths(3) + ended = null + child = null + user = userEntity + category = null + } + } + } + + @AfterTest + fun after() = transaction { + EntryEntity.all().forEach { it.delete() } + CategoryEntity.all().forEach { it.delete() } + UserEntity.all().forEach { it.delete() } + } + + @Test + fun testGetEntriesByCategoryString() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/test/entries") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + val shouldResponse: APIResponse> = wrapFailure("The ID you provided is not a number.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testGetEntriesByCategoryNotFound() = customTestApplicationWithLogin { client -> + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/5000/entries") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + val shouldResponse: APIResponse> = wrapFailure("Your category was not found.") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testGetEntriesByCategory() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = transaction { EntryEntity.all().first().id.value } + val entryList = getEntryListFromID(entryId, categoryId) + + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/$categoryId/entries") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + val shouldResponse = wrapSuccess(listOf(entryList[0])) + assertEquals(shouldResponse, responseBody) + } + + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/${categoryId + 1}/entries") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + val shouldResponse = wrapSuccess(listOf(entryList[1], entryList[2], entryList[3])) + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testGetEntriesByCategoryNull() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = transaction { EntryEntity.all().first().id.value } + val entryList = getEntryListFromID(entryId, categoryId) + + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/null/entries") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + val shouldResponse = wrapSuccess(listOf(entryList[4])) + assertEquals(shouldResponse, responseBody) + } + } +} diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categoriesWithEntries/GetEntriesByCategoryWithPeriodTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categoriesWithEntries/GetEntriesByCategoryWithPeriodTests.kt new file mode 100644 index 00000000..becc90ad --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/categoriesWithEntries/GetEntriesByCategoryWithPeriodTests.kt @@ -0,0 +1,238 @@ +package de.hsfl.budgetBinder.server.categoriesWithEntries + +import de.hsfl.budgetBinder.common.APIResponse +import de.hsfl.budgetBinder.common.Category +import de.hsfl.budgetBinder.common.Entry +import de.hsfl.budgetBinder.server.models.CategoryEntity +import de.hsfl.budgetBinder.server.models.EntryEntity +import de.hsfl.budgetBinder.server.models.UserEntity +import de.hsfl.budgetBinder.server.utils.* +import io.ktor.client.call.* +import io.ktor.http.* +import org.jetbrains.exposed.sql.transactions.transaction +import java.time.LocalDateTime +import kotlin.test.* + +class GetEntriesByCategoryWithPeriodTests { + + private fun getEntryListFromID(entryId: Int, categoryId: Int): List { + return listOf( + Entry(entryId, "Internet", -50f, true, categoryId), + Entry(entryId + 1, "Internet", -50f, true, categoryId + 1), + Entry(entryId + 2, "Phone", -50f, true, categoryId + 1), + Entry(entryId + 3, "Phone one Time", -250f, false, categoryId + 1), + Entry(entryId + 4, "Monthly Pay", 3000f, true, null), + ) + } + + @BeforeTest + fun before() = customTestApplication { client -> + client.registerUser() + + val userEntity = transaction { UserEntity.all().first() } + val now = LocalDateTime.now() + + transaction { + val internetCategory = CategoryEntity.new { + name = "Internet" + color = TestCategories.color + image = TestCategories.image + budget = 50f + created = now.minusMonths(3) + ended = now.minusMonths(2) + child = null + user = userEntity + } + + val internetPhoneCategory = CategoryEntity.new { + name = "Internet-Phone" + color = TestCategories.color + image = TestCategories.image + budget = 100f + created = now.minusMonths(2) + ended = null + child = null + user = userEntity + } + + internetCategory.child = internetPhoneCategory.id + + val internetEntry = EntryEntity.new { + name = "Internet" + amount = -50f + repeat = true + created = now.minusMonths(3) + ended = now.minusMonths(2) + child = null + user = userEntity + category = internetCategory.id + } + + EntryEntity.new { + name = "Internet" + amount = -50f + repeat = true + created = now.minusMonths(2) + ended = null + child = null + user = userEntity + category = internetPhoneCategory.id + }.let { internetEntry.child = it.id } + + EntryEntity.new { + name = "Phone" + amount = -50f + repeat = true + created = now.minusMonths(2) + ended = null + child = null + user = userEntity + category = internetPhoneCategory.id + } + + EntryEntity.new { + name = "Phone one Time" + amount = -250f + repeat = false + created = now.minusMonths(2) + ended = null + child = null + user = userEntity + category = internetPhoneCategory.id + } + + EntryEntity.new { + name = "Monthly Pay" + amount = 3000f + repeat = true + created = now.minusMonths(3) + ended = null + child = null + user = userEntity + category = null + } + } + } + + @AfterTest + fun after() = transaction { + EntryEntity.all().forEach { it.delete() } + CategoryEntity.all().forEach { it.delete() } + UserEntity.all().forEach { it.delete() } + } + + @Test + fun testGetEntriesByCategoryFalsePeriod() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + + client.sendAuthenticatedRequest(HttpMethod.Get, "/categories/$categoryId/entries?period=508346") { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + val shouldResponse = wrapFailure>("period has not the right pattern") + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testGetCurrentEntriesByCategory() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = transaction { EntryEntity.all().first().id.value } + val entryList = getEntryListFromID(entryId, categoryId) + + client.sendAuthenticatedRequest( + HttpMethod.Get, + "/categories/$categoryId/entries?current=true" + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + val shouldResponse: APIResponse> = wrapSuccess(emptyList()) + assertEquals(shouldResponse, responseBody) + } + + client.sendAuthenticatedRequest( + HttpMethod.Get, + "/categories/${categoryId + 1}/entries?current=true" + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + val shouldResponse = wrapSuccess(listOf(entryList[1], entryList[2])) + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testGetEntriesByCategoryFirst() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value } + val entryId = transaction { EntryEntity.all().first().id.value } + val entryList = getEntryListFromID(entryId, categoryId) + val now = LocalDateTime.now() + + client.sendAuthenticatedRequest( + HttpMethod.Get, + "/categories/${categoryId}/entries?period=${formatToPeriod(now.minusMonths(1))}" + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + val shouldResponse: APIResponse> = wrapSuccess(emptyList()) + assertEquals(shouldResponse, responseBody) + } + + client.sendAuthenticatedRequest( + HttpMethod.Get, + "/categories/${categoryId}/entries?period=${formatToPeriod(now.minusMonths(2))}" + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + val shouldResponse: APIResponse> = wrapSuccess(emptyList()) + assertEquals(shouldResponse, responseBody) + } + + client.sendAuthenticatedRequest( + HttpMethod.Get, + "/categories/${categoryId}/entries?period=${formatToPeriod(now.minusMonths(3))}" + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + val shouldResponse = wrapSuccess(listOf(entryList[0])) + assertEquals(shouldResponse, responseBody) + } + } + + @Test + fun testGetEntriesByCategorySecond() = customTestApplicationWithLogin { client -> + val categoryId = transaction { CategoryEntity.all().first().id.value + 1 } + val entryId = transaction { EntryEntity.all().first().id.value } + val entryList = getEntryListFromID(entryId, categoryId - 1) + val now = LocalDateTime.now() + + client.sendAuthenticatedRequest( + HttpMethod.Get, + "/categories/$categoryId/entries?period=${formatToPeriod(now.minusMonths(1))}" + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + val shouldResponse = wrapSuccess(listOf(entryList[1], entryList[2])) + assertEquals(shouldResponse, responseBody) + } + + client.sendAuthenticatedRequest( + HttpMethod.Get, + "/categories/$categoryId/entries?period=${formatToPeriod(now.minusMonths(2))}" + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + val shouldResponse = wrapSuccess(listOf(entryList[1], entryList[2], entryList[3])) + assertEquals(shouldResponse, responseBody) + } + + client.sendAuthenticatedRequest( + HttpMethod.Get, + "/categories/$categoryId/entries?period=${formatToPeriod(now.minusMonths(3))}" + ) { response -> + assertEquals(HttpStatusCode.OK, response.status) + val responseBody: APIResponse> = response.body() + val shouldResponse: APIResponse> = wrapSuccess(emptyList()) + assertEquals(shouldResponse, responseBody) + } + } +} From b57a30c31aad44659989de0adbfb280386b5a412 Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Fri, 24 Jun 2022 10:46:59 +0200 Subject: [PATCH 28/33] created Tests for the Utils-functions and fixed the regex according to the tests --- .../server/utils/LocalDateTimeHelper.kt | 2 +- .../de/hsfl/budgetBinder/server/UtilsTests.kt | 185 ++++++++++++++++++ 2 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UtilsTests.kt diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/utils/LocalDateTimeHelper.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/utils/LocalDateTimeHelper.kt index 0dfbb64c..9dd00ece 100644 --- a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/utils/LocalDateTimeHelper.kt +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/utils/LocalDateTimeHelper.kt @@ -17,7 +17,7 @@ fun parseParameterToLocalDateTimeOrErrorMessage(current: Boolean, param: String? if (param == null) return null to null - if (!param.matches("^(0?[1-9]|1[012])-([2-9]\\d[1-9]\\d|[1-9]\\d)\$".toRegex())) { + if (!param.matches("^(0?[1-9]|1[012])-([2-9]\\d\\d\\d)\$".toRegex())) { return "period has not the right pattern" to null } diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UtilsTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UtilsTests.kt new file mode 100644 index 00000000..c1f05a45 --- /dev/null +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UtilsTests.kt @@ -0,0 +1,185 @@ +package de.hsfl.budgetBinder.server + +import de.hsfl.budgetBinder.server.utils.formatToPeriod +import de.hsfl.budgetBinder.server.utils.isCreatedAndEndedInPeriod +import de.hsfl.budgetBinder.server.utils.parseParameterToLocalDateTimeOrErrorMessage +import java.time.LocalDateTime +import kotlin.test.* + +class UtilsTests { + private val errorString = "period has not the right pattern" + + @Test + fun testParsePeriodCurrentTruePeriodNull() { + val now = LocalDateTime.now() + val shouldPeriod = LocalDateTime.of(now.year, now.month.value, 1, 0, 0) + + val pair = parseParameterToLocalDateTimeOrErrorMessage(true, null) + assertNull(pair.first) + assertEquals(shouldPeriod, pair.second) + } + + @Test + fun testParsePeriodCurrentFalsePeriodNull() { + val pair = parseParameterToLocalDateTimeOrErrorMessage(false, null) + assertNull(pair.first) + assertNull(pair.second) + } + + @Test + fun testParsePeriodCurrentTruePeriodRight() { + val now = LocalDateTime.now() + val shouldPeriod = LocalDateTime.of(now.year, now.month.value, 1, 0, 0) + + val pair = parseParameterToLocalDateTimeOrErrorMessage(true, "06-2022") + assertNull(pair.first) + assertEquals(shouldPeriod, pair.second) + } + + @Test + fun testParsePeriodCurrentTruePeriodFalse() { + val pair = parseParameterToLocalDateTimeOrErrorMessage(false, "65416546") + assertEquals(errorString, pair.first) + assertNull(pair.second) + } + + @Test + fun testParsePeriodWithCharakters() { + val pair = parseParameterToLocalDateTimeOrErrorMessage(false, "sjdvhkf434") + assertEquals(errorString, pair.first) + assertNull(pair.second) + } + + @Test + fun testParsePeriodMoreNumbers() { + val pair = parseParameterToLocalDateTimeOrErrorMessage(false, "65416546") + assertEquals(errorString, pair.first) + assertNull(pair.second) + } + + @Test + fun testParsePeriodWithoutDash() { + val pair = parseParameterToLocalDateTimeOrErrorMessage(false, "0135970") + assertEquals(errorString, pair.first) + assertNull(pair.second) + } + + @Test + fun testParsePeriodWithDashButWrongPlace() { + val pair = parseParameterToLocalDateTimeOrErrorMessage(false, "01359-0") + assertEquals(errorString, pair.first) + assertNull(pair.second) + } + + @Test + fun testParsePeriodWithDashRightPlaceWrongNumbers() { + val pair = parseParameterToLocalDateTimeOrErrorMessage(false, "34-0001") + assertEquals(errorString, pair.first) + assertNull(pair.second) + } + + @Test + fun testParsePeriodWrongMonth() { + val pair = parseParameterToLocalDateTimeOrErrorMessage(false, "34-2000") + assertEquals(errorString, pair.first) + assertNull(pair.second) + } + + @Test + fun testParsePeriodWrongYear() { + val pair = parseParameterToLocalDateTimeOrErrorMessage(false, "01-1999") + assertEquals(errorString, pair.first) + assertNull(pair.second) + } + + @Test + fun testParsePeriodRightPatternMonth() { + repeat(12) { + val month = it + 1 + val shouldPeriod = LocalDateTime.of(2000, month, 1, 0, 0) + val pair = parseParameterToLocalDateTimeOrErrorMessage(false, formatToPeriod(shouldPeriod)) + + assertNull(pair.first) + assertEquals(shouldPeriod, pair.second) + } + } + + @Test + fun testParsePeriodRightPatternYear() { + repeat(8000) { + val year = it + 2000 + val shouldPeriod = LocalDateTime.of(year, 1, 1, 0, 0) + val pair = parseParameterToLocalDateTimeOrErrorMessage(false, formatToPeriod(shouldPeriod)) + + assertNull(pair.first) + assertEquals(shouldPeriod, pair.second) + } + } + + @Test + fun testIsCreatedAndEndedInPeriodAllNow() { + val now = LocalDateTime.now() + val period = LocalDateTime.of(now.year, now.month.value, 1, 0, 0) + assert(!isCreatedAndEndedInPeriod(now, now, period)) + } + + @Test + fun testIsCreatedAndEndedInPeriodCreatedNowEndedNullPeriodNow() { + val now = LocalDateTime.now() + val created = LocalDateTime.now() + val period = LocalDateTime.of(now.year, now.month.value, 1, 0, 0) + assert(isCreatedAndEndedInPeriod(created, null, period)) + } + + @Test + fun testIsCreatedAndEndedInPeriodCreatedNowEndedNullPeriodFuture() { + val now = LocalDateTime.now().plusMonths(1) + val created = LocalDateTime.now() + val period = LocalDateTime.of(now.year, now.month.value, 1, 0, 0) + assert(isCreatedAndEndedInPeriod(created, null, period)) + } + + @Test + fun testIsCreatedAndEndedInPeriodCreatedNowEndedNullPeriodPast() { + val now = LocalDateTime.now().minusMonths(1) + val created = LocalDateTime.now() + val period = LocalDateTime.of(now.year, now.month.value, 1, 0, 0) + assert(!isCreatedAndEndedInPeriod(created, null, period)) + } + + @Test + fun testIsCreatedAndEndedInPeriodCreatedNowEndedPastPeriodNow() { + val now = LocalDateTime.now() + val created = LocalDateTime.now() + val ended = LocalDateTime.now().minusMonths(1) + val period = LocalDateTime.of(now.year, now.month.value, 1, 0, 0) + assert(!isCreatedAndEndedInPeriod(created, ended, period)) + } + + @Test + fun testIsCreatedAndEndedInPeriodCreatedPastEndedNowPeriodNow() { + val now = LocalDateTime.now() + val created = LocalDateTime.now().minusMonths(1) + val ended = LocalDateTime.now() + val period = LocalDateTime.of(now.year, now.month.value, 1, 0, 0) + assert(!isCreatedAndEndedInPeriod(created, ended, period)) + } + + @Test + fun testIsCreatedAndEndedInPeriodCreatedPastEndedNowPeriodPast() { + val now = LocalDateTime.now().minusMonths(1) + val created = LocalDateTime.now().minusMonths(1) + val ended = LocalDateTime.now() + val period = LocalDateTime.of(now.year, now.month.value, 1, 0, 0) + assert(isCreatedAndEndedInPeriod(created, ended, period)) + } + + @Test + fun testIsCreatedAndEndedInPeriodCreatedPastEndedNowPeriodFuture() { + val now = LocalDateTime.now().plusMonths(1) + val created = LocalDateTime.now().minusMonths(1) + val ended = LocalDateTime.now() + val period = LocalDateTime.of(now.year, now.month.value, 1, 0, 0) + assert(!isCreatedAndEndedInPeriod(created, ended, period)) + } +} From ac4dd85aa7a8f054ff8a49d544f32a1e4b75c84b Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Fri, 24 Jun 2022 11:15:07 +0200 Subject: [PATCH 29/33] refactored change Entry function --- .../implementations/EntryServiceImpl.kt | 95 +++++++++---------- 1 file changed, 45 insertions(+), 50 deletions(-) diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/services/implementations/EntryServiceImpl.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/services/implementations/EntryServiceImpl.kt index 10f56236..ca738ce4 100644 --- a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/services/implementations/EntryServiceImpl.kt +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/services/implementations/EntryServiceImpl.kt @@ -49,74 +49,69 @@ class EntryServiceImpl : EntryService { }.toDto() } - private fun createOrChangeEntry( + override fun changeEntry(userId: Int, entryId: Int, entry: Entry.Patch): Entry? = transaction { + var entryEntity = EntryEntity[entryId] + if (entryEntity.ended != null) { + return@transaction null + } + + if (entry.category == null) { + if (!canEntryBeChanged(entryEntity, entry.repeat, entry.amount)) + entryEntity = entryEntity.createChild() + } else { + val categoryEntity = entry.category?.let { getCategoryByID(userId, it.id) } + val maybeEntity = changeEntryWithCategoryChange(entryEntity, entry.repeat, entry.amount, categoryEntity) + maybeEntity?.let { + entryEntity = it + it.category = categoryEntity?.id + } ?: return@transaction null + } + + entry.name?.let { entryEntity.name = it } + entry.amount?.let { entryEntity.amount = it } + entry.repeat?.let { entryEntity.repeat = it } + entryEntity.toDto() + } + + private fun changeEntryWithCategoryChange( oldEntry: EntryEntity, repeat: Boolean?, amount: Float?, - category: Entry.Category?, categoryEntity: CategoryEntity? ): EntryEntity? { + if (!canEntryBeChanged(oldEntry, repeat, amount)) { + return oldEntry.createChild() + } val now = LocalDateTime.now() val period = LocalDateTime.of(now.year, now.month.value, 1, 0, 0) + val entryPeriod = LocalDateTime.of(oldEntry.created.year, oldEntry.created.month.value, 1, 0, 0) - if (!oldEntry.repeat || (repeat != false && amount == null) || oldEntry.created > period) { - if (category == null) { - return oldEntry - } - val entryPeriod = LocalDateTime.of(oldEntry.created.year, oldEntry.created.month.value, 1, 0, 0) - - if (entryPeriod == period) { - return oldEntry - } - - if (categoryEntity == null) { - if (!oldEntry.repeat) { - return oldEntry - } - } else { - val categoryPeriod = - LocalDateTime.of(categoryEntity.created.year, categoryEntity.created.month.value, 1, 0, 0) - - if (!oldEntry.repeat && categoryPeriod > entryPeriod) { - return null - } - if (!oldEntry.repeat) { - return oldEntry - } - } + if (entryPeriod == period) { + return oldEntry } - val newEntry = oldEntry.createChild() - oldEntry.child = newEntry.id - oldEntry.ended = now - - return newEntry - } - + if (oldEntry.repeat) { + return oldEntry.createChild() + } - override fun changeEntry(userId: Int, entryId: Int, entry: Entry.Patch): Entry? = transaction { - var entryEntity: EntryEntity? = EntryEntity[entryId] - if (entryEntity!!.ended != null) { - return@transaction null + if (categoryEntity == null) { + return oldEntry } - val categoryEntity = entry.category?.let { getCategoryByID(userId, it.id) } + val categoryPeriod = LocalDateTime.of(categoryEntity.created.year, categoryEntity.created.month.value, 1, 0, 0) - if (categoryEntity?.ended != null) { - return@transaction null + if (categoryPeriod > entryPeriod) { + return null } - entryEntity = createOrChangeEntry(entryEntity, entry.repeat, entry.amount, entry.category, categoryEntity) - if (entryEntity == null) { - return@transaction null - } + return oldEntry + } - entry.name?.let { entryEntity.name = it } - entry.amount?.let { entryEntity.amount = it } - entry.repeat?.let { entryEntity.repeat = it } - entry.category?.let { entryEntity.category = categoryEntity?.id } + private fun canEntryBeChanged(oldEntry: EntryEntity, repeat: Boolean?, amount: Float?): Boolean { + val now = LocalDateTime.now() + val period = LocalDateTime.of(now.year, now.month.value, 1, 0, 0) - entryEntity.toDto() + return !oldEntry.repeat || (repeat != false && amount == null) || oldEntry.created > period } override fun deleteEntry(entryId: Int): Entry? = transaction { From 77fccd57f45d57ae02086e2e73b4a672941f6252 Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Fri, 24 Jun 2022 11:22:07 +0200 Subject: [PATCH 30/33] reformatted all files --- .../main/kotlin/de/hsfl/budgetBinder/server/config/Config.kt | 1 - .../kotlin/de/hsfl/budgetBinder/server/models/UserPrincipal.kt | 1 - .../kotlin/de/hsfl/budgetBinder/server/routes/AuthRoutes.kt | 1 - .../de/hsfl/budgetBinder/server/utils/UnauthorizedException.kt | 2 +- .../test/kotlin/de/hsfl/budgetBinder/server/BaseRoutesTests.kt | 1 - .../src/test/kotlin/de/hsfl/budgetBinder/server/LoginTests.kt | 1 - .../test/kotlin/de/hsfl/budgetBinder/server/RegisterTests.kt | 1 - .../src/test/kotlin/de/hsfl/budgetBinder/server/UserTests.kt | 1 - .../kotlin/de/hsfl/budgetBinder/server/utils/TestModels.kt | 3 +-- 9 files changed, 2 insertions(+), 10 deletions(-) diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/config/Config.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/config/Config.kt index 2dea4d5b..cd26a19c 100644 --- a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/config/Config.kt +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/config/Config.kt @@ -2,7 +2,6 @@ package de.hsfl.budgetBinder.server.config import java.io.File - data class Config(val server: Server, val database: Database, val jwt: JWT) { data class Server( val dev: Boolean, diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/models/UserPrincipal.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/models/UserPrincipal.kt index 71a69368..950cb6eb 100644 --- a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/models/UserPrincipal.kt +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/models/UserPrincipal.kt @@ -2,7 +2,6 @@ package de.hsfl.budgetBinder.server.models import io.ktor.server.auth.* - interface UserPrincipal : Principal { fun getUserID(): Int fun getUserTokenVersion(): Int diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/routes/AuthRoutes.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/routes/AuthRoutes.kt index 22bc3216..71d507c2 100644 --- a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/routes/AuthRoutes.kt +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/routes/AuthRoutes.kt @@ -17,7 +17,6 @@ import io.ktor.server.routing.* import org.kodein.di.instance import org.kodein.di.ktor.closestDI - fun Route.login() { authenticate("auth-form") { post("/login") { diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/utils/UnauthorizedException.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/utils/UnauthorizedException.kt index b26dde45..8714fe84 100644 --- a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/utils/UnauthorizedException.kt +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/utils/UnauthorizedException.kt @@ -1,3 +1,3 @@ package de.hsfl.budgetBinder.server.utils -class UnauthorizedException(override val message: String): Throwable() +class UnauthorizedException(override val message: String) : Throwable() diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/BaseRoutesTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/BaseRoutesTests.kt index 5e25431e..9719c928 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/BaseRoutesTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/BaseRoutesTests.kt @@ -6,7 +6,6 @@ import io.ktor.http.* import kotlin.test.* class BaseRoutesTests { - @Test fun testGetOpenApi() = customTestApplication { client -> client.get("/openapi.json").let { response -> diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LoginTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LoginTests.kt index 0ebff1e3..da0baa63 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LoginTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/LoginTests.kt @@ -15,7 +15,6 @@ import java.net.HttpCookie import kotlin.test.* class LoginTests { - @BeforeTest fun registerTestUser() = customTestApplication { client -> client.registerUser() diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/RegisterTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/RegisterTests.kt index fffa5bca..dde9a7e8 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/RegisterTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/RegisterTests.kt @@ -15,7 +15,6 @@ import kotlin.test.* import kotlin.test.assertEquals class RegisterTests { - @AfterTest fun deleteTestUser() = transaction { UserEntity.all().forEach { it.delete() } diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UserTests.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UserTests.kt index b92f2c0c..36637b59 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UserTests.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/UserTests.kt @@ -12,7 +12,6 @@ import org.jetbrains.exposed.sql.transactions.transaction import kotlin.test.* class UserTests { - @BeforeTest fun registerTestUser() = customTestApplication { client -> client.registerUser() diff --git a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/utils/TestModels.kt b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/utils/TestModels.kt index 1e22f846..74a13eda 100644 --- a/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/utils/TestModels.kt +++ b/budget-binder-server/src/test/kotlin/de/hsfl/budgetBinder/server/utils/TestModels.kt @@ -133,6 +133,5 @@ class CustomCookieStorage : CookiesStorage { oldestCookie = newOldest } - override fun close() { - } + override fun close() {} } From f53477ce7fc74819451fb59ec287d01e00f24985 Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Fri, 24 Jun 2022 11:24:46 +0200 Subject: [PATCH 31/33] changed the cookie-Filename to cookies.bin and reformatted some stuff --- .../budgetBinder/data/client/plugins/getCookieFileStorage.kt | 2 +- .../de/hsfl/budgetBinder/data/client/plugins/AuthPlugin.kt | 1 - .../budgetBinder/data/client/plugins/getCookieFileStorage.kt | 2 +- .../hsfl/budgetBinder/data/client/plugins/FileCookieStorage.kt | 3 +-- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/budget-binder-multiplatform-app/src/androidMain/kotlin/de/hsfl/budgetBinder/data/client/plugins/getCookieFileStorage.kt b/budget-binder-multiplatform-app/src/androidMain/kotlin/de/hsfl/budgetBinder/data/client/plugins/getCookieFileStorage.kt index c10157a9..507ea145 100644 --- a/budget-binder-multiplatform-app/src/androidMain/kotlin/de/hsfl/budgetBinder/data/client/plugins/getCookieFileStorage.kt +++ b/budget-binder-multiplatform-app/src/androidMain/kotlin/de/hsfl/budgetBinder/data/client/plugins/getCookieFileStorage.kt @@ -4,5 +4,5 @@ import de.hsfl.budgetBinder.android.BudgetBinderApplication import java.io.File actual fun getCookieFileStorage(): File { - return File(BudgetBinderApplication.instance.filesDir, "cookies.txt") + return File(BudgetBinderApplication.instance.filesDir, "cookies.bin") } diff --git a/budget-binder-multiplatform-app/src/commonMain/kotlin/de/hsfl/budgetBinder/data/client/plugins/AuthPlugin.kt b/budget-binder-multiplatform-app/src/commonMain/kotlin/de/hsfl/budgetBinder/data/client/plugins/AuthPlugin.kt index 2162f597..bcf400ef 100644 --- a/budget-binder-multiplatform-app/src/commonMain/kotlin/de/hsfl/budgetBinder/data/client/plugins/AuthPlugin.kt +++ b/budget-binder-multiplatform-app/src/commonMain/kotlin/de/hsfl/budgetBinder/data/client/plugins/AuthPlugin.kt @@ -9,7 +9,6 @@ import io.ktor.client.request.* import io.ktor.http.* import io.ktor.util.* - class AuthPlugin private constructor( val loginPath: String, val logoutPath: String, diff --git a/budget-binder-multiplatform-app/src/desktopMain/kotlin/de/hsfl/budgetBinder/data/client/plugins/getCookieFileStorage.kt b/budget-binder-multiplatform-app/src/desktopMain/kotlin/de/hsfl/budgetBinder/data/client/plugins/getCookieFileStorage.kt index 5c75a373..828e8bdc 100644 --- a/budget-binder-multiplatform-app/src/desktopMain/kotlin/de/hsfl/budgetBinder/data/client/plugins/getCookieFileStorage.kt +++ b/budget-binder-multiplatform-app/src/desktopMain/kotlin/de/hsfl/budgetBinder/data/client/plugins/getCookieFileStorage.kt @@ -11,7 +11,7 @@ actual fun getCookieFileStorage(): File { path += "/.bb-client" File(path).toPath().createDirectories() - path += "/cookies.txt" + path += "/cookies.bin" val file = File(path) file.createNewFile() return file diff --git a/budget-binder-multiplatform-app/src/jvmMain/kotlin/de/hsfl/budgetBinder/data/client/plugins/FileCookieStorage.kt b/budget-binder-multiplatform-app/src/jvmMain/kotlin/de/hsfl/budgetBinder/data/client/plugins/FileCookieStorage.kt index 8e3dbbd9..56e6e98e 100644 --- a/budget-binder-multiplatform-app/src/jvmMain/kotlin/de/hsfl/budgetBinder/data/client/plugins/FileCookieStorage.kt +++ b/budget-binder-multiplatform-app/src/jvmMain/kotlin/de/hsfl/budgetBinder/data/client/plugins/FileCookieStorage.kt @@ -138,6 +138,5 @@ class FileCookieStorage : CookiesStorage { writeCookiesToFile(container) } - override fun close() { - } + override fun close() {} } From 8a57ad3d25de17c813d7f2c19bbaa1c14b4bc37f Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Fri, 24 Jun 2022 11:32:37 +0200 Subject: [PATCH 32/33] don't need to specify parameter label --- .../src/main/kotlin/de/hsfl/budgetBinder/server/main.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/main.kt b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/main.kt index 9cd9fcc4..7473e610 100644 --- a/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/main.kt +++ b/budget-binder-server/src/main/kotlin/de/hsfl/budgetBinder/server/main.kt @@ -22,7 +22,7 @@ class ServerMain : CliktCommand() { ) override fun run(): Unit = runBlocking { - val config = Config.create(configFile = configFile) + val config = Config.create(configFile) val keyStore = when { config.server.dev && config.server.ssl -> { From 5628efd08abc84e45bf068945a3f9959daca883f Mon Sep 17 00:00:00 2001 From: Fabian Petersen Date: Fri, 24 Jun 2022 11:53:27 +0200 Subject: [PATCH 33/33] updated the script file so that it works on linux --- CopyScriptJsMain.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CopyScriptJsMain.sh b/CopyScriptJsMain.sh index 13642823..f2ba9055 100644 --- a/CopyScriptJsMain.sh +++ b/CopyScriptJsMain.sh @@ -1,2 +1,2 @@ #!/bin/bash -cp -r "./budget-binder-multiplatform-app/build/distributions/*" "./budget-binder-server/public/" +cp -r ./budget-binder-multiplatform-app/build/distributions/* ./budget-binder-server/public/