Merge branch 'release-0.9.7'
[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.hamcrest.Matchers.equalTo
12 import org.junit.Test
13 import org.mockito.ArgumentMatchers.any
14 import org.mockito.ArgumentMatchers.eq
15 import org.mockito.Mockito.`when`
16 import org.mockito.Mockito.times
17 import org.mockito.Mockito.verify
18 import java.io.ByteArrayOutputStream
19 import java.util.concurrent.TimeUnit
20
21 /**
22  * Unit test for [DefaultElementLoaderTest].
23  */
24 class DefaultElementLoaderTest {
25
26         companion object {
27                 private const val IMAGE_ID = "KSK@gpl.png"
28                 private val freenetURI = FreenetURI(IMAGE_ID)
29                 private const val decomposedKey = "CHK@DCiVgTWW9nnWHJc9EVwtFJ6jAfBSVyy~rgiPvhUKbS4,mNY85V0x7dYcv7SnEYo1PCC6y2wNWMDNt-y9UWQx9fI,AAMC--8/fru%CC%88hstu%CC%88ck.jpg"
30                 private const val normalizedKey = "CHK@DCiVgTWW9nnWHJc9EVwtFJ6jAfBSVyy~rgiPvhUKbS4,mNY85V0x7dYcv7SnEYo1PCC6y2wNWMDNt-y9UWQx9fI,AAMC--8/frühstück.jpg"
31                 private const val textKey = "KSK@gpl.html"
32                 private val sizeOkay = 2097152L
33                 private val sizeNotOkay = sizeOkay + 1
34         }
35
36         private val freenetInterface = mock<FreenetInterface>()
37         private val ticker = mock<Ticker>()
38         private val elementLoader = DefaultElementLoader(freenetInterface, ticker)
39         private val callback = capture<BackgroundFetchCallback>()
40
41         @Test
42         fun `image loader starts request for link that is not known`() {
43                 elementLoader.loadElement(IMAGE_ID)
44                 verify(freenetInterface).startFetch(eq(freenetURI), any<BackgroundFetchCallback>())
45         }
46
47         @Test
48         fun `element loader only starts request once`() {
49                 elementLoader.loadElement(IMAGE_ID)
50                 elementLoader.loadElement(IMAGE_ID)
51                 verify(freenetInterface).startFetch(eq(freenetURI), any<BackgroundFetchCallback>())
52         }
53
54         @Test
55         fun `element loader returns loading element on first call`() {
56                 assertThat(elementLoader.loadElement(IMAGE_ID).loading, `is`(true))
57         }
58
59         @Test
60         fun `element loader does not cancel on image mime type with 2 mib size`() {
61                 elementLoader.loadElement(IMAGE_ID)
62                 verify(freenetInterface).startFetch(eq(freenetURI), callback.capture())
63                 assertThat(callback.value.shouldCancel(freenetURI, "image/png", sizeOkay), `is`(false))
64         }
65
66         @Test
67         fun `element loader does cancel on image mime type with more than 2 mib size`() {
68                 elementLoader.loadElement(IMAGE_ID)
69                 verify(freenetInterface).startFetch(eq(freenetURI), callback.capture())
70                 assertThat(callback.value.shouldCancel(freenetURI, "image/png", sizeNotOkay), `is`(true))
71         }
72
73         @Test
74         fun `element loader does cancel on audio mime type`() {
75                 elementLoader.loadElement(IMAGE_ID)
76                 verify(freenetInterface).startFetch(eq(freenetURI), callback.capture())
77                 assertThat(callback.value.shouldCancel(freenetURI, "audio/mpeg", sizeOkay), `is`(true))
78         }
79
80         @Test
81         fun `element loader does cancel on video mime type`() {
82                 elementLoader.loadElement(IMAGE_ID)
83                 verify(freenetInterface).startFetch(eq(freenetURI), callback.capture())
84                 assertThat(callback.value.shouldCancel(freenetURI, "video/mkv", sizeOkay), `is`(true))
85         }
86
87         @Test
88         fun `element loader does cancel on text mime type`() {
89                 elementLoader.loadElement(IMAGE_ID)
90                 verify(freenetInterface).startFetch(eq(freenetURI), callback.capture())
91                 assertThat(callback.value.shouldCancel(freenetURI, "text/plain", sizeOkay), `is`(true))
92         }
93
94         @Test
95         fun `element loader does not cancel on text html mime type`() {
96                 elementLoader.loadElement(IMAGE_ID)
97                 verify(freenetInterface).startFetch(eq(freenetURI), callback.capture())
98                 assertThat(callback.value.shouldCancel(freenetURI, "text/html", sizeOkay), `is`(false))
99         }
100
101         @Test
102         fun `image loader can load image`() {
103                 elementLoader.loadElement(decomposedKey)
104                 verify(freenetInterface).startFetch(eq(FreenetURI(decomposedKey)), callback.capture())
105                 callback.value.loaded(FreenetURI(normalizedKey), "image/png", read("/static/images/unknown-image-0.png"))
106                 val linkedElement = elementLoader.loadElement(decomposedKey)
107                 assertThat(linkedElement, `is`(LinkedElement(normalizedKey, properties = mapOf(
108                                 "type" to "image", "size" to 2451, "sizeHuman" to "2 KiB"
109                 ))))
110         }
111
112         @Test
113         fun `element loader can extract description from description header`() {
114             elementLoader.loadElement(textKey)
115                 verify(freenetInterface).startFetch(eq(FreenetURI(textKey)), callback.capture())
116                 callback.value.loaded(FreenetURI(textKey), "text/html; charset=UTF-8", read("element-loader.html"))
117                 val linkedElement = elementLoader.loadElement(textKey)
118                 assertThat(linkedElement, equalTo(LinkedElement(textKey, properties = mapOf(
119                                 "type" to "html",
120                                 "size" to 266,
121                                 "sizeHuman" to "266 B",
122                                 "title" to "Some Nice Page Title",
123                                 "description" to "This is an example of a very nice freesite."
124                 ))))
125         }
126
127         @Test
128         fun `element loader can extract description from first non-heading paragraph`() {
129             elementLoader.loadElement(textKey)
130                 verify(freenetInterface).startFetch(eq(FreenetURI(textKey)), callback.capture())
131                 callback.value.loaded(FreenetURI(textKey), "text/html; charset=UTF-8", read("element-loader2.html"))
132                 val linkedElement = elementLoader.loadElement(textKey)
133                 assertThat(linkedElement, equalTo(LinkedElement(textKey, properties = mapOf(
134                                 "type" to "html",
135                                 "size" to 185,
136                                 "sizeHuman" to "185 B",
137                                 "title" to "Some Nice Page Title",
138                                 "description" to "This is the first paragraph of the very nice freesite."
139                 ))))
140         }
141
142         @Test
143         fun `element loader can not extract description if html is more complicated`() {
144             elementLoader.loadElement(textKey)
145                 verify(freenetInterface).startFetch(eq(FreenetURI(textKey)), callback.capture())
146                 callback.value.loaded(FreenetURI(textKey), "text/html; charset=UTF-8", read("element-loader3.html"))
147                 val linkedElement = elementLoader.loadElement(textKey)
148                 assertThat(linkedElement, equalTo(LinkedElement(textKey, properties = mapOf(
149                                 "type" to "html",
150                                 "size" to 204,
151                                 "sizeHuman" to "204 B",
152                                 "title" to "Some Nice Page Title",
153                                 "description" to null
154                 ))))
155         }
156
157         @Test
158         fun `element loader can not extract title if it is missing`() {
159             elementLoader.loadElement(textKey)
160                 verify(freenetInterface).startFetch(eq(FreenetURI(textKey)), callback.capture())
161                 callback.value.loaded(FreenetURI(textKey), "text/html; charset=UTF-8", read("element-loader4.html"))
162                 val linkedElement = elementLoader.loadElement(textKey)
163                 assertThat(linkedElement, equalTo(LinkedElement(textKey, properties = mapOf(
164                                 "type" to "html",
165                                 "size" to 229,
166                                 "sizeHuman" to "229 B",
167                                 "title" to null,
168                                 "description" to "This is an example of a very nice freesite."
169                 ))))
170         }
171
172         @Test
173         fun `image is not loaded again after it failed`() {
174                 elementLoader.loadElement(IMAGE_ID)
175                 verify(freenetInterface).startFetch(eq(freenetURI), callback.capture())
176                 callback.value.failed(freenetURI)
177                 assertThat(elementLoader.loadElement(IMAGE_ID).failed, `is`(true))
178                 verify(freenetInterface).startFetch(eq(freenetURI), callback.capture())
179         }
180
181         @Test
182         fun `image is loaded again after failure cache is expired`() {
183                 elementLoader.loadElement(IMAGE_ID)
184                 verify(freenetInterface).startFetch(eq(freenetURI), callback.capture())
185                 callback.value.failed(freenetURI)
186                 `when`(ticker.read()).thenReturn(TimeUnit.MINUTES.toNanos(31))
187                 val linkedElement = elementLoader.loadElement(IMAGE_ID)
188                 assertThat(linkedElement.failed, `is`(false))
189                 assertThat(linkedElement.loading, `is`(true))
190                 verify(freenetInterface, times(2)).startFetch(eq(freenetURI), callback.capture())
191         }
192
193         private fun read(resource: String): ByteArray =
194                         javaClass.getResourceAsStream(resource)?.use { input ->
195                                 ByteArrayOutputStream().use {
196                                         ByteStreams.copy(input, it)
197                                         it
198                                 }.toByteArray()
199                         } ?: ByteArray(0)
200
201 }