03b42c3033cca479001fb6eff87bd38c9dd3776a
[Sone.git] / src / test / kotlin / net / pterodactylus / sone / core / DefaultElementLoaderTest.kt
1 package net.pterodactylus.sone.core
2
3 import com.google.common.base.Ticker
4 import com.google.common.io.ByteStreams
5 import freenet.keys.FreenetURI
6 import net.pterodactylus.sone.core.FreenetInterface.BackgroundFetchCallback
7 import net.pterodactylus.sone.test.capture
8 import net.pterodactylus.sone.test.mock
9 import org.hamcrest.MatcherAssert.assertThat
10 import org.hamcrest.Matchers.`is`
11 import org.junit.Test
12 import org.mockito.ArgumentMatchers.any
13 import org.mockito.ArgumentMatchers.eq
14 import org.mockito.Mockito.`when`
15 import org.mockito.Mockito.times
16 import org.mockito.Mockito.verify
17 import java.io.ByteArrayOutputStream
18 import java.util.concurrent.TimeUnit
19
20 /**
21  * Unit test for [DefaultElementLoaderTest].
22  */
23 class DefaultElementLoaderTest {
24
25         companion object {
26                 private const val IMAGE_ID = "KSK@gpl.png"
27                 private val freenetURI = FreenetURI(IMAGE_ID)
28                 private const val decomposedKey = "CHK@DCiVgTWW9nnWHJc9EVwtFJ6jAfBSVyy~rgiPvhUKbS4,mNY85V0x7dYcv7SnEYo1PCC6y2wNWMDNt-y9UWQx9fI,AAMC--8/fru%CC%88hstu%CC%88ck.jpg"
29                 private const val normalizedKey = "CHK@DCiVgTWW9nnWHJc9EVwtFJ6jAfBSVyy~rgiPvhUKbS4,mNY85V0x7dYcv7SnEYo1PCC6y2wNWMDNt-y9UWQx9fI,AAMC--8/frühstück.jpg"
30                 private val sizeOkay = 2097152L
31                 private val sizeNotOkay = sizeOkay + 1
32         }
33
34         private val freenetInterface = mock<FreenetInterface>()
35         private val ticker = mock<Ticker>()
36         private val elementLoader = DefaultElementLoader(freenetInterface, ticker)
37         private val callback = capture<BackgroundFetchCallback>()
38
39         @Test
40         fun `image loader starts request for link that is not known`() {
41                 elementLoader.loadElement(IMAGE_ID)
42                 verify(freenetInterface).startFetch(eq(freenetURI), any<BackgroundFetchCallback>())
43         }
44
45         @Test
46         fun `element loader only starts request once`() {
47                 elementLoader.loadElement(IMAGE_ID)
48                 elementLoader.loadElement(IMAGE_ID)
49                 verify(freenetInterface).startFetch(eq(freenetURI), any<BackgroundFetchCallback>())
50         }
51
52         @Test
53         fun `element loader returns loading element on first call`() {
54                 assertThat(elementLoader.loadElement(IMAGE_ID).loading, `is`(true))
55         }
56
57         @Test
58         fun `element loader does not cancel on image mime type with 2 mib size`() {
59                 elementLoader.loadElement(IMAGE_ID)
60                 verify(freenetInterface).startFetch(eq(freenetURI), callback.capture())
61                 assertThat(callback.value.shouldCancel(freenetURI, "image/png", sizeOkay), `is`(false))
62         }
63
64         @Test
65         fun `element loader does cancel on image mime type with more than 2 mib size`() {
66                 elementLoader.loadElement(IMAGE_ID)
67                 verify(freenetInterface).startFetch(eq(freenetURI), callback.capture())
68                 assertThat(callback.value.shouldCancel(freenetURI, "image/png", sizeNotOkay), `is`(true))
69         }
70
71         @Test
72         fun `element loader does  cancel on audio mime type`() {
73                 elementLoader.loadElement(IMAGE_ID)
74                 verify(freenetInterface).startFetch(eq(freenetURI), callback.capture())
75                 assertThat(callback.value.shouldCancel(freenetURI, "audio/mpeg", sizeOkay), `is`(true))
76         }
77
78         @Test
79         fun `element loader does not cancel on video mime type`() {
80                 elementLoader.loadElement(IMAGE_ID)
81                 verify(freenetInterface).startFetch(eq(freenetURI), callback.capture())
82                 assertThat(callback.value.shouldCancel(freenetURI, "video/mkv", sizeOkay), `is`(true))
83         }
84
85         @Test
86         fun `element loader does not cancel on text mime type`() {
87                 elementLoader.loadElement(IMAGE_ID)
88                 verify(freenetInterface).startFetch(eq(freenetURI), callback.capture())
89                 assertThat(callback.value.shouldCancel(freenetURI, "text/plain", sizeOkay), `is`(true))
90         }
91
92         @Test
93         fun `image loader can load image`() {
94                 elementLoader.loadElement(decomposedKey)
95                 verify(freenetInterface).startFetch(eq(FreenetURI(decomposedKey)), callback.capture())
96                 callback.value.loaded(FreenetURI(normalizedKey), "image/png", read("/static/images/unknown-image-0.png"))
97                 val linkedElement = elementLoader.loadElement(decomposedKey)
98                 assertThat(linkedElement, `is`(LinkedElement(normalizedKey, properties = mapOf("size" to 2451, "sizeHuman" to "2 KiB"))))
99         }
100
101         @Test
102         fun `image is not loaded again after it failed`() {
103                 elementLoader.loadElement(IMAGE_ID)
104                 verify(freenetInterface).startFetch(eq(freenetURI), callback.capture())
105                 callback.value.failed(freenetURI)
106                 assertThat(elementLoader.loadElement(IMAGE_ID).failed, `is`(true))
107                 verify(freenetInterface).startFetch(eq(freenetURI), callback.capture())
108         }
109
110         @Test
111         fun `image is loaded again after failure cache is expired`() {
112                 elementLoader.loadElement(IMAGE_ID)
113                 verify(freenetInterface).startFetch(eq(freenetURI), callback.capture())
114                 callback.value.failed(freenetURI)
115                 `when`(ticker.read()).thenReturn(TimeUnit.MINUTES.toNanos(31))
116                 val linkedElement = elementLoader.loadElement(IMAGE_ID)
117                 assertThat(linkedElement.failed, `is`(false))
118                 assertThat(linkedElement.loading, `is`(true))
119                 verify(freenetInterface, times(2)).startFetch(eq(freenetURI), callback.capture())
120         }
121
122         private fun read(resource: String): ByteArray =
123                         javaClass.getResourceAsStream(resource)?.use { input ->
124                                 ByteArrayOutputStream().use {
125                                         ByteStreams.copy(input, it)
126                                         it
127                                 }.toByteArray()
128                         } ?: ByteArray(0)
129
130 }