1package com.parseable.android
3import com.parseable.android.data.model.*
4import com.parseable.android.data.repository.ParseableRepository
5import com.parseable.android.ui.screens.logviewer.StreamInfoViewModel
7import kotlinx.coroutines.Dispatchers
8import kotlinx.coroutines.ExperimentalCoroutinesApi
9import kotlinx.coroutines.test.*
10import kotlinx.serialization.json.JsonObject
11import kotlinx.serialization.json.JsonPrimitive
13import org.junit.Assert.*
14import org.junit.Before
17@OptIn(ExperimentalCoroutinesApi::class)
18class StreamInfoViewModelTest {
20 private lateinit var repository: ParseableRepository
21 private lateinit var viewModel: StreamInfoViewModel
22 private val testDispatcher = UnconfinedTestDispatcher()
26 Dispatchers.setMain(testDispatcher)
27 repository = mockk(relaxed = true)
28 viewModel = StreamInfoViewModel(repository)
33 Dispatchers.resetMain()
37 fun `initial state is empty`() {
38 val state = viewModel.state.value
39 assertEquals("", state.streamName)
40 assertNull(state.stats)
41 assertTrue(state.schema.isEmpty())
42 assertTrue(state.retention.isEmpty())
43 assertNull(state.rawInfo)
44 assertFalse(state.isLoading)
45 assertNull(state.error)
46 assertFalse(state.isDeleting)
47 assertFalse(state.deleteSuccess)
51 fun `load fetches all stream info in parallel`() = runTest {
52 val stats = StreamStats(
53 ingestion = IngestionStats(count = 1000, size = "5MB"),
54 storage = StorageStats(size = "2MB"),
56 val schema = StreamSchema(
58 SchemaField(name = "timestamp"),
59 SchemaField(name = "message"),
62 val retention = listOf(RetentionConfig(duration = "30d", action = "delete"))
63 val info = JsonObject(mapOf("created_at" to JsonPrimitive("2024-01-01")))
65 coEvery { repository.getStreamStats("test-stream") } returns ApiResult.Success(stats)
66 coEvery { repository.getStreamSchema("test-stream") } returns ApiResult.Success(schema)
67 coEvery { repository.getStreamRetention("test-stream") } returns ApiResult.Success(retention)
68 coEvery { repository.getStreamInfo("test-stream") } returns ApiResult.Success(info)
70 viewModel.load("test-stream")
72 val state = viewModel.state.value
73 assertEquals("test-stream", state.streamName)
74 assertEquals(1000L, state.stats?.ingestion?.count)
75 assertEquals(2, state.schema.size)
76 assertEquals(1, state.retention.size)
77 assertNotNull(state.rawInfo)
78 assertFalse(state.isLoading)
79 assertNull(state.error)
83 fun `load shows error when some requests fail`() = runTest {
84 coEvery { repository.getStreamStats("s1") } returns ApiResult.Error("Stats error", 500)
85 coEvery { repository.getStreamSchema("s1") } returns ApiResult.Success(StreamSchema())
86 coEvery { repository.getStreamRetention("s1") } returns ApiResult.Success(emptyList())
87 coEvery { repository.getStreamInfo("s1") } returns ApiResult.Error("Info error", 500)
91 val state = viewModel.state.value
92 assertNotNull(state.error)
93 assertFalse(state.isLoading)
94 // Successful results should still be populated
95 assertTrue(state.schema.isEmpty()) // empty but loaded
96 assertTrue(state.retention.isEmpty())
100 fun `load sets schemaFailed when schema request fails`() = runTest {
101 coEvery { repository.getStreamStats("s1") } returns ApiResult.Success(StreamStats())
102 coEvery { repository.getStreamSchema("s1") } returns ApiResult.Error("Schema error", 500)
103 coEvery { repository.getStreamRetention("s1") } returns ApiResult.Success(emptyList())
104 coEvery { repository.getStreamInfo("s1") } returns ApiResult.Success(JsonObject(emptyMap()))
108 val state = viewModel.state.value
109 assertTrue(state.schemaFailed)
110 assertTrue(state.schema.isEmpty())
111 assertFalse(state.isLoading)
115 fun `load clears schemaFailed on successful schema load`() = runTest {
116 coEvery { repository.getStreamStats("s1") } returns ApiResult.Success(StreamStats())
117 coEvery { repository.getStreamSchema("s1") } returns ApiResult.Success(
118 StreamSchema(fields = listOf(SchemaField(name = "msg")))
120 coEvery { repository.getStreamRetention("s1") } returns ApiResult.Success(emptyList())
121 coEvery { repository.getStreamInfo("s1") } returns ApiResult.Success(JsonObject(emptyMap()))
125 val state = viewModel.state.value
126 assertFalse(state.schemaFailed)
127 assertEquals(1, state.schema.size)
131 fun `load cancels previous load when called again`() = runTest {
132 coEvery { repository.getStreamStats(any()) } returns ApiResult.Success(StreamStats())
133 coEvery { repository.getStreamSchema(any()) } returns ApiResult.Success(StreamSchema())
134 coEvery { repository.getStreamRetention(any()) } returns ApiResult.Success(emptyList())
135 coEvery { repository.getStreamInfo(any()) } returns ApiResult.Success(JsonObject(emptyMap()))
137 viewModel.load("stream-a")
138 viewModel.load("stream-b")
140 val state = viewModel.state.value
141 assertEquals("stream-b", state.streamName)
145 fun `deleteStream sets success on API success`() = runTest {
146 coEvery { repository.getStreamStats(any()) } returns ApiResult.Success(StreamStats())
147 coEvery { repository.getStreamSchema(any()) } returns ApiResult.Success(StreamSchema())
148 coEvery { repository.getStreamRetention(any()) } returns ApiResult.Success(emptyList())
149 coEvery { repository.getStreamInfo(any()) } returns ApiResult.Success(JsonObject(emptyMap()))
150 coEvery { repository.deleteStream("my-stream") } returns ApiResult.Success("deleted")
152 viewModel.load("my-stream")
153 viewModel.deleteStream()
155 val state = viewModel.state.value
156 assertTrue(state.deleteSuccess)
157 assertFalse(state.isDeleting)
158 assertNull(state.error)
162 fun `deleteStream sets error on API failure`() = runTest {
163 coEvery { repository.getStreamStats(any()) } returns ApiResult.Success(StreamStats())
164 coEvery { repository.getStreamSchema(any()) } returns ApiResult.Success(StreamSchema())
165 coEvery { repository.getStreamRetention(any()) } returns ApiResult.Success(emptyList())
166 coEvery { repository.getStreamInfo(any()) } returns ApiResult.Success(JsonObject(emptyMap()))
167 coEvery { repository.deleteStream("my-stream") } returns ApiResult.Error("Forbidden", 403)
169 viewModel.load("my-stream")
170 viewModel.deleteStream()
172 val state = viewModel.state.value
173 assertFalse(state.deleteSuccess)
174 assertFalse(state.isDeleting)
175 assertNotNull(state.error)
176 assertTrue(state.error!!.contains("Delete failed"))
180 fun `deleteStream does nothing when streamName is empty`() = runTest {
181 viewModel.deleteStream()
183 val state = viewModel.state.value
184 assertFalse(state.isDeleting)
185 assertFalse(state.deleteSuccess)
186 coVerify(exactly = 0) { repository.deleteStream(any()) }
190 fun `consumeDeleteSuccess resets flag`() = runTest {
191 coEvery { repository.getStreamStats(any()) } returns ApiResult.Success(StreamStats())
192 coEvery { repository.getStreamSchema(any()) } returns ApiResult.Success(StreamSchema())
193 coEvery { repository.getStreamRetention(any()) } returns ApiResult.Success(emptyList())
194 coEvery { repository.getStreamInfo(any()) } returns ApiResult.Success(JsonObject(emptyMap()))
195 coEvery { repository.deleteStream("s") } returns ApiResult.Success("ok")
198 viewModel.deleteStream()
199 assertTrue(viewModel.state.value.deleteSuccess)
201 viewModel.consumeDeleteSuccess()
202 assertFalse(viewModel.state.value.deleteSuccess)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list