From 4be93fea861cccf217c2e2025177ec8f1e76d6c1 Mon Sep 17 00:00:00 2001
From: usbharu <i@usbharu.dev>
Date: Mon, 24 Feb 2025 23:42:46 +0900
Subject: [PATCH 1/7] =?UTF-8?q?feat:=20/.well-known/nodeinfo=E3=82=92?=
 =?UTF-8?q?=E4=BD=9C=E6=88=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../application/nodeinfo/Nodeinfo2_0.kt       |  3 +++
 .../config/ActivityPubSecurityConfig.kt       |  1 +
 .../wellknown/NodeinfoController.kt           | 23 +++++++++++++++++++
 .../activitypub/interfaces/wellknown/XRD.kt   |  7 +++---
 .../hideout/core/config/SecurityConfig.kt     |  2 +-
 5 files changed, 32 insertions(+), 4 deletions(-)
 create mode 100644 hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/Nodeinfo2_0.kt
 create mode 100644 hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/NodeinfoController.kt

diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/Nodeinfo2_0.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/Nodeinfo2_0.kt
new file mode 100644
index 00000000..5809ec69
--- /dev/null
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/Nodeinfo2_0.kt
@@ -0,0 +1,3 @@
+package dev.usbharu.hideout.activitypub.application.nodeinfo
+
+data class Nodeinfo2_0()
\ No newline at end of file
diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/config/ActivityPubSecurityConfig.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/config/ActivityPubSecurityConfig.kt
index b445a086..9dfbbe4c 100644
--- a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/config/ActivityPubSecurityConfig.kt
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/config/ActivityPubSecurityConfig.kt
@@ -24,6 +24,7 @@ class ActivityPubSecurityConfig {
             )
             authorizeHttpRequests {
                 authorize(GET, "/.well-known/**", permitAll)
+                authorize(GET, "/nodeinfo/**", permitAll)
                 authorize(GET, "/error", permitAll)
                 authorize(POST, "/inbox", permitAll)
                 authorize(POST, "/users/{username}/inbox", permitAll)
diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/NodeinfoController.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/NodeinfoController.kt
new file mode 100644
index 00000000..c2c63e27
--- /dev/null
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/NodeinfoController.kt
@@ -0,0 +1,23 @@
+package dev.usbharu.hideout.activitypub.interfaces.wellknown
+
+import dev.usbharu.hideout.core.config.ApplicationConfig
+import org.springframework.web.bind.annotation.GetMapping
+import org.springframework.web.bind.annotation.RequestMapping
+import org.springframework.web.bind.annotation.RestController
+
+@RestController
+@RequestMapping("/.well-known")
+class NodeinfoController(private val applicationConfig: ApplicationConfig) {
+    @GetMapping("/nodeinfo", produces = ["application/json"])
+    suspend fun nodeinfo(): XRD = XRD(
+        listOf(
+            Link(
+                "http://nodeinfo.diaspora.software/ns/schema/2.1",
+                href = applicationConfig.url.resolve("/nodeinfo/2.1").toString()
+            ), Link(
+                "http://nodeinfo.diaspora.software/ns/schema/2.0",
+                href = applicationConfig.url.resolve("/nodeinfo/2.0").toString()
+            )
+        )
+    )
+}
\ No newline at end of file
diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/XRD.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/XRD.kt
index e6617c3f..02a9792d 100644
--- a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/XRD.kt
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/XRD.kt
@@ -24,8 +24,9 @@ data class XRD(
 data class Link(
     @JacksonXmlProperty(localName = "rel", isAttribute = true) val rel: String,
     @JsonInclude(JsonInclude.Include.NON_NULL)
-    @JacksonXmlProperty(localName = "template", isAttribute = true) val template: String?,
-    @JacksonXmlProperty(localName = "type", isAttribute = true) val type: String,
+    @JacksonXmlProperty(localName = "template", isAttribute = true) val template: String? = null,
     @JsonInclude(JsonInclude.Include.NON_NULL)
-    @JacksonXmlProperty(localName = "href", isAttribute = true) val href: String?,
+    @JacksonXmlProperty(localName = "type", isAttribute = true) val type: String? = null,
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    @JacksonXmlProperty(localName = "href", isAttribute = true) val href: String? = null,
 )
diff --git a/hideout/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt b/hideout/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt
index 9a1ea2da..97d62803 100644
--- a/hideout/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt
+++ b/hideout/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt
@@ -90,7 +90,7 @@ class SecurityConfig {
                 authorize("/error", permitAll)
                 authorize("/auth/sign_in", permitAll)
                 authorize(GET, "/.well-known/**", permitAll)
-                authorize(GET, "/nodeinfo/2.0", permitAll)
+                authorize(GET, "/nodeinfo/**", permitAll)
 
                 authorize(GET, "/auth/sign_up", hasRole("ANONYMOUS"))
                 authorize(POST, "/auth/sign_up", permitAll)

From 4c026fb243023276814e2d50ca973924d0a7989c Mon Sep 17 00:00:00 2001
From: usbharu <i@usbharu.dev>
Date: Mon, 24 Feb 2025 23:56:59 +0900
Subject: [PATCH 2/7] =?UTF-8?q?feat:=20/nodeinfo/2.0,/nodeinfo/2.1?=
 =?UTF-8?q?=E3=82=92=E4=BD=9C=E6=88=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../application/nodeinfo/Nodeinfo2_0.kt       | 14 +++++-
 .../nodeinfo/NodeinfoApplicationService.kt    | 43 +++++++++++++++++++
 .../wellknown/NodeinfoController.kt           | 24 ++++++++---
 3 files changed, 75 insertions(+), 6 deletions(-)
 create mode 100644 hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoApplicationService.kt

diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/Nodeinfo2_0.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/Nodeinfo2_0.kt
index 5809ec69..db143776 100644
--- a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/Nodeinfo2_0.kt
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/Nodeinfo2_0.kt
@@ -1,3 +1,15 @@
 package dev.usbharu.hideout.activitypub.application.nodeinfo
 
-data class Nodeinfo2_0()
\ No newline at end of file
+data class Nodeinfo2_0(
+    val version: String = "2,0",
+    val software: Map<String, String>,
+    val protocol: List<String>,
+    val usage: NodeinfoUsage,
+    val openRegistration: Boolean,
+    val metadata: Map<String, Any>,
+)
+
+data class NodeinfoUsage(
+    val users: Map<String, String>,
+    val localPosts: Long,
+)
\ No newline at end of file
diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoApplicationService.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoApplicationService.kt
new file mode 100644
index 00000000..345f660d
--- /dev/null
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoApplicationService.kt
@@ -0,0 +1,43 @@
+package dev.usbharu.hideout.activitypub.application.nodeinfo
+
+import dev.usbharu.hideout.core.application.shared.AbstractApplicationService
+import dev.usbharu.hideout.core.application.shared.Transaction
+import dev.usbharu.hideout.core.config.ApplicationConfig
+import dev.usbharu.hideout.core.domain.model.support.principal.Principal
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.info.BuildProperties
+import org.springframework.stereotype.Service
+
+@Service/*
+ * 今後ユーザーエージェントに応じてレスポンスを変更する可能性があるためApplicationServiceとして作成する commandは現時点ではUnitだが今後変化する可能性あり
+ */
+class NodeinfoApplicationService(
+    @Autowired(required = false) private val buildInfo: BuildProperties? = null,
+    private val applicationConfig: ApplicationConfig,
+    transaction: Transaction,
+) : AbstractApplicationService<Unit, Nodeinfo2_0>(
+    transaction, logger
+) {
+    override suspend fun internalExecute(command: Unit, principal: Principal): Nodeinfo2_0 {
+        return Nodeinfo2_0(
+            version = "2.0",
+            software = mapOf(
+                "name" to "hideout", "version" to (buildInfo?.version ?: "UNKNOWN")
+            ),
+            protocol = listOf("activitypub"),
+            NodeinfoUsage(
+                users = mapOf(
+                    "total" to "0", "activeMonth" to "0", "activeHalfyear" to "0"
+                ), localPosts = 0
+            ),
+            openRegistration = applicationConfig.private.not(),
+            metadata = mapOf()
+        )
+    }
+
+    companion object {
+        private val logger: Logger = LoggerFactory.getLogger(NodeinfoApplicationService::class.java)
+    }
+}
diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/NodeinfoController.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/NodeinfoController.kt
index c2c63e27..7b6f2c95 100644
--- a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/NodeinfoController.kt
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/NodeinfoController.kt
@@ -1,15 +1,19 @@
 package dev.usbharu.hideout.activitypub.interfaces.wellknown
 
+import dev.usbharu.hideout.activitypub.application.nodeinfo.Nodeinfo2_0
+import dev.usbharu.hideout.activitypub.application.nodeinfo.NodeinfoApplicationService
 import dev.usbharu.hideout.core.config.ApplicationConfig
+import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
 import org.springframework.web.bind.annotation.GetMapping
-import org.springframework.web.bind.annotation.RequestMapping
 import org.springframework.web.bind.annotation.RestController
 
 @RestController
-@RequestMapping("/.well-known")
-class NodeinfoController(private val applicationConfig: ApplicationConfig) {
-    @GetMapping("/nodeinfo", produces = ["application/json"])
-    suspend fun nodeinfo(): XRD = XRD(
+class NodeinfoController(
+    private val applicationConfig: ApplicationConfig,
+    private val nodeinfoApplicationService: NodeinfoApplicationService,
+) {
+    @GetMapping("/.well-known/nodeinfo", produces = ["application/json"])
+    fun nodeinfo(): XRD = XRD(
         listOf(
             Link(
                 "http://nodeinfo.diaspora.software/ns/schema/2.1",
@@ -20,4 +24,14 @@ class NodeinfoController(private val applicationConfig: ApplicationConfig) {
             )
         )
     )
+
+    @GetMapping("/nodeinfo/2.0", produces = ["application/json"])
+    suspend fun nodeinfo2_0(): Nodeinfo2_0 {
+        return nodeinfoApplicationService.execute(Unit, Anonymous)
+    }
+
+    @GetMapping("/nodeinfo/2.1", produces = ["application/json"])
+    suspend fun nodeinfo2_1(): Nodeinfo2_0 {
+        return nodeinfoApplicationService.execute(Unit, Anonymous)
+    }
 }
\ No newline at end of file

From 5ac1e3b267af3d1d1afa5203d8667ad030b5e2dc Mon Sep 17 00:00:00 2001
From: usbharu <i@usbharu.dev>
Date: Tue, 25 Feb 2025 00:03:02 +0900
Subject: [PATCH 3/7] =?UTF-8?q?feat:=20nodeinfo=E3=81=AEusage=E3=82=92?=
 =?UTF-8?q?=E6=96=87=E5=AD=97=E3=81=8B=E3=82=89=E6=95=B0=E5=AD=97=E3=81=AB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../hideout/activitypub/application/nodeinfo/Nodeinfo2_0.kt     | 2 +-
 .../application/nodeinfo/NodeinfoApplicationService.kt          | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/Nodeinfo2_0.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/Nodeinfo2_0.kt
index db143776..3bcb35b2 100644
--- a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/Nodeinfo2_0.kt
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/Nodeinfo2_0.kt
@@ -10,6 +10,6 @@ data class Nodeinfo2_0(
 )
 
 data class NodeinfoUsage(
-    val users: Map<String, String>,
+    val users: Map<String, Long>,
     val localPosts: Long,
 )
\ No newline at end of file
diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoApplicationService.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoApplicationService.kt
index 345f660d..29a29e75 100644
--- a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoApplicationService.kt
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoApplicationService.kt
@@ -29,7 +29,7 @@ class NodeinfoApplicationService(
             protocol = listOf("activitypub"),
             NodeinfoUsage(
                 users = mapOf(
-                    "total" to "0", "activeMonth" to "0", "activeHalfyear" to "0"
+                    "total" to 0, "activeMonth" to 0, "activeHalfyear" to 0
                 ), localPosts = 0
             ),
             openRegistration = applicationConfig.private.not(),

From 8c14a966aa890620c8bc822d2854bafc0870e4cf Mon Sep 17 00:00:00 2001
From: usbharu <i@usbharu.dev>
Date: Tue, 25 Feb 2025 11:06:06 +0900
Subject: [PATCH 4/7] =?UTF-8?q?test:=20=E3=83=86=E3=82=B9=E3=83=88?=
 =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../nodeinfo/NodeinfoApplicationService.kt    | 38 ++++++++------
 .../application/nodeinfo/NodeinfoRequest.kt   |  3 ++
 .../wellknown/NodeinfoController.kt           |  5 +-
 .../NodeinfoApplicationServiceTest.kt         | 52 +++++++++++++++++++
 4 files changed, 79 insertions(+), 19 deletions(-)
 create mode 100644 hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoRequest.kt
 create mode 100644 hideout/hideout-activitypub/src/test/kotlin/dev/usbharu/hideout/activitypub/application/webfinger/NodeinfoApplicationServiceTest.kt

diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoApplicationService.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoApplicationService.kt
index 29a29e75..ad85ef99 100644
--- a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoApplicationService.kt
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoApplicationService.kt
@@ -14,27 +14,31 @@ import org.springframework.stereotype.Service
  * 今後ユーザーエージェントに応じてレスポンスを変更する可能性があるためApplicationServiceとして作成する commandは現時点ではUnitだが今後変化する可能性あり
  */
 class NodeinfoApplicationService(
-    @Autowired(required = false) private val buildInfo: BuildProperties? = null,
+    @Autowired(required = false) private val buildInfo: BuildProperties?,
     private val applicationConfig: ApplicationConfig,
     transaction: Transaction,
-) : AbstractApplicationService<Unit, Nodeinfo2_0>(
+) : AbstractApplicationService<NodeinfoRequest, Nodeinfo2_0>(
     transaction, logger
 ) {
-    override suspend fun internalExecute(command: Unit, principal: Principal): Nodeinfo2_0 {
-        return Nodeinfo2_0(
-            version = "2.0",
-            software = mapOf(
-                "name" to "hideout", "version" to (buildInfo?.version ?: "UNKNOWN")
-            ),
-            protocol = listOf("activitypub"),
-            NodeinfoUsage(
-                users = mapOf(
-                    "total" to 0, "activeMonth" to 0, "activeHalfyear" to 0
-                ), localPosts = 0
-            ),
-            openRegistration = applicationConfig.private.not(),
-            metadata = mapOf()
-        )
+    override suspend fun internalExecute(command: NodeinfoRequest, principal: Principal): Nodeinfo2_0 {
+        return when (command.version) {
+            "2.0", "2.1" -> Nodeinfo2_0(
+                version = command.version,
+                software = mapOf(
+                    "name" to "hideout", "version" to (buildInfo?.version ?: "UNKNOWN")
+                ),
+                protocol = listOf("activitypub"),
+                NodeinfoUsage(
+                    users = mapOf(
+                        "total" to 0, "activeMonth" to 0, "activeHalfyear" to 0
+                    ), localPosts = 0
+                ),
+                openRegistration = applicationConfig.private.not(),
+                metadata = mapOf()
+            )
+
+            else -> throw IllegalArgumentException("Invalid command version ${command.version}")
+        }
     }
 
     companion object {
diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoRequest.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoRequest.kt
new file mode 100644
index 00000000..49e67893
--- /dev/null
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoRequest.kt
@@ -0,0 +1,3 @@
+package dev.usbharu.hideout.activitypub.application.nodeinfo
+
+data class NodeinfoRequest(val version: String)
\ No newline at end of file
diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/NodeinfoController.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/NodeinfoController.kt
index 7b6f2c95..6f7bb780 100644
--- a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/NodeinfoController.kt
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/NodeinfoController.kt
@@ -2,6 +2,7 @@ package dev.usbharu.hideout.activitypub.interfaces.wellknown
 
 import dev.usbharu.hideout.activitypub.application.nodeinfo.Nodeinfo2_0
 import dev.usbharu.hideout.activitypub.application.nodeinfo.NodeinfoApplicationService
+import dev.usbharu.hideout.activitypub.application.nodeinfo.NodeinfoRequest
 import dev.usbharu.hideout.core.config.ApplicationConfig
 import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
 import org.springframework.web.bind.annotation.GetMapping
@@ -27,11 +28,11 @@ class NodeinfoController(
 
     @GetMapping("/nodeinfo/2.0", produces = ["application/json"])
     suspend fun nodeinfo2_0(): Nodeinfo2_0 {
-        return nodeinfoApplicationService.execute(Unit, Anonymous)
+        return nodeinfoApplicationService.execute(NodeinfoRequest("2.0"), Anonymous)
     }
 
     @GetMapping("/nodeinfo/2.1", produces = ["application/json"])
     suspend fun nodeinfo2_1(): Nodeinfo2_0 {
-        return nodeinfoApplicationService.execute(Unit, Anonymous)
+        return nodeinfoApplicationService.execute(NodeinfoRequest("2.1"), Anonymous)
     }
 }
\ No newline at end of file
diff --git a/hideout/hideout-activitypub/src/test/kotlin/dev/usbharu/hideout/activitypub/application/webfinger/NodeinfoApplicationServiceTest.kt b/hideout/hideout-activitypub/src/test/kotlin/dev/usbharu/hideout/activitypub/application/webfinger/NodeinfoApplicationServiceTest.kt
new file mode 100644
index 00000000..8888de4f
--- /dev/null
+++ b/hideout/hideout-activitypub/src/test/kotlin/dev/usbharu/hideout/activitypub/application/webfinger/NodeinfoApplicationServiceTest.kt
@@ -0,0 +1,52 @@
+package dev.usbharu.hideout.activitypub.application.webfinger
+
+
+import dev.usbharu.hideout.activitypub.application.nodeinfo.NodeinfoApplicationService
+import dev.usbharu.hideout.activitypub.application.nodeinfo.NodeinfoRequest
+import dev.usbharu.hideout.core.config.ApplicationConfig
+import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
+import kotlinx.coroutines.test.runTest
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
+import org.mockito.InjectMocks
+import org.mockito.Spy
+import org.mockito.junit.jupiter.MockitoExtension
+import org.springframework.boot.info.BuildProperties
+import util.TestTransaction
+import java.net.URI
+import java.util.*
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+
+@ExtendWith(MockitoExtension::class)
+class NodeinfoApplicationServiceTest {
+    @InjectMocks
+    lateinit var nodeinfoApplicationService: NodeinfoApplicationService
+
+    @Spy
+    val buildInfo: BuildProperties = BuildProperties(Properties())
+
+    @Spy
+    val applicationConfig = ApplicationConfig(URI.create("https://example.com"))
+
+    @Spy
+    val transaction = TestTransaction
+
+    @Test
+    fun nodeinfo2_0() = runTest {
+        val execute = nodeinfoApplicationService.execute(NodeinfoRequest("2.0"), Anonymous)
+
+        assertFalse(execute.openRegistration)
+        assertEquals("2.0", execute.version)
+        assertEquals("hideout", execute.software["name"])
+    }
+
+    @Test
+    fun nodeinfo2_1() = runTest {
+        val execute = nodeinfoApplicationService.execute(NodeinfoRequest("2.1"), Anonymous)
+
+        assertFalse(execute.openRegistration)
+        assertEquals("2.1", execute.version)
+        assertEquals("hideout", execute.software["name"])
+    }
+}
\ No newline at end of file

From ff6da7174bb8a48ca9f5e0df3d5ced004faf7335 Mon Sep 17 00:00:00 2001
From: usbharu <i@usbharu.dev>
Date: Tue, 25 Feb 2025 11:13:54 +0900
Subject: [PATCH 5/7] =?UTF-8?q?test:=20=E3=83=86=E3=82=B9=E3=83=88?=
 =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A02?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../wellknown/NodeinfoControllerTest.kt       | 69 +++++++++++++++++++
 1 file changed, 69 insertions(+)
 create mode 100644 hideout/hideout-activitypub/src/test/kotlin/wellknown/NodeinfoControllerTest.kt

diff --git a/hideout/hideout-activitypub/src/test/kotlin/wellknown/NodeinfoControllerTest.kt b/hideout/hideout-activitypub/src/test/kotlin/wellknown/NodeinfoControllerTest.kt
new file mode 100644
index 00000000..4c440e57
--- /dev/null
+++ b/hideout/hideout-activitypub/src/test/kotlin/wellknown/NodeinfoControllerTest.kt
@@ -0,0 +1,69 @@
+package wellknown
+
+import dev.usbharu.hideout.SpringApplication
+import org.flywaydb.core.Flyway
+import org.junit.jupiter.api.AfterAll
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.http.MediaType
+import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity
+import org.springframework.test.web.servlet.MockMvc
+import org.springframework.test.web.servlet.get
+import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder
+import org.springframework.test.web.servlet.setup.MockMvcBuilders
+import org.springframework.web.context.WebApplicationContext
+
+@SpringBootTest(classes = [SpringApplication::class])
+@AutoConfigureMockMvc
+class NodeinfoControllerTest {
+
+    @Autowired
+    private lateinit var context: WebApplicationContext
+
+    private lateinit var mockMvc: MockMvc
+
+    @BeforeEach
+    fun setUp() {
+        mockMvc = MockMvcBuilders.webAppContextSetup(context)
+            .apply<DefaultMockMvcBuilder>(springSecurity())
+            .build()
+    }
+
+    @Test
+    fun nodeinfo() {
+        mockMvc.get("/.well-known/nodeinfo")
+            .andDo { print() }
+            .andExpect { status { isOk() } }
+            .andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
+    }
+
+    @Test
+    fun nodeinfo2_0() {
+        mockMvc.get("/nodeinfo/2.0")
+            .asyncDispatch()
+            .andDo { print() }
+            .andExpect { status { isOk() } }
+            .andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
+    }
+
+    @Test
+    fun nodeinfo2_1() {
+        mockMvc.get("/nodeinfo/2.1")
+            .asyncDispatch()
+            .andDo { print() }
+            .andExpect { status { isOk() } }
+            .andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
+    }
+
+    companion object {
+        @JvmStatic
+        @AfterAll
+        fun dropDatabase(@Autowired flyway: Flyway) {
+            flyway.clean()
+            flyway.migrate()
+        }
+    }
+}
\ No newline at end of file

From 5e073233dae91d454919047c973eb0e54b1cd6ca Mon Sep 17 00:00:00 2001
From: usbharu <64310155+usbharu@users.noreply.github.com>
Date: Tue, 25 Feb 2025 02:22:07 +0000
Subject: [PATCH 6/7] style: fix lint (CI)

---
 .../activitypub/application/nodeinfo/Nodeinfo2_0.kt |  2 +-
 .../nodeinfo/NodeinfoApplicationService.kt          | 13 +++++++++----
 .../application/nodeinfo/NodeinfoRequest.kt         |  2 +-
 .../interfaces/wellknown/NodeinfoController.kt      |  5 +++--
 4 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/Nodeinfo2_0.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/Nodeinfo2_0.kt
index 3bcb35b2..519477dd 100644
--- a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/Nodeinfo2_0.kt
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/Nodeinfo2_0.kt
@@ -12,4 +12,4 @@ data class Nodeinfo2_0(
 data class NodeinfoUsage(
     val users: Map<String, Long>,
     val localPosts: Long,
-)
\ No newline at end of file
+)
diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoApplicationService.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoApplicationService.kt
index ad85ef99..e057084f 100644
--- a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoApplicationService.kt
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoApplicationService.kt
@@ -18,20 +18,25 @@ class NodeinfoApplicationService(
     private val applicationConfig: ApplicationConfig,
     transaction: Transaction,
 ) : AbstractApplicationService<NodeinfoRequest, Nodeinfo2_0>(
-    transaction, logger
+    transaction,
+    logger
 ) {
     override suspend fun internalExecute(command: NodeinfoRequest, principal: Principal): Nodeinfo2_0 {
         return when (command.version) {
             "2.0", "2.1" -> Nodeinfo2_0(
                 version = command.version,
                 software = mapOf(
-                    "name" to "hideout", "version" to (buildInfo?.version ?: "UNKNOWN")
+                    "name" to "hideout",
+                    "version" to (buildInfo?.version ?: "UNKNOWN")
                 ),
                 protocol = listOf("activitypub"),
                 NodeinfoUsage(
                     users = mapOf(
-                        "total" to 0, "activeMonth" to 0, "activeHalfyear" to 0
-                    ), localPosts = 0
+                        "total" to 0,
+                        "activeMonth" to 0,
+                        "activeHalfyear" to 0
+                    ),
+                    localPosts = 0
                 ),
                 openRegistration = applicationConfig.private.not(),
                 metadata = mapOf()
diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoRequest.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoRequest.kt
index 49e67893..7176f3d8 100644
--- a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoRequest.kt
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoRequest.kt
@@ -1,3 +1,3 @@
 package dev.usbharu.hideout.activitypub.application.nodeinfo
 
-data class NodeinfoRequest(val version: String)
\ No newline at end of file
+data class NodeinfoRequest(val version: String)
diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/NodeinfoController.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/NodeinfoController.kt
index 6f7bb780..35598fa9 100644
--- a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/NodeinfoController.kt
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/NodeinfoController.kt
@@ -19,7 +19,8 @@ class NodeinfoController(
             Link(
                 "http://nodeinfo.diaspora.software/ns/schema/2.1",
                 href = applicationConfig.url.resolve("/nodeinfo/2.1").toString()
-            ), Link(
+            ),
+            Link(
                 "http://nodeinfo.diaspora.software/ns/schema/2.0",
                 href = applicationConfig.url.resolve("/nodeinfo/2.0").toString()
             )
@@ -35,4 +36,4 @@ class NodeinfoController(
     suspend fun nodeinfo2_1(): Nodeinfo2_0 {
         return nodeinfoApplicationService.execute(NodeinfoRequest("2.1"), Anonymous)
     }
-}
\ No newline at end of file
+}

From 8f3286d745ce352ba33c919c79e24a33a0b7053a Mon Sep 17 00:00:00 2001
From: usbharu <i@usbharu.dev>
Date: Tue, 25 Feb 2025 11:30:38 +0900
Subject: [PATCH 7/7] style: fix lint

---
 .../activitypub/application/nodeinfo/Nodeinfo2_0.kt       | 1 +
 .../interfaces/wellknown/HostmetaController.kt            | 8 ++------
 .../interfaces/wellknown/NodeinfoController.kt            | 8 ++------
 3 files changed, 5 insertions(+), 12 deletions(-)

diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/Nodeinfo2_0.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/Nodeinfo2_0.kt
index 519477dd..281099e0 100644
--- a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/Nodeinfo2_0.kt
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/Nodeinfo2_0.kt
@@ -1,5 +1,6 @@
 package dev.usbharu.hideout.activitypub.application.nodeinfo
 
+@Suppress("ClassName")
 data class Nodeinfo2_0(
     val version: String = "2,0",
     val software: Map<String, String>,
diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/HostmetaController.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/HostmetaController.kt
index 7b11c9cb..81641d1a 100644
--- a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/HostmetaController.kt
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/HostmetaController.kt
@@ -19,12 +19,8 @@ class HostmetaController(private val linkList: List<Link> = emptyList()) {
 
     @Order(2)
     @GetMapping("/host-meta", produces = ["application/json"])
-    fun hostmetaJson(): XRD {
-        return XRD(linkList)
-    }
+    fun hostmetaJson(): XRD = XRD(linkList)
 
     @GetMapping("/host-meta.json", produces = ["application/json"])
-    fun hostmetaJson2(): XRD {
-        return XRD(linkList)
-    }
+    fun hostmetaJson2(): XRD = XRD(linkList)
 }
diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/NodeinfoController.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/NodeinfoController.kt
index 35598fa9..4fc95339 100644
--- a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/NodeinfoController.kt
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/NodeinfoController.kt
@@ -28,12 +28,8 @@ class NodeinfoController(
     )
 
     @GetMapping("/nodeinfo/2.0", produces = ["application/json"])
-    suspend fun nodeinfo2_0(): Nodeinfo2_0 {
-        return nodeinfoApplicationService.execute(NodeinfoRequest("2.0"), Anonymous)
-    }
+    suspend fun nodeinfo2_0(): Nodeinfo2_0 = nodeinfoApplicationService.execute(NodeinfoRequest("2.0"), Anonymous)
 
     @GetMapping("/nodeinfo/2.1", produces = ["application/json"])
-    suspend fun nodeinfo2_1(): Nodeinfo2_0 {
-        return nodeinfoApplicationService.execute(NodeinfoRequest("2.1"), Anonymous)
-    }
+    suspend fun nodeinfo2_1(): Nodeinfo2_0 = nodeinfoApplicationService.execute(NodeinfoRequest("2.1"), Anonymous)
 }