Skip to content

Commit db52ee4

Browse files
committed
test: add TUS integration tests for upload lifecycle, creation with upload, and error handling
1 parent d7ba049 commit db52ee4

1 file changed

Lines changed: 68 additions & 0 deletions

File tree

opencloudComLibrary/src/test/java/eu/opencloud/android/lib/resources/files/tus/TusIntegrationTest.kt

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ import eu.opencloud.android.lib.common.accounts.AccountUtils
1010
import eu.opencloud.android.lib.common.authentication.OpenCloudCredentialsFactory
1111
import eu.opencloud.android.lib.common.operations.RemoteOperationResult
1212
import eu.opencloud.android.lib.resources.files.tus.CreateTusUploadRemoteOperation.Base64Encoder
13+
import okhttp3.mockwebserver.Dispatcher
1314
import okhttp3.mockwebserver.MockResponse
1415
import okhttp3.mockwebserver.MockWebServer
16+
import okhttp3.mockwebserver.RecordedRequest
1517
import org.junit.After
1618
import org.junit.Assert.*
1719
import org.junit.Before
@@ -179,6 +181,72 @@ class TusIntegrationTest {
179181
assertEquals("1.0.0", delReq.getHeader("Tus-Resumable"))
180182
}
181183

184+
@Test
185+
fun creation_with_upload_returns_offset() {
186+
val client = newClient()
187+
val collectionPath = "/remote.php/dav/uploads/$userId"
188+
val locationPath = "$collectionPath/UPLD-WITH-DATA"
189+
val localFile = File.createTempFile("tus", ".bin").apply {
190+
writeBytes(ByteArray(100) { it.toByte() })
191+
}
192+
val firstChunkSize = 50L
193+
194+
// POST Create with Upload -> 201 + Location + Upload-Offset
195+
server.dispatcher = object : Dispatcher() {
196+
override fun dispatch(request: RecordedRequest): MockResponse {
197+
if (request.path == collectionPath) {
198+
// Verify body content
199+
val bodySize = request.bodySize
200+
assertEquals(firstChunkSize, bodySize)
201+
202+
// Verify body bytes (first 50 bytes of the file)
203+
val expectedBytes = ByteArray(firstChunkSize.toInt()) { it.toByte() }
204+
val actualBytes = request.body.readByteArray()
205+
assertArrayEquals(expectedBytes, actualBytes)
206+
207+
return MockResponse()
208+
.setResponseCode(201)
209+
.addHeader("Tus-Resumable", "1.0.0")
210+
.addHeader("Location", locationPath)
211+
.addHeader("Upload-Offset", bodySize.toString())
212+
}
213+
return MockResponse().setResponseCode(404)
214+
}
215+
}
216+
217+
val create = CreateTusUploadRemoteOperation(
218+
file = localFile,
219+
remotePath = "/test-with-data.bin",
220+
mimetype = "application/octet-stream",
221+
metadata = mapOf("filename" to "test-with-data.bin"),
222+
useCreationWithUpload = true,
223+
firstChunkSize = firstChunkSize,
224+
tusUrl = null,
225+
collectionUrlOverride = server.url(collectionPath).toString(),
226+
base64Encoder = object : Base64Encoder {
227+
override fun encode(bytes: ByteArray): String =
228+
Base64.getEncoder().encodeToString(bytes)
229+
}
230+
)
231+
232+
val createResult = create.execute(client)
233+
assertTrue("Create operation failed", createResult.isSuccess)
234+
235+
val creationResult = createResult.data
236+
assertNotNull(creationResult)
237+
assertEquals(firstChunkSize, creationResult!!.uploadOffset)
238+
assertTrue(creationResult.uploadUrl.endsWith(locationPath))
239+
240+
// Verify POST request
241+
val postReq = server.takeRequest()
242+
assertEquals("POST", postReq.method)
243+
assertEquals("Bearer $token", postReq.getHeader("Authorization"))
244+
assertEquals("1.0.0", postReq.getHeader("Tus-Resumable"))
245+
// creation-with-upload sends Content-Type and Content-Length for the chunk
246+
assertEquals("application/offset+octet-stream", postReq.getHeader("Content-Type"))
247+
assertEquals(firstChunkSize.toString(), postReq.getHeader("Content-Length"))
248+
}
249+
182250
@Test
183251
fun patch_wrong_offset_returns_conflict() {
184252
val client = newClient()

0 commit comments

Comments
 (0)