git.iko.soy/parseable-android
on master help sign in
parseable-android/app/src/test/java/com/parseable/android/ParseableRepositoryTest.kt
173 lines 5.8 kB history raw
1package com.parseable.android
3import com.parseable.android.data.api.ParseableApiClient
4import com.parseable.android.data.model.*
5import com.parseable.android.data.repository.ParseableRepository
6import io.mockk.*
7import kotlinx.coroutines.test.runTest
8import org.junit.Assert.*
9import org.junit.Before
10import org.junit.Test
12class ParseableRepositoryTest {
14 private lateinit var apiClient: ParseableApiClient
15 private lateinit var repository: ParseableRepository
17 @Before
18 fun setup() {
19 apiClient = mockk(relaxed = true)
20 repository = ParseableRepository(apiClient)
21 }
23 @Test
24 fun `getAbout caches result on success`() = runTest {
25 val aboutInfo = AboutInfo(version = "1.0.0", mode = "standalone")
26 coEvery { apiClient.getAbout() } returns ApiResult.Success(aboutInfo)
28 val first = repository.getAbout()
29 val second = repository.getAbout()
31 assertTrue(first is ApiResult.Success)
32 assertEquals("1.0.0", (first as ApiResult.Success).data.version)
33 // Should only call API once due to caching
34 coVerify(exactly = 1) { apiClient.getAbout() }
35 }
37 @Test
38 fun `getAbout does not cache errors`() = runTest {
39 coEvery { apiClient.getAbout() } returns ApiResult.Error("fail", 500)
41 val first = repository.getAbout()
42 repository.getAbout()
44 assertTrue(first is ApiResult.Error)
45 coVerify(exactly = 2) { apiClient.getAbout() }
46 }
48 @Test
49 fun `listStreams forceRefresh bypasses cache`() = runTest {
50 val streams = listOf(LogStream(name = "test"))
51 coEvery { apiClient.listStreams() } returns ApiResult.Success(streams)
53 repository.listStreams()
54 repository.listStreams(forceRefresh = true)
56 coVerify(exactly = 2) { apiClient.listStreams() }
57 }
59 @Test
60 fun `listStreams returns cached data within TTL`() = runTest {
61 val streams = listOf(LogStream(name = "test"))
62 coEvery { apiClient.listStreams() } returns ApiResult.Success(streams)
64 repository.listStreams()
65 val cached = repository.listStreams(forceRefresh = false)
67 assertTrue(cached is ApiResult.Success)
68 assertEquals(1, (cached as ApiResult.Success).data.size)
69 coVerify(exactly = 1) { apiClient.listStreams() }
70 }
72 @Test
73 fun `invalidateAll clears all caches`() = runTest {
74 val about = AboutInfo(version = "1.0.0")
75 val streams = listOf(LogStream(name = "s1"))
76 coEvery { apiClient.getAbout() } returns ApiResult.Success(about)
77 coEvery { apiClient.listStreams() } returns ApiResult.Success(streams)
79 repository.getAbout()
80 repository.listStreams()
81 repository.invalidateAll()
82 repository.getAbout()
83 repository.listStreams()
85 coVerify(exactly = 2) { apiClient.getAbout() }
86 coVerify(exactly = 2) { apiClient.listStreams() }
87 }
89 @Test
90 fun `configure invalidates cache and configures client`() = runTest {
91 val config = ServerConfig("https://test.com", "user", "pass", true)
92 repository.configure(config)
93 verify { apiClient.configure(config) }
94 }
96 @Test
97 fun `auth error triggers channel`() = runTest {
98 coEvery { apiClient.listStreams() } returns ApiResult.Error("Unauthorized", 401)
100 val result = repository.listStreams()
102 assertTrue(result is ApiResult.Error)
103 assertTrue((result as ApiResult.Error).isUnauthorized)
104 }
106 @Test
107 fun `queryLogs builds correct SQL with filter`() = runTest {
108 coEvery { apiClient.queryLogs(any(), any(), any()) } returns ApiResult.Success(emptyList())
110 repository.queryLogs(
111 stream = "my_stream",
112 startTime = "2024-01-01T00:00:00Z",
113 endTime = "2024-01-02T00:00:00Z",
114 filterSql = "\"level\" = 'ERROR'",
115 limit = 100,
116 )
118 coVerify {
119 apiClient.queryLogs(
120 match { it.contains("WHERE \"level\" = 'ERROR'") && it.contains("LIMIT 100") },
121 "2024-01-01T00:00:00Z",
122 "2024-01-02T00:00:00Z",
123 )
124 }
125 }
127 @Test
128 fun `queryLogs without filter builds SQL without WHERE`() = runTest {
129 coEvery { apiClient.queryLogs(any(), any(), any()) } returns ApiResult.Success(emptyList())
131 repository.queryLogs(
132 stream = "test",
133 startTime = "start",
134 endTime = "end",
135 )
137 coVerify {
138 apiClient.queryLogs(
139 match { !it.contains("WHERE") && it.contains("\"test\"") },
140 "start",
141 "end",
142 )
143 }
144 }
146 @Test
147 fun `deleteStream invalidates cache`() = runTest {
148 val streams = listOf(LogStream(name = "s1"))
149 coEvery { apiClient.listStreams() } returns ApiResult.Success(streams)
150 coEvery { apiClient.deleteStream(any()) } returns ApiResult.Success("ok")
152 repository.listStreams()
153 repository.deleteStream("s1")
154 repository.listStreams()
156 // After delete, cache was invalidated, so listStreams called twice
157 coVerify(exactly = 2) { apiClient.listStreams() }
158 }
160 @Test
161 fun `getStreamSchema caches per stream`() = runTest {
162 val schema = StreamSchema(fields = listOf(SchemaField(name = "col1")))
163 coEvery { apiClient.getStreamSchema("s1") } returns ApiResult.Success(schema)
164 coEvery { apiClient.getStreamSchema("s2") } returns ApiResult.Success(schema)
166 repository.getStreamSchema("s1")
167 repository.getStreamSchema("s1") // cached
168 repository.getStreamSchema("s2") // different stream, not cached
170 coVerify(exactly = 1) { apiClient.getStreamSchema("s1") }
171 coVerify(exactly = 1) { apiClient.getStreamSchema("s2") }
172 }