diff --git a/src/test/kotlin/dev/usbharu/hideout/plugins/SecurityKtTest.kt b/src/test/kotlin/dev/usbharu/hideout/plugins/SecurityKtTest.kt deleted file mode 100644 index a47cc283..00000000 --- a/src/test/kotlin/dev/usbharu/hideout/plugins/SecurityKtTest.kt +++ /dev/null @@ -1,573 +0,0 @@ -package dev.usbharu.hideout.plugins - -import com.auth0.jwk.Jwk -import com.auth0.jwk.JwkProvider -import com.auth0.jwt.JWT -import com.auth0.jwt.algorithms.Algorithm -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.config.Config -import dev.usbharu.hideout.config.ConfigData -import dev.usbharu.hideout.domain.model.hideout.dto.JwtToken -import dev.usbharu.hideout.domain.model.hideout.entity.Jwt -import dev.usbharu.hideout.domain.model.hideout.entity.User -import dev.usbharu.hideout.domain.model.hideout.form.RefreshToken -import dev.usbharu.hideout.domain.model.hideout.form.UserLogin -import dev.usbharu.hideout.exception.InvalidRefreshTokenException -import dev.usbharu.hideout.exception.InvalidUsernameOrPasswordException -import dev.usbharu.hideout.query.UserQueryService -import dev.usbharu.hideout.routing.api.internal.v1.auth -import dev.usbharu.hideout.service.api.UserAuthApiService -import dev.usbharu.hideout.service.auth.JwtService -import dev.usbharu.hideout.service.core.MetaService -import dev.usbharu.hideout.service.user.UserAuthService -import dev.usbharu.hideout.util.Base64Util -import dev.usbharu.hideout.util.JsonWebKeyUtil -import io.ktor.client.request.* -import io.ktor.client.statement.* -import io.ktor.http.* -import io.ktor.server.config.* -import io.ktor.server.routing.* -import io.ktor.server.testing.* -import org.junit.jupiter.api.Test -import org.mockito.ArgumentMatchers.anyString -import org.mockito.kotlin.* -import java.security.KeyPairGenerator -import java.security.interfaces.RSAPrivateKey -import java.security.interfaces.RSAPublicKey -import java.time.Instant -import java.time.temporal.ChronoUnit -import java.util.* -import kotlin.test.assertEquals - -class SecurityKtTest { - @Test - fun `login ログイン出来るか`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - Config.configData = ConfigData(url = "http://example.com", objectMapper = jacksonObjectMapper()) - val jwtToken = JwtToken("Token", "RefreshToken") - val userAuthService = mock { - onBlocking { login(eq("testUser"), eq("password")) } doReturn jwtToken - } - val metaService = mock() - val userQueryService = mock { - onBlocking { findByNameAndDomain(eq("testUser"), eq("example.com")) } doReturn User.of( - id = 1L, - name = "testUser", - domain = "example.com", - screenName = "test", - description = "", - password = "hashedPassword", - inbox = "https://example.com/inbox", - outbox = "https://example.com/outbox", - url = "https://example.com/profile", - publicKey = "", - privateKey = "", - createdAt = Instant.now() - ) - } - val jwkProvider = mock() - application { - configureSerialization() - configureSecurity(jwkProvider, metaService) - routing { - auth(userAuthService) - } - } - - client.post("/login") { - contentType(ContentType.Application.Json) - setBody(Config.configData.objectMapper.writeValueAsString(UserLogin("testUser", "password"))) - }.apply { - assertEquals(HttpStatusCode.OK, call.response.status) - assertEquals(jwtToken, Config.configData.objectMapper.readValue(call.response.bodyAsText())) - } - } - - @Test - fun `login 存在しないユーザーのログインに失敗する`() { - testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - Config.configData = ConfigData(url = "http://example.com", objectMapper = jacksonObjectMapper()) - mock { - onBlocking { verifyAccount(anyString(), anyString()) }.doReturn(false) - } - val metaService = mock() - mock() - mock() - val jwkProvider = mock() - val userAuthApiService = mock { - onBlocking { login(anyString(), anyString()) } doThrow InvalidUsernameOrPasswordException() - } - application { - configureStatusPages() - configureSerialization() - configureSecurity(jwkProvider, metaService) - routing { - auth(userAuthApiService) - } - } - client.post("/login") { - contentType(ContentType.Application.Json) - setBody(Config.configData.objectMapper.writeValueAsString(UserLogin("InvalidTtestUser", "password"))) - }.apply { - assertEquals(HttpStatusCode.Unauthorized, call.response.status) - } - } - } - - @Test - fun `login 不正なパスワードのログインに失敗する`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - Config.configData = ConfigData(url = "http://example.com", objectMapper = jacksonObjectMapper()) - val metaService = mock() - val jwkProvider = mock() - val userAuthApiService = mock { - onBlocking { login(anyString(), eq("InvalidPassword")) } doThrow InvalidUsernameOrPasswordException() - } - application { - configureStatusPages() - configureSerialization() - configureSecurity(jwkProvider, metaService) - routing { - auth(userAuthApiService) - } - } - client.post("/login") { - contentType(ContentType.Application.Json) - setBody(Config.configData.objectMapper.writeValueAsString(UserLogin("TestUser", "InvalidPassword"))) - }.apply { - assertEquals(HttpStatusCode.Unauthorized, call.response.status) - } - } - - @Test - fun `auth-check Authorizedヘッダーが無いと401が帰ってくる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - Config.configData = ConfigData(url = "http://example.com", objectMapper = jacksonObjectMapper()) - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - auth(mock()) - } - } - client.get("/auth-check").apply { - assertEquals(HttpStatusCode.Unauthorized, call.response.status) - } - } - - @Test - fun `auth-check Authorizedヘッダーの形式が間違っていると401が帰ってくる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - Config.configData = ConfigData(url = "http://example.com", objectMapper = jacksonObjectMapper()) - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - auth(mock()) - } - } - client.get("/auth-check") { - header("Authorization", "Digest dsfjjhogalkjdfmlhaog") - }.apply { - assertEquals(HttpStatusCode.Unauthorized, call.response.status) - } - } - - @Test - fun `auth-check Authorizedヘッダーが空だと401が帰ってくる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - Config.configData = ConfigData(url = "http://example.com", objectMapper = jacksonObjectMapper()) - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - auth(mock()) - } - } - client.get("/auth-check") { - header("Authorization", "") - }.apply { - assertEquals(HttpStatusCode.Unauthorized, call.response.status) - } - } - - @Test - fun `auth-check AuthorizedヘッダーがBeararで空だと401が帰ってくる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - Config.configData = ConfigData(url = "http://example.com", objectMapper = jacksonObjectMapper()) - - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - auth(mock()) - } - } - client.get("/auth-check") { - header("Authorization", "Bearer ") - }.apply { - assertEquals(HttpStatusCode.Unauthorized, call.response.status) - } - } - - @Test - fun `auth-check 正当なJWTだとアクセスできる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val keyPairGenerator = KeyPairGenerator.getInstance("RSA") - keyPairGenerator.initialize(2048) - val keyPair = keyPairGenerator.generateKeyPair() - val rsaPublicKey = keyPair.public as RSAPublicKey - - Config.configData = ConfigData(url = "https://localhost", objectMapper = jacksonObjectMapper()) - - val now = Instant.now() - val kid = UUID.randomUUID() - val token = JWT.create() - .withAudience("${Config.configData.url}/users/test") - .withIssuer(Config.configData.url) - .withKeyId(kid.toString()) - .withClaim("uid", 123456L) - .withExpiresAt(now.plus(30, ChronoUnit.MINUTES)) - .sign(Algorithm.RSA256(rsaPublicKey, keyPair.private as RSAPrivateKey)) - val metaService = mock { - onBlocking { getJwtMeta() }.doReturn( - Jwt( - kid, - Base64Util.encode(keyPair.private.encoded), - Base64Util.encode(rsaPublicKey.encoded) - ) - ) - } - - val readValue = Config.configData.objectMapper.readerFor(Map::class.java) - .readValue?>( - JsonWebKeyUtil.publicKeyToJwk( - rsaPublicKey, - kid.toString() - ) - ) - val jwkProvider = mock { - onBlocking { get(anyString()) }.doReturn( - Jwk.fromValues( - (readValue["keys"] as List>)[0] - ) - ) - } - application { - configureSerialization() - configureSecurity(jwkProvider, metaService) - routing { - auth(mock()) - } - } - - client.get("/auth-check") { - header("Authorization", "Bearer $token") - }.apply { - assertEquals(HttpStatusCode.OK, call.response.status) - assertEquals("Hello 123456", call.response.bodyAsText()) - } - } - - @Test - fun `auth-check 期限切れのトークンではアクセスできない`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val keyPairGenerator = KeyPairGenerator.getInstance("RSA") - keyPairGenerator.initialize(2048) - val keyPair = keyPairGenerator.generateKeyPair() - val rsaPublicKey = keyPair.public as RSAPublicKey - - Config.configData = ConfigData(url = "https://localhost", objectMapper = jacksonObjectMapper()) - - val now = Instant.now() - val kid = UUID.randomUUID() - val token = JWT.create() - .withAudience("${Config.configData.url}/users/test") - .withIssuer(Config.configData.url) - .withKeyId(kid.toString()) - .withClaim("uid", 123345L) - .withExpiresAt(now.minus(30, ChronoUnit.MINUTES)) - .sign(Algorithm.RSA256(rsaPublicKey, keyPair.private as RSAPrivateKey)) - val metaService = mock { - onBlocking { getJwtMeta() }.doReturn( - Jwt( - kid, - Base64Util.encode(keyPair.private.encoded), - Base64Util.encode(rsaPublicKey.encoded) - ) - ) - } - - val readValue = Config.configData.objectMapper.readerFor(Map::class.java) - .readValue?>( - JsonWebKeyUtil.publicKeyToJwk( - rsaPublicKey, - kid.toString() - ) - ) - val jwkProvider = mock { - onBlocking { get(anyString()) }.doReturn( - Jwk.fromValues( - (readValue["keys"] as List>)[0] - ) - ) - } - application { - configureSerialization() - configureSecurity(jwkProvider, metaService) - routing { - auth(mock()) - } - } - client.get("/auth-check") { - header("Authorization", "Bearer $token") - }.apply { - assertEquals(HttpStatusCode.Unauthorized, call.response.status) - } - } - - @Test - fun `auth-check issuerが間違っているとアクセスできない`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val keyPairGenerator = KeyPairGenerator.getInstance("RSA") - keyPairGenerator.initialize(2048) - val keyPair = keyPairGenerator.generateKeyPair() - val rsaPublicKey = keyPair.public as RSAPublicKey - - Config.configData = ConfigData(url = "https://localhost", objectMapper = jacksonObjectMapper()) - - val now = Instant.now() - val kid = UUID.randomUUID() - val token = JWT.create() - .withAudience("${Config.configData.url}/users/test") - .withIssuer("https://example.com") - .withKeyId(kid.toString()) - .withClaim("uid", 12345L) - .withExpiresAt(now.plus(30, ChronoUnit.MINUTES)) - .sign(Algorithm.RSA256(rsaPublicKey, keyPair.private as RSAPrivateKey)) - val metaService = mock { - onBlocking { getJwtMeta() }.doReturn( - Jwt( - kid, - Base64Util.encode(keyPair.private.encoded), - Base64Util.encode(rsaPublicKey.encoded) - ) - ) - } - - val readValue = Config.configData.objectMapper.readerFor(Map::class.java) - .readValue?>( - JsonWebKeyUtil.publicKeyToJwk( - rsaPublicKey, - kid.toString() - ) - ) - val jwkProvider = mock { - onBlocking { get(anyString()) }.doReturn( - Jwk.fromValues( - (readValue["keys"] as List>)[0] - ) - ) - } - application { - configureSerialization() - configureSecurity(jwkProvider, metaService) - routing { - auth(mock()) - } - } - client.get("/auth-check") { - header("Authorization", "Bearer $token") - }.apply { - assertEquals(HttpStatusCode.Unauthorized, call.response.status) - } - } - - @Test - fun `auth-check usernameが空だと失敗する`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val keyPairGenerator = KeyPairGenerator.getInstance("RSA") - keyPairGenerator.initialize(2048) - val keyPair = keyPairGenerator.generateKeyPair() - val rsaPublicKey = keyPair.public as RSAPublicKey - - Config.configData = ConfigData(url = "https://localhost", objectMapper = jacksonObjectMapper()) - - val now = Instant.now() - val kid = UUID.randomUUID() - val token = JWT.create() - .withAudience("${Config.configData.url}/users/test") - .withIssuer(Config.configData.url) - .withKeyId(kid.toString()) - .withClaim("uid", null as Long?) - .withExpiresAt(now.plus(30, ChronoUnit.MINUTES)) - .sign(Algorithm.RSA256(rsaPublicKey, keyPair.private as RSAPrivateKey)) - val metaService = mock { - onBlocking { getJwtMeta() }.doReturn( - Jwt( - kid, - Base64Util.encode(keyPair.private.encoded), - Base64Util.encode(rsaPublicKey.encoded) - ) - ) - } - - val readValue = Config.configData.objectMapper.readerFor(Map::class.java) - .readValue?>( - JsonWebKeyUtil.publicKeyToJwk( - rsaPublicKey, - kid.toString() - ) - ) - val jwkProvider = mock { - onBlocking { get(anyString()) }.doReturn( - Jwk.fromValues( - (readValue["keys"] as List>)[0] - ) - ) - } - application { - configureSerialization() - configureSecurity(jwkProvider, metaService) - routing { - auth(mock()) - } - } - client.get("/auth-check") { - header("Authorization", "Bearer $token") - }.apply { - assertEquals(HttpStatusCode.Unauthorized, call.response.status) - } - } - - @Test - fun `auth-check usernameが存在しないと失敗する`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val keyPairGenerator = KeyPairGenerator.getInstance("RSA") - keyPairGenerator.initialize(2048) - val keyPair = keyPairGenerator.generateKeyPair() - val rsaPublicKey = keyPair.public as RSAPublicKey - - Config.configData = ConfigData(url = "https://localhost", objectMapper = jacksonObjectMapper()) - - val now = Instant.now() - val kid = UUID.randomUUID() - val token = JWT.create() - .withAudience("${Config.configData.url}/users/test") - .withIssuer(Config.configData.url) - .withKeyId(kid.toString()) - .withExpiresAt(now.plus(30, ChronoUnit.MINUTES)) - .sign(Algorithm.RSA256(rsaPublicKey, keyPair.private as RSAPrivateKey)) - val metaService = mock { - onBlocking { getJwtMeta() }.doReturn( - Jwt( - kid, - Base64Util.encode(keyPair.private.encoded), - Base64Util.encode(rsaPublicKey.encoded) - ) - ) - } - - val readValue = Config.configData.objectMapper.readerFor(Map::class.java) - .readValue?>( - JsonWebKeyUtil.publicKeyToJwk( - rsaPublicKey, - kid.toString() - ) - ) - val jwkProvider = mock { - onBlocking { get(anyString()) }.doReturn( - Jwk.fromValues( - (readValue["keys"] as List>)[0] - ) - ) - } - application { - configureSerialization() - configureSecurity(jwkProvider, metaService) - routing { - auth(mock()) - } - } - client.get("/auth-check") { - header("Authorization", "Bearer $token") - }.apply { - assertEquals(HttpStatusCode.Unauthorized, call.response.status) - } - } - - @Test - fun `refresh-token リフレッシュトークンが正当だとトークンを発行する`() = testApplication { - Config.configData = ConfigData(url = "https://localhost", objectMapper = jacksonObjectMapper()) - environment { - config = ApplicationConfig("empty.conf") - } - val jwtService = mock { - onBlocking { refreshToken(any()) }.doReturn(JwtToken("token", "refreshToken2")) - } - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - auth(jwtService) - } - } - client.post("/refresh-token") { - header("Content-Type", "application/json") - setBody(Config.configData.objectMapper.writeValueAsString(RefreshToken("refreshToken"))) - }.apply { - assertEquals(HttpStatusCode.OK, call.response.status) - } - } - - @Test - fun `refresh-token リフレッシュトークンが不正だと失敗する`() = testApplication { - Config.configData = ConfigData(url = "https://localhost", objectMapper = jacksonObjectMapper()) - environment { - config = ApplicationConfig("empty.conf") - } - val jwtService = mock { - onBlocking { refreshToken(any()) } doThrow InvalidRefreshTokenException("Invalid Refresh Token") - } - application { - configureStatusPages() - configureSerialization() - configureSecurity(mock(), mock()) - routing { - auth(jwtService) - } - } - client.post("/refresh-token") { - header("Content-Type", "application/json") - setBody(Config.configData.objectMapper.writeValueAsString(RefreshToken("InvalidRefreshToken"))) - }.apply { - assertEquals(HttpStatusCode.BadRequest, call.response.status) - } - } -} diff --git a/src/test/kotlin/dev/usbharu/hideout/routing/activitypub/ContentTypeRouteSelectorTest.kt b/src/test/kotlin/dev/usbharu/hideout/routing/activitypub/ContentTypeRouteSelectorTest.kt deleted file mode 100644 index b10e2a07..00000000 --- a/src/test/kotlin/dev/usbharu/hideout/routing/activitypub/ContentTypeRouteSelectorTest.kt +++ /dev/null @@ -1,134 +0,0 @@ -package dev.usbharu.hideout.routing.activitypub - -import io.ktor.client.request.* -import io.ktor.client.statement.* -import io.ktor.http.* -import io.ktor.server.application.* -import io.ktor.server.config.* -import io.ktor.server.response.* -import io.ktor.server.routing.* -import io.ktor.server.testing.* -import org.junit.jupiter.api.Test -import kotlin.test.assertEquals - -class ContentTypeRouteSelectorTest { - @Test - fun `Content-Typeが一つでマッチする`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - application { - routing { - route("/test") { - createChild(ContentTypeRouteSelector(ContentType.Application.Json)).handle { - call.respondText("OK") - } - get { - call.respondText("NG") - } - } - } - } - - client.get("/test") { - accept(ContentType.Text.Html) - }.apply { - assertEquals("NG", bodyAsText()) - } - client.get("/test") { - accept(ContentType.Application.Json) - }.apply { - assertEquals("OK", bodyAsText()) - } - } - - @Test - fun `Content-Typeが一つのとき違うとマッチしない`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - application { - routing { - route("/test") { - createChild(ContentTypeRouteSelector(ContentType.Application.Json)).handle { - call.respondText("OK") - } - get { - call.respondText("NG") - } - } - } - } - - client.get("/test") { - accept(ContentType.Text.Html) - }.apply { - assertEquals("NG", bodyAsText()) - } - } - - @Test - fun `Content-Typeがからのときマッチしない`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - application { - routing { - route("/test") { - createChild(ContentTypeRouteSelector()).handle { - call.respondText("OK") - } - get { - call.respondText("NG") - } - } - } - } - - client.get("/test") { - accept(ContentType.Text.Html) - }.apply { - assertEquals("NG", bodyAsText()) - } - - client.get("/test").apply { - assertEquals("NG", bodyAsText()) - } - } - - @Test - fun `Content-Typeが複数指定されていてマッチする`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - application { - routing { - route("/test") { - createChild(ContentTypeRouteSelector(ContentType.Application.Json, ContentType.Text.Html)).handle { - call.respondText("OK") - } - get { - call.respondText("NG") - } - } - } - } - - client.get("/test") { - accept(ContentType.Text.Html) - }.apply { - assertEquals("OK", bodyAsText()) - } - - client.get("/test") { - accept(ContentType.Application.Json) - }.apply { - assertEquals("OK", bodyAsText()) - } - client.get("/test") { - accept(ContentType.Application.Xml) - }.apply { - assertEquals("NG", bodyAsText()) - } - } -} diff --git a/src/test/kotlin/dev/usbharu/hideout/routing/activitypub/InboxRoutingKtTest.kt b/src/test/kotlin/dev/usbharu/hideout/routing/activitypub/InboxRoutingKtTest.kt deleted file mode 100644 index 75a2c335..00000000 --- a/src/test/kotlin/dev/usbharu/hideout/routing/activitypub/InboxRoutingKtTest.kt +++ /dev/null @@ -1,104 +0,0 @@ -package dev.usbharu.hideout.routing.activitypub - -import dev.usbharu.hideout.exception.JsonParseException -import dev.usbharu.hideout.plugins.configureSerialization -import dev.usbharu.hideout.plugins.configureStatusPages -import dev.usbharu.hideout.service.ap.APService -import dev.usbharu.hideout.service.ap.APUserService -import dev.usbharu.hideout.service.auth.HttpSignatureVerifyService -import dev.usbharu.hideout.service.user.UserService -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.server.config.* -import io.ktor.server.routing.* -import io.ktor.server.testing.* -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Test -import org.mockito.kotlin.any -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.doThrow -import org.mockito.kotlin.mock - -class InboxRoutingKtTest { - @Test - fun `sharedInboxにGETしたら405が帰ってくる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - application { - configureSerialization() - routing { - inbox(mock(), mock()) - } - } - client.get("/inbox").let { - Assertions.assertEquals(HttpStatusCode.MethodNotAllowed, it.status) - } - } - - @Test - fun `sharedInboxに空のリクエストボディでPOSTしたら400が帰ってくる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val httpSignatureVerifyService = mock { - on { verify(any()) } doReturn true - } - val apService = mock { - on { parseActivity(any()) } doThrow JsonParseException() - } - mock() - mock() - application { - configureStatusPages() - configureSerialization() - routing { - inbox(httpSignatureVerifyService, apService) - } - } - client.post("/inbox").let { - Assertions.assertEquals(HttpStatusCode.BadRequest, it.status) - } - } - - @Test - fun `ユーザのinboxにGETしたら405が帰ってくる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - application { - configureSerialization() - routing { - inbox(mock(), mock()) - } - } - client.get("/users/test/inbox").let { - Assertions.assertEquals(HttpStatusCode.MethodNotAllowed, it.status) - } - } - - @Test - fun `ユーザーのinboxに空のリクエストボディでPOSTしたら400が帰ってくる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val httpSignatureVerifyService = mock { - on { verify(any()) } doReturn true - } - val apService = mock { - on { parseActivity(any()) } doThrow JsonParseException() - } - mock() - mock() - application { - configureStatusPages() - configureSerialization() - routing { - inbox(httpSignatureVerifyService, apService) - } - } - client.post("/users/test/inbox").let { - Assertions.assertEquals(HttpStatusCode.BadRequest, it.status) - } - } -} diff --git a/src/test/kotlin/dev/usbharu/hideout/routing/activitypub/UsersAPTest.kt b/src/test/kotlin/dev/usbharu/hideout/routing/activitypub/UsersAPTest.kt deleted file mode 100644 index 81b780c8..00000000 --- a/src/test/kotlin/dev/usbharu/hideout/routing/activitypub/UsersAPTest.kt +++ /dev/null @@ -1,202 +0,0 @@ -package dev.usbharu.hideout.routing.activitypub - -import com.fasterxml.jackson.annotation.JsonInclude -import com.fasterxml.jackson.annotation.JsonSetter -import com.fasterxml.jackson.annotation.Nulls -import com.fasterxml.jackson.databind.DeserializationFeature -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.domain.model.ap.Image -import dev.usbharu.hideout.domain.model.ap.Key -import dev.usbharu.hideout.domain.model.ap.Person -import dev.usbharu.hideout.domain.model.hideout.entity.User -import dev.usbharu.hideout.plugins.configureSerialization -import dev.usbharu.hideout.query.UserQueryService -import dev.usbharu.hideout.service.ap.APUserService -import dev.usbharu.hideout.util.HttpUtil.Activity -import dev.usbharu.hideout.util.HttpUtil.JsonLd -import io.ktor.client.request.* -import io.ktor.client.statement.* -import io.ktor.http.* -import io.ktor.server.config.* -import io.ktor.server.routing.* -import io.ktor.server.testing.* -import org.junit.jupiter.api.Test -import org.mockito.ArgumentMatchers.anyString -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq -import org.mockito.kotlin.mock -import utils.TestTransaction -import java.time.Instant -import kotlin.test.assertEquals -import kotlin.test.assertTrue - -class UsersAPTest { - - @Test() - fun `ユーザのURLにAcceptヘッダーをActivityにしてアクセスしたときPersonが返ってくる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val person = Person( - type = emptyList(), - name = "test", - id = "http://example.com/users/test", - preferredUsername = "test", - summary = "test user", - inbox = "http://example.com/users/test/inbox", - outbox = "http://example.com/users/test/outbox", - url = "http://example.com/users/test", - icon = Image( - type = emptyList(), - name = "http://example.com/users/test/icon.png", - mediaType = "image/png", - url = "http://example.com/users/test/icon.png" - ), - publicKey = Key( - type = emptyList(), - name = "Public Key", - id = "http://example.com/users/test#pubkey", - owner = "https://example.com/users/test", - publicKeyPem = "-----BEGIN PUBLIC KEY-----\n\n-----END PUBLIC KEY-----" - ) - ) - person.context = listOf("https://www.w3.org/ns/activitystreams") - - val apUserService = mock { - onBlocking { getPersonByName(anyString()) } doReturn person - } - - application { - configureSerialization() - routing { - usersAP(apUserService, mock(), mock(), TestTransaction) - } - } - client.get("/users/test") { - accept(ContentType.Application.Activity) - }.let { - val objectMapper = jacksonObjectMapper().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) - .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - objectMapper.configOverride(List::class.java).setSetterInfo( - JsonSetter.Value.forValueNulls( - Nulls.AS_EMPTY - ) - ) - val actual = it.bodyAsText() - val readValue = objectMapper.readValue(actual) - assertEquals(person, readValue) - } - } - - // @Disabled - @Test() - fun `ユーザのURLにAcceptヘッダーをActivityとJson-LDにしてアクセスしたときPersonが返ってくる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val person = Person( - type = emptyList(), - name = "test", - id = "http://example.com/users/test", - preferredUsername = "test", - summary = "test user", - inbox = "http://example.com/users/test/inbox", - outbox = "http://example.com/users/test/outbox", - url = "http://example.com/users/test", - icon = Image( - type = emptyList(), - name = "http://example.com/users/test/icon.png", - mediaType = "image/png", - url = "http://example.com/users/test/icon.png" - ), - publicKey = Key( - type = emptyList(), - name = "Public Key", - id = "http://example.com/users/test#pubkey", - owner = "https://example.com/users/test", - publicKeyPem = "-----BEGIN PUBLIC KEY-----\n\n-----END PUBLIC KEY-----" - ) - ) - person.context = listOf("https://www.w3.org/ns/activitystreams") - - val apUserService = mock { - onBlocking { getPersonByName(anyString()) } doReturn person - } - - application { - configureSerialization() - routing { - usersAP(apUserService, mock(), mock(), TestTransaction) - } - } - client.get("/users/test") { - accept(ContentType.Application.JsonLd) - accept(ContentType.Application.Activity) - }.let { - val objectMapper = jacksonObjectMapper().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) - .setSerializationInclusion(JsonInclude.Include.NON_EMPTY) - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - objectMapper.configOverride(List::class.java).setSetterInfo( - JsonSetter.Value.forValueNulls( - Nulls.AS_EMPTY - ) - ) - val actual = it.bodyAsText() - val readValue = objectMapper.readValue(actual) - assertEquals(person, readValue) - } - } - - // @Disabled - @Test - fun contentType_Test() { - assertTrue(ContentType.Application.Activity.match("application/activity+json")) - val listOf = listOf(ContentType.Application.JsonLd, ContentType.Application.Activity) - assertTrue( - listOf.find { contentType -> - contentType.match("application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") - }.let { it != null } - ) - assertTrue( - ContentType.Application.JsonLd.match( - "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" - ) - ) - } - - @Test - fun ユーザーのURLにAcceptヘッダーをhtmlにしてアクセスしたときはただの文字を返す() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val userService = mock { - onBlocking { findByNameAndDomain(eq("test"), anyString()) } doReturn User.of( - 1L, - "test", - "example.com", - "test", - "", - "hashedPassword", - "https://example.com/inbox", - "https://example.com/outbox", - "https://example.com", - "-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----", - "-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----", - Instant.now() - ) - } - application { - routing { - usersAP(mock(), userService, mock(), TestTransaction) - } - } - client.get("/users/test") { - accept(ContentType.Text.Html) - }.let { - assertEquals(HttpStatusCode.OK, it.status) - assertTrue(it.contentType()?.match(ContentType.Text.Plain) == true) - } - } -} diff --git a/src/test/kotlin/dev/usbharu/hideout/routing/api/internal/v1/PostsTest.kt b/src/test/kotlin/dev/usbharu/hideout/routing/api/internal/v1/PostsTest.kt deleted file mode 100644 index 81f9e66c..00000000 --- a/src/test/kotlin/dev/usbharu/hideout/routing/api/internal/v1/PostsTest.kt +++ /dev/null @@ -1,734 +0,0 @@ -package dev.usbharu.hideout.routing.api.internal.v1 - -import com.auth0.jwt.interfaces.Claim -import com.auth0.jwt.interfaces.Payload -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.config.Config -import dev.usbharu.hideout.domain.model.hideout.dto.PostResponse -import dev.usbharu.hideout.domain.model.hideout.dto.UserResponse -import dev.usbharu.hideout.domain.model.hideout.entity.Visibility -import dev.usbharu.hideout.plugins.TOKEN_AUTH -import dev.usbharu.hideout.plugins.configureSecurity -import dev.usbharu.hideout.plugins.configureSerialization -import dev.usbharu.hideout.service.api.PostApiService -import io.ktor.client.request.* -import io.ktor.client.statement.* -import io.ktor.http.* -import io.ktor.server.auth.* -import io.ktor.server.auth.jwt.* -import io.ktor.server.config.* -import io.ktor.server.routing.* -import io.ktor.server.testing.* -import org.junit.jupiter.api.Test -import org.mockito.kotlin.* -import utils.JsonObjectMapper -import java.time.Instant -import kotlin.test.assertContentEquals -import kotlin.test.assertEquals - -class PostsTest { - - @Test - fun 認証情報無しでpostsにGETしたらPUBLICな投稿一覧が返ってくる() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val user = UserResponse( - id = "54321", - name = "user1", - domain = "example.com", - screenName = "user 1", - description = "Test user", - url = "https://example.com/users/54321", - createdAt = Instant.now().toEpochMilli() - ) - val posts = listOf( - PostResponse( - id = "12345", - user = user, - text = "test1", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/1" - ), - PostResponse( - id = "123456", - user = user, - text = "test2", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/2" - ) - ) - val postService = mock { - onBlocking { - getAll( - since = anyOrNull(), - until = anyOrNull(), - minId = anyOrNull(), - maxId = anyOrNull(), - limit = anyOrNull(), - userId = isNull() - ) - } doReturn posts - } - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - route("/api/internal/v1") { - posts(postService) - } - } - } - - client.get("/api/internal/v1/posts").apply { - assertEquals(HttpStatusCode.OK, status) - assertContentEquals(posts, JsonObjectMapper.objectMapper.readValue(bodyAsText())) - } - } - - @Test - fun 認証情報ありでpostsにGETすると権限のある投稿が返ってくる() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val claim = mock { - on { asLong() } doReturn 1234 - } - val payload = mock { - on { getClaim(eq("uid")) } doReturn claim - } - val user = UserResponse( - id = "54321", - name = "user1", - domain = "example.com", - screenName = "user 1", - description = "Test user", - url = "https://example.com/users/54321", - createdAt = Instant.now().toEpochMilli() - ) - val posts = listOf( - PostResponse( - id = "12345", - user = user, - text = "test1", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/1" - ), - PostResponse( - id = "123456", - user = user, - text = "test2", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/2" - ), - PostResponse( - id = "1234567", - user = user, - text = "Followers only", - visibility = Visibility.FOLLOWERS, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/3" - ) - ) - - val postService = mock { - onBlocking { - getAll( - since = anyOrNull(), - until = anyOrNull(), - minId = anyOrNull(), - maxId = anyOrNull(), - limit = anyOrNull(), - userId = isNotNull() - ) - } doReturn posts - } - application { - authentication { - bearer(TOKEN_AUTH) { - authenticate { - JWTPrincipal(payload) - } - } - } - configureSerialization() - routing { - route("/api/internal/v1") { - posts(postService) - } - } - } - client.get("/api/internal/v1/posts") { - header("Authorization", "Bearer asdkaf") - }.apply { - assertEquals(HttpStatusCode.OK, status) - } - } - - @Test - fun `posts id にGETしたらPUBLICな投稿を取得できる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val user = UserResponse( - id = "54321", - name = "user1", - domain = "example.com", - screenName = "user 1", - description = "Test user", - url = "https://example.com/users/54321", - createdAt = Instant.now().toEpochMilli() - ) - val post = PostResponse( - id = "12345", - user = user, - text = "aaa", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/1" - ) - val postService = mock { - onBlocking { getById(any(), anyOrNull()) } doReturn post - } - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - route("/api/internal/v1") { - posts(postService) - } - } - } - client.get("/api/internal/v1/posts/1").apply { - assertEquals(HttpStatusCode.OK, status) - assertEquals(post, JsonObjectMapper.objectMapper.readValue(bodyAsText())) - } - } - - @Test - fun `認証情報ありでposts id にGETしたら権限のある投稿を取得できる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val post = PostResponse( - "12345", - UserResponse( - id = "54321", - name = "user1", - domain = "example.com", - screenName = "user 1", - description = "Test user", - url = "https://example.com/users/54321", - createdAt = Instant.now().toEpochMilli() - ), - text = "aaa", - visibility = Visibility.FOLLOWERS, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/1" - ) - val postService = mock { - onBlocking { getById(any(), isNotNull()) } doReturn post - } - val claim = mock { - on { asLong() } doReturn 1234 - } - val payload = mock { - on { getClaim(eq("uid")) } doReturn claim - } - application { - configureSerialization() - authentication { - bearer(TOKEN_AUTH) { - authenticate { - JWTPrincipal(payload) - } - } - } - routing { - route("/api/internal/v1") { - posts(postService) - } - } - } - client.get("/api/internal/v1/posts/1") { - header("Authorization", "Bearer asdkaf") - }.apply { - assertEquals(HttpStatusCode.OK, status) - assertEquals(post, JsonObjectMapper.objectMapper.readValue(bodyAsText())) - } - } - - @Test - fun `posts-post postsにpostしたら投稿できる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val claim = mock { - on { asLong() } doReturn 1234 - } - val payload = mock { - on { getClaim(eq("uid")) } doReturn claim - } - val postService = mock { - onBlocking { createPost(any(), any()) } doAnswer { - val argument = it.getArgument(0) - val userId = it.getArgument(1) - PostResponse( - id = "123", - user = UserResponse( - id = "54321", - name = "user1", - domain = "example.com", - screenName = "user 1", - description = "Test user", - url = "https://example.com/users/54321", - createdAt = Instant.now().toEpochMilli() - ), - overview = null, - text = argument.text, - createdAt = Instant.now().toEpochMilli(), - visibility = Visibility.PUBLIC, - url = "https://example.com" - ) - } - } - application { - authentication { - bearer(TOKEN_AUTH) { - authenticate { - println("aaaaaaaaaaaa") - JWTPrincipal(payload) - } - } - } - routing { - route("/api/internal/v1") { - posts(postService) - } - } - configureSerialization() - } - - val post = dev.usbharu.hideout.domain.model.hideout.form.Post("test") - client.post("/api/internal/v1/posts") { - header("Authorization", "Bearer asdkaf") - contentType(ContentType.Application.Json) - setBody(Config.configData.objectMapper.writeValueAsString(post)) - }.apply { - assertEquals(HttpStatusCode.OK, status) - assertEquals("https://example.com", headers["Location"]) - } - argumentCaptor { - verify(postService).createPost(capture(), any()) - assertEquals(dev.usbharu.hideout.domain.model.hideout.form.Post("test"), firstValue) - } - } - - @Test - fun `users userId postsにGETしたらユーザーのPUBLICな投稿一覧を取得できる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val user = UserResponse( - id = "54321", - name = "user1", - domain = "example.com", - screenName = "user 1", - description = "Test user", - url = "https://example.com/users/54321", - createdAt = Instant.now().toEpochMilli() - ) - val posts = listOf( - PostResponse( - id = "12345", - user = user, - text = "test1", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/1" - ), - PostResponse( - id = "123456", - user = user, - text = "test2", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/2" - ) - ) - val postService = mock { - onBlocking { - getByUser( - nameOrId = any(), - since = anyOrNull(), - until = anyOrNull(), - minId = anyOrNull(), - maxId = anyOrNull(), - limit = anyOrNull(), - userId = anyOrNull() - ) - } doReturn posts - } - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - route("/api/internal/v1") { - posts(postService) - } - } - } - - client.get("/api/internal/v1/users/1/posts").apply { - assertEquals(HttpStatusCode.OK, status) - assertEquals(posts, JsonObjectMapper.objectMapper.readValue(bodyAsText())) - } - } - - @Test - fun `users username postsにGETしたらユーザーのPUBLICな投稿一覧を取得できる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val user = UserResponse( - id = "54321", - name = "user1", - domain = "example.com", - screenName = "user 1", - description = "Test user", - url = "https://example.com/users/54321", - createdAt = Instant.now().toEpochMilli() - ) - val posts = listOf( - PostResponse( - id = "12345", - user = user, - text = "test1", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/1" - ), - PostResponse( - id = "123456", - user = user, - text = "test2", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/2" - ) - ) - val postService = mock { - onBlocking { - getByUser( - nameOrId = eq("test1"), - since = anyOrNull(), - until = anyOrNull(), - minId = anyOrNull(), - maxId = anyOrNull(), - limit = anyOrNull(), - userId = anyOrNull() - ) - } doReturn posts - } - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - route("/api/internal/v1") { - posts(postService) - } - } - } - - client.get("/api/internal/v1/users/test1/posts").apply { - assertEquals(HttpStatusCode.OK, status) - assertEquals(posts, JsonObjectMapper.objectMapper.readValue(bodyAsText())) - } - } - - @Test - fun `users username@domain postsにGETしたらユーザーのPUBLICな投稿一覧を取得できる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val user = UserResponse( - id = "54321", - name = "user1", - domain = "example.com", - screenName = "user 1", - description = "Test user", - url = "https://example.com/users/54321", - createdAt = Instant.now().toEpochMilli() - ) - val posts = listOf( - PostResponse( - id = "12345", - user = user, - text = "test1", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/1" - ), - PostResponse( - id = "123456", - user = user, - text = "test2", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/2" - ) - ) - val postService = mock { - onBlocking { - getByUser( - nameOrId = eq("test1@example.com"), - since = anyOrNull(), - until = anyOrNull(), - minId = anyOrNull(), - maxId = anyOrNull(), - limit = anyOrNull(), - userId = anyOrNull() - ) - } doReturn posts - } - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - route("/api/internal/v1") { - posts(postService) - } - } - } - - client.get("/api/internal/v1/users/test1@example.com/posts").apply { - assertEquals(HttpStatusCode.OK, status) - assertEquals(posts, JsonObjectMapper.objectMapper.readValue(bodyAsText())) - } - } - - @Test - fun `users @username@domain postsにGETしたらユーザーのPUBLICな投稿一覧を取得できる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val user = UserResponse( - id = "54321", - name = "user1", - domain = "example.com", - screenName = "user 1", - description = "Test user", - url = "https://example.com/users/54321", - createdAt = Instant.now().toEpochMilli() - ) - val posts = listOf( - PostResponse( - id = "12345", - user = user, - text = "test1", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/1" - ), - PostResponse( - id = "123456", - user = user, - text = "test2", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/2" - ) - ) - val postService = mock { - onBlocking { - getByUser( - nameOrId = eq("@test1@example.com"), - since = anyOrNull(), - until = anyOrNull(), - minId = anyOrNull(), - maxId = anyOrNull(), - limit = anyOrNull(), - userId = anyOrNull() - ) - } doReturn posts - } - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - route("/api/internal/v1") { - posts(postService) - } - } - } - - client.get("/api/internal/v1/users/@test1@example.com/posts").apply { - assertEquals(HttpStatusCode.OK, status) - assertEquals(posts, JsonObjectMapper.objectMapper.readValue(bodyAsText())) - } - } - - @Test - fun `users name posts id にGETしたらPUBLICな投稿を取得できる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val post = PostResponse( - id = "123456", - user = UserResponse( - id = "54321", - name = "user1", - domain = "example.com", - screenName = "user 1", - description = "Test user", - url = "https://example.com/users/54321", - createdAt = Instant.now().toEpochMilli() - ), - text = "test2", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/2" - ) - val postService = mock { - onBlocking { getById(eq(12345L), anyOrNull()) } doReturn post - } - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - route("/api/internal/v1") { - posts(postService) - } - } - } - - client.get("/api/internal/v1/users/test/posts/12345").apply { - assertEquals(HttpStatusCode.OK, status) - assertEquals(post, JsonObjectMapper.objectMapper.readValue(bodyAsText())) - } - } - - @Test - fun `users id posts id にGETしたらPUBLICな投稿を取得できる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val post = PostResponse( - id = "123456", - user = UserResponse( - id = "54321", - name = "user1", - domain = "example.com", - screenName = "user 1", - description = "Test user", - url = "https://example.com/users/54321", - createdAt = Instant.now().toEpochMilli() - ), - text = "test2", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/2" - ) - val postService = mock { - onBlocking { getById(eq(12345L), anyOrNull()) } doReturn post - } - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - route("/api/internal/v1") { - posts(postService) - } - } - } - - client.get("/api/internal/v1/users/1/posts/12345").apply { - assertEquals(HttpStatusCode.OK, status) - assertEquals(post, JsonObjectMapper.objectMapper.readValue(bodyAsText())) - } - } - - @Test - fun `users name posts id にGETしたらUserIdが間違っててもPUBLICな投稿を取得できる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val post = PostResponse( - id = "123456", - user = UserResponse( - id = "54321", - name = "user1", - domain = "example.com", - screenName = "user 1", - description = "Test user", - url = "https://example.com/users/54321", - createdAt = Instant.now().toEpochMilli() - ), - text = "test2", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/2" - ) - val postService = mock { - onBlocking { getById(eq(12345L), anyOrNull()) } doReturn post - } - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - route("/api/internal/v1") { - posts(postService) - } - } - } - - client.get("/api/internal/v1/users/423827849732847/posts/12345").apply { - assertEquals(HttpStatusCode.OK, status) - assertEquals(post, JsonObjectMapper.objectMapper.readValue(bodyAsText())) - } - } - - @Test - fun `users name posts id にGETしたらuserNameが間違っててもPUBLICな投稿を取得できる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val post = PostResponse( - id = "123456", - user = UserResponse( - id = "54321", - name = "user1", - domain = "example.com", - screenName = "user 1", - description = "Test user", - url = "https://example.com/users/54321", - createdAt = Instant.now().toEpochMilli() - ), - text = "test2", - visibility = Visibility.PUBLIC, - createdAt = Instant.now().toEpochMilli(), - url = "https://example.com/posts/2" - ) - val postService = mock { - onBlocking { getById(eq(12345L), anyOrNull()) } doReturn post - } - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - route("/api/internal/v1") { - posts(postService) - } - } - } - - client.get("/api/internal/v1/users/invalidUserName/posts/12345").apply { - assertEquals(HttpStatusCode.OK, status) - assertEquals(post, JsonObjectMapper.objectMapper.readValue(bodyAsText())) - } - } -} diff --git a/src/test/kotlin/dev/usbharu/hideout/routing/api/internal/v1/UsersTest.kt b/src/test/kotlin/dev/usbharu/hideout/routing/api/internal/v1/UsersTest.kt deleted file mode 100644 index 9bc0db7d..00000000 --- a/src/test/kotlin/dev/usbharu/hideout/routing/api/internal/v1/UsersTest.kt +++ /dev/null @@ -1,692 +0,0 @@ -package dev.usbharu.hideout.routing.api.internal.v1 - -import com.auth0.jwt.interfaces.Claim -import com.auth0.jwt.interfaces.Payload -import com.fasterxml.jackson.module.kotlin.readValue -import dev.usbharu.hideout.config.Config -import dev.usbharu.hideout.domain.model.Acct -import dev.usbharu.hideout.domain.model.hideout.dto.UserResponse -import dev.usbharu.hideout.domain.model.hideout.entity.User -import dev.usbharu.hideout.domain.model.hideout.form.UserCreate -import dev.usbharu.hideout.plugins.TOKEN_AUTH -import dev.usbharu.hideout.plugins.configureSecurity -import dev.usbharu.hideout.plugins.configureSerialization -import dev.usbharu.hideout.service.api.UserApiService -import dev.usbharu.hideout.service.user.UserService -import io.ktor.client.request.* -import io.ktor.client.statement.* -import io.ktor.http.* -import io.ktor.server.auth.* -import io.ktor.server.auth.jwt.* -import io.ktor.server.config.* -import io.ktor.server.routing.* -import io.ktor.server.testing.* -import org.junit.jupiter.api.Test -import org.mockito.kotlin.* -import utils.JsonObjectMapper -import java.time.Instant -import kotlin.test.assertEquals -@Suppress("LargeClass") -class UsersTest { - @Test - fun `users にGETするとユーザー一覧を取得できる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - - val users = listOf( - UserResponse( - "12345", - "test1", - "example.com", - "test", - "", - "https://example.com/test", - Instant.now().toEpochMilli() - ), - UserResponse( - "12343", - "tes2", - "example.com", - "test", - "", - "https://example.com/tes2", - Instant.now().toEpochMilli() - ), - ) - val userService = mock { - onBlocking { findAll(anyOrNull(), anyOrNull()) } doReturn users - } - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - route("/api/internal/v1") { - users(mock(), userService) - } - } - } - client.get("/api/internal/v1/users").apply { - assertEquals(HttpStatusCode.OK, status) - assertEquals(users, JsonObjectMapper.objectMapper.readValue(bodyAsText())) - } - } - - @Test - fun `users にPOSTすると新規ユーザー作成ができる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val userCreateDto = UserCreate("test", "XXXXXXX") - val userService = mock { - onBlocking { usernameAlreadyUse(any()) } doReturn false - onBlocking { createLocalUser(any()) } doReturn User.of( - id = 12345, - name = "test", - domain = "example.com", - screenName = "testUser", - description = "test user", - password = "XXXXXXX", - inbox = "https://example.com/inbox", - outbox = "https://example.com/outbox", - url = "https://example.com", - publicKey = "-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----", - privateKey = "-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----", - createdAt = Instant.now() - ) - } - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - route("/api/internal/v1") { - users(userService, mock()) - } - } - } - - client.post("/api/internal/v1/users") { - contentType(ContentType.Application.Json) - setBody(JsonObjectMapper.objectMapper.writeValueAsString(userCreateDto)) - }.apply { - assertEquals(HttpStatusCode.Created, status) - assertEquals( - "${Config.configData.url}/api/internal/v1/users/${userCreateDto.username}", - headers["Location"] - ) - } - } - - @Test - fun `users 既にユーザー名が使用されているときはBadRequestが帰ってくる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val userCreateDto = UserCreate("test", "XXXXXXX") - val userService = mock { - onBlocking { usernameAlreadyUse(any()) } doReturn true - } - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - route("/api/internal/v1") { - users(userService, mock()) - } - } - } - - client.post("/api/internal/v1/users") { - contentType(ContentType.Application.Json) - setBody(JsonObjectMapper.objectMapper.writeValueAsString(userCreateDto)) - }.apply { - assertEquals(HttpStatusCode.BadRequest, status) - } - } - - @Test - fun `users name にGETしたらユーザーを取得できる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val userResponse = UserResponse( - "1234", - "test1", - "example.com", - "test", - "test User", - "https://example.com/test", - Instant.now().toEpochMilli() - ) - val userApiService = mock { - onBlocking { findByAcct(any()) } doReturn userResponse - } - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - route("/api/internal/v1") { - users(mock(), userApiService) - } - } - } - - client.get("/api/internal/v1/users/test1").apply { - assertEquals(HttpStatusCode.OK, status) - assertEquals(userResponse, JsonObjectMapper.objectMapper.readValue(bodyAsText())) - } - } - - @Test - fun `users id にGETしたらユーザーを取得できる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val userResponse = UserResponse( - "1234", - "test1", - "example.com", - "test", - "test User", - "https://example.com/test", - Instant.now().toEpochMilli() - ) - val userApiService = mock { - onBlocking { findById(any()) } doReturn userResponse - } - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - route("/api/internal/v1") { - users(mock(), userApiService) - } - } - } - - client.get("/api/internal/v1/users/1234").apply { - assertEquals(HttpStatusCode.OK, status) - assertEquals(userResponse, JsonObjectMapper.objectMapper.readValue(bodyAsText())) - } - } - - @Test - fun `users name@domain にGETしたらユーザーを取得できる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val userResponse = UserResponse( - "1234", - "test1", - "example.com", - "test", - "test User", - "https://example.com/test", - Instant.now().toEpochMilli() - ) - val userApiService = mock { - onBlocking { findByAcct(any()) } doReturn userResponse - } - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - route("/api/internal/v1") { - users(mock(), userApiService) - } - } - } - - client.get("/api/internal/v1/users/test1").apply { - assertEquals(HttpStatusCode.OK, status) - assertEquals(userResponse, JsonObjectMapper.objectMapper.readValue(bodyAsText())) - } - } - - @Test - fun `users @name@domain にGETしたらユーザーを取得できる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - val userResponse = UserResponse( - "1234", - "test1", - "example.com", - "test", - "test User", - "https://example.com/test", - Instant.now().toEpochMilli() - ) - val userApiService = mock { - onBlocking { findByAcct(any()) } doReturn userResponse - } - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - route("/api/internal/v1") { - users(mock(), userApiService) - } - } - } - - client.get("/api/internal/v1/users/test1").apply { - assertEquals(HttpStatusCode.OK, status) - assertEquals(userResponse, JsonObjectMapper.objectMapper.readValue(bodyAsText())) - } - } - - @Test - fun `users name followers にGETしたらフォロワー一覧を取得できる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - - val followers = listOf( - UserResponse( - "1235", - "follower1", - "example.com", - "test", - "test User", - "https://example.com/test", - Instant.now().toEpochMilli() - ), - UserResponse( - "1236", - "follower2", - "example.com", - "test", - "test User", - "https://example.com/test", - Instant.now().toEpochMilli() - ) - ) - val userApiService = mock { - onBlocking { findFollowersByAcct(any()) } doReturn followers - } - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - route("/api/internal/v1") { - users(mock(), userApiService) - } - } - } - - client.get("/api/internal/v1/users/test1/followers").apply { - assertEquals(HttpStatusCode.OK, status) - assertEquals(followers, JsonObjectMapper.objectMapper.readValue(bodyAsText())) - } - } - - @Test - fun `users name@domain followers にGETしたらフォロワー一覧を取得できる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - - val followers = listOf( - UserResponse( - "1235", - "follower1", - "example.com", - "test", - "test User", - "https://example.com/test", - Instant.now().toEpochMilli() - ), - UserResponse( - "1236", - "follower2", - "example.com", - "test", - "test User", - "https://example.com/test", - Instant.now().toEpochMilli() - ) - ) - val userApiService = mock { - onBlocking { findFollowersByAcct(any()) } doReturn followers - } - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - route("/api/internal/v1") { - users(mock(), userApiService) - } - } - } - - client.get("/api/internal/v1/users/@test1@example.com/followers").apply { - assertEquals(HttpStatusCode.OK, status) - assertEquals(followers, JsonObjectMapper.objectMapper.readValue(bodyAsText())) - } - } - - @Test - fun `users id followers にGETしたらフォロワー一覧を取得できる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - - val followers = listOf( - UserResponse( - "1235", - "follower1", - "example.com", - "test", - "test User", - "https://example.com/test", - Instant.now().toEpochMilli() - ), - UserResponse( - "1236", - "follower2", - "example.com", - "test", - "test User", - "https://example.com/test", - Instant.now().toEpochMilli() - ) - ) - val userApiService = mock { - onBlocking { findFollowers(any()) } doReturn followers - } - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - route("/api/internal/v1") { - users(mock(), userApiService) - } - } - } - - client.get("/api/internal/v1/users/1234/followers").apply { - assertEquals(HttpStatusCode.OK, status) - assertEquals(followers, JsonObjectMapper.objectMapper.readValue(bodyAsText())) - } - } - - @Test - fun `users name followers に認証情報ありでGETしたらフォローできる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - - val claim = mock { - on { asLong() } doReturn 1234 - } - val payload = mock { - on { getClaim(eq("uid")) } doReturn claim - } - - val userApiService = mock { - onBlocking { findByAcct(any()) } doReturn UserResponse( - "1235", - "follower1", - "example.com", - "test", - "test User", - "https://example.com/test", - Instant.now().toEpochMilli() - ) - onBlocking { follow(any(), eq(1234)) } doReturn true - } - application { - configureSerialization() - authentication { - bearer(TOKEN_AUTH) { - authenticate { - JWTPrincipal(payload) - } - } - } - routing { - route("/api/internal/v1") { - users(mock(), userApiService) - } - } - } - - client.post("/api/internal/v1/users/test1/followers") { - header(HttpHeaders.Authorization, "Bearer test") - }.apply { - assertEquals(HttpStatusCode.OK, status) - } - } - - @Test - fun `users name followers に認証情報ありでGETしたらフォロー処理受付になることもある`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - - val claim = mock { - on { asLong() } doReturn 1234 - } - val payload = mock { - on { getClaim(eq("uid")) } doReturn claim - } - - val userApiService = mock { - onBlocking { findByAcct(any()) } doReturn UserResponse( - "1235", - "follower1", - "example.com", - "test", - "test User", - "https://example.com/test", - Instant.now().toEpochMilli() - ) - onBlocking { follow(any(), eq(1234)) } doReturn false - } - application { - configureSerialization() - authentication { - bearer(TOKEN_AUTH) { - authenticate { - JWTPrincipal(payload) - } - } - } - routing { - route("/api/internal/v1") { - users(mock(), userApiService) - } - } - } - - client.post("/api/internal/v1/users/test1/followers") { - header(HttpHeaders.Authorization, "Bearer test") - }.apply { - assertEquals(HttpStatusCode.Accepted, status) - } - } - - @Test - fun `users id followers に認証情報ありでGETしたらフォロー処理受付になることもある`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - - val claim = mock { - on { asLong() } doReturn 1234 - } - val payload = mock { - on { getClaim(eq("uid")) } doReturn claim - } - - val userApiService = mock { - onBlocking { findById(any()) } doReturn UserResponse( - "1235", - "follower1", - "example.com", - "test", - "test User", - "https://example.com/test", - Instant.now().toEpochMilli() - ) - onBlocking { follow(eq(1235), eq(1234)) } doReturn false - } - application { - configureSerialization() - authentication { - bearer(TOKEN_AUTH) { - authenticate { - JWTPrincipal(payload) - } - } - } - routing { - route("/api/internal/v1") { - users(mock(), userApiService) - } - } - } - - client.post("/api/internal/v1/users/1235/followers") { - header(HttpHeaders.Authorization, "Bearer test") - }.apply { - assertEquals(HttpStatusCode.Accepted, status) - } - } - - @Test - fun `users name following にGETしたらフォロイー一覧を取得できる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - - val followers = listOf( - UserResponse( - "1235", - "follower1", - "example.com", - "test", - "test User", - "https://example.com/test", - Instant.now().toEpochMilli() - ), - UserResponse( - "1236", - "follower2", - "example.com", - "test", - "test User", - "https://example.com/test", - Instant.now().toEpochMilli() - ) - ) - val userApiService = mock { - onBlocking { findFollowingsByAcct(any()) } doReturn followers - } - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - route("/api/internal/v1") { - users(mock(), userApiService) - } - } - } - - client.get("/api/internal/v1/users/test1/following").apply { - assertEquals(HttpStatusCode.OK, status) - assertEquals(followers, JsonObjectMapper.objectMapper.readValue(bodyAsText())) - } - } - - @Test - fun `users name@domain following にGETしたらフォロイー一覧を取得できる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - - val followers = listOf( - UserResponse( - "1235", - "follower1", - "example.com", - "test", - "test User", - "https://example.com/test", - Instant.now().toEpochMilli() - ), - UserResponse( - "1236", - "follower2", - "example.com", - "test", - "test User", - "https://example.com/test", - Instant.now().toEpochMilli() - ) - ) - val userApiService = mock { - onBlocking { findFollowingsByAcct(any()) } doReturn followers - } - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - route("/api/internal/v1") { - users(mock(), userApiService) - } - } - } - - client.get("/api/internal/v1/users/test1@domain/following").apply { - assertEquals(HttpStatusCode.OK, status) - assertEquals(followers, JsonObjectMapper.objectMapper.readValue(bodyAsText())) - } - } - - @Test - fun `users id following にGETしたらフォロイー一覧を取得できる`() = testApplication { - environment { - config = ApplicationConfig("empty.conf") - } - - val followers = listOf( - UserResponse( - "1235", - "follower1", - "example.com", - "test", - "test User", - "https://example.com/test", - Instant.now().toEpochMilli() - ), - UserResponse( - "1236", - "follower2", - "example.com", - "test", - "test User", - "https://example.com/test", - Instant.now().toEpochMilli() - ) - ) - val userApiService = mock { - onBlocking { findFollowings(any()) } doReturn followers - } - application { - configureSerialization() - configureSecurity(mock(), mock()) - routing { - route("/api/internal/v1") { - users(mock(), userApiService) - } - } - } - - client.get("/api/internal/v1/users/1234/following").apply { - assertEquals(HttpStatusCode.OK, status) - assertEquals(followers, JsonObjectMapper.objectMapper.readValue(bodyAsText())) - } - } -}