Skip to content

Commit b06ed08

Browse files
committed
test: add integration tests for absolute upload URIs with and without path
- Add testUploadWithAbsoluteUploadUri (https://uploads.example.com) and testUploadWithAbsoluteUploadUriWithPath (https://uploads.example.com/api) in AbstractITTusFileUploadService and AbstractITRufhProtocol - Assert returned Location header matches configured base URL with persisted UploadId - Validate upload, download, and info retrieval across all storage backends (Disk, Lease-file, S3, Azure Blob)
1 parent 4ea56d7 commit b06ed08

7 files changed

Lines changed: 267 additions & 4 deletions

File tree

src/test/java/me/desair/tus/server/AbstractITRufhProtocol.java

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import static org.hamcrest.MatcherAssert.assertThat;
55
import static org.hamcrest.Matchers.containsInAnyOrder;
66
import static org.junit.Assert.assertFalse;
7+
import static org.junit.Assert.assertNotNull;
78
import static org.junit.Assert.assertNull;
89
import static org.junit.Assert.assertTrue;
910

@@ -43,6 +44,17 @@ public abstract class AbstractITRufhProtocol {
4344
*/
4445
protected abstract TusFileUploadService createTusFileUploadService() throws Exception;
4546

47+
/**
48+
* Factory method implemented by subclasses to supply a {@link TusFileUploadService} instance
49+
* configured with a specific upload URI.
50+
*
51+
* @param uploadUri The upload URI to configure
52+
* @return configured TusFileUploadService instance
53+
* @throws Exception if service creation fails
54+
*/
55+
protected abstract TusFileUploadService createTusFileUploadService(String uploadUri)
56+
throws Exception;
57+
4658
@Before
4759
public void setUp() throws Exception {
4860
reset();
@@ -608,6 +620,110 @@ public void testContentDigestValidation() throws Exception {
608620
assertResponseHeader(HttpHeader.UPLOAD_OFFSET, "12");
609621
}
610622

623+
@Test
624+
public void testUploadWithAbsoluteUploadUri() throws Exception {
625+
String absoluteBaseUri = "https://uploads.example.com";
626+
TusFileUploadService service = createTusFileUploadService(absoluteBaseUri);
627+
628+
String uploadContent = "RUFH Absolute URL content";
629+
630+
// Step 1: POST to create upload on root endpoint "/"
631+
servletRequest.setMethod("POST");
632+
servletRequest.setRequestURI("/");
633+
servletRequest.addHeader(HttpHeader.UPLOAD_LENGTH, "" + uploadContent.getBytes().length);
634+
servletRequest.addHeader(HttpHeader.UPLOAD_COMPLETE, "?0");
635+
636+
service.process(servletRequest, servletResponse, OWNER_KEY);
637+
assertThat(servletResponse.getStatus(), is(HttpServletResponse.SC_CREATED));
638+
String locationHeader = servletResponse.getHeader(HttpHeader.LOCATION);
639+
assertNotNull(locationHeader);
640+
641+
// Retrieve upload info using the full Location header to verify ID lookup works with absolute
642+
// URLs
643+
UploadInfo infoByLocation = service.getUploadInfo(locationHeader, OWNER_KEY);
644+
assertTrue(infoByLocation != null && infoByLocation.getId() != null);
645+
assertThat(locationHeader, is("https://uploads.example.com/" + infoByLocation.getId()));
646+
647+
String uploadPath = "/" + infoByLocation.getId();
648+
649+
// Step 2: PATCH upload bytes
650+
reset();
651+
servletRequest.setMethod("PATCH");
652+
servletRequest.setRequestURI(uploadPath);
653+
servletRequest.addHeader(HttpHeader.CONTENT_TYPE, HttpHeader.CONTENT_TYPE_PARTIAL_UPLOAD);
654+
servletRequest.addHeader(HttpHeader.UPLOAD_OFFSET, "0");
655+
servletRequest.addHeader(HttpHeader.UPLOAD_COMPLETE, "?1");
656+
servletRequest.setContent(uploadContent.getBytes());
657+
658+
service.process(servletRequest, servletResponse, OWNER_KEY);
659+
assertThat(servletResponse.getStatus(), is(HttpServletResponse.SC_OK));
660+
assertThat(
661+
servletResponse.getHeader(HttpHeader.UPLOAD_OFFSET),
662+
is("" + uploadContent.getBytes().length));
663+
assertThat(servletResponse.getHeader(HttpHeader.UPLOAD_COMPLETE), is("?1"));
664+
665+
// Verify upload info is also retrievable via relative path
666+
UploadInfo infoByPath = service.getUploadInfo(uploadPath, OWNER_KEY);
667+
assertTrue(infoByPath != null && infoByLocation.getId().equals(infoByPath.getId()));
668+
669+
// Step 3: Verify content
670+
try (InputStream stream = service.getUploadedBytes(uploadPath, OWNER_KEY)) {
671+
assertThat(IOUtils.toString(stream, StandardCharsets.UTF_8), is(uploadContent));
672+
}
673+
}
674+
675+
@Test
676+
public void testUploadWithAbsoluteUploadUriWithPath() throws Exception {
677+
String absoluteBaseUri = "https://uploads.example.com/api";
678+
TusFileUploadService service = createTusFileUploadService(absoluteBaseUri);
679+
680+
String uploadContent = "RUFH Absolute URL with path content";
681+
682+
// Step 1: POST to create upload on endpoint "/api"
683+
servletRequest.setMethod("POST");
684+
servletRequest.setRequestURI("/api");
685+
servletRequest.addHeader(HttpHeader.UPLOAD_LENGTH, "" + uploadContent.getBytes().length);
686+
servletRequest.addHeader(HttpHeader.UPLOAD_COMPLETE, "?0");
687+
688+
service.process(servletRequest, servletResponse, OWNER_KEY);
689+
assertThat(servletResponse.getStatus(), is(HttpServletResponse.SC_CREATED));
690+
String locationHeader = servletResponse.getHeader(HttpHeader.LOCATION);
691+
assertNotNull(locationHeader);
692+
693+
// Retrieve upload info using the full Location header to verify ID lookup works with absolute
694+
// URLs
695+
UploadInfo infoByLocation = service.getUploadInfo(locationHeader, OWNER_KEY);
696+
assertTrue(infoByLocation != null && infoByLocation.getId() != null);
697+
assertThat(locationHeader, is("https://uploads.example.com/api/" + infoByLocation.getId()));
698+
699+
String uploadPath = "/api/" + infoByLocation.getId();
700+
701+
// Step 2: PATCH upload bytes
702+
reset();
703+
servletRequest.setMethod("PATCH");
704+
servletRequest.setRequestURI(uploadPath);
705+
servletRequest.addHeader(HttpHeader.CONTENT_TYPE, HttpHeader.CONTENT_TYPE_PARTIAL_UPLOAD);
706+
servletRequest.addHeader(HttpHeader.UPLOAD_OFFSET, "0");
707+
servletRequest.addHeader(HttpHeader.UPLOAD_COMPLETE, "?1");
708+
servletRequest.setContent(uploadContent.getBytes());
709+
710+
service.process(servletRequest, servletResponse, OWNER_KEY);
711+
assertThat(servletResponse.getStatus(), is(HttpServletResponse.SC_OK));
712+
assertThat(
713+
servletResponse.getHeader(HttpHeader.UPLOAD_OFFSET),
714+
is("" + uploadContent.getBytes().length));
715+
assertThat(servletResponse.getHeader(HttpHeader.UPLOAD_COMPLETE), is("?1"));
716+
717+
// Verify upload info is also retrievable via relative path
718+
UploadInfo infoByPath = service.getUploadInfo(uploadPath, OWNER_KEY);
719+
assertTrue(infoByPath != null && infoByLocation.getId().equals(infoByPath.getId()));
720+
721+
// Step 3: Verify content
722+
try (InputStream stream = service.getUploadedBytes(uploadPath, OWNER_KEY)) {
723+
assertThat(IOUtils.toString(stream, StandardCharsets.UTF_8), is(uploadContent));
724+
}
725+
}
726+
611727
// ===============================================================================================
612728
// ASSERTION HELPERS
613729
// ===============================================================================================

src/test/java/me/desair/tus/server/AbstractITTusFileUploadService.java

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1950,6 +1950,132 @@ public void testCreationWithUploadChecksumMismatch() throws Exception {
19501950
assertResponseStatus(460); // Checksum mismatch
19511951
}
19521952

1953+
@Test
1954+
public void testUploadWithAbsoluteUploadUri() throws Exception {
1955+
String absoluteBaseUri = "https://uploads.example.com";
1956+
TusFileUploadService service = createTusFileUploadService(absoluteBaseUri);
1957+
1958+
String uploadContent = "Absolute URL upload content";
1959+
1960+
// Step 1: POST to create upload on root endpoint "/"
1961+
servletRequest.setMethod("POST");
1962+
servletRequest.setRequestURI("/");
1963+
servletRequest.addHeader(HttpHeader.CONTENT_LENGTH, 0);
1964+
servletRequest.addHeader(HttpHeader.UPLOAD_LENGTH, uploadContent.getBytes().length);
1965+
servletRequest.addHeader(HttpHeader.TUS_RESUMABLE, "1.0.0");
1966+
1967+
service.process(servletRequest, servletResponse, OWNER_KEY);
1968+
assertResponseStatus(HttpServletResponse.SC_CREATED);
1969+
assertResponseHeader(HttpHeader.TUS_RESUMABLE, "1.0.0");
1970+
String locationHeader = servletResponse.getHeader(HttpHeader.LOCATION);
1971+
assertResponseHeaderNotBlank(HttpHeader.LOCATION);
1972+
1973+
// Retrieve upload info using the full Location header to verify ID lookup works with absolute
1974+
// URLs
1975+
UploadInfo infoByLocation = service.getUploadInfo(locationHeader, OWNER_KEY);
1976+
assertTrue(infoByLocation != null && infoByLocation.getId() != null);
1977+
assertThat(locationHeader, is("https://uploads.example.com/" + infoByLocation.getId()));
1978+
1979+
String uploadPath = "/" + infoByLocation.getId();
1980+
1981+
// Step 2: PATCH bytes to the upload resource
1982+
reset();
1983+
servletRequest.setMethod("PATCH");
1984+
servletRequest.setRequestURI(uploadPath);
1985+
servletRequest.addHeader(HttpHeader.CONTENT_TYPE, "application/offset+octet-stream");
1986+
servletRequest.addHeader(HttpHeader.CONTENT_LENGTH, uploadContent.getBytes().length);
1987+
servletRequest.addHeader(HttpHeader.UPLOAD_OFFSET, 0);
1988+
servletRequest.addHeader(HttpHeader.TUS_RESUMABLE, "1.0.0");
1989+
servletRequest.setContent(uploadContent.getBytes());
1990+
1991+
service.process(servletRequest, servletResponse, OWNER_KEY);
1992+
assertResponseStatus(HttpServletResponse.SC_NO_CONTENT);
1993+
assertResponseHeader(HttpHeader.UPLOAD_OFFSET, "" + uploadContent.getBytes().length);
1994+
1995+
// Step 3: HEAD request to verify completion
1996+
reset();
1997+
servletRequest.setMethod("HEAD");
1998+
servletRequest.setRequestURI(uploadPath);
1999+
servletRequest.addHeader(HttpHeader.TUS_RESUMABLE, "1.0.0");
2000+
2001+
service.process(servletRequest, servletResponse, OWNER_KEY);
2002+
assertResponseStatus(HttpServletResponse.SC_NO_CONTENT);
2003+
assertResponseHeader(HttpHeader.UPLOAD_OFFSET, "" + uploadContent.getBytes().length);
2004+
assertResponseHeader(HttpHeader.UPLOAD_LENGTH, "" + uploadContent.getBytes().length);
2005+
2006+
// Verify upload info is also retrievable via relative path
2007+
UploadInfo infoByPath = service.getUploadInfo(uploadPath, OWNER_KEY);
2008+
assertTrue(infoByPath != null && infoByLocation.getId().equals(infoByPath.getId()));
2009+
2010+
// Step 4: Verify uploaded bytes
2011+
try (InputStream stream = service.getUploadedBytes(uploadPath, OWNER_KEY)) {
2012+
assertThat(IOUtils.toString(stream, StandardCharsets.UTF_8), is(uploadContent));
2013+
}
2014+
}
2015+
2016+
@Test
2017+
public void testUploadWithAbsoluteUploadUriWithPath() throws Exception {
2018+
String absoluteBaseUri = "https://uploads.example.com/api";
2019+
TusFileUploadService service = createTusFileUploadService(absoluteBaseUri);
2020+
2021+
String uploadContent = "Absolute URL with path upload content";
2022+
2023+
// Step 1: POST to create upload on endpoint "/api"
2024+
servletRequest.setMethod("POST");
2025+
servletRequest.setRequestURI("/api");
2026+
servletRequest.addHeader(HttpHeader.CONTENT_LENGTH, 0);
2027+
servletRequest.addHeader(HttpHeader.UPLOAD_LENGTH, uploadContent.getBytes().length);
2028+
servletRequest.addHeader(HttpHeader.TUS_RESUMABLE, "1.0.0");
2029+
2030+
service.process(servletRequest, servletResponse, OWNER_KEY);
2031+
assertResponseStatus(HttpServletResponse.SC_CREATED);
2032+
assertResponseHeader(HttpHeader.TUS_RESUMABLE, "1.0.0");
2033+
String locationHeader = servletResponse.getHeader(HttpHeader.LOCATION);
2034+
assertResponseHeaderNotBlank(HttpHeader.LOCATION);
2035+
2036+
// Retrieve upload info using the full Location header to verify ID lookup works with absolute
2037+
// URLs
2038+
UploadInfo infoByLocation = service.getUploadInfo(locationHeader, OWNER_KEY);
2039+
assertTrue(infoByLocation != null && infoByLocation.getId() != null);
2040+
assertThat(locationHeader, is("https://uploads.example.com/api/" + infoByLocation.getId()));
2041+
2042+
String uploadPath = "/api/" + infoByLocation.getId();
2043+
2044+
// Step 2: PATCH bytes to the upload resource
2045+
reset();
2046+
servletRequest.setMethod("PATCH");
2047+
servletRequest.setRequestURI(uploadPath);
2048+
servletRequest.addHeader(HttpHeader.CONTENT_TYPE, "application/offset+octet-stream");
2049+
servletRequest.addHeader(HttpHeader.CONTENT_LENGTH, uploadContent.getBytes().length);
2050+
servletRequest.addHeader(HttpHeader.UPLOAD_OFFSET, 0);
2051+
servletRequest.addHeader(HttpHeader.TUS_RESUMABLE, "1.0.0");
2052+
servletRequest.setContent(uploadContent.getBytes());
2053+
2054+
service.process(servletRequest, servletResponse, OWNER_KEY);
2055+
assertResponseStatus(HttpServletResponse.SC_NO_CONTENT);
2056+
assertResponseHeader(HttpHeader.UPLOAD_OFFSET, "" + uploadContent.getBytes().length);
2057+
2058+
// Step 3: HEAD request to verify completion
2059+
reset();
2060+
servletRequest.setMethod("HEAD");
2061+
servletRequest.setRequestURI(uploadPath);
2062+
servletRequest.addHeader(HttpHeader.TUS_RESUMABLE, "1.0.0");
2063+
2064+
service.process(servletRequest, servletResponse, OWNER_KEY);
2065+
assertResponseStatus(HttpServletResponse.SC_NO_CONTENT);
2066+
assertResponseHeader(HttpHeader.UPLOAD_OFFSET, "" + uploadContent.getBytes().length);
2067+
assertResponseHeader(HttpHeader.UPLOAD_LENGTH, "" + uploadContent.getBytes().length);
2068+
2069+
// Verify upload info is also retrievable via relative path
2070+
UploadInfo infoByPath = service.getUploadInfo(uploadPath, OWNER_KEY);
2071+
assertTrue(infoByPath != null && infoByLocation.getId().equals(infoByPath.getId()));
2072+
2073+
// Step 4: Verify uploaded bytes
2074+
try (InputStream stream = service.getUploadedBytes(uploadPath, OWNER_KEY)) {
2075+
assertThat(IOUtils.toString(stream, StandardCharsets.UTF_8), is(uploadContent));
2076+
}
2077+
}
2078+
19532079
protected void assertResponseHeader(final String header, final String value) {
19542080
assertThat(servletResponse.getHeader(header), is(value));
19552081
}

src/test/java/me/desair/tus/server/ITLeaseFileRufhProtocol.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,13 @@ public static void destroyDataFolder() throws IOException {
3232

3333
@Override
3434
protected TusFileUploadService createTusFileUploadService() {
35+
return createTusFileUploadService(UPLOAD_URI);
36+
}
37+
38+
@Override
39+
protected TusFileUploadService createTusFileUploadService(String uploadUri) {
3540
return new TusFileUploadService()
36-
.withUploadUri(UPLOAD_URI)
41+
.withUploadUri(uploadUri)
3742
.withUploadStorageService(new DiskStorageService(storagePath.toAbsolutePath().toString()))
3843
.withUploadLockingService(
3944
new LeaseFileLockingService(storagePath.toAbsolutePath().toString()))

src/test/java/me/desair/tus/server/ITRufhProtocol.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,13 @@ public static void destroyDataFolder() throws IOException {
2828

2929
@Override
3030
protected TusFileUploadService createTusFileUploadService() {
31+
return createTusFileUploadService(UPLOAD_URI);
32+
}
33+
34+
@Override
35+
protected TusFileUploadService createTusFileUploadService(String uploadUri) {
3136
return new TusFileUploadService()
32-
.withUploadUri(UPLOAD_URI)
37+
.withUploadUri(uploadUri)
3338
.withStoragePath(storagePath.toAbsolutePath().toString())
3439
.withMaxUploadSize(1073741824L)
3540
.withUploadExpirationPeriod(2L * 24 * 60 * 60 * 1000)

src/test/java/me/desair/tus/server/upload/azure/ITAzureBlobRufhProtocol.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ public static void tearDownClass() {
4040

4141
@Override
4242
protected TusFileUploadService createTusFileUploadService() {
43+
return createTusFileUploadService(UPLOAD_URI);
44+
}
45+
46+
@Override
47+
protected TusFileUploadService createTusFileUploadService(String uploadUri) {
4348
org.junit.Assume.assumeTrue(TestUtils.isContainerRuntimeAvailable());
4449

4550
AzureBlobStorageService azureStorage = new AzureBlobStorageService(containerClient);
@@ -49,7 +54,7 @@ protected TusFileUploadService createTusFileUploadService() {
4954
azureStorage.setUploadConcatenationService(azureConcat);
5055

5156
return new TusFileUploadService()
52-
.withUploadUri(UPLOAD_URI)
57+
.withUploadUri(uploadUri)
5358
.withUploadStorageService(azureStorage)
5459
.withUploadLockingService(azureLocking)
5560
.withMaxUploadSize(1073741824L)

src/test/java/me/desair/tus/server/upload/s3/ITS3RufhProtocol.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ public static void tearDownClass() {
4040

4141
@Override
4242
protected TusFileUploadService createTusFileUploadService() {
43+
return createTusFileUploadService(UPLOAD_URI);
44+
}
45+
46+
@Override
47+
protected TusFileUploadService createTusFileUploadService(String uploadUri) {
4348
org.junit.Assume.assumeTrue(TestUtils.isContainerRuntimeAvailable());
4449

4550
S3StorageService s3Storage = new S3StorageService(minioClient, BUCKET);
@@ -48,7 +53,7 @@ protected TusFileUploadService createTusFileUploadService() {
4853
s3Storage.setUploadConcatenationService(s3Concat);
4954

5055
return new TusFileUploadService()
51-
.withUploadUri(UPLOAD_URI)
56+
.withUploadUri(uploadUri)
5257
.withUploadStorageService(s3Storage)
5358
.withUploadLockingService(s3Locking)
5459
.withMaxUploadSize(1073741824L)

src/test/java/me/desair/tus/server/util/UtilsTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,7 @@ public void testExtractUriPath() {
449449
assertThat(Utils.extractUriPath("/api/files"), is("/api/files"));
450450
assertThat(Utils.extractUriPath("https://upload.example.com/api/files"), is("/api/files"));
451451
assertThat(Utils.extractUriPath("http://localhost:8080/files/upload"), is("/files/upload"));
452+
assertThat(Utils.extractUriPath("https://test.example.com/uploads"), is("/uploads"));
452453
assertThat(Utils.extractUriPath("https://upload.example.com"), is("/"));
453454
assertThat(Utils.extractUriPath("https://upload.example.com/"), is("/"));
454455
assertThat(Utils.extractUriPath(null), is("/"));

0 commit comments

Comments
 (0)