1
+ package com.segment.analytics.kotlin.core
2
+
3
+ import com.segment.analytics.kotlin.core.utilities.ConcreteStorageProvider
4
+ import com.segment.analytics.kotlin.core.utilities.EncodeDefaultsJson
5
+ import com.segment.analytics.kotlin.core.utilities.EventStream
6
+ import com.segment.analytics.kotlin.core.utilities.FileEventStream
7
+ import com.segment.analytics.kotlin.core.utilities.InMemoryEventStream
8
+ import com.segment.analytics.kotlin.core.utilities.InMemoryPrefs
9
+ import com.segment.analytics.kotlin.core.utilities.InMemoryStorageProvider
10
+ import com.segment.analytics.kotlin.core.utilities.KVS
11
+ import com.segment.analytics.kotlin.core.utilities.PropertiesFile
12
+ import com.segment.analytics.kotlin.core.utilities.StorageImpl
13
+ import com.segment.analytics.kotlin.core.utils.clearPersistentStorage
14
+ import com.segment.analytics.kotlin.core.utils.testAnalytics
15
+ import io.mockk.every
16
+ import io.mockk.mockk
17
+ import io.mockk.verify
18
+ import kotlinx.coroutines.test.TestScope
19
+ import kotlinx.coroutines.test.UnconfinedTestDispatcher
20
+ import kotlinx.coroutines.test.runTest
21
+ import kotlinx.serialization.encodeToString
22
+ import kotlinx.serialization.json.buildJsonObject
23
+ import kotlinx.serialization.json.put
24
+ import org.junit.jupiter.api.Assertions.*
25
+ import org.junit.jupiter.api.BeforeEach
26
+ import org.junit.jupiter.api.Nested
27
+ import org.junit.jupiter.api.Test
28
+ import org.junit.jupiter.api.assertThrows
29
+ import sovran.kotlin.Store
30
+ import java.lang.StringBuilder
31
+ import java.util.Date
32
+
33
+ class StorageTest {
34
+ @Nested
35
+ inner class StorageProviderTest {
36
+ private lateinit var analytics: Analytics
37
+ private val testDispatcher = UnconfinedTestDispatcher ()
38
+ private val testScope = TestScope (testDispatcher)
39
+
40
+ @BeforeEach
41
+ fun setup () {
42
+ clearPersistentStorage()
43
+ val config = Configuration (
44
+ writeKey = " 123" ,
45
+ application = " Test"
46
+ )
47
+
48
+ analytics = testAnalytics(config, testScope, testDispatcher)
49
+ }
50
+
51
+ @Test
52
+ fun concreteStorageProviderTest () {
53
+ val storage = ConcreteStorageProvider .createStorage(analytics) as StorageImpl
54
+ assertTrue(storage.eventStream is FileEventStream )
55
+ assertTrue(storage.propertiesFile is PropertiesFile )
56
+
57
+ val eventStream = storage.eventStream as FileEventStream
58
+ val propertiesFile = (storage.propertiesFile as PropertiesFile ).file
59
+
60
+ val dir = " /tmp/analytics-kotlin/${analytics.configuration.writeKey} "
61
+ // we don't cache storage directory, but we can use the parent of the event storage to verify
62
+ assertEquals(dir, eventStream.directory.parent)
63
+ assertTrue(eventStream.directory.path.contains(dir))
64
+ assertTrue(propertiesFile.path.contains(dir))
65
+ assertTrue(eventStream.directory.exists())
66
+ assertTrue(propertiesFile.exists())
67
+ }
68
+
69
+ @Test
70
+ fun inMemoryStorageProviderTest () {
71
+ val storage = InMemoryStorageProvider .createStorage(analytics) as StorageImpl
72
+ assertTrue(storage.eventStream is InMemoryEventStream )
73
+ assertTrue(storage.propertiesFile is InMemoryPrefs )
74
+ }
75
+ }
76
+
77
+ @Nested
78
+ inner class StorageTest {
79
+ private lateinit var storage: StorageImpl
80
+
81
+ private lateinit var prefs: KVS
82
+
83
+ private lateinit var stream: EventStream
84
+
85
+ private lateinit var payload: String
86
+
87
+ @BeforeEach
88
+ fun setup () {
89
+ val trackEvent = TrackEvent (
90
+ event = " clicked" ,
91
+ properties = buildJsonObject { put(" foo" , " bar" ) })
92
+ .apply {
93
+ messageId = " qwerty-1234"
94
+ anonymousId = " anonId"
95
+ integrations = emptyJsonObject
96
+ context = emptyJsonObject
97
+ timestamp = Date (0 ).toInstant().toString()
98
+ }
99
+ payload = EncodeDefaultsJson .encodeToString(trackEvent)
100
+ prefs = InMemoryPrefs ()
101
+ stream = mockk<EventStream >(relaxed = true )
102
+ storage = StorageImpl (prefs, stream, mockk<Store >(), " test" , " key" , UnconfinedTestDispatcher ())
103
+ }
104
+
105
+ @Test
106
+ fun writeNewFileTest () = runTest {
107
+ every { stream.openOrCreate(any()) } returns true
108
+ storage.write(Storage .Constants .Events , payload)
109
+ verify(exactly = 1 ) {
110
+ stream.write(storage.begin)
111
+ stream.write(payload)
112
+ }
113
+ }
114
+
115
+ @Test
116
+ fun rolloverToNewFileTest () = runTest {
117
+ every { stream.openOrCreate(any()) } returns false andThen true
118
+ every { stream.length } returns Storage .MAX_FILE_SIZE + 1L
119
+ every { stream.isOpened } returns true
120
+
121
+ storage.write(Storage .Constants .Events , payload)
122
+ assertEquals(1 , prefs.get(storage.fileIndexKey, 0 ))
123
+ verify (exactly = 1 ) {
124
+ stream.finishAndClose(any())
125
+ stream.write(storage.begin)
126
+ stream.write(payload)
127
+ }
128
+
129
+ verify (exactly = 3 ){
130
+ stream.write(any())
131
+ }
132
+ }
133
+
134
+ @Test
135
+ fun largePayloadCauseExceptionTest () = runTest {
136
+ val letters = " abcdefghijklmnopqrstuvwxyz1234567890"
137
+ val largePayload = StringBuilder ()
138
+ for (i in 0 .. 1000 ) {
139
+ largePayload.append(letters)
140
+ }
141
+
142
+ assertThrows<Exception > {
143
+ storage.write(Storage .Constants .Events , largePayload.toString())
144
+ }
145
+ }
146
+
147
+ @Test
148
+ fun writePrefsAsyncTest () = runTest {
149
+ val expected = " userid"
150
+ assertNull(storage.read(Storage .Constants .UserId ))
151
+ storage.write(Storage .Constants .UserId , expected)
152
+ assertEquals(expected, storage.read(Storage .Constants .UserId ))
153
+ }
154
+
155
+ @Test
156
+ fun writePrefsTest () {
157
+ val expected = " userId"
158
+ assertNull(storage.read(Storage .Constants .UserId ))
159
+ storage.writePrefs(Storage .Constants .UserId , expected)
160
+ assertEquals(expected, storage.read(Storage .Constants .UserId ))
161
+ }
162
+
163
+ @Test
164
+ fun rolloverTest () = runTest {
165
+ every { stream.isOpened } returns true
166
+
167
+ storage.rollover()
168
+
169
+ verify (exactly = 1 ) {
170
+ stream.write(any())
171
+ stream.finishAndClose(any())
172
+ }
173
+
174
+ assertEquals(1 , prefs.get(storage.fileIndexKey, 0 ))
175
+ }
176
+
177
+ @Test
178
+ fun readTest () {
179
+ val files = listOf (" test1.tmp" , " test2" , " test3.tmp" , " test4" )
180
+ every { stream.read() } returns files
181
+ prefs.put(Storage .Constants .UserId .rawVal, " userId" )
182
+
183
+ val actual = storage.read(Storage .Constants .Events )
184
+ assertEquals(listOf (files[1 ], files[3 ]).joinToString(), actual)
185
+ assertEquals(" userId" , storage.read(Storage .Constants .UserId ))
186
+ }
187
+
188
+ @Test
189
+ fun removeTest () {
190
+ prefs.put(Storage .Constants .UserId .rawVal, " userId" )
191
+ storage.remove(Storage .Constants .UserId )
192
+
193
+ assertTrue(storage.remove(Storage .Constants .Events ))
194
+ assertNull(storage.read(Storage .Constants .UserId ))
195
+ }
196
+
197
+ @Test
198
+ fun removeFileTest () {
199
+ storage.removeFile(" file" )
200
+ verify (exactly = 1 ) {
201
+ stream.remove(" file" )
202
+ }
203
+
204
+ every { stream.remove(any()) } throws java.lang.Exception ()
205
+ assertFalse(storage.removeFile(" file" ))
206
+ }
207
+
208
+ @Test
209
+ fun readAsStream () {
210
+ storage.readAsStream(" file" )
211
+ verify (exactly = 1 ) {
212
+ stream.readAsStream(any())
213
+ }
214
+ }
215
+ }
216
+ }
0 commit comments