Skip to content

Commit 33e99a8

Browse files
committed
test: add unit tests for OSIABPdfHelper
1 parent 09d63f0 commit 33e99a8

File tree

4 files changed

+219
-3
lines changed

4 files changed

+219
-3
lines changed

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ dependencies {
135135
testImplementation 'junit:junit:4.13.2'
136136
testImplementation 'org.robolectric:robolectric:4.12.2'
137137
testImplementation 'org.mockito:mockito-core:5.12.0'
138+
testImplementation "io.mockk:mockk:1.13.10"
138139
testImplementation 'androidx.test:core:1.6.1'
139140
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3'
140141
}

src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/helpers/OSIABPdfHelper.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ import java.net.HttpURLConnection
77
import java.net.URL
88

99
object OSIABPdfHelper {
10+
11+
interface UrlFactory {
12+
fun create(url: String): URL
13+
}
14+
15+
private class DefaultUrlFactory : UrlFactory {
16+
override fun create(url: String): URL = URL(url)
17+
}
1018

1119
fun isContentTypeApplicationPdf(urlString: String): Boolean {
1220
return try {
@@ -23,10 +31,10 @@ object OSIABPdfHelper {
2331
}
2432
}
2533

26-
fun checkPdfByRequest(urlString: String, method: String): Boolean {
34+
fun checkPdfByRequest(urlString: String, method: String, urlFactory: UrlFactory = DefaultUrlFactory()): Boolean {
2735
var conn: HttpURLConnection? = null
2836
return try {
29-
conn = (URL(urlString).openConnection() as? HttpURLConnection)
37+
conn = (urlFactory.create(urlString).openConnection() as? HttpURLConnection)
3038
conn?.run {
3139
instanceFollowRedirects = true
3240
requestMethod = method

src/main/java/com.outsystems.plugins.inappbrowser/osinappbrowserlib/views/OSIABWebViewActivity.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,6 @@ class OSIABWebViewActivity : AppCompatActivity() {
379379
}
380380
}
381381

382-
383382
// set back to false so that the next successful load
384383
// if the load fails, onReceivedError takes care of setting it back to true
385384
hasLoadError = false
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
package com.outsystems.plugins.inappbrowser.osinappbrowserlib.helpers
2+
3+
import android.content.Context
4+
import io.mockk.every
5+
import io.mockk.mockk
6+
import io.mockk.mockkObject
7+
import io.mockk.unmockkObject
8+
import io.mockk.verify
9+
import org.junit.Assert.assertFalse
10+
import org.junit.Assert.assertTrue
11+
import org.junit.Test
12+
import java.net.HttpURLConnection
13+
import java.net.ServerSocket
14+
import java.net.Socket
15+
import java.net.URL
16+
import java.nio.file.Files
17+
import kotlin.concurrent.thread
18+
19+
class OSIABPdfHelperTest {
20+
21+
@Test
22+
fun `isContentTypeApplicationPdf returns true if HEAD is PDF`() {
23+
mockkObject(OSIABPdfHelper)
24+
every { OSIABPdfHelper.checkPdfByRequest(any(), "HEAD", any()) } returns true
25+
26+
val result = OSIABPdfHelper.isContentTypeApplicationPdf("http://example.com")
27+
28+
assertTrue(result)
29+
verify { OSIABPdfHelper.checkPdfByRequest(any(), "HEAD", any()) }
30+
verify(exactly = 0) { OSIABPdfHelper.checkPdfByRequest(any(), "GET", any()) }
31+
unmockkObject(OSIABPdfHelper)
32+
}
33+
34+
@Test
35+
fun `isContentTypeApplicationPdf falls back to GET if HEAD fails`() {
36+
mockkObject(OSIABPdfHelper)
37+
every { OSIABPdfHelper.checkPdfByRequest(any(), "HEAD", any()) } returns false
38+
every { OSIABPdfHelper.checkPdfByRequest(any(), "GET", any()) } returns true
39+
40+
val result = OSIABPdfHelper.isContentTypeApplicationPdf("http://example.com")
41+
42+
assertTrue(result)
43+
verify { OSIABPdfHelper.checkPdfByRequest(any(), "HEAD", any()) }
44+
verify { OSIABPdfHelper.checkPdfByRequest(any(), "GET", any()) }
45+
unmockkObject(OSIABPdfHelper)
46+
}
47+
48+
@Test
49+
fun `isContentTypeApplicationPdf returns false if both HEAD and GET fail`() {
50+
mockkObject(OSIABPdfHelper)
51+
every { OSIABPdfHelper.checkPdfByRequest(any(), "HEAD", any()) } returns false
52+
every { OSIABPdfHelper.checkPdfByRequest(any(), "GET", any()) } returns false
53+
54+
val result = OSIABPdfHelper.isContentTypeApplicationPdf("http://example.com")
55+
56+
assertFalse(result)
57+
verify { OSIABPdfHelper.checkPdfByRequest(any(), "HEAD", any()) }
58+
verify { OSIABPdfHelper.checkPdfByRequest(any(), "GET", any()) }
59+
unmockkObject(OSIABPdfHelper)
60+
}
61+
62+
@Test
63+
fun `isContentTypeApplicationPdf returns false if exception occurs`() {
64+
mockkObject(OSIABPdfHelper)
65+
every {
66+
OSIABPdfHelper.checkPdfByRequest(
67+
any(),
68+
any(),
69+
any()
70+
)
71+
} throws RuntimeException("Network error")
72+
73+
val result = OSIABPdfHelper.isContentTypeApplicationPdf("http://example.com")
74+
75+
assertFalse(result)
76+
verify { OSIABPdfHelper.checkPdfByRequest(any(), "HEAD", any()) }
77+
unmockkObject(OSIABPdfHelper)
78+
}
79+
80+
@Test
81+
fun `returns true when content type is application_pdf`() {
82+
val urlFactory = mockk<OSIABPdfHelper.UrlFactory>()
83+
val url = mockk<URL>()
84+
val conn = mockk<HttpURLConnection>(relaxed = true)
85+
every { urlFactory.create(any()) } returns url
86+
every { url.openConnection() } returns conn
87+
every { conn.contentType } returns "application/pdf"
88+
every { conn.connect() } returns Unit
89+
90+
val result = OSIABPdfHelper.checkPdfByRequest("http://example.com/test.pdf", "HEAD", urlFactory)
91+
92+
assertTrue(result)
93+
verify { conn.connect() }
94+
verify { conn.disconnect() }
95+
}
96+
97+
@Test
98+
fun `returns true when disposition header contains pdf and content type is empty`() {
99+
val urlFactory = mockk<OSIABPdfHelper.UrlFactory>()
100+
val url = mockk<URL>()
101+
val conn = mockk<HttpURLConnection>(relaxed = true)
102+
every { urlFactory.create(any()) } returns url
103+
every { url.openConnection() } returns conn
104+
every { conn.contentType } returns null
105+
every { conn.getHeaderField("Content-Disposition") } returns "attachment; filename=test.pdf"
106+
every { conn.connect() } returns Unit
107+
108+
val result = OSIABPdfHelper.checkPdfByRequest("http://example.com/test.pdf", "HEAD", urlFactory)
109+
110+
assertTrue(result)
111+
verify { conn.connect() }
112+
verify { conn.disconnect() }
113+
}
114+
115+
@Test
116+
fun `returns false when neither content type nor disposition indicate pdf`() {
117+
val urlFactory = mockk<OSIABPdfHelper.UrlFactory>()
118+
val url = mockk<URL>()
119+
val conn = mockk<HttpURLConnection>(relaxed = true)
120+
every { urlFactory.create(any()) } returns url
121+
every { url.openConnection() } returns conn
122+
every { conn.contentType } returns "text/html"
123+
every { conn.getHeaderField("Content-Disposition") } returns "inline"
124+
every { conn.connect() } returns Unit
125+
126+
val result = OSIABPdfHelper.checkPdfByRequest("http://example.com/test.pdf", "HEAD", urlFactory)
127+
128+
assertFalse(result)
129+
verify { conn.connect() }
130+
verify { conn.disconnect() }
131+
}
132+
133+
@Test
134+
fun `sets Range header for GET method`() {
135+
val urlFactory = mockk<OSIABPdfHelper.UrlFactory>()
136+
val url = mockk<URL>()
137+
val conn = mockk<HttpURLConnection>(relaxed = true)
138+
every { urlFactory.create(any()) } returns url
139+
every { url.openConnection() } returns conn
140+
every { conn.contentType } returns "application/pdf"
141+
every { conn.connect() } returns Unit
142+
143+
OSIABPdfHelper.checkPdfByRequest("http://example.com/test.pdf", "GET", urlFactory)
144+
145+
verify { conn.setRequestProperty("Range", "bytes=0-0") }
146+
verify { conn.connect() }
147+
verify { conn.disconnect() }
148+
}
149+
150+
@Test
151+
fun `returns false if connection is null`() {
152+
val urlFactory = mockk<OSIABPdfHelper.UrlFactory>()
153+
val url = mockk<URL>()
154+
every { urlFactory.create(any()) } returns url
155+
every { url.openConnection() } returns null
156+
157+
val result = OSIABPdfHelper.checkPdfByRequest("http://example.com/test.pdf", "HEAD", urlFactory)
158+
159+
assertFalse(result)
160+
}
161+
162+
@Test
163+
fun `returns false if exception is thrown`() {
164+
val urlFactory = mockk<OSIABPdfHelper.UrlFactory>()
165+
every { urlFactory.create(any()) } throws RuntimeException("Network error")
166+
167+
val result = try {
168+
OSIABPdfHelper.checkPdfByRequest("http://example.com/test.pdf", "HEAD", urlFactory)
169+
} catch (_: Exception) {
170+
false
171+
}
172+
173+
assertFalse(result)
174+
}
175+
176+
@Test
177+
fun `downloadPdfToCache creates file with content`() {
178+
val server = ServerSocket(0)
179+
val port = server.localPort
180+
val pdfBytes = "%PDF-1.4 test".toByteArray()
181+
thread {
182+
val client: Socket = server.accept()
183+
val out = client.getOutputStream()
184+
out.write(
185+
("HTTP/1.1 200 OK\r\n" +
186+
"Content-Type: application/pdf\r\n" +
187+
"Content-Length: ${pdfBytes.size}\r\n" +
188+
"\r\n").toByteArray()
189+
)
190+
out.write(pdfBytes)
191+
out.flush()
192+
client.close()
193+
server.close()
194+
}
195+
196+
val context = mockk<Context>()
197+
val cacheDir = Files.createTempDirectory("test_cache").toFile()
198+
every { context.cacheDir } returns cacheDir
199+
200+
val url = "http://localhost:$port/test.pdf"
201+
val file = OSIABPdfHelper.downloadPdfToCache(context, url)
202+
203+
assertTrue(file.exists())
204+
assertTrue(file.readBytes().copyOfRange(0, 8).contentEquals("%PDF-1.4".toByteArray()))
205+
file.delete()
206+
cacheDir.deleteRecursively()
207+
}
208+
}

0 commit comments

Comments
 (0)