diff --git a/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/ContextTemplateLoaderTestCase.java b/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/ContextTemplateLoaderTestCase.java new file mode 100644 index 0000000000..e7c56f1cfa --- /dev/null +++ b/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/ContextTemplateLoaderTestCase.java @@ -0,0 +1,119 @@ +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. + */ +package org.restlet.ext.freemarker; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.util.Date; +import org.junit.jupiter.api.Test; +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; +import org.restlet.data.Reference; +import org.restlet.representation.StringRepresentation; + +class ContextTemplateLoaderTestCase { + + /** Context whose dispatcher records the last URI it received. */ + private static class CapturingContext extends Context { + String lastRequestedUri; + + CapturingContext() { + setClientDispatcher( + new Restlet() { + @Override + public void handle(Request request, Response response) { + lastRequestedUri = request.getResourceRef().toString(); + response.setEntity(new StringRepresentation("template content")); + } + }); + } + } + + /** Representation that tracks whether release() was called. */ + private static class TrackingRepresentation extends StringRepresentation { + boolean released = false; + + TrackingRepresentation() { + super("content"); + } + + @Override + public void release() { + released = true; + super.release(); + } + } + + @Test + void findTemplateSource_nullContext_returnsNull() throws IOException { + ContextTemplateLoader loader = new ContextTemplateLoader(null, "clap://test"); + assertNull(loader.findTemplateSource("test.ftl")); + } + + @Test + void findTemplateSource_baseUriWithTrailingSlash_buildsCorrectUri() throws IOException { + CapturingContext ctx = new CapturingContext(); + ContextTemplateLoader loader = new ContextTemplateLoader(ctx, "clap://test/"); + loader.findTemplateSource("hello.ftl"); + assertEquals("clap://test/hello.ftl", ctx.lastRequestedUri); + } + + @Test + void findTemplateSource_baseUriWithoutTrailingSlash_buildsCorrectUri() throws IOException { + CapturingContext ctx = new CapturingContext(); + ContextTemplateLoader loader = new ContextTemplateLoader(ctx, "clap://test"); + loader.findTemplateSource("hello.ftl"); + assertEquals("clap://test/hello.ftl", ctx.lastRequestedUri); + } + + @Test + void constructor_withReference_usesReferenceToString() throws IOException { + CapturingContext ctx = new CapturingContext(); + Reference ref = new Reference("clap://test/"); + ContextTemplateLoader loader = new ContextTemplateLoader(ctx, ref); + loader.findTemplateSource("hello.ftl"); + assertEquals("clap://test/hello.ftl", ctx.lastRequestedUri); + } + + @Test + void closeTemplateSource_withRepresentation_callsRelease() { + ContextTemplateLoader loader = new ContextTemplateLoader(null, "clap://test"); + TrackingRepresentation rep = new TrackingRepresentation(); + loader.closeTemplateSource(rep); + assertTrue(rep.released); + } + + @Test + void closeTemplateSource_withNonRepresentation_doesNotThrow() { + ContextTemplateLoader loader = new ContextTemplateLoader(null, "clap://test"); + assertDoesNotThrow(() -> loader.closeTemplateSource("notARepresentation")); + } + + @Test + void getLastModified_withModificationDate_returnsTimestamp() { + ContextTemplateLoader loader = new ContextTemplateLoader(null, "clap://test"); + Date now = new Date(); + StringRepresentation rep = new StringRepresentation("content"); + rep.setModificationDate(now); + assertEquals(now.getTime(), loader.getLastModified(rep)); + } + + @Test + void getLastModified_withNullModificationDate_returnsMinusOne() { + ContextTemplateLoader loader = new ContextTemplateLoader(null, "clap://test"); + StringRepresentation rep = new StringRepresentation("content"); + assertEquals(-1L, loader.getLastModified(rep)); + } +} diff --git a/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/FreeMarkerTestCase.java b/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/FreeMarkerTestCase.java index 7afafb2b70..8c7cb39f44 100644 --- a/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/FreeMarkerTestCase.java +++ b/org.restlet.ext.freemarker/src/test/java/org/restlet/ext/freemarker/FreeMarkerTestCase.java @@ -9,8 +9,12 @@ package org.restlet.ext.freemarker; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import freemarker.template.Configuration; +import freemarker.template.Template; import java.io.File; import java.io.FileWriter; import java.nio.file.Files; @@ -18,6 +22,8 @@ import org.junit.jupiter.api.Test; import org.restlet.data.MediaType; import org.restlet.engine.io.IoUtils; +import org.restlet.representation.StringRepresentation; +import org.restlet.representation.Variant; /** * Unit test for the FreeMarker extension. @@ -37,7 +43,7 @@ void testTemplate() throws Exception { fw.write("Value=${value}"); fw.close(); - final Configuration fmc = new Configuration(); + final Configuration fmc = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS); fmc.setDirectoryForTemplateLoading(testDir); final Map map = Map.of("value", "myValue"); @@ -49,4 +55,76 @@ void testTemplate() throws Exception { // Clean-up IoUtils.delete(testDir, true); } + + @Test + void testTemplateRepresentationConstructors() { + // Constructor with Template object (null template) + TemplateRepresentation tr = + new TemplateRepresentation((Template) null, MediaType.TEXT_PLAIN); + assertNull(tr.getTemplate()); + assertNull(tr.getDataModel()); + + // setDataModel and setTemplate + tr.setDataModel("myModel"); + assertEquals("myModel", tr.getDataModel()); + tr.setTemplate(null); + assertNull(tr.getTemplate()); + + // Constructor with template name + config: template not found → null + Configuration cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS); + tr = new TemplateRepresentation("nonexistent.ftl", cfg, MediaType.TEXT_PLAIN); + assertNull(tr.getTemplate()); + + // Constructor with template name + config + data model + tr = + new TemplateRepresentation( + "nonexistent.ftl", cfg, Map.of("k", "v"), MediaType.TEXT_PLAIN); + assertEquals(Map.of("k", "v"), tr.getDataModel()); + + // Constructor with Representation + config (no directory set → template loading fails) + StringRepresentation rep = new StringRepresentation("Hello ${name}"); + rep.setCharacterSet(org.restlet.data.CharacterSet.UTF_8); + TemplateRepresentation tr2 = new TemplateRepresentation(rep, cfg, MediaType.TEXT_PLAIN); + assertNotNull(tr2); // The template is loaded inline from the representation + + // setDataModel with a Resolver (anonymous class, since Resolver is abstract) + tr = new TemplateRepresentation((Template) null, MediaType.TEXT_PLAIN); + tr.setDataModel( + new org.restlet.util.Resolver<>() { + @Override + public Object resolve(String name) { + return "resolved-" + name; + } + }); + assertNotNull(tr.getDataModel()); + + // static getTemplate(config, name) — name not found returns null (already tested above via + // constructor) + assertNull(TemplateRepresentation.getTemplate(cfg, "nonexistent.ftl")); + } + + @Test + void testTemplateRepresentationWriteNullTemplate() throws Exception { + // write() with null template should log a warning and not throw + TemplateRepresentation tr = + new TemplateRepresentation((Template) null, MediaType.TEXT_PLAIN); + java.io.StringWriter sw = new java.io.StringWriter(); + tr.write(sw); // must not throw + assertEquals("", sw.toString()); + } + + @Test + void testFreemarkerConverterScore() { + FreemarkerConverter converter = new FreemarkerConverter(); + + assertEquals(-1.0f, converter.score(null, new Variant(MediaType.TEXT_PLAIN), null)); + assertEquals(-1.0f, converter.score("hello", new Variant(MediaType.TEXT_PLAIN), null)); + assertEquals(-1.0f, converter.score(new StringRepresentation("x"), String.class, null)); + assertTrue(converter.getObjectClasses(new Variant(MediaType.TEXT_PLAIN)).isEmpty()); + assertTrue(converter.getVariants(String.class).isEmpty()); + assertNull(converter.toObject(null, String.class, null)); + assertNull( + converter.toRepresentation( + "notATemplate", new Variant(MediaType.TEXT_PLAIN), null)); + } } diff --git a/org.restlet.ext.gson/src/test/java/org/restlet/ext/gson/GsonTestCase.java b/org.restlet.ext.gson/src/test/java/org/restlet/ext/gson/GsonTestCase.java index a07d1938a8..36b7535afd 100644 --- a/org.restlet.ext.gson/src/test/java/org/restlet/ext/gson/GsonTestCase.java +++ b/org.restlet.ext.gson/src/test/java/org/restlet/ext/gson/GsonTestCase.java @@ -39,7 +39,7 @@ private static class User { private final Date createAt; @Since(2.0) - private Date lastLogin; + private final Date lastLogin; private final String loginId; diff --git a/org.restlet.ext.jaas/pom.xml b/org.restlet.ext.jaas/pom.xml index a961b39f4e..0d4d204090 100644 --- a/org.restlet.ext.jaas/pom.xml +++ b/org.restlet.ext.jaas/pom.xml @@ -20,6 +20,12 @@ org.restlet ${project.version} + + org.junit.jupiter + junit-jupiter-api + ${lib-junit-version} + test + diff --git a/org.restlet.ext.jaas/src/test/java/org/restlet/ext/jaas/ChallengeCallbackHandlerTestCase.java b/org.restlet.ext.jaas/src/test/java/org/restlet/ext/jaas/ChallengeCallbackHandlerTestCase.java new file mode 100644 index 0000000000..8054933fc9 --- /dev/null +++ b/org.restlet.ext.jaas/src/test/java/org/restlet/ext/jaas/ChallengeCallbackHandlerTestCase.java @@ -0,0 +1,111 @@ +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. + */ +package org.restlet.ext.jaas; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.data.ChallengeResponse; +import org.restlet.data.ChallengeScheme; +import org.restlet.data.Method; +import org.restlet.data.Reference; + +/** Unit tests for {@link ChallengeCallbackHandler}. */ +class ChallengeCallbackHandlerTestCase { + + private Request request; + private Response response; + private ChallengeCallbackHandler handler; + + @BeforeEach + void setUp() { + request = new Request(Method.GET, new Reference("http://localhost/test")); + response = new Response(request); + handler = new ChallengeCallbackHandler(request, response); + } + + @Test + void testConstructor() { + assertEquals(request, handler.getRequest()); + assertEquals(response, handler.getResponse()); + } + + @Test + void testSetRequest() { + Request newRequest = new Request(Method.POST, new Reference("http://localhost/other")); + handler.setRequest(newRequest); + assertEquals(newRequest, handler.getRequest()); + } + + @Test + void testSetResponse() { + Response newResponse = new Response(request); + handler.setResponse(newResponse); + assertEquals(newResponse, handler.getResponse()); + } + + @Test + void testHandleNameCallback() throws UnsupportedCallbackException { + request.setChallengeResponse( + new ChallengeResponse(ChallengeScheme.HTTP_BASIC, "john", "secret")); + + NameCallback nameCallback = new NameCallback("Username:"); + handler.handle(new javax.security.auth.callback.Callback[] {nameCallback}); + + assertEquals("john", nameCallback.getName()); + } + + @Test + void testHandlePasswordCallback() throws UnsupportedCallbackException { + request.setChallengeResponse( + new ChallengeResponse(ChallengeScheme.HTTP_BASIC, "john", "secret")); + + PasswordCallback passwordCallback = new PasswordCallback("Password:", false); + handler.handle(new javax.security.auth.callback.Callback[] {passwordCallback}); + + assertArrayEquals("secret".toCharArray(), passwordCallback.getPassword()); + } + + @Test + void testHandleNullCallbacks() throws UnsupportedCallbackException { + handler.handle((javax.security.auth.callback.Callback[]) null); + } + + @Test + void testHandleEmptyCallbacks() throws UnsupportedCallbackException { + handler.handle(new javax.security.auth.callback.Callback[] {}); + } + + @Test + void testHandleUnsupportedCallback() { + javax.security.auth.callback.Callback unsupported = + new javax.security.auth.callback.Callback() {}; + assertThrows( + UnsupportedCallbackException.class, + () -> handler.handle(new javax.security.auth.callback.Callback[] {unsupported})); + } + + @Test + void testHandleNameCallbackWithoutChallengeResponse() { + assertNull(request.getChallengeResponse()); + NameCallback nameCallback = new NameCallback("Username:"); + assertThrows( + UnsupportedCallbackException.class, + () -> handler.handle(new javax.security.auth.callback.Callback[] {nameCallback})); + } +} diff --git a/org.restlet.ext.jaas/src/test/java/org/restlet/ext/jaas/JaasUtilsTestCase.java b/org.restlet.ext.jaas/src/test/java/org/restlet/ext/jaas/JaasUtilsTestCase.java new file mode 100644 index 0000000000..f3584248b5 --- /dev/null +++ b/org.restlet.ext.jaas/src/test/java/org/restlet/ext/jaas/JaasUtilsTestCase.java @@ -0,0 +1,106 @@ +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. + */ +package org.restlet.ext.jaas; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.security.Principal; +import javax.security.auth.Subject; +import org.junit.jupiter.api.Test; +import org.restlet.data.ClientInfo; +import org.restlet.security.Role; +import org.restlet.security.User; + +/** Unit tests for {@link JaasUtils}. */ +class JaasUtilsTestCase { + + @Test + void testCreateSubjectWithNullClientInfo() { + Subject subject = JaasUtils.createSubject(null); + + assertNotNull(subject); + assertTrue(subject.getPrincipals().isEmpty()); + } + + @Test + void testCreateSubjectWithEmptyClientInfo() { + ClientInfo clientInfo = new ClientInfo(); + Subject subject = JaasUtils.createSubject(clientInfo); + + assertNotNull(subject); + assertTrue(subject.getPrincipals().isEmpty()); + } + + @Test + void testCreateSubjectWithUser() { + ClientInfo clientInfo = new ClientInfo(); + User user = new User("alice"); + clientInfo.setUser(user); + + Subject subject = JaasUtils.createSubject(clientInfo); + + assertEquals(1, subject.getPrincipals().size()); + assertTrue(subject.getPrincipals().contains(user)); + } + + @Test + void testCreateSubjectWithRole() { + ClientInfo clientInfo = new ClientInfo(); + Role role = new Role(null, "admin"); + clientInfo.getRoles().add(role); + + Subject subject = JaasUtils.createSubject(clientInfo); + + assertEquals(1, subject.getPrincipals().size()); + assertTrue(subject.getPrincipals().contains(role)); + } + + @Test + void testCreateSubjectWithPrincipal() { + ClientInfo clientInfo = new ClientInfo(); + Principal principal = () -> "custom-principal"; + clientInfo.getPrincipals().add(principal); + + Subject subject = JaasUtils.createSubject(clientInfo); + + assertEquals(1, subject.getPrincipals().size()); + assertTrue(subject.getPrincipals().contains(principal)); + } + + @Test + void testCreateSubjectWithAllTypes() { + ClientInfo clientInfo = new ClientInfo(); + User user = new User("bob"); + Role role = new Role(null, "editor"); + Principal principal = () -> "extra"; + + clientInfo.setUser(user); + clientInfo.getRoles().add(role); + clientInfo.getPrincipals().add(principal); + + Subject subject = JaasUtils.createSubject(clientInfo); + + assertEquals(3, subject.getPrincipals().size()); + assertTrue(subject.getPrincipals().contains(user)); + assertTrue(subject.getPrincipals().contains(role)); + assertTrue(subject.getPrincipals().contains(principal)); + } + + @Test + void testDoAsPrivileged() { + ClientInfo clientInfo = new ClientInfo(); + clientInfo.setUser(new User("eve")); + + String result = JaasUtils.doAsPriviledged(clientInfo, () -> "executed"); + + assertEquals("executed", result); + } +} diff --git a/org.restlet.ext.jaas/src/test/java/org/restlet/ext/jaas/JaasVerifierTestCase.java b/org.restlet.ext.jaas/src/test/java/org/restlet/ext/jaas/JaasVerifierTestCase.java new file mode 100644 index 0000000000..1590d5639f --- /dev/null +++ b/org.restlet.ext.jaas/src/test/java/org/restlet/ext/jaas/JaasVerifierTestCase.java @@ -0,0 +1,70 @@ +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. + */ +package org.restlet.ext.jaas; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.Test; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.data.Method; +import org.restlet.data.Reference; +import org.restlet.security.Verifier; + +/** Unit tests for {@link JaasVerifier}. */ +class JaasVerifierTestCase { + + @Test + void testConstructor() { + JaasVerifier verifier = new JaasVerifier("myApp"); + assertEquals("myApp", verifier.getName()); + assertNull(verifier.getConfiguration()); + assertNull(verifier.getUserPrincipalClassName()); + } + + @Test + void testSetName() { + JaasVerifier verifier = new JaasVerifier("initial"); + verifier.setName("updated"); + assertEquals("updated", verifier.getName()); + } + + @Test + void testSetUserPrincipalClassName() { + JaasVerifier verifier = new JaasVerifier("myApp"); + verifier.setUserPrincipalClassName("com.example.MyPrincipal"); + assertEquals("com.example.MyPrincipal", verifier.getUserPrincipalClassName()); + } + + @Test + void testCreateCallbackHandler() { + JaasVerifier verifier = new JaasVerifier("myApp"); + Request request = new Request(Method.GET, new Reference("http://localhost/test")); + Response response = new Response(request); + + var handler = verifier.createCallbackHandler(request, response); + + assertNotNull(handler); + assertInstanceOf(ChallengeCallbackHandler.class, handler); + } + + @Test + void testVerifyReturnsInvalidWhenLoginFails() { + JaasVerifier verifier = new JaasVerifier("nonExistentLoginModule"); + Request request = new Request(Method.GET, new Reference("http://localhost/test")); + Response response = new Response(request); + + int result = verifier.verify(request, response); + + assertEquals(Verifier.RESULT_INVALID, result); + } +} diff --git a/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonConverterTestCase.java b/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonConverterTestCase.java new file mode 100644 index 0000000000..f4e5284692 --- /dev/null +++ b/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonConverterTestCase.java @@ -0,0 +1,240 @@ +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. + */ +package org.restlet.ext.json; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.List; +import org.json.JSONArray; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.junit.jupiter.api.Test; +import org.restlet.data.MediaType; +import org.restlet.data.Preference; +import org.restlet.representation.Representation; +import org.restlet.representation.StringRepresentation; +import org.restlet.representation.Variant; + +/** Unit tests for {@link JsonConverter}. */ +class JsonConverterTestCase { + + @Test + void getObjectClasses_withJsonVariant_returnsThreeClasses() { + List> classes = + new JsonConverter().getObjectClasses(new Variant(MediaType.APPLICATION_JSON)); + assertNotNull(classes); + assertEquals(3, classes.size()); + assertTrue(classes.contains(JSONArray.class)); + assertTrue(classes.contains(JSONObject.class)); + assertTrue(classes.contains(JSONTokener.class)); + } + + @Test + void getObjectClasses_withNonJsonVariant_returnsNull() { + assertNull(new JsonConverter().getObjectClasses(new Variant(MediaType.TEXT_PLAIN))); + } + + @Test + void getVariants_forJsonArray_returnsApplicationJson() { + List variants = new JsonConverter().getVariants(JSONArray.class); + assertEquals(1, variants.size()); + assertTrue( + MediaType.APPLICATION_JSON.isCompatible( + ((Variant) variants.getFirst()).getMediaType())); + } + + @Test + void getVariants_forJsonObject_returnsApplicationJson() { + List variants = new JsonConverter().getVariants(JSONObject.class); + assertEquals(1, variants.size()); + assertTrue( + MediaType.APPLICATION_JSON.isCompatible( + ((Variant) variants.getFirst()).getMediaType())); + } + + @Test + void getVariants_forJsonTokener_returnsApplicationJson() { + List variants = new JsonConverter().getVariants(JSONTokener.class); + assertEquals(1, variants.size()); + assertTrue( + MediaType.APPLICATION_JSON.isCompatible( + ((Variant) variants.getFirst()).getMediaType())); + } + + @Test + void getVariants_forOtherClass_returnsEmptyList() { + assertTrue(new JsonConverter().getVariants(String.class).isEmpty()); + } + + @Test + void score_jsonObjectWithNullTarget_returnsHalf() { + assertEquals(0.5f, new JsonConverter().score(new JSONObject(), null, null)); + } + + @Test + void score_jsonObjectWithJsonVariant_returnsOne() { + assertEquals( + 1.0f, + new JsonConverter() + .score(new JSONObject(), new Variant(MediaType.APPLICATION_JSON), null)); + } + + @Test + void score_jsonObjectWithOtherMediaType_returnsHalf() { + assertEquals( + 0.5f, + new JsonConverter() + .score(new JSONObject(), new Variant(MediaType.TEXT_PLAIN), null)); + } + + @Test + void score_nonJsonObject_returnsMinusOne() { + assertEquals( + -1.0f, + new JsonConverter().score("string", new Variant(MediaType.APPLICATION_JSON), null)); + } + + @Test + void score_repr_withNullTarget_returnsMinusOne() { + assertEquals( + -1.0f, + new JsonConverter().score(new JsonRepresentation("{}"), (Class) null, null)); + } + + @Test + void score_repr_forJsonRepresentationTarget_returnsOne() { + assertEquals( + 1.0f, + new JsonConverter() + .score(new JsonRepresentation("{}"), JsonRepresentation.class, null)); + } + + @Test + void score_repr_forJsonArrayTargetWithJsonMedia_returnsOne() { + assertEquals( + 1.0f, + new JsonConverter().score(new JsonRepresentation("[]"), JSONArray.class, null)); + } + + @Test + void score_repr_forJsonArrayTargetWithOtherMedia_returnsHalf() { + assertEquals( + 0.5f, + new JsonConverter() + .score( + new StringRepresentation("[]", MediaType.TEXT_PLAIN), + JSONArray.class, + null)); + } + + @Test + void score_repr_forOtherTarget_returnsMinusOne() { + assertEquals( + -1.0f, new JsonConverter().score(new JsonRepresentation("{}"), String.class, null)); + } + + @Test + void toObject_withNullTarget_returnsNull() throws Exception { + assertNull(new JsonConverter().toObject(new JsonRepresentation("{}"), null, null)); + } + + @Test + void toObject_toJsonArray_returnsJsonArray() throws Exception { + JSONArray result = + new JsonConverter() + .toObject(new JsonRepresentation("[1,2,3]"), JSONArray.class, null); + assertNotNull(result); + assertEquals(3, result.length()); + } + + @Test + void toObject_toJsonObject_returnsJsonObject() throws Exception { + JSONObject result = + new JsonConverter() + .toObject(new JsonRepresentation("{\"k\":\"v\"}"), JSONObject.class, null); + assertNotNull(result); + assertEquals("v", result.getString("k")); + } + + @Test + void toObject_toJsonTokener_returnsJsonTokener() throws Exception { + JSONTokener result = + new JsonConverter() + .toObject(new JsonRepresentation("{\"k\":\"v\"}"), JSONTokener.class, null); + assertNotNull(result); + } + + @Test + void toObject_toJsonRepresentation_returnsJsonRepresentation() throws Exception { + JsonRepresentation result = + new JsonConverter() + .toObject(new JsonRepresentation("{}"), JsonRepresentation.class, null); + assertNotNull(result); + } + + @Test + void toRepresentation_fromJsonArray_returnsJsonRepresentation() { + Representation result = + new JsonConverter() + .toRepresentation( + new JSONArray("[1,2,3]"), + new Variant(MediaType.APPLICATION_JSON), + null); + assertInstanceOf(JsonRepresentation.class, result); + } + + @Test + void toRepresentation_fromJsonObject_returnsJsonRepresentation() { + Representation result = + new JsonConverter() + .toRepresentation( + new JSONObject("{\"k\":\"v\"}"), + new Variant(MediaType.APPLICATION_JSON), + null); + assertInstanceOf(JsonRepresentation.class, result); + } + + @Test + void toRepresentation_fromJsonTokener_returnsJsonRepresentation() { + Representation result = + new JsonConverter() + .toRepresentation( + new JSONTokener("{\"k\":\"v\"}"), + new Variant(MediaType.APPLICATION_JSON), + null); + assertInstanceOf(JsonRepresentation.class, result); + } + + @Test + void toRepresentation_fromOtherObject_returnsNull() { + assertNull( + new JsonConverter() + .toRepresentation("string", new Variant(MediaType.APPLICATION_JSON), null)); + } + + @Test + void updatePreferences_forJsonArray_addsApplicationJsonPreference() { + List> prefs = new ArrayList<>(); + new JsonConverter().updatePreferences(prefs, JSONArray.class); + assertEquals(1, prefs.size()); + assertEquals(MediaType.APPLICATION_JSON, prefs.getFirst().getMetadata()); + } + + @Test + void updatePreferences_forOtherClass_doesNotModifyList() { + List> prefs = new ArrayList<>(); + new JsonConverter().updatePreferences(prefs, String.class); + assertTrue(prefs.isEmpty()); + } +} diff --git a/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonRepresentationTestCase.java b/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonRepresentationTestCase.java new file mode 100644 index 0000000000..d4ac6dd5e8 --- /dev/null +++ b/org.restlet.ext.json/src/test/java/org/restlet/ext/json/JsonRepresentationTestCase.java @@ -0,0 +1,150 @@ +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. + */ +package org.restlet.ext.json; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Map; +import org.json.JSONArray; +import org.json.JSONObject; +import org.json.JSONStringer; +import org.json.JSONTokener; +import org.junit.jupiter.api.Test; +import org.restlet.representation.Representation; +import org.restlet.representation.StringRepresentation; + +/** Unit tests for {@link JsonRepresentation}. */ +class JsonRepresentationTestCase { + + @Test + void write_fromJsonObject_producesCorrectJson() throws Exception { + JSONObject obj = new JSONObject("{\"key\":\"value\"}"); + String result = new JsonRepresentation(obj).getText(); + assertEquals("value", new JSONObject(result).getString("key")); + } + + @Test + void write_fromJsonArray_producesCorrectJson() throws Exception { + JSONArray arr = new JSONArray("[1,2,3]"); + String result = new JsonRepresentation(arr).getText(); + assertEquals(3, new JSONArray(result).length()); + } + + @Test + void write_fromJsonStringer_producesCorrectJson() throws Exception { + JSONStringer stringer = new JSONStringer(); + stringer.object().key("key").value("value").endObject(); + String result = new JsonRepresentation(stringer).getText(); + assertEquals("{\"key\":\"value\"}", result); + } + + @Test + void write_fromString_producesOriginalText() throws Exception { + String json = "{\"key\":\"value\"}"; + assertEquals(json, new JsonRepresentation(json).getText()); + } + + @Test + void write_fromRepresentation_producesWrappedText() throws Exception { + String json = "{\"key\":\"value\"}"; + assertEquals(json, new JsonRepresentation(new StringRepresentation(json)).getText()); + } + + @Test + void write_fromMap_containsExpectedKey() throws Exception { + String result = new JsonRepresentation(Map.of("key", "value")).getText(); + assertEquals("value", new JSONObject(result).getString("key")); + } + + @Test + void getJsonObject_fromJsonObjectValue_returnsValue() { + JSONObject obj = new JSONObject("{\"key\":\"value\"}"); + assertEquals("value", new JsonRepresentation(obj).getJsonObject().getString("key")); + } + + @Test + void getJsonArray_fromJsonArrayValue_returnsValue() { + JSONArray arr = new JSONArray("[1,2,3]"); + assertEquals(3, new JsonRepresentation(arr).getJsonArray().length()); + } + + @Test + void getJsonObject_fromString_parsesCorrectly() { + JSONObject result = new JsonRepresentation("{\"key\":\"value\"}").getJsonObject(); + assertEquals("value", result.getString("key")); + } + + @Test + void getJsonArray_fromString_parsesCorrectly() { + JSONArray result = new JsonRepresentation("[1,2,3]").getJsonArray(); + assertEquals(3, result.length()); + } + + @Test + void getJsonTokener_fromString_returnsUsableTokener() { + JSONTokener tokener = new JsonRepresentation("{\"key\":\"value\"}").getJsonTokener(); + assertNotNull(tokener); + assertEquals("value", new JSONObject(tokener).getString("key")); + } + + @Test + void indenting_defaultIsFalse() { + assertFalse(new JsonRepresentation(new JSONObject()).isIndenting()); + } + + @Test + void indentingSize_defaultIsThree() { + assertEquals(3, new JsonRepresentation(new JSONObject()).getIndentingSize()); + } + + @Test + void write_withIndentingOnJsonObject_producesFormattedOutput() throws Exception { + // json.org adds a space after ":" when indenting — output differs from compact form + String compact = new JsonRepresentation(new JSONObject("{\"key\":\"value\"}")).getText(); + JsonRepresentation jr = new JsonRepresentation(new JSONObject("{\"key\":\"value\"}")); + jr.setIndenting(true); + assertNotEquals(compact, jr.getText()); + } + + @Test + void write_withIndentingOnJsonArray_producesFormattedOutput() throws Exception { + JsonRepresentation jr = new JsonRepresentation(new JSONArray("[1,2,3]")); + jr.setIndenting(true); + assertTrue(jr.getText().contains("\n")); + } + + @Test + void setIndentingSize_changesIndentationWidth() throws Exception { + // Arrays produce multi-line indented output; use one to verify the indent width + JsonRepresentation jr = new JsonRepresentation(new JSONArray("[1,2,3]")); + jr.setIndenting(true); + jr.setIndentingSize(2); + assertEquals(2, jr.getIndentingSize()); + String result = jr.getText(); + assertTrue(result.contains("\n")); + assertTrue(result.contains(" ")); // 2-space indent present + assertFalse(result.contains(" ")); // 3-space indent absent + } + + @Test + void getSize_fromString_delegatesToWrappedRepresentation() { + String json = "{\"key\":\"value\"}"; + assertEquals(json.length(), new JsonRepresentation(json).getSize()); + } + + @Test + void getSize_fromJsonValue_returnsUnknownSize() { + assertEquals( + Representation.UNKNOWN_SIZE, new JsonRepresentation(new JSONObject()).getSize()); + } +} diff --git a/org.restlet.ext.slf4j/pom.xml b/org.restlet.ext.slf4j/pom.xml index 5b7df3f144..9a36ecc440 100644 --- a/org.restlet.ext.slf4j/pom.xml +++ b/org.restlet.ext.slf4j/pom.xml @@ -25,6 +25,18 @@ org.restlet ${project.version} + + org.slf4j + slf4j-jdk14 + ${lib-slf4j-version} + test + + + org.junit.jupiter + junit-jupiter-api + ${lib-junit-version} + test + diff --git a/org.restlet.ext.slf4j/src/test/java/org/restlet/ext/slf4j/Slf4jLoggerFacadeTestCase.java b/org.restlet.ext.slf4j/src/test/java/org/restlet/ext/slf4j/Slf4jLoggerFacadeTestCase.java new file mode 100644 index 0000000000..95dc4a0a8a --- /dev/null +++ b/org.restlet.ext.slf4j/src/test/java/org/restlet/ext/slf4j/Slf4jLoggerFacadeTestCase.java @@ -0,0 +1,41 @@ +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. + */ +package org.restlet.ext.slf4j; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.util.logging.Logger; +import org.junit.jupiter.api.Test; + +/** Unit tests for {@link Slf4jLoggerFacade}. */ +class Slf4jLoggerFacadeTestCase { + + @Test + void testGetAnonymousLogger() { + Slf4jLoggerFacade facade = new Slf4jLoggerFacade(); + Logger logger = facade.getAnonymousLogger(); + + assertNotNull(logger); + assertInstanceOf(Slf4jLogger.class, logger); + assertEquals("", logger.getName()); + } + + @Test + void testGetLogger() { + Slf4jLoggerFacade facade = new Slf4jLoggerFacade(); + String loggerName = "org.restlet.test"; + Logger logger = facade.getLogger(loggerName); + + assertNotNull(logger); + assertInstanceOf(Slf4jLogger.class, logger); + assertEquals(loggerName, logger.getName()); + } +} diff --git a/org.restlet.ext.slf4j/src/test/java/org/restlet/ext/slf4j/Slf4jLoggerTestCase.java b/org.restlet.ext.slf4j/src/test/java/org/restlet/ext/slf4j/Slf4jLoggerTestCase.java new file mode 100644 index 0000000000..77a503f94b --- /dev/null +++ b/org.restlet.ext.slf4j/src/test/java/org/restlet/ext/slf4j/Slf4jLoggerTestCase.java @@ -0,0 +1,167 @@ +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. + */ +package org.restlet.ext.slf4j; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.logging.Level; +import java.util.logging.LogRecord; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.LoggerFactory; + +/** Unit tests for {@link Slf4jLogger}. */ +class Slf4jLoggerTestCase { + + private Slf4jLogger logger; + + @BeforeEach + void setUp() { + logger = new Slf4jLogger(LoggerFactory.getLogger(Slf4jLoggerTestCase.class.getName())); + } + + @Test + void testConstructor() { + assertNotNull(logger.getSlf4jLogger()); + assertEquals(Slf4jLoggerTestCase.class.getName(), logger.getName()); + } + + @Test + void testSetSlf4jLogger() { + var newLogger = LoggerFactory.getLogger("other"); + logger.setSlf4jLogger(newLogger); + assertEquals(newLogger, logger.getSlf4jLogger()); + } + + @Test + void testLoggingMethods() { + assertDoesNotThrow( + () -> { + logger.config("config message"); + logger.fine("fine message"); + logger.finer("finer message"); + logger.finest("finest message"); + logger.info("info message"); + logger.severe("severe message"); + logger.warning("warning message"); + }); + } + + @Test + void testIsLoggableFixedLevels() { + assertTrue(logger.isLoggable(Level.ALL)); + assertFalse(logger.isLoggable(Level.OFF)); + } + + @Test + void testIsLoggableAllLevels() { + assertDoesNotThrow( + () -> { + logger.isLoggable(Level.CONFIG); + logger.isLoggable(Level.FINE); + logger.isLoggable(Level.FINER); + logger.isLoggable(Level.FINEST); + logger.isLoggable(Level.INFO); + logger.isLoggable(Level.SEVERE); + logger.isLoggable(Level.WARNING); + }); + } + + @Test + void testIsLoggableUnknownLevel() { + Level unknownLevel = new Level("CUSTOM", 450) {}; + assertFalse(logger.isLoggable(unknownLevel)); + } + + @Test + void testLogLevelMessage() { + assertDoesNotThrow( + () -> { + logger.log(Level.CONFIG, "config"); + logger.log(Level.FINE, "fine"); + logger.log(Level.FINER, "finer"); + logger.log(Level.FINEST, "finest"); + logger.log(Level.INFO, "info"); + logger.log(Level.SEVERE, "severe"); + logger.log(Level.WARNING, "warning"); + logger.log(Level.ALL, "unmapped"); + }); + } + + @Test + void testLogLevelMessageObject() { + assertDoesNotThrow( + () -> { + logger.log(Level.CONFIG, "msg {}", "p"); + logger.log(Level.FINE, "msg {}", "p"); + logger.log(Level.FINER, "msg {}", "p"); + logger.log(Level.FINEST, "msg {}", "p"); + logger.log(Level.INFO, "msg {}", "p"); + logger.log(Level.SEVERE, "msg {}", "p"); + logger.log(Level.WARNING, "msg {}", "p"); + logger.log(Level.ALL, "msg {}", "p"); + }); + } + + @Test + void testLogLevelMessageObjects() { + Object[] params = {"p1", "p2"}; + assertDoesNotThrow( + () -> { + logger.log(Level.CONFIG, "msg {} {}", params); + logger.log(Level.FINE, "msg {} {}", params); + logger.log(Level.FINER, "msg {} {}", params); + logger.log(Level.FINEST, "msg {} {}", params); + logger.log(Level.INFO, "msg {} {}", params); + logger.log(Level.SEVERE, "msg {} {}", params); + logger.log(Level.WARNING, "msg {} {}", params); + logger.log(Level.ALL, "msg {} {}", params); + }); + } + + @Test + void testLogLevelMessageThrowable() { + Throwable t = new RuntimeException("test"); + assertDoesNotThrow( + () -> { + logger.log(Level.CONFIG, "msg", t); + logger.log(Level.FINE, "msg", t); + logger.log(Level.FINER, "msg", t); + logger.log(Level.FINEST, "msg", t); + logger.log(Level.INFO, "msg", t); + logger.log(Level.SEVERE, "msg", t); + logger.log(Level.WARNING, "msg", t); + logger.log(Level.ALL, "msg", t); + }); + } + + @Test + void testLogRecord_withThrowable() { + LogRecord logRecord = new LogRecord(Level.SEVERE, "test"); + logRecord.setThrown(new RuntimeException("test")); + assertDoesNotThrow(() -> logger.log(logRecord)); + } + + @Test + void testLogRecord_withParams() { + LogRecord logRecord = new LogRecord(Level.INFO, "test {} {}"); + logRecord.setParameters(new Object[] {"p1", "p2"}); + assertDoesNotThrow(() -> logger.log(logRecord)); + } + + @Test + void testLogRecord_simple() { + LogRecord logRecord = new LogRecord(Level.WARNING, "simple message"); + assertDoesNotThrow(() -> logger.log(logRecord)); + } +} diff --git a/org.restlet.ext.thymeleaf/src/test/java/org/restlet/ext/thymeleaf/TemplateRepresentationTestCase.java b/org.restlet.ext.thymeleaf/src/test/java/org/restlet/ext/thymeleaf/TemplateRepresentationTestCase.java new file mode 100644 index 0000000000..c8723c3d9f --- /dev/null +++ b/org.restlet.ext.thymeleaf/src/test/java/org/restlet/ext/thymeleaf/TemplateRepresentationTestCase.java @@ -0,0 +1,138 @@ +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. + */ +package org.restlet.ext.thymeleaf; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.util.Locale; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.data.MediaType; +import org.restlet.representation.StringRepresentation; +import org.restlet.util.Resolver; +import org.thymeleaf.TemplateEngine; +import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver; +import org.thymeleaf.templateresolver.ITemplateResolver; + +/** Unit tests for {@link TemplateRepresentation}. */ +class TemplateRepresentationTestCase { + + private static TemplateEngine buildEngine() { + ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver(); + resolver.setPrefix("org/restlet/ext/thymeleaf/"); + resolver.setSuffix(".html"); + return TemplateRepresentation.createTemplateEngine(resolver); + } + + @Test + void getLocale_returnsLocaleFromConstructor() { + TemplateRepresentation tr = + new TemplateRepresentation( + "test", buildEngine(), Locale.FRENCH, MediaType.TEXT_PLAIN); + assertEquals(Locale.FRENCH, tr.getLocale()); + } + + @Test + void getTemplateName_returnsNameFromConstructor() { + TemplateRepresentation tr = + new TemplateRepresentation( + "myTemplate", buildEngine(), Locale.getDefault(), MediaType.TEXT_PLAIN); + assertEquals("myTemplate", tr.getTemplateName()); + } + + @Test + void setTemplateName_changesTemplateName() { + TemplateRepresentation tr = + new TemplateRepresentation( + "original", buildEngine(), Locale.getDefault(), MediaType.TEXT_PLAIN); + tr.setTemplateName("changed"); + assertEquals("changed", tr.getTemplateName()); + } + + @Test + void createTemplateEngine_noArg_returnsNonNull() { + assertNotNull(TemplateRepresentation.createTemplateEngine()); + } + + @Test + void createTemplateResolver_returnsNonNull() { + ITemplateResolver resolver = TemplateRepresentation.createTemplateResolver(); + assertNotNull(resolver); + } + + @Test + void wrapConstructor_copiesTemplateName() { + TemplateRepresentation original = + new TemplateRepresentation( + "test", buildEngine(), Locale.getDefault(), MediaType.TEXT_PLAIN); + TemplateRepresentation wrapped = + new TemplateRepresentation( + original, buildEngine(), Locale.ENGLISH, MediaType.TEXT_HTML); + assertEquals("test", wrapped.getTemplateName()); + } + + @Test + void setDataModel_withResolver_rendersTemplateWithoutThrowing() throws Exception { + // ResolverContext.getVariableNames() returns an empty set, so Thymeleaf's OGNL evaluator + // cannot see resolver variables — ${welcome} resolves to null and the

renders empty. + TemplateRepresentation tr = + new TemplateRepresentation( + "test", buildEngine(), Locale.getDefault(), Map.of(), MediaType.TEXT_PLAIN); + tr.setDataModel( + new Resolver<>() { + @Override + public Object resolve(String name) { + return "welcome".equals(name) ? "Hello, resolver" : null; + } + }); + String result = tr.getText(); + assertNotNull(result); + assertFalse(result.contains("Hello, resolver")); + } + + @Test + void write_whenTemplateNotFound_throwsIoException() { + // createTemplateEngine() uses /WEB-INF/templates/ prefix; "nonexistent" won't resolve + TemplateRepresentation tr = + new TemplateRepresentation( + "nonexistent", Locale.getDefault(), MediaType.TEXT_PLAIN); + assertThrows(IOException.class, tr::getText); + } + + @Test + void setDataModel_withFormRequest_rendersFormValues() throws Exception { + TemplateRepresentation tr = + new TemplateRepresentation( + "test", buildEngine(), Locale.getDefault(), MediaType.TEXT_PLAIN); + Request request = new Request(); + request.setEntity( + new StringRepresentation( + "welcome=Hello+from+form", MediaType.APPLICATION_WWW_FORM)); + tr.setDataModel(request, new Response(request)); + assertTrue(tr.getText().contains("Hello from form")); + } + + @Test + void setDataModel_withEmptyFormRequest_rendersTemplateSuccessfully() throws Exception { + TemplateRepresentation tr = + new TemplateRepresentation( + "test", buildEngine(), Locale.getDefault(), MediaType.TEXT_PLAIN); + Request request = new Request(); + request.setEntity(new StringRepresentation("", MediaType.APPLICATION_WWW_FORM)); + tr.setDataModel(request, new Response(request)); + assertNotNull(tr.getText()); + } +} diff --git a/org.restlet.ext.thymeleaf/src/test/java/org/restlet/ext/thymeleaf/ThymeleafConverterTestCase.java b/org.restlet.ext.thymeleaf/src/test/java/org/restlet/ext/thymeleaf/ThymeleafConverterTestCase.java new file mode 100644 index 0000000000..910bc62d2a --- /dev/null +++ b/org.restlet.ext.thymeleaf/src/test/java/org/restlet/ext/thymeleaf/ThymeleafConverterTestCase.java @@ -0,0 +1,130 @@ +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. + */ +package org.restlet.ext.thymeleaf; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.Reader; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.restlet.data.MediaType; +import org.restlet.data.Preference; +import org.restlet.representation.StringRepresentation; +import org.restlet.representation.Variant; +import org.thymeleaf.templateresource.ITemplateResource; + +/** Unit tests for {@link ThymeleafConverter}. */ +class ThymeleafConverterTestCase { + + private static ITemplateResource dummyResource() { + return new ITemplateResource() { + @Override + public String getDescription() { + return "dummy"; + } + + @Override + public String getBaseName() { + return "test"; + } + + @Override + public boolean exists() { + return true; + } + + @Override + public Reader reader() { + return new StringReader(""); + } + + @Override + public ITemplateResource relative(String relativeLocation) { + return this; + } + }; + } + + @Test + void getObjectClasses_alwaysReturnsEmptyList() { + assertTrue( + new ThymeleafConverter() + .getObjectClasses(new Variant(MediaType.TEXT_HTML)) + .isEmpty()); + } + + @Test + void getVariants_forITemplateResource_returnsMediaTypeAll() { + List variants = new ThymeleafConverter().getVariants(ITemplateResource.class); + assertEquals(1, variants.size()); + assertTrue(MediaType.ALL.isCompatible(((Variant) variants.getFirst()).getMediaType())); + } + + @Test + void getVariants_forOtherClass_returnsEmptyList() { + assertTrue(new ThymeleafConverter().getVariants(String.class).isEmpty()); + } + + @Test + void score_iTemplateResource_returnsOne() { + assertEquals( + 1.0f, + new ThymeleafConverter() + .score(dummyResource(), new Variant(MediaType.TEXT_HTML), null)); + } + + @Test + void score_nonITemplateResource_returnsMinusOne() { + assertEquals( + -1.0f, + new ThymeleafConverter() + .score("not a template", new Variant(MediaType.TEXT_HTML), null)); + } + + @Test + void score_repr_alwaysReturnsMinusOne() { + assertEquals( + -1.0f, + new ThymeleafConverter() + .score(new StringRepresentation("text"), String.class, null)); + } + + @Test + void toObject_alwaysReturnsNull() { + assertNull( + new ThymeleafConverter() + .toObject(new StringRepresentation("text"), String.class, null)); + } + + @Test + void toRepresentation_nonITemplateResource_returnsNull() { + assertNull( + new ThymeleafConverter() + .toRepresentation("string", new Variant(MediaType.TEXT_HTML), null)); + } + + @Test + void updatePreferences_forITemplateResource_addsMediaTypeAll() { + List> prefs = new ArrayList<>(); + new ThymeleafConverter().updatePreferences(prefs, ITemplateResource.class); + assertEquals(1, prefs.size()); + assertEquals(MediaType.ALL, prefs.getFirst().getMetadata()); + } + + @Test + void updatePreferences_forOtherClass_doesNotModify() { + List> prefs = new ArrayList<>(); + new ThymeleafConverter().updatePreferences(prefs, String.class); + assertTrue(prefs.isEmpty()); + } +} diff --git a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/DomRepresentationTestCase.java b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/DomRepresentationTestCase.java new file mode 100644 index 0000000000..662901e8d9 --- /dev/null +++ b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/DomRepresentationTestCase.java @@ -0,0 +1,94 @@ +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. + */ +package org.restlet.ext.xml; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.dom.DOMSource; +import org.junit.jupiter.api.Test; +import org.restlet.data.MediaType; +import org.restlet.representation.StringRepresentation; +import org.w3c.dom.Document; + +/** Unit tests for {@link DomRepresentation}. */ +class DomRepresentationTestCase { + + private static final String XML = "hello"; + + @Test + void constructor_default_createsEmptyDocument() throws Exception { + DomRepresentation dr = new DomRepresentation(); + assertNotNull(dr.getDocument()); + assertEquals(MediaType.TEXT_XML, dr.getMediaType()); + } + + @Test + void constructor_fromRepresentation_parsesDocumentElement() throws Exception { + DomRepresentation dr = + new DomRepresentation(new StringRepresentation(XML, MediaType.TEXT_XML)); + assertEquals("root", dr.getDocument().getDocumentElement().getTagName()); + } + + @Test + void constructor_fromDocument_returnsSameDocument() throws Exception { + Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + doc.appendChild(doc.createElement("root")); + DomRepresentation dr = new DomRepresentation(MediaType.TEXT_XML, doc); + assertSame(doc, dr.getDocument()); + } + + @Test + void write_serialisesDocumentToXmlText() throws Exception { + DomRepresentation dr = + new DomRepresentation(new StringRepresentation(XML, MediaType.TEXT_XML)); + String result = dr.getText(); + assertTrue(result.contains("root")); + assertTrue(result.contains("hello")); + } + + @Test + void isIndenting_defaultFalse() throws Exception { + assertFalse(new DomRepresentation().isIndenting()); + } + + @Test + void setIndenting_changesFlag() throws Exception { + DomRepresentation dr = new DomRepresentation(); + dr.setIndenting(true); + assertTrue(dr.isIndenting()); + } + + @Test + void getDomSource_returnsSourceWrappingDocument() throws Exception { + DomRepresentation dr = + new DomRepresentation(new StringRepresentation(XML, MediaType.TEXT_XML)); + DOMSource source = dr.getDomSource(); + assertNotNull(source); + assertNotNull(source.getNode()); + } + + @Test + void release_onDocumentWrapper_createsNewDocumentOnNextAccess() throws Exception { + Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + doc.appendChild(doc.createElement("root")); + DomRepresentation dr = new DomRepresentation(MediaType.TEXT_XML, doc); + assertSame(doc, dr.getDocument()); + dr.release(); + // document is nulled; xmlRepresentation is null → getDocument() creates a new empty one + Document newDoc = dr.getDocument(); + assertNotNull(newDoc); + assertNotSame(doc, newDoc); + } +} diff --git a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/ResolvingTransformerTestCase.java b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/ResolvingTransformerTestCase.java index 8794737e6d..7b8efe20cb 100644 --- a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/ResolvingTransformerTestCase.java +++ b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/ResolvingTransformerTestCase.java @@ -47,16 +47,7 @@ */ class ResolvingTransformerTestCase { - static class AssertResolvingHelper { - - final String baseUri; - - final URIResolver resolver; - - AssertResolvingHelper(final String baseUri, final URIResolver resolver) { - this.baseUri = baseUri; - this.resolver = resolver; - } + record AssertResolvingHelper(String baseUri, URIResolver resolver) { /** Asserts that the testUri resolves into the expectedUri */ void assertResolving(String message, String testUri, String testData) @@ -175,8 +166,7 @@ void testResolving() throws Exception { String absoluteUri = testBase + "/" + testCode; test.assertResolving("error in absolute resolving.", absoluteUri, testData); - String relUri = testCode; - test.assertResolving("error in relative resolving.", relUri, testData); + test.assertResolving("error in relative resolving.", testCode, testData); String relLocalUri = "./" + testCode; test.assertResolving("error in relative resolving to ./", relLocalUri, testData); diff --git a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/SaxRepresentationTestCase.java b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/SaxRepresentationTestCase.java new file mode 100644 index 0000000000..9675246036 --- /dev/null +++ b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/SaxRepresentationTestCase.java @@ -0,0 +1,85 @@ +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. + */ +package org.restlet.ext.xml; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; +import javax.xml.transform.sax.SAXSource; +import org.junit.jupiter.api.Test; +import org.restlet.data.MediaType; +import org.restlet.representation.StringRepresentation; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.helpers.DefaultHandler; + +/** Unit tests for {@link SaxRepresentation}. */ +class SaxRepresentationTestCase { + + private static final String XML = "text"; + + @Test + void isSecureProcessing_defaultTrue() { + assertTrue(new SaxRepresentation().isSecureProcessing()); + } + + @Test + void setSecureProcessing_changesFlag() { + SaxRepresentation sr = new SaxRepresentation(); + sr.setSecureProcessing(false); + assertFalse(sr.isSecureProcessing()); + } + + @Test + void getSaxSource_fromRepresentation_returnsNonNull() throws Exception { + SaxRepresentation sr = + new SaxRepresentation(new StringRepresentation(XML, MediaType.TEXT_XML)); + assertNotNull(sr.getSaxSource()); + } + + @Test + void parse_withContentHandler_firesElementEvents() throws Exception { + SaxRepresentation sr = + new SaxRepresentation(new StringRepresentation(XML, MediaType.TEXT_XML)); + List names = new ArrayList<>(); + sr.parse( + new DefaultHandler() { + @Override + public void startElement( + String uri, String localName, String qName, Attributes atts) { + names.add(qName); + } + }); + assertEquals(List.of("root", "child"), names); + } + + @Test + void parse_withNullContentHandler_throwsIoException() { + SaxRepresentation sr = + new SaxRepresentation(new StringRepresentation(XML, MediaType.TEXT_XML)); + assertThrows(IOException.class, () -> sr.parse(null)); + } + + @Test + void release_clearsSource() throws Exception { + SAXSource source = new SAXSource(new InputSource(new StringReader(XML))); + SaxRepresentation sr = new SaxRepresentation(MediaType.TEXT_XML, source); + assertNotNull(sr.getSaxSource()); + sr.release(); + assertNull(sr.getSaxSource()); + } +} diff --git a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformerTestCase.java b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformerTestCase.java index 71589705ea..c5d2c2371a 100644 --- a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformerTestCase.java +++ b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/TransformerTestCase.java @@ -40,7 +40,6 @@ void trackFailure(String message) { } void trackFailure(String message, int index, Throwable e) { - e.printStackTrace(); trackFailure(message + " " + index + ": " + e.getMessage()); } } @@ -82,19 +81,17 @@ void parallelTestTransform() { for (int i = 0; i < parallelTransform.length; i++) { final int index = i; parallelTransform[i] = - new Thread() { - - @Override - public void run() { - try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { - tr.write(out); - final String result = out.toString(); - assertEquals(TransformerTestCase.this.output, result); - } catch (IOException e) { - tracker.trackFailure("Exception during write in thread ", index, e); - } - } - }; + new Thread( + () -> { + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + tr.write(out); + final String result = out.toString(); + assertEquals(TransformerTestCase.this.output, result); + } catch (IOException e) { + tracker.trackFailure( + "Exception during write in thread ", index, e); + } + }); } for (final Thread pt : parallelTransform) { diff --git a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/XmlConverterTestCase.java b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/XmlConverterTestCase.java new file mode 100644 index 0000000000..64ecae6dcd --- /dev/null +++ b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/XmlConverterTestCase.java @@ -0,0 +1,212 @@ +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. + */ +package org.restlet.ext.xml; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.List; +import javax.xml.parsers.DocumentBuilderFactory; +import org.junit.jupiter.api.Test; +import org.restlet.data.MediaType; +import org.restlet.data.Preference; +import org.restlet.representation.StringRepresentation; +import org.restlet.representation.Variant; +import org.w3c.dom.Document; + +/** Unit tests for {@link XmlConverter}. */ +class XmlConverterTestCase { + + private static final String XML = ""; + + private static Document buildDocument() throws Exception { + return DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + } + + @Test + void getObjectClasses_withTextXmlVariant_returnsThreeClasses() { + List> classes = + new XmlConverter().getObjectClasses(new Variant(MediaType.TEXT_XML)); + assertNotNull(classes); + assertEquals(3, classes.size()); + assertTrue(classes.contains(Document.class)); + assertTrue(classes.contains(DomRepresentation.class)); + assertTrue(classes.contains(SaxRepresentation.class)); + } + + @Test + void getObjectClasses_withNonXmlVariant_returnsNull() { + assertNull(new XmlConverter().getObjectClasses(new Variant(MediaType.TEXT_PLAIN))); + } + + @Test + void getVariants_forDocument_returnsThreeVariants() { + assertEquals(3, new XmlConverter().getVariants(Document.class).size()); + } + + @Test + void getVariants_forSaxRepresentation_returnsThreeVariants() { + assertEquals(3, new XmlConverter().getVariants(SaxRepresentation.class).size()); + } + + @Test + void getVariants_forOtherClass_returnsEmptyList() { + assertTrue(new XmlConverter().getVariants(String.class).isEmpty()); + } + + @Test + void score_documentWithNullTarget_returnsHalf() throws Exception { + assertEquals(0.5f, new XmlConverter().score(buildDocument(), null, null)); + } + + @Test + void score_documentWithTextXmlTarget_returns0_9() throws Exception { + assertEquals( + 0.9f, + new XmlConverter().score(buildDocument(), new Variant(MediaType.TEXT_XML), null)); + } + + @Test + void score_documentWithApplicationXmlTarget_returns0_8() throws Exception { + // APPLICATION_ALL_XML.isCompatible(APPLICATION_XML) is true, so the first branch wins + assertEquals( + 0.8f, + new XmlConverter() + .score(buildDocument(), new Variant(MediaType.APPLICATION_XML), null)); + } + + @Test + void score_documentWithAllXmlTarget_returns0_8() throws Exception { + assertEquals( + 0.8f, + new XmlConverter() + .score(buildDocument(), new Variant(MediaType.APPLICATION_ALL_XML), null)); + } + + @Test + void score_nonDocument_returnsMinusOne() { + assertEquals( + -1.0f, new XmlConverter().score("not xml", new Variant(MediaType.TEXT_XML), null)); + } + + @Test + void score_repr_documentTargetWithTextXml_returns0_9() { + assertEquals( + 0.9f, + new XmlConverter() + .score( + new StringRepresentation(XML, MediaType.TEXT_XML), + Document.class, + null)); + } + + @Test + void score_repr_nullTarget_returnsMinusOne() { + assertEquals( + -1.0f, + new XmlConverter() + .score( + new StringRepresentation(XML, MediaType.TEXT_XML), + (Class) null, + null)); + } + + @Test + void score_repr_nonXmlTarget_returnsMinusOne() { + assertEquals( + -1.0f, + new XmlConverter() + .score( + new StringRepresentation(XML, MediaType.TEXT_XML), + String.class, + null)); + } + + @Test + void toObject_nullTarget_returnsNull() throws Exception { + assertNull( + new XmlConverter() + .toObject(new StringRepresentation(XML, MediaType.TEXT_XML), null, null)); + } + + @Test + void toObject_toDocument_returnsDocument() throws Exception { + Document result = + new XmlConverter() + .toObject( + new StringRepresentation(XML, MediaType.TEXT_XML), + Document.class, + null); + assertNotNull(result); + } + + @Test + void toObject_toDomRepresentation_returnsInstance() throws Exception { + DomRepresentation result = + new XmlConverter() + .toObject( + new StringRepresentation(XML, MediaType.TEXT_XML), + DomRepresentation.class, + null); + assertNotNull(result); + } + + @Test + void toObject_toSaxRepresentation_returnsInstance() throws Exception { + SaxRepresentation result = + new XmlConverter() + .toObject( + new StringRepresentation(XML, MediaType.TEXT_XML), + SaxRepresentation.class, + null); + assertNotNull(result); + } + + @Test + void toRepresentation_fromDocument_returnsDomRepresentation() throws Exception { + assertInstanceOf( + DomRepresentation.class, + new XmlConverter() + .toRepresentation(buildDocument(), new Variant(MediaType.TEXT_XML), null)); + } + + @Test + void toRepresentation_fromRepresentation_returnsSameInstance() { + StringRepresentation rep = new StringRepresentation(XML, MediaType.TEXT_XML); + assertSame( + rep, + new XmlConverter().toRepresentation(rep, new Variant(MediaType.TEXT_XML), null)); + } + + @Test + void toRepresentation_fromOtherObject_returnsNull() { + assertNull( + new XmlConverter() + .toRepresentation("other", new Variant(MediaType.TEXT_XML), null)); + } + + @Test + void updatePreferences_forDocument_addsThreePreferences() { + List> prefs = new ArrayList<>(); + new XmlConverter().updatePreferences(prefs, Document.class); + assertEquals(3, prefs.size()); + } + + @Test + void updatePreferences_forOtherClass_doesNotModify() { + List> prefs = new ArrayList<>(); + new XmlConverter().updatePreferences(prefs, String.class); + assertTrue(prefs.isEmpty()); + } +} diff --git a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/XmlRepresentationTestCase.java b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/XmlRepresentationTestCase.java new file mode 100644 index 0000000000..4863c4fee6 --- /dev/null +++ b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/XmlRepresentationTestCase.java @@ -0,0 +1,146 @@ +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. + */ +package org.restlet.ext.xml; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Iterator; +import javax.xml.transform.stream.StreamSource; +import org.junit.jupiter.api.Test; +import org.restlet.data.MediaType; +import org.restlet.representation.StringRepresentation; +import org.w3c.dom.Node; + +/** + * Unit tests for the XPath API of {@link XmlRepresentation} (exercised via {@link + * DomRepresentation}) and for {@link NodeList}. + */ +class XmlRepresentationTestCase { + + private static final String XML = + "" + + "" + + "" + + "XML Guidetrue" + + "" + + "" + + "Java Basicsfalse" + + "" + + ""; + + private static DomRepresentation dom() { + return new DomRepresentation(new StringRepresentation(XML, MediaType.TEXT_XML)); + } + + @Test + void getText_evaluatesStringExpression() { + assertEquals("XML Guide", dom().getText("/catalog/book[1]/title")); + } + + @Test + void getNumber_evaluatesNumericExpression() { + assertEquals(29.99, dom().getNumber("/catalog/book[1]/@price"), 0.001); + } + + @Test + void getBoolean_evaluatesBooleanPredicate() { + assertTrue(dom().getBoolean("count(/catalog/book) = 2")); + } + + @Test + void getNode_returnsSingleNode() { + Node node = dom().getNode("/catalog/book[2]/title"); + assertNotNull(node); + assertEquals("Java Basics", node.getTextContent()); + } + + @Test + void getNodes_returnsNodeListCoveringAllMethods() { + NodeList nodes = dom().getNodes("/catalog/book"); + assertNotNull(nodes); + // NodeList.size() and NodeList.getLength() both delegate to the underlying list length + assertEquals(2, nodes.size()); + assertEquals(nodes.size(), nodes.getLength()); + // NodeList.get(index) and NodeList.item(index) both retrieve the node at that position + assertNotNull(nodes.getFirst()); + assertNotNull(nodes.item(1)); + assertEquals("book", nodes.getFirst().getNodeName()); + } + + @Test + void getNamespaceURI_whenRegistered_returnsUri() { + DomRepresentation dr = dom(); + dr.getNamespaces().put("cat", "http://example.com/catalog"); + assertEquals("http://example.com/catalog", dr.getNamespaceURI("cat")); + } + + @Test + void getNamespaceURI_withNullNamespacesMap_returnsNull() { + DomRepresentation dr = dom(); + assertNull(dr.getNamespaceURI("any")); + } + + @Test + void getPrefix_whenRegistered_returnsPrefix() { + DomRepresentation dr = dom(); + dr.getNamespaces().put("ex", "http://example.com/"); + assertEquals("ex", dr.getPrefix("http://example.com/")); + } + + @Test + void getPrefixes_returnsIteratorWithMatchingEntries() { + DomRepresentation dr = dom(); + dr.getNamespaces().put("ex", "http://example.com/"); + Iterator it = dr.getPrefixes("http://example.com/"); + assertTrue(it.hasNext()); + assertEquals("ex", it.next()); + } + + @Test + void getStreamSource_returnsNonNull() throws Exception { + StreamSource source = dom().getStreamSource(); + assertNotNull(source); + } + + @Test + void getTextContent_returnsNodeTextContent() { + Node node = dom().getNode("/catalog/book[1]/title"); + assertNotNull(node); + assertEquals("XML Guide", XmlRepresentation.getTextContent(node)); + } + + @Test + void setNamespaceAware_isNamespaceAware_roundTrip() { + DomRepresentation dr = dom(); + assertFalse(dr.isNamespaceAware()); + dr.setNamespaceAware(true); + assertTrue(dr.isNamespaceAware()); + } + + @Test + void setCoalescing_isCoalescing_roundTrip() { + DomRepresentation dr = dom(); + assertFalse(dr.isCoalescing()); + dr.setCoalescing(true); + assertTrue(dr.isCoalescing()); + } + + @Test + void setIgnoringExtraWhitespaces_alsoSetsValidatingDtd() { + DomRepresentation dr = dom(); + assertFalse(dr.isIgnoringExtraWhitespaces()); + dr.setIgnoringExtraWhitespaces(true); + assertTrue(dr.isIgnoringExtraWhitespaces()); + assertTrue(dr.isValidatingDtd()); + } +} diff --git a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/XmlWriterTestCase.java b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/XmlWriterTestCase.java new file mode 100644 index 0000000000..a11b05b763 --- /dev/null +++ b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/XmlWriterTestCase.java @@ -0,0 +1,248 @@ +/** + * Copyright 2005-2026 Qlik + *

+ * The content of this file is subject to the terms of the Apache 2.0 open + * source license available at https://www.opensource.org/licenses/apache-2.0 + *

+ * Restlet is a registered trademark of QlikTech International AB. + */ +package org.restlet.ext.xml; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.StringWriter; +import org.junit.jupiter.api.Test; +import org.xml.sax.helpers.AttributesImpl; + +/** Unit tests for {@link XmlWriter}. */ +class XmlWriterTestCase { + + private static StringWriter sw() { + return new StringWriter(); + } + + @Test + void basicDocument_containsXmlDeclarationAndElement() throws Exception { + StringWriter out = sw(); + XmlWriter w = new XmlWriter(out); + w.startDocument(); + w.startElement("root"); + w.characters("hello"); + w.endElement("root"); + w.endDocument(); + String result = out.toString(); + assertTrue(result.startsWith("")); + assertTrue(result.contains("")); + assertTrue(result.contains("hello")); + assertTrue(result.contains("")); + } + + @Test + void dataElement_localName_writesElement() throws Exception { + StringWriter out = sw(); + XmlWriter w = new XmlWriter(out); + w.startDocument(); + w.dataElement("greeting", "Hello, world!"); + w.endDocument(); + assertTrue(out.toString().contains("Hello, world!")); + } + + @Test + void dataElement_uriAndLocalName_writesElement() throws Exception { + StringWriter out = sw(); + XmlWriter w = new XmlWriter(out); + w.startDocument(); + w.dataElement("", "name", "Alice"); + w.endDocument(); + assertTrue(out.toString().contains("Alice")); + } + + @Test + void emptyElement_localName_writesEmptyTag() throws Exception { + StringWriter out = sw(); + XmlWriter w = new XmlWriter(out); + w.startDocument(); + w.startElement("root"); + w.emptyElement("br"); + w.endElement("root"); + w.endDocument(); + assertTrue(out.toString().contains("
")); + } + + @Test + void emptyElement_uriAndLocalName_writesEmptyTag() throws Exception { + StringWriter out = sw(); + XmlWriter w = new XmlWriter(out); + w.startDocument(); + w.startElement("root"); + w.emptyElement("", "hr"); + w.endElement("root"); + w.endDocument(); + assertTrue(out.toString().contains("


")); + } + + @Test + void startElement_withAttributes_writesAttributeValue() throws Exception { + StringWriter out = sw(); + XmlWriter w = new XmlWriter(out); + AttributesImpl atts = new AttributesImpl(); + atts.addAttribute("", "id", "id", "CDATA", "42"); + w.startDocument(); + w.startElement("", "item", "", atts); + w.endElement("item"); + w.endDocument(); + assertTrue(out.toString().contains("id=\"42\"")); + } + + @Test + void characters_specialChars_areEscaped() throws Exception { + StringWriter out = sw(); + XmlWriter w = new XmlWriter(out); + w.startDocument(); + w.startElement("root"); + w.characters("a&c>d"); + w.endElement("root"); + w.endDocument(); + String result = out.toString(); + assertTrue(result.contains("<")); + assertTrue(result.contains("&")); + assertTrue(result.contains(">")); + } + + @Test + void attributeValue_withQuote_isEscaped() throws Exception { + StringWriter out = sw(); + XmlWriter w = new XmlWriter(out); + AttributesImpl atts = new AttributesImpl(); + atts.addAttribute("", "val", "val", "CDATA", "say \"hello\""); + w.startDocument(); + w.startElement("", "e", "", atts); + w.endElement("e"); + w.endDocument(); + assertTrue(out.toString().contains(""")); + } + + @Test + void characters_highUnicodeChar_isNumericEscaped() throws Exception { + StringWriter out = sw(); + XmlWriter w = new XmlWriter(out); + w.startDocument(); + w.startElement("root"); + w.characters("café"); + w.endElement("root"); + w.endDocument(); + assertTrue(out.toString().contains("é")); + } + + @Test + void setDataFormat_isDataFormat_roundTrip() { + XmlWriter w = new XmlWriter(sw()); + assertFalse(w.isDataFormat()); + w.setDataFormat(true); + assertTrue(w.isDataFormat()); + } + + @Test + void setIndentStep_getIndentStep_roundTrip() { + XmlWriter w = new XmlWriter(sw()); + assertEquals(0, w.getIndentStep()); + w.setIndentStep(4); + assertEquals(4, w.getIndentStep()); + } + + @Test + void setPrefix_getPrefix_roundTrip() { + XmlWriter w = new XmlWriter(sw()); + w.setPrefix("http://example.com/", "ex"); + assertEquals("ex", w.getPrefix("http://example.com/")); + } + + @Test + void forceNSDecl_withPrefix_declaresOnRootElement() throws Exception { + StringWriter out = sw(); + XmlWriter w = new XmlWriter(out); + w.forceNSDecl("http://example.com/ns", "ex"); + w.startDocument(); + w.startElement("root"); + w.endElement("root"); + w.endDocument(); + assertTrue(out.toString().contains("xmlns:ex=\"http://example.com/ns\"")); + } + + @Test + void processingInstruction_writesPI() throws Exception { + StringWriter out = sw(); + XmlWriter w = new XmlWriter(out); + w.startDocument(); + w.processingInstruction("xml-stylesheet", "type=\"text/css\" href=\"style.css\""); + w.startElement("root"); + w.endElement("root"); + w.endDocument(); + assertTrue(out.toString().contains("ok")); + } + + @Test + void reset_inDataFormatMode_allowsNewDocument() throws Exception { + StringWriter first = sw(); + XmlWriter w = new XmlWriter(first); + w.setDataFormat(true); + w.setIndentStep(2); + w.startDocument(); + w.startElement("root"); + w.reset(); + StringWriter second = sw(); + w.setOutput(second); + w.startDocument(); + w.dataElement("fresh", "yes"); + w.endDocument(); + assertTrue(second.toString().contains("yes")); + } + + @Test + void ignorableWhitespace_writesWhitespaceChars() throws Exception { + StringWriter out = sw(); + XmlWriter w = new XmlWriter(out); + w.startDocument(); + w.startElement("root"); + char[] ws = {' ', ' '}; + w.ignorableWhitespace(ws, 0, 2); + w.endElement("root"); + w.endDocument(); + assertTrue(out.toString().contains(" ")); + } + + @Test + void dataFormat_withIndentStep_producesNewlinesAndIndentation() throws Exception { + StringWriter out = sw(); + XmlWriter w = new XmlWriter(out); + w.setDataFormat(true); + w.setIndentStep(2); + w.startDocument(); + w.startElement("Person"); + w.dataElement("name", "Jane"); + w.endElement("Person"); + w.endDocument(); + String result = out.toString(); + assertTrue(result.contains("\n")); + assertTrue(result.contains(" ")); + } +} diff --git a/pom.xml b/pom.xml index c9503bd4ba..3fc5b96372 100644 --- a/pom.xml +++ b/pom.xml @@ -276,6 +276,45 @@ + + org.jacoco + jacoco-maven-plugin + 0.8.12 + + + + prepare-agent + + + + report + test + + report + + + + check + + check + + + + + BUNDLE + + + INSTRUCTION + COVEREDRATIO + 0.50 + + + + + + + + maven-compiler-plugin