diff options
Diffstat (limited to 'android/app/src/test/java/org')
| -rw-r--r-- | android/app/src/test/java/org/terst/doot/widget/SlotPackerTest.kt | 80 | ||||
| -rw-r--r-- | android/app/src/test/java/org/terst/doot/widget/WidgetRepositoryTest.kt | 61 |
2 files changed, 141 insertions, 0 deletions
diff --git a/android/app/src/test/java/org/terst/doot/widget/SlotPackerTest.kt b/android/app/src/test/java/org/terst/doot/widget/SlotPackerTest.kt new file mode 100644 index 0000000..b6a078c --- /dev/null +++ b/android/app/src/test/java/org/terst/doot/widget/SlotPackerTest.kt @@ -0,0 +1,80 @@ +package org.terst.doot.widget + +import org.junit.Assert.* +import org.junit.Test +import org.terst.doot.widget.data.* +import java.time.Instant +import java.time.temporal.ChronoUnit + +class SlotPackerTest { + + private fun makeEvent(startIso: String, endIso: String): WidgetItem = WidgetItem( + id = "evt-$startIso", title = "Event", source = "calendar", type = "event", + start = startIso, end = endIso + ) + + private fun makeTask(id: String): WidgetItem = WidgetItem( + id = id, title = "Task $id", source = "todoist", type = "task", + completable = true + ) + + private val now = Instant.parse("2026-06-28T10:15:00Z") + + @Test + fun `empty tasks returns empty fragments`() { + val result = SlotPacker.pack(emptyList(), emptyList(), now) + assertTrue(result.isEmpty()) + } + + @Test + fun `single task gets slot starting at now`() { + val result = SlotPacker.pack(listOf(makeTask("t1")), emptyList(), now) + assertEquals(1, result.size) + assertEquals(1, result[0].slots.size) + assertEquals(now, result[0].slots[0].slotStart) + assertEquals(now.plus(1, ChronoUnit.HOURS), result[0].slots[0].slotEnd) + } + + @Test + fun `three tasks with no events form one fragment`() { + val tasks = listOf(makeTask("t1"), makeTask("t2"), makeTask("t3")) + val result = SlotPacker.pack(tasks, emptyList(), now) + assertEquals(1, result.size) + assertEquals(3, result[0].slots.size) + assertEquals(result[0].slots[0].slotEnd, result[0].slots[1].slotStart) + assertEquals(result[0].slots[1].slotEnd, result[0].slots[2].slotStart) + } + + @Test + fun `event between tasks splits into two fragments`() { + val tasks = listOf(makeTask("t1"), makeTask("t2"), makeTask("t3")) + // Event at 11:15–12:15 falls between slot 1 (10:15–11:15) and slot 2 + val event = makeEvent("2026-06-28T11:15:00Z", "2026-06-28T12:15:00Z") + val result = SlotPacker.pack(tasks, listOf(event), now) + + assertEquals(2, result.size) + assertEquals(1, result[0].slots.size) // t1 before event + assertEquals(2, result[1].slots.size) // t2, t3 after event + } + + @Test + fun `task slot jumps past overlapping event`() { + val tasks = listOf(makeTask("t1")) + // Event occupies 10:00–11:30, overlaps with now (10:15) + val event = makeEvent("2026-06-28T10:00:00Z", "2026-06-28T11:30:00Z") + val result = SlotPacker.pack(tasks, listOf(event), now) + + assertEquals(1, result.size) + val slotStart = result[0].slots[0].slotStart + assertEquals(Instant.parse("2026-06-28T11:30:00Z"), slotStart) + } + + @Test + fun `fragment startTime and endTime match first and last slot`() { + val tasks = listOf(makeTask("t1"), makeTask("t2")) + val result = SlotPacker.pack(tasks, emptyList(), now) + val frag = result[0] + assertEquals(frag.slots.first().slotStart, frag.startTime) + assertEquals(frag.slots.last().slotEnd, frag.endTime) + } +} diff --git a/android/app/src/test/java/org/terst/doot/widget/WidgetRepositoryTest.kt b/android/app/src/test/java/org/terst/doot/widget/WidgetRepositoryTest.kt new file mode 100644 index 0000000..d4c2ce3 --- /dev/null +++ b/android/app/src/test/java/org/terst/doot/widget/WidgetRepositoryTest.kt @@ -0,0 +1,61 @@ +package org.terst.doot.widget + +import io.mockk.* +import kotlinx.coroutines.test.runTest +import okhttp3.* +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.ResponseBody.Companion.toResponseBody +import org.junit.Assert.* +import org.junit.Test +import org.terst.doot.widget.data.WidgetRepository + +class WidgetRepositoryTest { + + private val mockClient = mockk<OkHttpClient>() + private val mockCall = mockk<Call>() + + private fun responseOf(code: Int, body: String): Response { + return Response.Builder() + .request(Request.Builder().url("https://example.com").build()) + .protocol(Protocol.HTTP_1_1) + .code(code) + .message("") + .body(body.toResponseBody("application/json".toMediaType())) + .build() + } + + @Test + fun `fetch returns failure on non-200`() = runTest { + every { mockClient.newCall(any()) } returns mockCall + every { mockCall.execute() } returns responseOf(401, "unauthorized") + + val repo = WidgetRepository(mockClient, "https://doot.example.com", "token") + val result = repo.fetchRaw() + assertTrue(result.isFailure) + } + + @Test + fun `fetch returns success with valid JSON`() = runTest { + val json = """{"now":"2026-06-28T10:15:00Z","items":[]}""" + every { mockClient.newCall(any()) } returns mockCall + every { mockCall.execute() } returns responseOf(200, json) + + val repo = WidgetRepository(mockClient, "https://doot.example.com", "token") + val result = repo.fetchRaw() + assertTrue(result.isSuccess) + assertEquals(0, result.getOrThrow().items.size) + } + + @Test + fun `fetch sets Authorization header`() = runTest { + val json = """{"now":"2026-06-28T10:15:00Z","items":[]}""" + val capturedRequest = slot<Request>() + every { mockClient.newCall(capture(capturedRequest)) } returns mockCall + every { mockCall.execute() } returns responseOf(200, json) + + val repo = WidgetRepository(mockClient, "https://doot.example.com", "mysecret") + repo.fetchRaw() + + assertEquals("Bearer mysecret", capturedRequest.captured.header("Authorization")) + } +} |
