mirror of
https://github.com/apache/struts.git
synced 2026-08-05 22:56:59 +00:00
Adds missing test cases of temporary files
This commit is contained in:
+9
-5
@@ -209,7 +209,7 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
|
||||
List<UploadedFile> values = uploadedFiles.computeIfAbsent(fieldName, k -> new ArrayList<>());
|
||||
|
||||
if (item.isInMemory()) {
|
||||
LOG.debug("Creating temporary file representing in-memory uploaded item: {}", normalizeSpace(item.getFieldName()));
|
||||
LOG.debug(() -> "Creating temporary file representing in-memory uploaded item: " + normalizeSpace(item.getFieldName()));
|
||||
try {
|
||||
File tempFile = createTemporaryFile(item.getName(), Path.of(saveDir));
|
||||
|
||||
@@ -229,8 +229,10 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
|
||||
.build();
|
||||
values.add(uploadedFile);
|
||||
|
||||
LOG.debug("Created temporary file for in-memory uploaded item: {} at {}",
|
||||
normalizeSpace(item.getName()), tempFile.getAbsolutePath());
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Created temporary file for in-memory uploaded item: {} at {}",
|
||||
normalizeSpace(item.getName()), tempFile.getAbsolutePath());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Failed to create temporary file for in-memory uploaded item: {}",
|
||||
normalizeSpace(item.getName()), e);
|
||||
@@ -277,10 +279,12 @@ public class JakartaMultiPartRequest extends AbstractMultiPartRequest {
|
||||
for (DiskFileItem item : diskFileItems) {
|
||||
try {
|
||||
if (item.isInMemory()) {
|
||||
LOG.debug("Cleaning up in-memory item: {}", normalizeSpace(item.getFieldName()));
|
||||
LOG.debug(() -> "Cleaning up in-memory item: " + normalizeSpace(item.getFieldName()));
|
||||
} else {
|
||||
Path itemPath = item.getPath();
|
||||
LOG.debug("Cleaning up disk item: {} at {}", normalizeSpace(item.getFieldName()), itemPath);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Cleaning up disk item: {} at {}", normalizeSpace(item.getFieldName()), itemPath);
|
||||
}
|
||||
if (itemPath != null) {
|
||||
File itemFile = itemPath.toFile();
|
||||
if (itemFile.exists() && !itemFile.delete()) {
|
||||
|
||||
-14
@@ -242,20 +242,6 @@ public class JakartaStreamMultiPartRequest extends AbstractMultiPartRequest {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a temporary file based on the given filename and location.
|
||||
*
|
||||
* @param fileName file name
|
||||
* @param location location
|
||||
* @return a temporary file based on the given filename and location
|
||||
*/
|
||||
protected File createTemporaryFile(String fileName, Path location) {
|
||||
String uid = UUID.randomUUID().toString().replace("-", "_");
|
||||
File file = location.resolve("upload_" + uid + ".tmp").toFile();
|
||||
LOG.debug("Creating temporary file: {} (originally: {})", file.getName(), fileName);
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Streams the file upload stream to the specified file.
|
||||
*
|
||||
|
||||
+157
@@ -30,6 +30,9 @@ import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -586,6 +589,160 @@ abstract class AbstractMultiPartRequestTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createTemporaryFileGeneratesSecureNames() {
|
||||
// Create a test instance to access the protected method
|
||||
AbstractMultiPartRequest testRequest = createMultipartRequest();
|
||||
Path testLocation = Paths.get(tempDir);
|
||||
|
||||
// when - create multiple temporary files
|
||||
File tempFile1 = testRequest.createTemporaryFile("test1.csv", testLocation);
|
||||
File tempFile2 = testRequest.createTemporaryFile("test2.csv", testLocation);
|
||||
File tempFile3 = testRequest.createTemporaryFile("../../../malicious.csv", testLocation);
|
||||
|
||||
// then - verify secure naming
|
||||
assertThat(tempFile1.getName()).startsWith("upload_");
|
||||
assertThat(tempFile1.getName()).endsWith(".tmp");
|
||||
assertThat(tempFile2.getName()).startsWith("upload_");
|
||||
assertThat(tempFile2.getName()).endsWith(".tmp");
|
||||
assertThat(tempFile3.getName()).startsWith("upload_");
|
||||
assertThat(tempFile3.getName()).endsWith(".tmp");
|
||||
|
||||
// Verify each file has a unique name
|
||||
assertThat(tempFile1.getName()).isNotEqualTo(tempFile2.getName());
|
||||
assertThat(tempFile2.getName()).isNotEqualTo(tempFile3.getName());
|
||||
assertThat(tempFile1.getName()).isNotEqualTo(tempFile3.getName());
|
||||
|
||||
// Verify all files are in the correct location
|
||||
assertThat(tempFile1.getParent()).isEqualTo(tempDir);
|
||||
assertThat(tempFile2.getParent()).isEqualTo(tempDir);
|
||||
assertThat(tempFile3.getParent()).isEqualTo(tempDir);
|
||||
|
||||
// Verify malicious filename doesn't affect the location
|
||||
assertThat(tempFile3.getName()).doesNotContain("..");
|
||||
assertThat(tempFile3.getName()).doesNotContain("/");
|
||||
assertThat(tempFile3.getName()).doesNotContain("\\");
|
||||
|
||||
// Clean up test files
|
||||
tempFile1.delete();
|
||||
tempFile2.delete();
|
||||
tempFile3.delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createTemporaryFileInSpecificDirectory() throws IOException {
|
||||
// Create a subdirectory for testing
|
||||
Path subDir = Paths.get(tempDir, "subdir");
|
||||
Files.createDirectories(subDir);
|
||||
|
||||
AbstractMultiPartRequest testRequest = createMultipartRequest();
|
||||
|
||||
// when
|
||||
File tempFile = testRequest.createTemporaryFile("test.csv", subDir);
|
||||
|
||||
// then - verify file is created in the specified subdirectory
|
||||
assertThat(tempFile.getParent()).isEqualTo(subDir.toString());
|
||||
assertThat(tempFile.getName()).startsWith("upload_");
|
||||
assertThat(tempFile.getName()).endsWith(".tmp");
|
||||
|
||||
// Clean up
|
||||
tempFile.delete();
|
||||
Files.delete(subDir);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createTemporaryFileWithNullFileName() throws IOException {
|
||||
AbstractMultiPartRequest testRequest = createMultipartRequest();
|
||||
Path testLocation = Paths.get(tempDir);
|
||||
|
||||
// when - create temp file with null filename
|
||||
File tempFile = testRequest.createTemporaryFile(null, testLocation);
|
||||
|
||||
// then - should still create a valid temporary file
|
||||
assertThat(tempFile.getName()).startsWith("upload_");
|
||||
assertThat(tempFile.getName()).endsWith(".tmp");
|
||||
assertThat(tempFile.getParent()).isEqualTo(tempDir);
|
||||
|
||||
// Clean up
|
||||
tempFile.delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createTemporaryFileWithEmptyFileName() throws IOException {
|
||||
AbstractMultiPartRequest testRequest = createMultipartRequest();
|
||||
Path testLocation = Paths.get(tempDir);
|
||||
|
||||
// when - create temp file with empty filename
|
||||
File tempFile = testRequest.createTemporaryFile("", testLocation);
|
||||
|
||||
// then - should still create a valid temporary file
|
||||
assertThat(tempFile.getName()).startsWith("upload_");
|
||||
assertThat(tempFile.getName()).endsWith(".tmp");
|
||||
assertThat(tempFile.getParent()).isEqualTo(tempDir);
|
||||
|
||||
// Clean up
|
||||
tempFile.delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createTemporaryFileWithSpecialCharacters() {
|
||||
AbstractMultiPartRequest testRequest = createMultipartRequest();
|
||||
Path testLocation = Paths.get(tempDir);
|
||||
|
||||
// when - create temp files with various special characters
|
||||
File tempFile1 = testRequest.createTemporaryFile("file with spaces.csv", testLocation);
|
||||
File tempFile2 = testRequest.createTemporaryFile("file@#$%^&*().csv", testLocation);
|
||||
File tempFile3 = testRequest.createTemporaryFile("файл.csv", testLocation); // Cyrillic
|
||||
|
||||
// then - all should create valid secure temporary files
|
||||
File[] tempFiles = {tempFile1, tempFile2, tempFile3};
|
||||
for (File tempFile : tempFiles) {
|
||||
assertThat(tempFile.getName()).startsWith("upload_");
|
||||
assertThat(tempFile.getName()).endsWith(".tmp");
|
||||
assertThat(tempFile.getParent()).isEqualTo(tempDir);
|
||||
// Verify no special characters leak into the actual filename
|
||||
assertThat(tempFile.getName()).matches("upload_[a-zA-Z0-9_]+\\.tmp");
|
||||
}
|
||||
|
||||
// All should have unique names
|
||||
assertThat(tempFile1.getName()).isNotEqualTo(tempFile2.getName());
|
||||
assertThat(tempFile2.getName()).isNotEqualTo(tempFile3.getName());
|
||||
assertThat(tempFile1.getName()).isNotEqualTo(tempFile3.getName());
|
||||
|
||||
// Clean up
|
||||
tempFile1.delete();
|
||||
tempFile2.delete();
|
||||
tempFile3.delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createTemporaryFileConsistentNaming() {
|
||||
AbstractMultiPartRequest testRequest = createMultipartRequest();
|
||||
Path testLocation = Paths.get(tempDir);
|
||||
|
||||
// when - create many temporary files to verify naming consistency
|
||||
List<File> tempFiles = new ArrayList<>();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
tempFiles.add(testRequest.createTemporaryFile("test" + i + ".csv", testLocation));
|
||||
}
|
||||
|
||||
// then - all should follow the same naming pattern
|
||||
for (File tempFile : tempFiles) {
|
||||
assertThat(tempFile.getName()).startsWith("upload_");
|
||||
assertThat(tempFile.getName()).endsWith(".tmp");
|
||||
assertThat(tempFile.getParent()).isEqualTo(tempDir);
|
||||
// Verify UUID pattern (without hyphens, replaced with underscores)
|
||||
assertThat(tempFile.getName()).matches("upload_[a-zA-Z0-9_]+\\.tmp");
|
||||
}
|
||||
|
||||
// Verify all names are unique
|
||||
List<String> fileNames = tempFiles.stream().map(File::getName).toList();
|
||||
assertThat(fileNames).doesNotHaveDuplicates();
|
||||
|
||||
// Clean up
|
||||
tempFiles.forEach(File::delete);
|
||||
}
|
||||
|
||||
protected String formFile(String fieldName, String filename, String content) {
|
||||
return endline +
|
||||
"--" + boundary + endline +
|
||||
|
||||
+172
@@ -20,6 +20,7 @@ package org.apache.struts2.dispatcher.multipart;
|
||||
|
||||
import org.apache.commons.fileupload2.core.DiskFileItem;
|
||||
import org.apache.struts2.dispatcher.LocalizedMessage;
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
@@ -281,4 +282,175 @@ public class JakartaMultiPartRequestTest extends AbstractMultiPartRequestTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processNormalFormFieldHandlesNullFieldName() throws IOException {
|
||||
// Test null field name handling in processNormalFormField
|
||||
String content =
|
||||
endline + "--" + boundary + endline +
|
||||
"Content-Disposition: form-data" + endline + // No name attribute
|
||||
endline +
|
||||
"field value without name" +
|
||||
endline + "--" + boundary + endline +
|
||||
"Content-Disposition: form-data; name=\"validfield\"" + endline +
|
||||
endline +
|
||||
"valid field value" +
|
||||
endline + "--" + boundary + "--";
|
||||
|
||||
mockRequest.setContent(content.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// when
|
||||
multiPart.parse(mockRequest, tempDir);
|
||||
|
||||
// then - should only process the valid field
|
||||
assertThat(multiPart.getErrors()).isEmpty();
|
||||
assertThat(multiPart.getParameter("validfield")).isEqualTo("valid field value");
|
||||
assertThat(multiPart.getParameterNames().asIterator()).toIterable().hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processFileFieldHandlesNullFieldName() throws IOException {
|
||||
// Test null field name handling in processFileField
|
||||
String content =
|
||||
endline + "--" + boundary + endline +
|
||||
"Content-Disposition: form-data; filename=\"orphan.txt\"" + endline + // No name attribute
|
||||
"Content-Type: text/plain" + endline +
|
||||
endline +
|
||||
"orphaned file content" +
|
||||
endline + "--" + boundary + endline +
|
||||
"Content-Disposition: form-data; name=\"validfile\"; filename=\"valid.txt\"" + endline +
|
||||
"Content-Type: text/plain" + endline +
|
||||
endline +
|
||||
"valid file content" +
|
||||
endline + "--" + boundary + "--";
|
||||
|
||||
mockRequest.setContent(content.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// when
|
||||
multiPart.parse(mockRequest, tempDir);
|
||||
|
||||
// then - should only process the valid file
|
||||
assertThat(multiPart.getErrors()).isEmpty();
|
||||
assertThat(multiPart.uploadedFiles).hasSize(1);
|
||||
assertThat(multiPart.getFile("validfile")).hasSize(1);
|
||||
assertThat(multiPart.getFile("validfile")[0].getContent())
|
||||
.asInstanceOf(InstanceOfAssertFactories.FILE)
|
||||
.content()
|
||||
.isEqualTo("valid file content");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void diskFileItemCleanupCoverage() throws IOException, NoSuchFieldException, IllegalAccessException {
|
||||
// Test disk file item cleanup paths
|
||||
String content = formFile("file1", "test1.csv", "1,2,3,4") +
|
||||
endline + "--" + boundary + "--";
|
||||
|
||||
mockRequest.setContent(content.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// when - force files to disk with small buffer
|
||||
multiPart.setBufferSize("1");
|
||||
multiPart.parse(mockRequest, tempDir);
|
||||
|
||||
// Access private field to verify disk file items are tracked
|
||||
Field diskFileItemsField = JakartaMultiPartRequest.class.getDeclaredField("diskFileItems");
|
||||
diskFileItemsField.setAccessible(true);
|
||||
@SuppressWarnings("unchecked")
|
||||
java.util.List<org.apache.commons.fileupload2.core.DiskFileItem> diskFileItems =
|
||||
(java.util.List<org.apache.commons.fileupload2.core.DiskFileItem>) diskFileItemsField.get(multiPart);
|
||||
|
||||
// then - should have disk file items tracked
|
||||
assertThat(diskFileItems).isNotEmpty();
|
||||
|
||||
// when - cleanup
|
||||
multiPart.cleanUp();
|
||||
|
||||
// then - should clear tracking
|
||||
assertThat(diskFileItems).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inMemoryVsDiskFileHandling() throws IOException {
|
||||
// Test both in-memory and disk file handling paths
|
||||
String smallContent = "small"; // Should be in-memory
|
||||
String largeContent = "x".repeat(20000); // Should go to disk
|
||||
|
||||
String content = formFile("smallfile", "small.txt", smallContent) +
|
||||
formFile("largefile", "large.txt", largeContent) +
|
||||
endline + "--" + boundary + "--";
|
||||
|
||||
mockRequest.setContent(content.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// when - use default buffer size
|
||||
multiPart.parse(mockRequest, tempDir);
|
||||
|
||||
// then - both files should be processed
|
||||
assertThat(multiPart.getErrors()).isEmpty();
|
||||
assertThat(multiPart.uploadedFiles).hasSize(2);
|
||||
assertThat(multiPart.getFile("smallfile")).hasSize(1);
|
||||
assertThat(multiPart.getFile("largefile")).hasSize(1);
|
||||
|
||||
// Verify content
|
||||
assertThat(multiPart.getFile("smallfile")[0].getContent())
|
||||
.asInstanceOf(InstanceOfAssertFactories.FILE)
|
||||
.content()
|
||||
.isEqualTo(smallContent);
|
||||
assertThat(multiPart.getFile("largefile")[0].getContent())
|
||||
.asInstanceOf(InstanceOfAssertFactories.FILE)
|
||||
.content()
|
||||
.isEqualTo(largeContent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void errorDuplicationPrevention() throws IOException {
|
||||
// Test that duplicate errors are not added
|
||||
JakartaMultiPartRequest multiPartRequest = new JakartaMultiPartRequest();
|
||||
|
||||
// Simulate adding the same error multiple times
|
||||
IOException testException = new IOException("Test error");
|
||||
LocalizedMessage errorMessage = multiPartRequest.buildErrorMessage(
|
||||
testException.getClass(), testException.getMessage(), new Object[]{"test.csv"});
|
||||
|
||||
// when - try to add same error multiple times
|
||||
multiPartRequest.errors.add(errorMessage);
|
||||
if (!multiPartRequest.errors.contains(errorMessage)) {
|
||||
multiPartRequest.errors.add(errorMessage); // Should not be added
|
||||
}
|
||||
if (!multiPartRequest.errors.contains(errorMessage)) {
|
||||
multiPartRequest.errors.add(errorMessage); // Should not be added
|
||||
}
|
||||
|
||||
// then - should only have one error
|
||||
assertThat(multiPartRequest.getErrors()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processFileFieldHandlesEmptyFileName() throws IOException {
|
||||
String content =
|
||||
endline + "--" + boundary + endline +
|
||||
"Content-Disposition: form-data; name=\"emptyfile\"; filename=\"\"" + endline +
|
||||
"Content-Type: text/plain" + endline +
|
||||
endline +
|
||||
"some content that should be ignored" +
|
||||
endline + "--" + boundary + endline +
|
||||
"Content-Disposition: form-data; name=\"validfile\"; filename=\"test.txt\"" + endline +
|
||||
"Content-Type: text/plain" + endline +
|
||||
endline +
|
||||
"valid file content" +
|
||||
endline + "--" + boundary + "--";
|
||||
|
||||
mockRequest.setContent(content.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// when
|
||||
multiPart.parse(mockRequest, tempDir);
|
||||
|
||||
// then - should only process the file with valid filename
|
||||
assertThat(multiPart.getErrors()).isEmpty();
|
||||
assertThat(multiPart.uploadedFiles).hasSize(1);
|
||||
assertThat(multiPart.getFile("validfile")).hasSize(1);
|
||||
assertThat(multiPart.getFile("emptyfile")).isEmpty();
|
||||
assertThat(multiPart.getFile("validfile")[0].getContent())
|
||||
.asInstanceOf(InstanceOfAssertFactories.FILE)
|
||||
.content()
|
||||
.isEqualTo("valid file content");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+230
@@ -23,11 +23,15 @@ import org.apache.struts2.dispatcher.LocalizedMessage;
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
@@ -161,4 +165,230 @@ public class JakartaStreamMultiPartRequestTest extends AbstractMultiPartRequestT
|
||||
assertThat(result.length()).isGreaterThan(1024); // Verify it's larger than internal buffer
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processFileItemAsFormFieldHandlesNullFieldName() throws IOException {
|
||||
// Test the null field name path in processFileItemAsFormField
|
||||
String content = formFile("", "test.csv", "data") + // Field name will be empty/null-like
|
||||
endline + "--" + boundary + "--";
|
||||
|
||||
mockRequest.setContent(content.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// when
|
||||
multiPart.parse(mockRequest, tempDir);
|
||||
|
||||
// then - should complete without error, but no parameters should be added
|
||||
assertThat(multiPart.getErrors()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processFileItemAsFileFieldHandlesNullFieldName() throws IOException {
|
||||
// This test covers the null field name path in processFileItemAsFileField
|
||||
JakartaStreamMultiPartRequest streamMultiPart = new JakartaStreamMultiPartRequest();
|
||||
|
||||
// Create a mock file item with null field name
|
||||
String content = "--" + boundary + endline +
|
||||
"Content-Disposition: form-data; filename=\"test.csv\"" + endline +
|
||||
"Content-Type: text/csv" + endline +
|
||||
endline +
|
||||
"test data" +
|
||||
endline + "--" + boundary + "--";
|
||||
|
||||
mockRequest.setContent(content.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// when
|
||||
streamMultiPart.parse(mockRequest, tempDir);
|
||||
|
||||
// then - should complete without error but no files should be uploaded
|
||||
assertThat(streamMultiPart.getErrors()).isEmpty();
|
||||
assertThat(streamMultiPart.uploadedFiles).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exceedsMaxFilesPath() throws IOException {
|
||||
// Test the exceedsMaxFiles method path
|
||||
String content = formFile("file1", "test1.csv", "data1") +
|
||||
formFile("file2", "test2.csv", "data2") +
|
||||
formFile("file3", "test3.csv", "data3") +
|
||||
endline + "--" + boundary + "--";
|
||||
|
||||
mockRequest.setContent(content.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// when - set max files to 1
|
||||
multiPart.setMaxFiles("1");
|
||||
multiPart.parse(mockRequest, tempDir);
|
||||
|
||||
// then - should have only 1 file and errors for others
|
||||
assertThat(multiPart.uploadedFiles).hasSize(1);
|
||||
assertThat(multiPart.getErrors())
|
||||
.isNotEmpty()
|
||||
.allSatisfy(error ->
|
||||
assertThat(error.getTextKey()).isEqualTo("struts.messages.upload.error.FileUploadFileCountLimitException")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void actualSizeOfUploadedFilesCalculation() throws IOException {
|
||||
// Test the actualSizeOfUploadedFiles method
|
||||
String content = formFile("file1", "test1.csv", "data1234567890") + // 14 bytes + headers
|
||||
formFile("file2", "test2.csv", "moredata") + // 8 bytes + headers
|
||||
endline + "--" + boundary + "--";
|
||||
|
||||
mockRequest.setContent(content.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// when
|
||||
multiPart.parse(mockRequest, tempDir);
|
||||
|
||||
// then - should have uploaded files and calculate their total size
|
||||
assertThat(multiPart.uploadedFiles).hasSize(2);
|
||||
assertThat(multiPart.getFile("file1")).hasSize(1);
|
||||
assertThat(multiPart.getFile("file2")).hasSize(1);
|
||||
|
||||
// Verify files have the expected content
|
||||
assertThat(multiPart.getFile("file1")[0].getContent())
|
||||
.asInstanceOf(InstanceOfAssertFactories.FILE)
|
||||
.content()
|
||||
.isEqualTo("data1234567890");
|
||||
assertThat(multiPart.getFile("file2")[0].getContent())
|
||||
.asInstanceOf(InstanceOfAssertFactories.FILE)
|
||||
.content()
|
||||
.isEqualTo("moredata");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createTemporaryFileMethod() throws Exception {
|
||||
// Test the createTemporaryFile method directly
|
||||
JakartaStreamMultiPartRequest streamMultiPart = new JakartaStreamMultiPartRequest();
|
||||
Path testLocation = Paths.get(tempDir);
|
||||
|
||||
// when
|
||||
File tempFile1 = streamMultiPart.createTemporaryFile("test.csv", testLocation);
|
||||
File tempFile2 = streamMultiPart.createTemporaryFile("another.txt", testLocation);
|
||||
|
||||
// then
|
||||
assertThat(tempFile1.getName()).startsWith("upload_");
|
||||
assertThat(tempFile1.getName()).endsWith(".tmp");
|
||||
assertThat(tempFile1.getParent()).isEqualTo(tempDir);
|
||||
|
||||
assertThat(tempFile2.getName()).startsWith("upload_");
|
||||
assertThat(tempFile2.getName()).endsWith(".tmp");
|
||||
assertThat(tempFile2.getParent()).isEqualTo(tempDir);
|
||||
|
||||
// Should be unique names
|
||||
assertThat(tempFile1.getName()).isNotEqualTo(tempFile2.getName());
|
||||
|
||||
// Clean up
|
||||
tempFile1.delete();
|
||||
tempFile2.delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void streamFileToDiskWithDifferentBufferSizes() throws IOException {
|
||||
// Test streamFileToDisk with different buffer sizes
|
||||
String largeContent = "x".repeat(5000); // Content larger than default buffer
|
||||
String content = formFile("largefile", "large.csv", largeContent) +
|
||||
endline + "--" + boundary + "--";
|
||||
|
||||
mockRequest.setContent(content.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// when - use small buffer size to ensure multiple reads
|
||||
multiPart.setBufferSize("100");
|
||||
multiPart.parse(mockRequest, tempDir);
|
||||
|
||||
// then
|
||||
assertThat(multiPart.getErrors()).isEmpty();
|
||||
assertThat(multiPart.getFile("largefile")).hasSize(1);
|
||||
assertThat(multiPart.getFile("largefile")[0].getContent())
|
||||
.asInstanceOf(InstanceOfAssertFactories.FILE)
|
||||
.content()
|
||||
.isEqualTo(largeContent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exceedsMaxSizeOfFilesWithFileCleanup() throws IOException {
|
||||
// Test the file deletion path when max size is exceeded
|
||||
String content = formFile("file1", "test1.csv", "small") +
|
||||
formFile("file2", "test2.csv", "this is a much larger file content that should exceed the limit") +
|
||||
endline + "--" + boundary + "--";
|
||||
|
||||
mockRequest.setContent(content.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// when - set very small max size
|
||||
multiPart.setMaxSizeOfFiles("20");
|
||||
multiPart.parse(mockRequest, tempDir);
|
||||
|
||||
// then - should have first file uploaded but error for second
|
||||
assertThat(multiPart.uploadedFiles).hasSize(1);
|
||||
assertThat(multiPart.getFile("file1")).hasSize(1);
|
||||
assertThat(multiPart.getFile("file2")).isEmpty();
|
||||
assertThat(multiPart.getErrors())
|
||||
.isNotEmpty()
|
||||
.anyMatch(error ->
|
||||
error.getTextKey().equals("struts.messages.upload.error.FileUploadSizeException")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createUploadedFileWithVariousContentTypes() throws IOException {
|
||||
// Test different content types and file names
|
||||
String content =
|
||||
endline + "--" + boundary + endline +
|
||||
"Content-Disposition: form-data; name=\"textfile\"; filename=\"document.txt\"" + endline +
|
||||
"Content-Type: text/plain" + endline +
|
||||
endline +
|
||||
"Plain text content" +
|
||||
endline + "--" + boundary + endline +
|
||||
"Content-Disposition: form-data; name=\"jsonfile\"; filename=\"data.json\"" + endline +
|
||||
"Content-Type: application/json" + endline +
|
||||
endline +
|
||||
"{\"key\": \"value\"}" +
|
||||
endline + "--" + boundary + "--";
|
||||
|
||||
mockRequest.setContent(content.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// when
|
||||
multiPart.parse(mockRequest, tempDir);
|
||||
|
||||
// then
|
||||
assertThat(multiPart.getErrors()).isEmpty();
|
||||
assertThat(multiPart.uploadedFiles).hasSize(2);
|
||||
|
||||
// Verify text file
|
||||
assertThat(multiPart.getFile("textfile")).hasSize(1);
|
||||
assertThat(multiPart.getFile("textfile")[0].getContentType()).isEqualTo("text/plain");
|
||||
assertThat(multiPart.getFile("textfile")[0].getOriginalName()).isEqualTo("document.txt");
|
||||
|
||||
// Verify JSON file
|
||||
assertThat(multiPart.getFile("jsonfile")).hasSize(1);
|
||||
assertThat(multiPart.getFile("jsonfile")[0].getContentType()).isEqualTo("application/json");
|
||||
assertThat(multiPart.getFile("jsonfile")[0].getOriginalName()).isEqualTo("data.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyFileNameFieldsAreSkipped() throws IOException {
|
||||
// Test files with empty names are skipped
|
||||
String content =
|
||||
endline + "--" + boundary + endline +
|
||||
"Content-Disposition: form-data; name=\"emptyfile\"; filename=\"\"" + endline +
|
||||
"Content-Type: text/plain" + endline +
|
||||
endline +
|
||||
"This should be skipped" +
|
||||
endline + "--" + boundary + endline +
|
||||
"Content-Disposition: form-data; name=\"validfile\"; filename=\"valid.txt\"" + endline +
|
||||
"Content-Type: text/plain" + endline +
|
||||
endline +
|
||||
"This should be processed" +
|
||||
endline + "--" + boundary + "--";
|
||||
|
||||
mockRequest.setContent(content.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// when
|
||||
multiPart.parse(mockRequest, tempDir);
|
||||
|
||||
// then
|
||||
assertThat(multiPart.getErrors()).isEmpty();
|
||||
assertThat(multiPart.uploadedFiles).hasSize(1);
|
||||
assertThat(multiPart.getFile("emptyfile")).isEmpty();
|
||||
assertThat(multiPart.getFile("validfile")).hasSize(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user