diff --git a/src/org/labkey/test/tests/ClientAPITest.java b/src/org/labkey/test/tests/ClientAPITest.java
index a9b4549d91..3a6ceea1d1 100644
--- a/src/org/labkey/test/tests/ClientAPITest.java
+++ b/src/org/labkey/test/tests/ClientAPITest.java
@@ -57,6 +57,7 @@
import org.labkey.test.util.PermissionsHelper;
import org.labkey.test.util.PortalHelper;
import org.labkey.test.util.StudyHelper;
+import org.labkey.test.util.TestDataGenerator;
import org.labkey.test.util.data.TestDataUtils;
import org.labkey.test.util.UIUserHelper;
import org.labkey.test.util.WikiHelper;
@@ -77,9 +78,12 @@
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.endsWith;
+import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.labkey.test.WebTestHelper.getHttpResponse;
import static org.labkey.test.params.FieldDefinition.DOMAIN_TRICKY_CHARACTERS;
@@ -93,8 +97,10 @@ public class ClientAPITest extends BaseWebDriverTest
private static final String PROJECT_NAME = "ClientAPITestProject";
private static final String OTHER_PROJECT = "OtherClientAPITestProject"; // for cross-project query test
- protected static final String FOLDER_NAME = "api folder";
+ protected static final String API_FOLDER_NAME = "api folder";
+ private static final String API_FOLDER_PATH = PROJECT_NAME + "/" + API_FOLDER_NAME;
private static final String SUBFOLDER_NAME = "subfolder";
+ private static final String SUBFOLDER_PATH = API_FOLDER_PATH + "/" + SUBFOLDER_NAME;
private static final String TIME_STUDY_FOLDER = "timeStudyFolder";
private static final String TIME_STUDY_NAME = "timeStudyName";
private static final String VISIT_STUDY_FOLDER = "visitStudyFolder";
@@ -129,10 +135,6 @@ public class ClientAPITest extends BaseWebDriverTest
public static final String TEST_DIV_NAME = "testDiv";
- private static final String GRIDTEST_GRIDTITLE = "ClientAPITest Grid Title";
-
- private static final int PAGE_SIZE = 4;
-
public static final String SRC_PREFIX = "Loading XSS"));
}
+ @Test // Issue 53699
+ public void testLargeMultibytePayload()
+ {
+ for (int i = 0; i < 10; i++)
+ {
+ // Arrange
+ int characterCount = 5_000;
+ // We had problems fetching the JSON content server-side due to Tomcat's InputBuffer not dealing with
+ // characters that span multiple "pages" of the byte buffer. Test with different numbers of single-byte
+ // UTF-8 characters before the 4-byte characters to ensure we get different boundary conditions
+ String tooManyMultibyteCharacters = "a".repeat(i) + "\uD83D\uDC7E".repeat(characterCount);
+
+ TestDataGenerator dataGenerator = new TestDataGenerator("lists", LIST_NAME, API_FOLDER_PATH)
+ .addCustomRow(Map.of("FirstName", tooManyMultibyteCharacters, "LastName", "Chaplin", "Age", 42));
+
+ // Act
+ // Prior to fix for Issue 53699 this would throw a BufferUnderflowException when processing the JSON payload
+ Exception exception = assertThrows(Exception.class, dataGenerator::insertRows);
+
+ // Assert
+ // Prior to fix length validation incorrectly checked for string length rather than character code point length
+ String expectedPrefix = "Value is too long for column 'FirstName', a maximum length of 4000 is allowed.";
+ String expectedSuffix = String.format("was %d characters long.", characterCount + i);
+
+ assertThat(exception.getMessage(), startsWith(expectedPrefix));
+ assertThat(exception.getMessage(), endsWith(expectedSuffix));
+ }
+ }
+
@Override
public BrowserType bestBrowser()
{