feat(sessions): show project and last activity on history cards (#1133)

This commit is contained in:
Alfredo Ortegón Sepúlveda authored and GitHub committed 2026-09-16 00:14:09 +03:00
1 parent 529b424eb4
commit aed8ba4a6d
25 files changed
+1617 -80

No files matched your search

@@ -0,0 +1,67 @@
package com.m57.hermescontrol.data.model
import com.m57.hermescontrol.data.remote.OkHttpProvider
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
/** Wire-shape tests for the session workspace fields and the `projects.list` RPC result. */
class ProjectModelsTest {
private val json = OkHttpProvider.json
@Test
fun `session list row decodes its workspace`() {
val session =
json.decodeFromString<SessionInfo>(
"""{"id":"s1","cwd":"/srv/app/src","git_repo_root":"/srv/app","git_branch":"main"}""",
)
assertEquals("/srv/app/src", session.cwd)
assertEquals("/srv/app", session.git_repo_root)
}
@Test
fun `session list row without a workspace decodes nulls`() {
val session = json.decodeFromString<SessionInfo>("""{"id":"s1"}""")
assertNull(session.cwd)
assertNull(session.git_repo_root)
}
@Test
fun `projects list payload decodes folders, color and archived`() {
val response =
json.decodeFromString<ProjectsListResponse>(
"""
{"projects":[
{"id":"p_1","slug":"app","name":"App","description":null,"icon":"rocket",
"color":"hsl(120 68% 58%)","board_slug":null,"primary_path":"/srv/app",
"archived":false,"created_at":1787590315,
"folders":[{"path":"/srv/app","label":null,"is_primary":true,"added_at":1787590315}]},
{"id":"p_2","slug":"old","name":"Old","archived":1,"folders":[]}
],"active_id":"p_1"}
""".trimIndent(),
)
val app = response.projects[0]
assertEquals("App", app.name)
assertEquals("hsl(120 68% 58%)", app.color)
assertEquals("/srv/app", app.primaryPath)
assertEquals(listOf("/srv/app"), app.folders.map { it.path })
assertTrue(app.folders[0].isPrimary == true)
assertFalse(app.isArchived)
assertTrue(response.projects[1].isArchived)
assertEquals("p_1", response.activeId)
}
@Test
fun `project with only an id and name decodes defaults`() {
val project = json.decodeFromString<ProjectInfo>("""{"id":"p_3","name":"Bare"}""")
assertNull(project.color)
assertTrue(project.folders.isEmpty())
assertFalse(project.isArchived)
}
}
@@ -0,0 +1,85 @@
package com.m57.hermescontrol.data.ws
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
class ProjectsSourceTest {
@Test
fun `decodes the untyped rpc map`() =
runTest {
var calledMethod: String? = null
val source =
HermesProjectsSource(isConnected = { true }) { method, _ ->
calledMethod = method
mapOf(
"projects" to
listOf(
mapOf(
"id" to "p_1",
"name" to "App",
"color" to "hsl(1 2% 3%)",
"archived" to false,
"folders" to listOf(mapOf("path" to "/srv/app", "is_primary" to true)),
),
),
"active_id" to "p_1",
)
}
val projects = source.fetchProjects()
assertEquals(WsMethods.PROJECTS_LIST, calledMethod)
assertEquals(listOf("App"), projects?.map { it.name })
assertEquals(
"/srv/app",
projects
?.single()
?.folders
?.single()
?.path,
)
}
@Test
fun `skips the rpc while the socket is not connected`() =
runTest {
var calls = 0
val source =
HermesProjectsSource(
rpcRequest = { _, _ ->
calls++
mapOf("projects" to emptyList<Any>())
},
isConnected = { false },
)
assertNull(source.fetchProjects())
assertEquals("a disconnected fetch must not queue an RPC", 0, calls)
}
@Test
fun `rpc failure or unexpected payload yields null`() =
runTest {
assertNull(
HermesProjectsSource(isConnected = { true }) { _, _ -> error("method not found") }.fetchProjects(),
)
assertNull(HermesProjectsSource(isConnected = { true }) { _, _ -> "nope" }.fetchProjects())
assertNull(HermesProjectsSource(isConnected = { true }) { _, _ -> null }.fetchProjects())
}
@Test
fun `cancellation propagates`() =
runTest {
val thrown =
runCatching {
HermesProjectsSource(
isConnected = { true },
) { _, _ -> throw CancellationException("gone") }.fetchProjects()
}.exceptionOrNull()
assertTrue(thrown is CancellationException)
}
}