diff --git a/build.gradle.kts b/build.gradle.kts index ced76e7..2dabb52 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile alias(libs.plugins.sqldelight) alias(libs.plugins.ktor) alias(libs.plugins.spotless) + alias(libs.plugins.power.assert) } application { diff --git a/libs.versions.toml b/libs.versions.toml index 97e0c80..a0e8be9 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -21,6 +21,7 @@ slugify="3.0.6" suspendapp="0.4.0" cohort="2.3.0" spotless="6.22.0" +power-assert = "0.13.0" [libraries] arrow-core = { module = "io.arrow-kt:arrow-core", version.ref = "arrow" } @@ -110,4 +111,5 @@ detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } kotlinx-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" } ktor = { id = "io.ktor.plugin", version.ref = "ktor" } -spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } \ No newline at end of file +spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } +power-assert = { id = "com.bnorm.power.kotlin-power-assert", version.ref = "power-assert" } diff --git a/src/main/kotlin/io/github/nomisrev/service/JwtService.kt b/src/main/kotlin/io/github/nomisrev/service/JwtService.kt index 7dfa859..3958007 100644 --- a/src/main/kotlin/io/github/nomisrev/service/JwtService.kt +++ b/src/main/kotlin/io/github/nomisrev/service/JwtService.kt @@ -47,11 +47,11 @@ fun jwtService(env: Env.Auth, repo: UserPersistence) = val jwt = JWT.decodeT(token.value, JWSHMAC512Algorithm).mapLeft { JwtInvalid(it.toString()) }.bind() val userId = - ensureNotNull(jwt.claimValueAsLong("id").orNull()) { + ensureNotNull(jwt.claimValueAsLong("id").getOrNull()) { JwtInvalid("id missing from JWT Token") } val expiresAt = - ensureNotNull(jwt.expiresAt().orNull()) { JwtInvalid("exp missing from JWT Token") } + ensureNotNull(jwt.expiresAt().getOrNull()) { JwtInvalid("exp missing from JWT Token") } ensure(expiresAt.isAfter(Instant.now(Clock.systemUTC()))) { JwtInvalid("JWT Token expired") } repo.select(UserId(userId)).bind() UserId(userId) diff --git a/src/test/kotlin/io/github/nomisrev/routes/ArticleRouteSpec.kt b/src/test/kotlin/io/github/nomisrev/routes/ArticleRouteSpec.kt index e6350f5..836f6b1 100644 --- a/src/test/kotlin/io/github/nomisrev/routes/ArticleRouteSpec.kt +++ b/src/test/kotlin/io/github/nomisrev/routes/ArticleRouteSpec.kt @@ -8,7 +8,6 @@ import io.github.nomisrev.auth.JwtToken import io.github.nomisrev.service.RegisterUser import io.github.nomisrev.withServer import io.kotest.assertions.arrow.core.shouldBeRight -import io.kotest.matchers.shouldBe import io.ktor.client.call.body import io.ktor.client.plugins.resources.get import io.ktor.client.plugins.resources.post @@ -48,10 +47,12 @@ class ArticleRouteSpec : bearerAuth(token.value) } - response.status shouldBe HttpStatusCode.OK - response.body>().article.articles.toSet() shouldBe - emptySet() - response.body>().article.articlesCount shouldBe 0 + assert(response.status == HttpStatusCode.OK) + assert( + response.body>().article.articles == + emptyList
() + ) + assert(response.body>().article.articlesCount == 0) } } @@ -63,10 +64,12 @@ class ArticleRouteSpec : contentType(ContentType.Application.Json) } - response.status shouldBe HttpStatusCode.OK - response.body>().article.articles.toSet() shouldBe - emptySet() - response.body>().article.articlesCount shouldBe 0 + assert(response.status == HttpStatusCode.OK) + assert( + response.body>().article.articles == + emptyList
() + ) + assert(response.body>().article.articlesCount == 0) } } @@ -78,9 +81,11 @@ class ArticleRouteSpec : contentType(ContentType.Application.Json) } - response.status shouldBe HttpStatusCode.UnprocessableEntity - response.body().errors.body shouldBe - listOf("feed offset: too small, minimum is $MIN_FEED_OFFSET, and found -1") + assert(response.status == HttpStatusCode.UnprocessableEntity) + assert( + response.body().errors.body == + listOf("feed offset: too small, minimum is $MIN_FEED_OFFSET, and found -1") + ) } } @@ -92,9 +97,11 @@ class ArticleRouteSpec : contentType(ContentType.Application.Json) } - response.status shouldBe HttpStatusCode.UnprocessableEntity - response.body().errors.body shouldBe - listOf("feed limit: too small, minimum is $MIN_FEED_LIMIT, and found 0") + assert(response.status == HttpStatusCode.UnprocessableEntity) + assert( + response.body().errors.body == + listOf("feed limit: too small, minimum is $MIN_FEED_LIMIT, and found 0") + ) } } @@ -106,12 +113,14 @@ class ArticleRouteSpec : contentType(ContentType.Application.Json) } - response.status shouldBe HttpStatusCode.UnprocessableEntity - response.body().errors.body shouldBe - listOf( - "feed offset: too small, minimum is $MIN_FEED_OFFSET, and found -1", - "feed limit: too small, minimum is $MIN_FEED_LIMIT, and found 0" - ) + assert(response.status == HttpStatusCode.UnprocessableEntity) + assert( + response.body().errors.body == + listOf( + "feed offset: too small, minimum is $MIN_FEED_OFFSET, and found -1", + "feed limit: too small, minimum is $MIN_FEED_LIMIT, and found 0" + ) + ) } } @@ -124,15 +133,15 @@ class ArticleRouteSpec : setBody(ArticleWrapper(NewArticle(title, description, body, tags.toList()))) } - response.status shouldBe HttpStatusCode.Created + assert(response.status == HttpStatusCode.Created) with(response.body()) { - title shouldBe title - description shouldBe description - body shouldBe body - favoritesCount shouldBe 0 - favorited shouldBe false - author.username shouldBe username - tagList.toSet() shouldBe tags + assert(this.title == title) + assert(this.description == description) + assert(this.body == body) + assert(this.favoritesCount == 0L) + assert(this.favorited == false) + assert(this.author.username == username) + assert(this.tagList.toSet() == tags) } } } @@ -146,15 +155,15 @@ class ArticleRouteSpec : setBody(ArticleWrapper(NewArticle(title, description, body, emptyList()))) } - response.status shouldBe HttpStatusCode.Created + assert(response.status == HttpStatusCode.Created) with(response.body()) { - title shouldBe title - description shouldBe description - body shouldBe body - favoritesCount shouldBe 0 - favorited shouldBe false - author.username shouldBe username - tagList.size shouldBe 0 + assert(this.title == title) + assert(this.description == description) + assert(this.body == body) + assert(this.favoritesCount == 0L) + assert(this.favorited == false) + assert(this.author.username == username) + assert(this.tagList.size == 0) } } } @@ -168,7 +177,7 @@ class ArticleRouteSpec : setBody(ArticleWrapper(NewArticle(title, description, "", emptyList()))) } - response.status shouldBe HttpStatusCode.UnprocessableEntity + assert(response.status == HttpStatusCode.UnprocessableEntity) } } @@ -181,7 +190,7 @@ class ArticleRouteSpec : setBody(ArticleWrapper(NewArticle(title, "", body, emptyList()))) } - response.status shouldBe HttpStatusCode.UnprocessableEntity + assert(response.status == HttpStatusCode.UnprocessableEntity) } } @@ -194,7 +203,7 @@ class ArticleRouteSpec : setBody(ArticleWrapper(NewArticle("", description, body, emptyList()))) } - response.status shouldBe HttpStatusCode.UnprocessableEntity + assert(response.status == HttpStatusCode.UnprocessableEntity) } } @@ -206,7 +215,7 @@ class ArticleRouteSpec : setBody(ArticleWrapper(NewArticle(title, description, body, emptyList()))) } - response.status shouldBe HttpStatusCode.Unauthorized + assert(response.status == HttpStatusCode.Unauthorized) } } }) diff --git a/src/test/kotlin/io/github/nomisrev/routes/ArticlesRouteSpec.kt b/src/test/kotlin/io/github/nomisrev/routes/ArticlesRouteSpec.kt index 47e1e75..9de77e6 100644 --- a/src/test/kotlin/io/github/nomisrev/routes/ArticlesRouteSpec.kt +++ b/src/test/kotlin/io/github/nomisrev/routes/ArticlesRouteSpec.kt @@ -10,7 +10,6 @@ import io.github.nomisrev.withServer import io.kotest.assertions.arrow.core.shouldBeRight import io.kotest.assertions.arrow.core.shouldBeSome import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.shouldBe import io.ktor.client.call.body import io.ktor.client.plugins.resources.get import io.ktor.http.HttpStatusCode @@ -31,9 +30,10 @@ class ArticlesRouteSpec : withServer { val response = get(ArticlesResource.Slug(slug = "slug")) - response.status shouldBe HttpStatusCode.UnprocessableEntity - response.body().errors.body shouldBe - listOf("Article by slug slug not found") + assert(response.status == HttpStatusCode.UnprocessableEntity) + assert( + response.body().errors.body == listOf("Article by slug slug not found") + ) } } @@ -55,8 +55,8 @@ class ArticlesRouteSpec : val response = get(ArticlesResource.Slug(slug = article.slug)) - response.status shouldBe HttpStatusCode.OK - response.body().article shouldBe article + assert(response.status == HttpStatusCode.OK) + assert(response.body().article == article) } } }) diff --git a/src/test/kotlin/io/github/nomisrev/routes/TagRouteSpec.kt b/src/test/kotlin/io/github/nomisrev/routes/TagRouteSpec.kt index 27abde6..ddd8d2f 100644 --- a/src/test/kotlin/io/github/nomisrev/routes/TagRouteSpec.kt +++ b/src/test/kotlin/io/github/nomisrev/routes/TagRouteSpec.kt @@ -10,8 +10,6 @@ import io.github.nomisrev.withServer import io.kotest.assertions.arrow.core.shouldBeRight import io.kotest.assertions.arrow.core.shouldBeSome import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.collections.shouldHaveSize -import io.kotest.matchers.shouldBe import io.ktor.client.call.body import io.ktor.client.plugins.resources.get import io.ktor.http.ContentType @@ -34,8 +32,8 @@ class TagRouteSpec : withServer { val response = get(TagsResource()) { contentType(ContentType.Application.Json) } - response.status shouldBe HttpStatusCode.OK - response.body().tags.toSet() shouldBe emptySet() + assert(response.status == HttpStatusCode.OK) + assert(response.body().tags == emptyList()) } } @@ -53,11 +51,10 @@ class TagRouteSpec : CreateArticle(UserId(userId), validTitle, validDescription, validBody, validTags) ) .shouldBeRight() - val response = get(TagsResource()) { contentType(ContentType.Application.Json) } - response.status shouldBe HttpStatusCode.OK - response.body().tags shouldHaveSize 4 + assert(response.status == HttpStatusCode.OK) + assert(response.body().tags.size == 4) } } }) diff --git a/src/test/kotlin/io/github/nomisrev/routes/UserRouteSpec.kt b/src/test/kotlin/io/github/nomisrev/routes/UserRouteSpec.kt index bbfadb6..846224d 100644 --- a/src/test/kotlin/io/github/nomisrev/routes/UserRouteSpec.kt +++ b/src/test/kotlin/io/github/nomisrev/routes/UserRouteSpec.kt @@ -4,7 +4,6 @@ import io.github.nomisrev.service.RegisterUser import io.github.nomisrev.withServer import io.kotest.assertions.arrow.core.shouldBeRight import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.shouldBe import io.ktor.client.call.body import io.ktor.client.plugins.resources.get import io.ktor.client.plugins.resources.post @@ -29,12 +28,12 @@ class UserRouteSpec : setBody(UserWrapper(NewUser(validUsername, validEmail, validPw))) } - response.status shouldBe HttpStatusCode.Created + assert(response.status == HttpStatusCode.Created) with(response.body>().user) { - username shouldBe validUsername - email shouldBe validEmail - bio shouldBe "" - image shouldBe "" + assert(username == validUsername) + assert(email == validEmail) + assert(bio == "") + assert(image == "") } } } @@ -51,12 +50,12 @@ class UserRouteSpec : setBody(UserWrapper(LoginUser(validEmail, validPw))) } - response.status shouldBe HttpStatusCode.OK + assert(response.status == HttpStatusCode.OK) with(response.body>().user) { - username shouldBe validUsername - email shouldBe validEmail - bio shouldBe "" - image shouldBe "" + assert(username == validUsername) + assert(email == validEmail) + assert(bio == "") + assert(image == "") } } } @@ -70,13 +69,13 @@ class UserRouteSpec : val response = get(UserResource()) { bearerAuth(expected.value) } - response.status shouldBe HttpStatusCode.OK + assert(response.status == HttpStatusCode.OK) with(response.body>().user) { - username shouldBe validUsername - email shouldBe validEmail - token shouldBe expected.value - bio shouldBe "" - image shouldBe "" + assert(username == validUsername) + assert(email == validEmail) + assert(token == expected.value) + assert(bio == "") + assert(image == "") } } } @@ -96,13 +95,13 @@ class UserRouteSpec : setBody(UserWrapper(UpdateUser(username = newUsername))) } - response.status shouldBe HttpStatusCode.OK + assert(response.status == HttpStatusCode.OK) with(response.body>().user) { - username shouldBe newUsername - email shouldBe validEmail - token shouldBe expected.value - bio shouldBe "" - image shouldBe "" + assert(username == newUsername) + assert(email == validEmail) + assert(token == expected.value) + assert(bio == "") + assert(image == "") } } } @@ -113,18 +112,20 @@ class UserRouteSpec : dependencies.userService .register(RegisterUser(validUsername, validEmail, validPw)) .shouldBeRight() - val inalidEmail = "invalidEmail" + val invalidEmail = "invalidEmail" val response = put(UserResource()) { bearerAuth(token.value) contentType(ContentType.Application.Json) - setBody(UserWrapper(UpdateUser(email = inalidEmail))) + setBody(UserWrapper(UpdateUser(email = invalidEmail))) } - response.status shouldBe HttpStatusCode.UnprocessableEntity - response.body().errors.body shouldBe - listOf("email: 'invalidEmail' is invalid email") + assert(response.status == HttpStatusCode.UnprocessableEntity) + assert( + response.body().errors.body == + listOf("email: 'invalidEmail' is invalid email") + ) } } }) diff --git a/src/test/kotlin/io/github/nomisrev/service/ArticleServiceSpec.kt b/src/test/kotlin/io/github/nomisrev/service/ArticleServiceSpec.kt index 7fbc121..ab80c0e 100644 --- a/src/test/kotlin/io/github/nomisrev/service/ArticleServiceSpec.kt +++ b/src/test/kotlin/io/github/nomisrev/service/ArticleServiceSpec.kt @@ -4,12 +4,13 @@ import arrow.core.Either import arrow.core.flatMap import io.github.nefilim.kjwt.JWSHMAC512Algorithm import io.github.nefilim.kjwt.JWT -import io.github.nomisrev.* +import io.github.nomisrev.DomainError +import io.github.nomisrev.KotestProject +import io.github.nomisrev.SuspendFun import io.github.nomisrev.auth.JwtToken import io.github.nomisrev.repo.UserId import io.kotest.assertions.arrow.core.shouldBeRight import io.kotest.assertions.arrow.core.shouldBeSome -import io.kotest.matchers.ints.shouldBeExactly class ArticleServiceSpec : SuspendFun({ @@ -66,7 +67,7 @@ class ArticleServiceSpec : input = GetFeed(userId = UserId(kaavehId), limit = 20, offset = 0) ) - feed.articlesCount shouldBeExactly 0 + assert(feed.articlesCount == 0) } "get kaaveh's feed when he followed simon" { // Create users