From b33feb2d4f254f6ac84dc9dcf2ad53bc48d5c2fe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 8 Aug 2026 06:03:57 +0000 Subject: [PATCH 1/5] chore(deps): bump org.jsoup:jsoup from 1.15.3 to 1.23.1 Bumps [org.jsoup:jsoup](https://github.com/jhy/jsoup) from 1.15.3 to 1.23.1. - [Release notes](https://github.com/jhy/jsoup/releases) - [Changelog](https://github.com/jhy/jsoup/blob/master/CHANGES.md) - [Commits](https://github.com/jhy/jsoup/compare/jsoup-1.15.3...jsoup-1.23.1) --- updated-dependencies: - dependency-name: org.jsoup:jsoup dependency-version: 1.23.1 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dd8c46d6e9b..0a8ec4b88e7 100644 --- a/pom.xml +++ b/pom.xml @@ -511,7 +511,7 @@ org.jsoup jsoup - 1.15.3 + 1.23.1 io.searchbox From 3db1ce2cf267d503178c0663084d64adb28e20fd Mon Sep 17 00:00:00 2001 From: qqmyers Date: Thu, 13 Aug 2026 10:02:58 -0400 Subject: [PATCH 2/5] fix tests by ignoring attribute order --- .../iq/dataverse/util/MarkupCheckerTest.java | 58 ++++++++++++++----- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/src/test/java/edu/harvard/iq/dataverse/util/MarkupCheckerTest.java b/src/test/java/edu/harvard/iq/dataverse/util/MarkupCheckerTest.java index 02219059db3..bb6bba9ee19 100644 --- a/src/test/java/edu/harvard/iq/dataverse/util/MarkupCheckerTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/util/MarkupCheckerTest.java @@ -1,10 +1,12 @@ package edu.harvard.iq.dataverse.util; +import org.jsoup.Jsoup; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; public class MarkupCheckerTest { @@ -28,7 +30,10 @@ public class MarkupCheckerTest { "NULL, NULL" }, nullValues = {"NULL"}) public void testSanitizeBasicHTML(String unsafe, String safe) { - assertEquals(safe, MarkupChecker.sanitizeBasicHTML(unsafe)); + String actual = MarkupChecker.sanitizeBasicHTML(unsafe); + assertHtmlEqual(safe, actual); + // Sanity check that the key tag we strip is truly gone + assertFalse(actual.toLowerCase().contains("script")); } /** @@ -51,21 +56,10 @@ public void testSanitizeBasicHTML(String unsafe, String safe) { "NULL, NULL" }, nullValues = {"NULL"}) public void testSanitizeAdvancedHTML(String unsafe, String safe) { - String sanitizedOutput = MarkupChecker.sanitizeAdvancedHTML(unsafe); - - // Normalize both the expected and actual content by removing whitespaces - - String normalizedSafe = null; - if (safe != null) { - normalizedSafe = safe.replaceAll("\\s+", "").trim(); - } - - String normalizedOutput = null; - if (sanitizedOutput != null) { - normalizedOutput = sanitizedOutput.replaceAll("\\s+", "").trim(); - } - - assertEquals(normalizedSafe, normalizedOutput); + String actual = MarkupChecker.sanitizeAdvancedHTML(unsafe); + assertHtmlEqual(safe, actual); + // Sanity check that the key tag we strip is truly gone + assertFalse(actual.toLowerCase().contains("script")); } /** @@ -91,4 +85,36 @@ public void testEscapeHtml() { assertEquals("foo<br>bar", MarkupChecker.escapeHtml("foo
bar")); } + /** Test HTML equivalence and ignore differences in the order of attributes + * This does (in normalizeHtml()) use Jsoup to help test Jsoup though. + * + * @param expected + * @param actual + */ + private void assertHtmlEqual(String expected, String actual) { + if (expected == null || actual == null) { + assertEquals(expected, actual); + return; + } + + String normalizedExpected = normalizeHtml(expected); + String normalizedActual = normalizeHtml(actual); + assertEquals(normalizedExpected, normalizedActual); + } + + private String normalizeHtml(String html) { + org.jsoup.nodes.Document doc = Jsoup.parseBodyFragment(html); + for (org.jsoup.nodes.Element el : doc.getAllElements()) { + org.jsoup.nodes.Attributes attrs = el.attributes(); + java.util.List list = new java.util.ArrayList<>(attrs.asList()); + list.sort(java.util.Comparator.comparing(org.jsoup.nodes.Attribute::getKey)); + for (org.jsoup.nodes.Attribute a : list) { + attrs.remove(a.getKey()); + } + for (org.jsoup.nodes.Attribute a : list) { + attrs.put(a); + } + } + return doc.body().html().replaceAll("\\s+", ""); + } } From f8abb07182fac84d37c020b60f1f5eee34bd6883 Mon Sep 17 00:00:00 2001 From: qqmyers Date: Thu, 13 Aug 2026 10:06:16 -0400 Subject: [PATCH 3/5] fix null tests, address warnings --- .../iq/dataverse/util/MarkupCheckerTest.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/test/java/edu/harvard/iq/dataverse/util/MarkupCheckerTest.java b/src/test/java/edu/harvard/iq/dataverse/util/MarkupCheckerTest.java index bb6bba9ee19..9859902da7d 100644 --- a/src/test/java/edu/harvard/iq/dataverse/util/MarkupCheckerTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/util/MarkupCheckerTest.java @@ -33,7 +33,9 @@ public void testSanitizeBasicHTML(String unsafe, String safe) { String actual = MarkupChecker.sanitizeBasicHTML(unsafe); assertHtmlEqual(safe, actual); // Sanity check that the key tag we strip is truly gone - assertFalse(actual.toLowerCase().contains("script")); + if(actual != null) { + assertFalse(actual.toLowerCase().contains("script")); + } } /** @@ -59,7 +61,9 @@ public void testSanitizeAdvancedHTML(String unsafe, String safe) { String actual = MarkupChecker.sanitizeAdvancedHTML(unsafe); assertHtmlEqual(safe, actual); // Sanity check that the key tag we strip is truly gone - assertFalse(actual.toLowerCase().contains("script")); + if(actual != null) { + assertFalse(actual.toLowerCase().contains("script")); + } } /** @@ -88,8 +92,8 @@ public void testEscapeHtml() { /** Test HTML equivalence and ignore differences in the order of attributes * This does (in normalizeHtml()) use Jsoup to help test Jsoup though. * - * @param expected - * @param actual + * @param expected - the HTML we expect to be returned + * @param actual - the HTML we actually got */ private void assertHtmlEqual(String expected, String actual) { if (expected == null || actual == null) { @@ -107,7 +111,7 @@ private String normalizeHtml(String html) { for (org.jsoup.nodes.Element el : doc.getAllElements()) { org.jsoup.nodes.Attributes attrs = el.attributes(); java.util.List list = new java.util.ArrayList<>(attrs.asList()); - list.sort(java.util.Comparator.comparing(org.jsoup.nodes.Attribute::getKey)); + list.sort(java.util.Map.Entry.comparingByKey()); for (org.jsoup.nodes.Attribute a : list) { attrs.remove(a.getKey()); } From f5b0c8fdcdd777f6c256da366ec8fe74127ccaa4 Mon Sep 17 00:00:00 2001 From: qqmyers Date: Thu, 20 Aug 2026 13:41:00 -0400 Subject: [PATCH 4/5] add sleep for search --- src/test/java/edu/harvard/iq/dataverse/api/SearchIT.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/edu/harvard/iq/dataverse/api/SearchIT.java b/src/test/java/edu/harvard/iq/dataverse/api/SearchIT.java index 410a72a1054..1c1a9756360 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/SearchIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/SearchIT.java @@ -994,6 +994,7 @@ public void testSubtreePermissions() { // Wait a little while for the index to pick up the datasets, otherwise timing issue with searching for it. UtilIT.sleepForDatasetIndex(datasetId2.toString(), apiToken); + UtilIT.sleepForDatasetIndex(datasetId.toString(), apiToken); String identifier = JsonPath.from(datasetAsJson.getBody().asString()).getString("data.identifier"); String identifier2 = JsonPath.from(datasetAsJson2.getBody().asString()).getString("data.identifier"); From cf80b82ee373cf4dda9b41e356d965977a72a4f3 Mon Sep 17 00:00:00 2001 From: qqmyers Date: Thu, 20 Aug 2026 15:59:50 -0400 Subject: [PATCH 5/5] refactor to use separate util, add hamcrest.Matcher for htmlEqualTo --- .../api/DataverseFeaturedItemsIT.java | 3 +- .../iq/dataverse/util/MarkupCheckerTest.java | 37 +------ .../iq/dataverse/util/MarkupCheckerUtil.java | 97 +++++++++++++++++++ 3 files changed, 101 insertions(+), 36 deletions(-) create mode 100644 src/test/java/edu/harvard/iq/dataverse/util/MarkupCheckerUtil.java diff --git a/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java b/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java index d7dc47bccea..d4ce246ba5a 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/DataverseFeaturedItemsIT.java @@ -9,6 +9,7 @@ import java.text.MessageFormat; +import static edu.harvard.iq.dataverse.util.MarkupCheckerUtil.htmlEqualTo; import static jakarta.ws.rs.core.Response.Status.*; import java.io.File; import java.io.IOException; @@ -408,7 +409,7 @@ private Long createFeaturedItemAndGetId(String dataverseAlias, String apiToken, private void verifyUpdatedFeaturedItem(Response response, String expectedContent, String expectedImageFileName, int expectedDisplayOrder, String type, String dvObject, String dvObjectDisplayName) { response.prettyPrint(); response.then().assertThat() - .body("data.content", equalTo(expectedContent)) + .body("data.content", htmlEqualTo(expectedContent)) .body("data.imageFileName", equalTo(expectedImageFileName)) .body("data.displayOrder", equalTo(expectedDisplayOrder)) .body("data.type", equalTo(type)) diff --git a/src/test/java/edu/harvard/iq/dataverse/util/MarkupCheckerTest.java b/src/test/java/edu/harvard/iq/dataverse/util/MarkupCheckerTest.java index 9859902da7d..d1527734ab3 100644 --- a/src/test/java/edu/harvard/iq/dataverse/util/MarkupCheckerTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/util/MarkupCheckerTest.java @@ -31,7 +31,7 @@ public class MarkupCheckerTest { }, nullValues = {"NULL"}) public void testSanitizeBasicHTML(String unsafe, String safe) { String actual = MarkupChecker.sanitizeBasicHTML(unsafe); - assertHtmlEqual(safe, actual); + MarkupCheckerUtil.assertHtmlEqual(safe, actual); // Sanity check that the key tag we strip is truly gone if(actual != null) { assertFalse(actual.toLowerCase().contains("script")); @@ -59,7 +59,7 @@ public void testSanitizeBasicHTML(String unsafe, String safe) { }, nullValues = {"NULL"}) public void testSanitizeAdvancedHTML(String unsafe, String safe) { String actual = MarkupChecker.sanitizeAdvancedHTML(unsafe); - assertHtmlEqual(safe, actual); + MarkupCheckerUtil.assertHtmlEqual(safe, actual); // Sanity check that the key tag we strip is truly gone if(actual != null) { assertFalse(actual.toLowerCase().contains("script")); @@ -88,37 +88,4 @@ public void testStripAllTags(String unsafe, String safe) { public void testEscapeHtml() { assertEquals("foo<br>bar", MarkupChecker.escapeHtml("foo
bar")); } - - /** Test HTML equivalence and ignore differences in the order of attributes - * This does (in normalizeHtml()) use Jsoup to help test Jsoup though. - * - * @param expected - the HTML we expect to be returned - * @param actual - the HTML we actually got - */ - private void assertHtmlEqual(String expected, String actual) { - if (expected == null || actual == null) { - assertEquals(expected, actual); - return; - } - - String normalizedExpected = normalizeHtml(expected); - String normalizedActual = normalizeHtml(actual); - assertEquals(normalizedExpected, normalizedActual); - } - - private String normalizeHtml(String html) { - org.jsoup.nodes.Document doc = Jsoup.parseBodyFragment(html); - for (org.jsoup.nodes.Element el : doc.getAllElements()) { - org.jsoup.nodes.Attributes attrs = el.attributes(); - java.util.List list = new java.util.ArrayList<>(attrs.asList()); - list.sort(java.util.Map.Entry.comparingByKey()); - for (org.jsoup.nodes.Attribute a : list) { - attrs.remove(a.getKey()); - } - for (org.jsoup.nodes.Attribute a : list) { - attrs.put(a); - } - } - return doc.body().html().replaceAll("\\s+", ""); - } } diff --git a/src/test/java/edu/harvard/iq/dataverse/util/MarkupCheckerUtil.java b/src/test/java/edu/harvard/iq/dataverse/util/MarkupCheckerUtil.java new file mode 100644 index 00000000000..c28c983618f --- /dev/null +++ b/src/test/java/edu/harvard/iq/dataverse/util/MarkupCheckerUtil.java @@ -0,0 +1,97 @@ +package edu.harvard.iq.dataverse.util; + +import org.jsoup.Jsoup; + + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class MarkupCheckerUtil { + + /** Test HTML equivalence and ignore differences in the order of attributes + * This does (in normalizeHtml()) use Jsoup to help test Jsoup though. + * + * @param expected - the HTML we expect to be returned + * @param actual - the HTML we actually got + */ + public static void assertHtmlEqual(String expected, String actual) { + if (expected == null || actual == null) { + assertEquals(expected, actual); + return; + } + + String normalizedExpected = normalizeHtml(expected); + String normalizedActual = normalizeHtml(actual); + assertEquals(normalizedExpected, normalizedActual); + } + + /** + * Creates a Hamcrest matcher that compares HTML after normalization, ignoring + * differences such as attribute ordering and whitespace. + * + * @param expected the expected HTML + * @return a matcher for HTML-equivalent strings + */ + public static org.hamcrest.Matcher htmlEqualTo(String expected) { + return new org.hamcrest.BaseMatcher() { + + @Override + public boolean matches(Object actual) { + if (expected == null || actual == null) { + return expected == actual; + } + + if (!(actual instanceof String actualHtml)) { + return false; + } + + return normalizeHtml(expected).equals(normalizeHtml(actualHtml)); + } + + @Override + public void describeTo(org.hamcrest.Description description) { + description.appendText("HTML equal to ") + .appendValue(expected); + + if (expected != null) { + description.appendText(" after normalization as ") + .appendValue(normalizeHtml(expected)); + } + } + + @Override + public void describeMismatch(Object actual, org.hamcrest.Description description) { + if (actual == null) { + description.appendText("was null"); + return; + } + + if (!(actual instanceof String actualHtml)) { + description.appendText("was ") + .appendValue(actual); + return; + } + + description.appendText("was ") + .appendValue(actualHtml) + .appendText(" after normalization as ") + .appendValue(normalizeHtml(actualHtml)); + } + }; + } + + private static String normalizeHtml(String html) { + org.jsoup.nodes.Document doc = Jsoup.parseBodyFragment(html); + for (org.jsoup.nodes.Element el : doc.getAllElements()) { + org.jsoup.nodes.Attributes attrs = el.attributes(); + java.util.List list = new java.util.ArrayList<>(attrs.asList()); + list.sort(java.util.Map.Entry.comparingByKey()); + for (org.jsoup.nodes.Attribute a : list) { + attrs.remove(a.getKey()); + } + for (org.jsoup.nodes.Attribute a : list) { + attrs.put(a); + } + } + return doc.body().html().replaceAll("\\s+", ""); + } +}