diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/AwsAuthenticatorTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/AwsAuthenticatorTestCase.java new file mode 100644 index 0000000000..f2075d09f2 --- /dev/null +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/AwsAuthenticatorTestCase.java @@ -0,0 +1,80 @@ +/** + * 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.crypto; + +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.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; +import org.restlet.Context; +import org.restlet.data.ChallengeScheme; +import org.restlet.ext.crypto.internal.AwsVerifier; +import org.restlet.security.MapVerifier; +import org.restlet.security.Verifier; + +/** Unit tests for {@link AwsAuthenticator}. */ +class AwsAuthenticatorTestCase { + + @Test + void constructor_withContextAndRealm_isNotOptional() { + AwsAuthenticator authenticator = new AwsAuthenticator(new Context(), "realm"); + assertFalse(authenticator.isOptional()); + assertEquals(ChallengeScheme.HTTP_AWS_S3, authenticator.getScheme()); + assertNotNull(authenticator.getVerifier()); + } + + @Test + void constructor_withOptionalFlag_setsOptional() { + AwsAuthenticator authenticator = new AwsAuthenticator(new Context(), true, "realm"); + assertTrue(authenticator.isOptional()); + } + + @Test + void constructor_withExplicitVerifier_usesIt() { + AwsVerifier verifier = new AwsVerifier(null); + AwsAuthenticator authenticator = + new AwsAuthenticator(new Context(), false, "realm", verifier); + assertSame(verifier, authenticator.getVerifier()); + } + + @Test + void maxRequestAge_getterAndSetter_delegateToVerifier() { + AwsAuthenticator authenticator = new AwsAuthenticator(new Context(), "realm"); + authenticator.setMaxRequestAge(1000L); + assertEquals(1000L, authenticator.getMaxRequestAge()); + } + + @Test + void wrappedVerifier_getterAndSetter_delegateToVerifier() { + AwsAuthenticator authenticator = new AwsAuthenticator(new Context(), "realm"); + MapVerifier wrapped = new MapVerifier(); + authenticator.setWrappedVerifier(wrapped); + assertSame(wrapped, authenticator.getWrappedVerifier()); + } + + @Test + void setVerifier_withNonAwsVerifier_throwsIllegalArgumentException() { + AwsAuthenticator authenticator = new AwsAuthenticator(new Context(), "realm"); + Verifier notAnAwsVerifier = new MapVerifier(); + assertThrows( + IllegalArgumentException.class, () -> authenticator.setVerifier(notAnAwsVerifier)); + } + + @Test + void setVerifier_withAwsVerifier_replacesIt() { + AwsAuthenticator authenticator = new AwsAuthenticator(new Context(), "realm"); + AwsVerifier newVerifier = new AwsVerifier(null); + authenticator.setVerifier(newVerifier); + assertSame(newVerifier, authenticator.getVerifier()); + } +} diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/DigestUtilsTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/DigestUtilsTestCase.java new file mode 100644 index 0000000000..05096e826b --- /dev/null +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/DigestUtilsTestCase.java @@ -0,0 +1,113 @@ +/** + * 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.crypto; + +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 java.util.Base64; +import org.junit.jupiter.api.Test; +import org.restlet.data.Digest; + +/** Unit tests for {@link DigestUtils}. */ +class DigestUtilsTestCase { + + @Test + void toMd5_string_matchesKnownVector() { + assertEquals("68e109f0f40ca72a15e05cc22786f8e6", DigestUtils.toMd5("HelloWorld")); + } + + @Test + void toMd5_withCharset_matchesKnownVector() throws Exception { + assertEquals("68e109f0f40ca72a15e05cc22786f8e6", DigestUtils.toMd5("HelloWorld", "UTF-8")); + } + + @Test + void toSha1_string_isDeterministicBase64() { + String result = DigestUtils.toSha1("HelloWorld"); + assertEquals(result, DigestUtils.toSha1("HelloWorld")); + assertEquals(28, result.length()); + } + + @Test + void toSha1_withCharset_isDeterministic() throws Exception { + assertEquals( + DigestUtils.toSha1("HelloWorld", "UTF-8"), + DigestUtils.toSha1("HelloWorld", "UTF-8")); + } + + @Test + void digest_md5Algorithm_delegatesToToMd5() { + assertEquals( + DigestUtils.toMd5("HelloWorld"), + DigestUtils.digest("HelloWorld", Digest.ALGORITHM_MD5)); + } + + @Test + void digest_sha1Algorithm_delegatesToToSha1() { + assertEquals( + DigestUtils.toSha1("HelloWorld"), + DigestUtils.digest("HelloWorld", Digest.ALGORITHM_SHA_1)); + } + + @Test + void digest_unsupportedAlgorithm_throwsIllegalArgumentException() { + assertThrows( + IllegalArgumentException.class, + () -> DigestUtils.digest("HelloWorld", "unsupported")); + } + + @Test + void digest_charArrayOverload_matchesStringOverload() { + assertArrayEquals( + DigestUtils.digest("HelloWorld", Digest.ALGORITHM_MD5).toCharArray(), + DigestUtils.digest("HelloWorld".toCharArray(), Digest.ALGORITHM_MD5)); + } + + @Test + void toHMacSha256_bytesKey_matchesKnownVector() { + byte[] result = DigestUtils.toHMacSha256("hello", "secret-key".getBytes()); + assertEquals( + "mOf/uWS7Wj+QLbH8EBpbqpi28s1WhYIQydcPJqx2L8c=", + Base64.getEncoder().encodeToString(result)); + } + + @Test + void toHMacSha256_stringKey_matchesBytesKeyOverload() { + assertArrayEquals( + DigestUtils.toHMacSha256("hello", "secret-key".getBytes()), + DigestUtils.toHMacSha256("hello", "secret-key")); + } + + @Test + void toHMacSha1_bytesKey_matchesKnownVector() { + byte[] result = DigestUtils.toHMacSha1("hello", "secret-key".getBytes()); + assertEquals("J5rb+6R5G3Md2aC4ofFuty27aR0=", Base64.getEncoder().encodeToString(result)); + } + + @Test + void toHMacSha1_stringKey_matchesBytesKeyOverload() { + assertArrayEquals( + DigestUtils.toHMacSha1("hello", "secret-key".getBytes()), + DigestUtils.toHMacSha1("hello", "secret-key")); + } + + @Test + void toHttpDigest_withSecret_hashesIdentifierRealmAndSecret() { + String expected = DigestUtils.toMd5("user:realm:pass"); + assertEquals(expected, DigestUtils.toHttpDigest("user", "pass".toCharArray(), "realm")); + } + + @Test + void toHttpDigest_withNullSecret_returnsNull() { + assertNull(DigestUtils.toHttpDigest("user", null, "realm")); + } +} diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/AwsUtilsQueryTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/AwsUtilsQueryTestCase.java new file mode 100644 index 0000000000..d9ffbc73ec --- /dev/null +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/AwsUtilsQueryTestCase.java @@ -0,0 +1,72 @@ +/** + * 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.crypto.internal; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.restlet.data.Method; +import org.restlet.data.Parameter; +import org.restlet.data.Reference; + +/** Unit tests for the query-signature related methods of {@link AwsUtils}. */ +class AwsUtilsQueryTestCase { + + private List params() { + List params = new ArrayList<>(); + params.add(new Parameter("Version", "2009-04-15")); + params.add(new Parameter("Action", "ListDomains")); + return params; + } + + @Test + void getQueryStringToSign_sortsParametersAndConcatenatesFields() { + Reference resourceRef = new Reference("http://sdb.amazonaws.com/"); + + String result = AwsUtils.getQueryStringToSign(Method.GET, resourceRef, params()); + + assertEquals("GET\nsdb.amazonaws.com\n/\nAction=ListDomains&Version=2009-04-15", result); + } + + @Test + void getQueryStringToSign_nullMethod_omitsMethodName() { + Reference resourceRef = new Reference("http://sdb.amazonaws.com/"); + + String result = AwsUtils.getQueryStringToSign(null, resourceRef, new ArrayList<>()); + + assertEquals("\nsdb.amazonaws.com\n/\n", result); + } + + @Test + void getQuerySignature_matchesKnownVector() { + Reference resourceRef = new Reference("http://sdb.amazonaws.com/"); + + String signature = + AwsUtils.getQuerySignature( + Method.GET, resourceRef, params(), "mysecret".toCharArray()); + + assertEquals("DG2zC7EgmtLBDdwPHV71VBw4mPpvIl1zymtFXAl/trQ=", signature); + } + + @Test + void getHmacSha256Signature_isDeterministic() { + String signature1 = AwsUtils.getHmacSha256Signature("hello", "secret".toCharArray()); + String signature2 = AwsUtils.getHmacSha256Signature("hello", "secret".toCharArray()); + assertEquals(signature1, signature2); + } + + @Test + void getHmacSha1Signature_isDeterministic() { + String signature1 = AwsUtils.getHmacSha1Signature("hello", "secret".toCharArray()); + String signature2 = AwsUtils.getHmacSha1Signature("hello", "secret".toCharArray()); + assertEquals(signature1, signature2); + } +} diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsQueryHelperTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsQueryHelperTestCase.java new file mode 100644 index 0000000000..c2d9917474 --- /dev/null +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAwsQueryHelperTestCase.java @@ -0,0 +1,64 @@ +/** + * 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.crypto.internal; + +import static org.junit.jupiter.api.Assertions.assertEquals; +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 org.junit.jupiter.api.Test; +import org.restlet.Request; +import org.restlet.data.ChallengeResponse; +import org.restlet.data.ChallengeScheme; +import org.restlet.data.Method; +import org.restlet.data.Reference; + +/** Unit tests for {@link HttpAwsQueryHelper}. */ +class HttpAwsQueryHelperTestCase { + + @Test + void constructor_registersAwsQueryScheme() { + HttpAwsQueryHelper helper = new HttpAwsQueryHelper(); + assertEquals(ChallengeScheme.HTTP_AWS_QUERY, helper.getChallengeScheme()); + } + + @Test + void updateReference_withoutActionParameter_returnsSameReference() { + HttpAwsQueryHelper helper = new HttpAwsQueryHelper(); + Request request = new Request(Method.GET, "http://sdb.amazonaws.com/"); + Reference resourceRef = new Reference("http://sdb.amazonaws.com/"); + + Reference result = helper.updateReference(resourceRef, null, request); + + assertSame(resourceRef, result); + } + + @Test + void updateReference_withActionParameter_signsAndAppendsParameters() { + HttpAwsQueryHelper helper = new HttpAwsQueryHelper(); + ChallengeResponse challengeResponse = + new ChallengeResponse( + ChallengeScheme.HTTP_AWS_QUERY, "myaccesskey", "mysecret".toCharArray()); + Request request = new Request(Method.GET, "http://sdb.amazonaws.com/?Action=ListDomains"); + request.setChallengeResponse(challengeResponse); + Reference resourceRef = new Reference("http://sdb.amazonaws.com/?Action=ListDomains"); + + Reference result = helper.updateReference(resourceRef, challengeResponse, request); + + assertNotSame(resourceRef, result); + String query = result.getQuery(); + assertTrue(query.contains("AWSAccessKeyId=myaccesskey")); + assertTrue(query.contains("SignatureMethod=HmacSHA256")); + assertTrue(query.contains("SignatureVersion=2")); + assertTrue(query.contains("Version=2009-04-15")); + assertTrue(query.contains("Timestamp=")); + assertTrue(query.contains("Signature=")); + } +} diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyHelperTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyHelperTestCase.java new file mode 100644 index 0000000000..c3b0ed3da4 --- /dev/null +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyHelperTestCase.java @@ -0,0 +1,89 @@ +/** + * 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.crypto.internal; + +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 org.junit.jupiter.api.Test; +import org.restlet.Request; +import org.restlet.data.ChallengeResponse; +import org.restlet.data.ChallengeScheme; +import org.restlet.data.Header; +import org.restlet.data.Method; +import org.restlet.engine.header.ChallengeWriter; +import org.restlet.engine.header.HeaderConstants; +import org.restlet.util.Series; + +/** Unit tests for {@link HttpAzureSharedKeyHelper}. */ +class HttpAzureSharedKeyHelperTestCase { + + @Test + void constructor_registersAzureSharedKeyScheme() { + HttpAzureSharedKeyHelper helper = new HttpAzureSharedKeyHelper(); + assertEquals(ChallengeScheme.HTTP_AZURE_SHAREDKEY, helper.getChallengeScheme()); + } + + @Test + void formatResponse_withoutOptionalHeaders_addsDateHeaderAndFormatsCredential() { + HttpAzureSharedKeyHelper helper = new HttpAzureSharedKeyHelper(); + Request request = + new Request(Method.GET, "http://myaccount.blob.core.windows.net/container"); + ChallengeResponse challenge = + new ChallengeResponse( + ChallengeScheme.HTTP_AZURE_SHAREDKEY, + "myaccount", + "c2VjcmV0".toCharArray()); + Series

httpHeaders = new Series<>(Header.class); + + ChallengeWriter cw = new ChallengeWriter(); + helper.formatResponse(cw, challenge, request, httpHeaders); + + String result = cw.toString(); + assertTrue(result.startsWith("myaccount:")); + assertNotNull(httpHeaders.getFirstValue(HeaderConstants.HEADER_DATE, true)); + } + + @Test + void formatResponse_withHeadersAndCompQuery_isDeterministic() { + HttpAzureSharedKeyHelper helper = new HttpAzureSharedKeyHelper(); + Request request = + new Request( + Method.PUT, + "http://myaccount.blob.core.windows.net/container/blob?comp=metadata"); + ChallengeResponse challenge = + new ChallengeResponse( + ChallengeScheme.HTTP_AZURE_SHAREDKEY, + "myaccount", + "c2VjcmV0".toCharArray()); + + Series
httpHeaders1 = new Series<>(Header.class); + httpHeaders1.add(HeaderConstants.HEADER_CONTENT_TYPE, "application/octet-stream"); + httpHeaders1.add(HeaderConstants.HEADER_CONTENT_MD5, "abc123"); + httpHeaders1.add(HeaderConstants.HEADER_DATE, "Wed, 21 Oct 2015 07:28:00 GMT"); + httpHeaders1.add("x-ms-version", "2020-02-10"); + httpHeaders1.add("x-ms-blob-type", "BlockBlob"); + + Series
httpHeaders2 = new Series<>(Header.class); + httpHeaders2.add(HeaderConstants.HEADER_CONTENT_TYPE, "application/octet-stream"); + httpHeaders2.add(HeaderConstants.HEADER_CONTENT_MD5, "abc123"); + httpHeaders2.add(HeaderConstants.HEADER_DATE, "Wed, 21 Oct 2015 07:28:00 GMT"); + httpHeaders2.add("x-ms-version", "2020-02-10"); + httpHeaders2.add("x-ms-blob-type", "BlockBlob"); + + ChallengeWriter cw1 = new ChallengeWriter(); + helper.formatResponse(cw1, challenge, request, httpHeaders1); + + ChallengeWriter cw2 = new ChallengeWriter(); + helper.formatResponse(cw2, challenge, request, httpHeaders2); + + assertEquals(cw1.toString(), cw2.toString()); + } +} diff --git a/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyLiteHelperTestCase.java b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyLiteHelperTestCase.java new file mode 100644 index 0000000000..72272f1b61 --- /dev/null +++ b/org.restlet.ext.crypto/src/test/java/org/restlet/ext/crypto/internal/HttpAzureSharedKeyLiteHelperTestCase.java @@ -0,0 +1,76 @@ +/** + * 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.crypto.internal; + +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 org.junit.jupiter.api.Test; +import org.restlet.Request; +import org.restlet.data.ChallengeResponse; +import org.restlet.data.ChallengeScheme; +import org.restlet.data.Header; +import org.restlet.data.Method; +import org.restlet.engine.header.ChallengeWriter; +import org.restlet.engine.header.HeaderConstants; +import org.restlet.util.Series; + +/** Unit tests for {@link HttpAzureSharedKeyLiteHelper}. */ +class HttpAzureSharedKeyLiteHelperTestCase { + + @Test + void constructor_registersAzureSharedKeyLiteScheme() { + HttpAzureSharedKeyLiteHelper helper = new HttpAzureSharedKeyLiteHelper(); + assertEquals(ChallengeScheme.HTTP_AZURE_SHAREDKEY_LITE, helper.getChallengeScheme()); + } + + @Test + void formatResponse_withoutDateHeader_addsFreshDateHeader() { + HttpAzureSharedKeyLiteHelper helper = new HttpAzureSharedKeyLiteHelper(); + Request request = + new Request(Method.GET, "http://myaccount.table.core.windows.net/mytable"); + ChallengeResponse challenge = + new ChallengeResponse( + ChallengeScheme.HTTP_AZURE_SHAREDKEY_LITE, + "myaccount", + "c2VjcmV0".toCharArray()); + Series

httpHeaders = new Series<>(Header.class); + + ChallengeWriter cw = new ChallengeWriter(); + helper.formatResponse(cw, challenge, request, httpHeaders); + + assertTrue(cw.toString().startsWith("myaccount:")); + assertNotNull(httpHeaders.getFirstValue(HeaderConstants.HEADER_DATE, true)); + } + + @Test + void formatResponse_withXmsDateHeader_usesItInsteadOfDateHeader() { + HttpAzureSharedKeyLiteHelper helper = new HttpAzureSharedKeyLiteHelper(); + Request request = + new Request( + Method.GET, + "http://myaccount.table.core.windows.net/mytable?comp=metadata"); + ChallengeResponse challenge = + new ChallengeResponse( + ChallengeScheme.HTTP_AZURE_SHAREDKEY_LITE, + "myaccount", + "c2VjcmV0".toCharArray()); + Series
httpHeaders = new Series<>(Header.class); + httpHeaders.add("x-ms-date", "Wed, 21 Oct 2015 07:28:00 GMT"); + httpHeaders.add(HeaderConstants.HEADER_DATE, "should-be-ignored"); + + ChallengeWriter cw = new ChallengeWriter(); + helper.formatResponse(cw, challenge, request, httpHeaders); + + assertTrue(cw.toString().startsWith("myaccount:")); + assertEquals( + "should-be-ignored", httpHeaders.getFirstValue(HeaderConstants.HEADER_DATE, true)); + } +} diff --git a/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/JacksonConverterTestCase.java b/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/JacksonConverterTestCase.java new file mode 100644 index 0000000000..6096f2b1a9 --- /dev/null +++ b/org.restlet.ext.jackson/src/test/java/org/restlet/ext/jackson/JacksonConverterTestCase.java @@ -0,0 +1,192 @@ +/** + * 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.jackson; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +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.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 JacksonConverter}. */ +class JacksonConverterTestCase { + + private final JacksonConverter converter = new JacksonConverter(); + + @Test + void isCompatible_forSupportedMediaTypes_returnsTrue() { + assertTrue(converter.isCompatible(new Variant(MediaType.APPLICATION_JSON))); + assertTrue(converter.isCompatible(new Variant(MediaType.APPLICATION_JSON_SMILE))); + assertTrue(converter.isCompatible(new Variant(MediaType.APPLICATION_XML))); + assertTrue(converter.isCompatible(new Variant(MediaType.TEXT_XML))); + assertTrue(converter.isCompatible(new Variant(MediaType.APPLICATION_YAML))); + assertTrue(converter.isCompatible(new Variant(MediaType.TEXT_YAML))); + assertTrue(converter.isCompatible(new Variant(MediaType.TEXT_CSV))); + } + + @Test + void isCompatible_forUnsupportedMediaType_returnsFalse() { + assertFalse(converter.isCompatible(new Variant(MediaType.IMAGE_PNG))); + } + + @Test + void isCompatible_forNullVariant_returnsFalse() { + assertFalse(converter.isCompatible(null)); + } + + @Test + void getObjectClasses_forCompatibleVariant_returnsObjectAndJacksonRepresentation() { + List> result = converter.getObjectClasses(new Variant(MediaType.APPLICATION_JSON)); + assertEquals(List.of(Object.class, JacksonRepresentation.class), result); + } + + @Test + void getObjectClasses_forIncompatibleVariant_returnsNull() { + assertNull(converter.getObjectClasses(new Variant(MediaType.IMAGE_PNG))); + } + + @Test + void getVariants_forNonNullClass_returnsAllSupportedVariants() { + List variants = converter.getVariants(String.class); + assertEquals(7, variants.size()); + } + + @Test + void getVariants_forNullClass_returnsEmptyList() { + assertTrue(converter.getVariants(null).isEmpty()); + } + + @Test + void score_jacksonRepresentation_returnsOne() { + JacksonRepresentation source = + new JacksonRepresentation<>(MediaType.APPLICATION_JSON, "text"); + assertEquals(1.0f, converter.score(source, new Variant(MediaType.APPLICATION_JSON), null)); + } + + @Test + void score_nullTarget_returnsHalf() { + assertEquals(0.5f, converter.score("text", null, null)); + } + + @Test + void score_compatibleTarget_returnsPointEight() { + assertEquals(0.8f, converter.score("text", new Variant(MediaType.APPLICATION_JSON), null)); + } + + @Test + void score_incompatibleTarget_returnsHalf() { + assertEquals(0.5f, converter.score("text", new Variant(MediaType.IMAGE_PNG), null)); + } + + @Test + void scoreRepresentation_jacksonRepresentation_returnsOne() { + JacksonRepresentation source = + new JacksonRepresentation<>(MediaType.APPLICATION_JSON, "text"); + assertEquals(1.0f, converter.score(source, String.class, null)); + } + + @Test + void scoreRepresentation_forJacksonRepresentationTarget_returnsOne() { + assertEquals( + 1.0f, + converter.score( + new StringRepresentation("text", MediaType.TEXT_PLAIN), + JacksonRepresentation.class, + null)); + } + + @Test + void scoreRepresentation_forCompatibleSource_returnsPointEight() { + assertEquals( + 0.8f, + converter.score( + new StringRepresentation("text", MediaType.APPLICATION_JSON), + String.class, + null)); + } + + @Test + void scoreRepresentation_forIncompatibleSource_returnsMinusOne() { + assertEquals( + -1.0f, + converter.score( + new StringRepresentation("text", MediaType.IMAGE_PNG), String.class, null)); + } + + @Test + void toObject_fromJacksonRepresentation_returnsUnderlyingObject() throws Exception { + JacksonRepresentation source = + new JacksonRepresentation<>(MediaType.APPLICATION_JSON, "text"); + assertEquals("text", converter.toObject(source, String.class, null)); + } + + @Test + void toObject_fromJacksonRepresentation_forJacksonRepresentationTarget_returnsRepresentation() + throws Exception { + JacksonRepresentation source = + new JacksonRepresentation<>(MediaType.APPLICATION_JSON, "text"); + Object result = converter.toObject(source, JacksonRepresentation.class, null); + assertTrue(result instanceof JacksonRepresentation); + } + + @Test + void toObject_fromCompatibleRepresentation_returnsParsedObject() throws Exception { + StringRepresentation source = + new StringRepresentation("\"text\"", MediaType.APPLICATION_JSON); + assertEquals("text", converter.toObject(source, String.class, null)); + } + + @Test + void toObject_fromIncompatibleRepresentation_returnsNull() throws Exception { + StringRepresentation source = new StringRepresentation("text", MediaType.IMAGE_PNG); + assertNull(converter.toObject(source, String.class, null)); + } + + @Test + void toRepresentation_fromJacksonRepresentation_returnsSameInstance() { + JacksonRepresentation source = + new JacksonRepresentation<>(MediaType.APPLICATION_JSON, "text"); + Representation result = + converter.toRepresentation(source, new Variant(MediaType.APPLICATION_JSON), null); + assertEquals(source, result); + } + + @Test + void toRepresentation_withoutMediaType_defaultsToJson() { + Variant target = new Variant(); + Representation result = converter.toRepresentation("text", target, null); + assertEquals(MediaType.APPLICATION_JSON, target.getMediaType()); + assertTrue(result instanceof JacksonRepresentation); + } + + @Test + void toRepresentation_forIncompatibleTarget_returnsNull() { + Representation result = + converter.toRepresentation("text", new Variant(MediaType.IMAGE_PNG), null); + assertNull(result); + } + + @Test + void updatePreferences_addsAllSupportedMediaTypesWithFullPriority() { + List> preferences = new ArrayList<>(); + converter.updatePreferences(preferences, String.class); + assertEquals(7, preferences.size()); + for (Preference preference : preferences) { + assertEquals(1.0f, preference.getQuality()); + } + } +} diff --git a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringContextTestCase.java b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringContextTestCase.java new file mode 100644 index 0000000000..23f0e90960 --- /dev/null +++ b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringContextTestCase.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.spring; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; +import org.restlet.Client; +import org.restlet.Context; +import org.restlet.data.Protocol; + +/** Unit tests for {@link SpringContext}. */ +class SpringContextTestCase { + + private Context restletContextWithClapDispatcher() { + Context context = new Context(); + context.setClientDispatcher(new Client(Protocol.CLAP)); + return context; + } + + @Test + void getRestletContext_returnsConstructorArgument() { + Context restletContext = new Context(); + SpringContext springContext = new SpringContext(restletContext); + assertSame(restletContext, springContext.getRestletContext()); + } + + @Test + void getPropertyConfigRefs_lazyInitializesToEmptyModifiableList() { + SpringContext springContext = new SpringContext(new Context()); + assertNotNull(springContext.getPropertyConfigRefs()); + assertTrue(springContext.getPropertyConfigRefs().isEmpty()); + springContext.getPropertyConfigRefs().add("some-ref"); + assertEquals(1, springContext.getPropertyConfigRefs().size()); + } + + @Test + void getXmlConfigRefs_lazyInitializesToEmptyModifiableList() { + SpringContext springContext = new SpringContext(new Context()); + assertNotNull(springContext.getXmlConfigRefs()); + assertTrue(springContext.getXmlConfigRefs().isEmpty()); + springContext.getXmlConfigRefs().add("some-ref"); + assertEquals(1, springContext.getXmlConfigRefs().size()); + } + + @Test + void refresh_withoutConfigRefs_refreshesEmptyContext() { + SpringContext springContext = new SpringContext(new Context()); + springContext.refresh(); + assertTrue(springContext.isActive()); + } + + @Test + void refresh_withPropertyConfigRef_loadsBeanDefinitionsFromProperties() { + SpringContext springContext = new SpringContext(restletContextWithClapDispatcher()); + springContext + .getPropertyConfigRefs() + .add("clap://class/org/restlet/ext/spring/SpringContextTestCase.properties"); + + springContext.refresh(); + + assertNotNull(springContext.getBean("myPropertyBean")); + } + + @Test + void refresh_withXmlConfigRef_loadsBeanDefinitionsFromXml() { + SpringContext springContext = new SpringContext(restletContextWithClapDispatcher()); + springContext + .getXmlConfigRefs() + .add("clap://class/org/restlet/ext/spring/SpringContextTestCase.xml"); + + springContext.refresh(); + + assertNotNull(springContext.getBean("myXmlBean")); + } +} diff --git a/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringResourceTestCase.java b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringResourceTestCase.java new file mode 100644 index 0000000000..e219334b7c --- /dev/null +++ b/org.restlet.ext.spring/src/test/java/org/restlet/ext/spring/SpringResourceTestCase.java @@ -0,0 +1,105 @@ +/** + * 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.spring; + +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.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.io.InputStream; +import org.junit.jupiter.api.Test; +import org.restlet.engine.io.IoUtils; +import org.restlet.representation.Representation; +import org.restlet.representation.StringRepresentation; + +/** Unit tests for {@link SpringResource}. */ +class SpringResourceTestCase { + + @Test + void constructor_withNullRepresentation_throwsIllegalArgumentException() { + assertThrows(IllegalArgumentException.class, () -> new SpringResource(null)); + } + + @Test + void constructor_withoutDescription_usesDefaultDescription() { + SpringResource resource = new SpringResource(new StringRepresentation("text")); + assertEquals("Restlet Representation", resource.getDescription()); + } + + @Test + void constructor_withNullDescription_usesEmptyDescription() { + SpringResource resource = new SpringResource(new StringRepresentation("text"), null); + assertEquals("", resource.getDescription()); + } + + @Test + void constructor_withCustomDescription_usesIt() { + SpringResource resource = + new SpringResource(new StringRepresentation("text"), "my description"); + assertEquals("my description", resource.getDescription()); + } + + @Test + void existsAndIsOpen_alwaysReturnTrue() { + SpringResource resource = new SpringResource(new StringRepresentation("text")); + assertTrue(resource.exists()); + assertTrue(resource.isOpen()); + } + + @Test + void getInputStream_returnsUnderlyingStream() throws Exception { + SpringResource resource = new SpringResource(new StringRepresentation("hello")); + assertEquals("hello", IoUtils.toString(resource.getInputStream())); + } + + @Test + void getInputStream_readTwiceOnTransientRepresentation_throwsIllegalStateException() + throws Exception { + StringRepresentation transientRepresentation = new StringRepresentation("hello"); + transientRepresentation.setTransient(true); + SpringResource resource = new SpringResource(transientRepresentation); + + resource.getInputStream(); + + assertThrows(IllegalStateException.class, resource::getInputStream); + } + + @Test + void getInputStream_whenStreamIsNull_throwsIllegalStateException() { + Representation nullStreamRepresentation = + new StringRepresentation("hello") { + @Override + public InputStream getStream() throws IOException { + return null; + } + }; + SpringResource resource = new SpringResource(nullStreamRepresentation); + + assertThrows(IllegalStateException.class, resource::getInputStream); + } + + @Test + void equalsAndHashCode_basedOnWrappedRepresentation() { + StringRepresentation representation = new StringRepresentation("hello"); + SpringResource resource1 = new SpringResource(representation); + SpringResource resource2 = new SpringResource(representation); + SpringResource resource3 = + new SpringResource( + new StringRepresentation("other", org.restlet.data.MediaType.TEXT_HTML)); + + assertEquals(resource1, resource1); + assertEquals(resource1, resource2); + assertEquals(resource1.hashCode(), resource2.hashCode()); + assertNotEquals(resource1, resource3); + assertFalse(resource1.equals("not a resource")); + } +} diff --git a/org.restlet.ext.spring/src/test/resources/org/restlet/ext/spring/SpringContextTestCase.properties b/org.restlet.ext.spring/src/test/resources/org/restlet/ext/spring/SpringContextTestCase.properties new file mode 100644 index 0000000000..c0e7a28871 --- /dev/null +++ b/org.restlet.ext.spring/src/test/resources/org/restlet/ext/spring/SpringContextTestCase.properties @@ -0,0 +1 @@ +myPropertyBean.(class)=java.lang.Object diff --git a/org.restlet.ext.spring/src/test/resources/org/restlet/ext/spring/SpringContextTestCase.xml b/org.restlet.ext.spring/src/test/resources/org/restlet/ext/spring/SpringContextTestCase.xml new file mode 100644 index 0000000000..e152d6814c --- /dev/null +++ b/org.restlet.ext.spring/src/test/resources/org/restlet/ext/spring/SpringContextTestCase.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/org.restlet.ext.thymeleaf/src/test/java/org/restlet/ext/thymeleaf/TemplateFilterTestCase.java b/org.restlet.ext.thymeleaf/src/test/java/org/restlet/ext/thymeleaf/TemplateFilterTestCase.java new file mode 100644 index 0000000000..b7856931d9 --- /dev/null +++ b/org.restlet.ext.thymeleaf/src/test/java/org/restlet/ext/thymeleaf/TemplateFilterTestCase.java @@ -0,0 +1,152 @@ +/** + * 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.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.junit.jupiter.api.Test; +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.data.Encoding; +import org.restlet.data.MediaType; +import org.restlet.data.Method; +import org.restlet.representation.Representation; +import org.restlet.representation.StringRepresentation; +import org.restlet.util.Resolver; + +/** Unit tests for {@link TemplateFilter}. */ +class TemplateFilterTestCase { + + private static final Encoding THYMELEAF = new Encoding("thymeleaf", "Thymeleaf"); + + private static class TestTemplateFilter extends TemplateFilter { + TestTemplateFilter() { + super(); + } + + TestTemplateFilter(org.restlet.Context context) { + super(context); + } + + TestTemplateFilter(org.restlet.Context context, org.restlet.Restlet next) { + super(context, next); + } + + TestTemplateFilter( + org.restlet.Context context, org.restlet.Restlet next, Map model) { + super(context, next, model); + } + + TestTemplateFilter( + org.restlet.Context context, org.restlet.Restlet next, Resolver resolver) { + super(context, next, resolver); + } + } + + private Request newRequest() { + Request request = new Request(Method.GET, "/"); + request.setEntity(new StringRepresentation("foo=bar", MediaType.APPLICATION_WWW_FORM)); + return request; + } + + private TemplateRepresentation templateEntity() { + TemplateRepresentation representation = + new TemplateRepresentation("myTemplate", Locale.ENGLISH, MediaType.TEXT_HTML); + representation.getEncodings().add(THYMELEAF); + return representation; + } + + @Test + void getLocale_defaultsToJvmDefault() { + assertEquals(Locale.getDefault(), new TestTemplateFilter().getLocale()); + } + + @Test + void afterHandle_noEntity_doesNothing() { + TestTemplateFilter filter = new TestTemplateFilter(new Context()); + Request request = newRequest(); + Response response = new Response(request); + + filter.afterHandle(request, response); + + assertNull(response.getEntity()); + } + + @Test + void afterHandle_entityWithoutThymeleafEncoding_leavesEntityUnchanged() { + TestTemplateFilter filter = new TestTemplateFilter(new Context()); + Request request = newRequest(); + Response response = new Response(request); + Representation entity = new StringRepresentation("plain", MediaType.TEXT_PLAIN); + response.setEntity(entity); + + filter.afterHandle(request, response); + + assertSame(entity, response.getEntity()); + } + + @Test + void afterHandle_withoutDataModel_usesRequestResponseResolver() { + TestTemplateFilter filter = new TestTemplateFilter(new Context()); + Request request = newRequest(); + Response response = new Response(request); + response.setEntity(templateEntity()); + + filter.afterHandle(request, response); + + Representation result = response.getEntity(); + assertTrue(result instanceof TemplateRepresentation); + TemplateRepresentation tr = (TemplateRepresentation) result; + assertEquals("myTemplate", tr.getTemplateName()); + assertEquals(MediaType.TEXT_HTML, tr.getMediaType()); + assertEquals("bar", tr.context.getVariable("foo")); + } + + @Test + void afterHandle_withMapDataModel_usesMap() { + Map model = new ConcurrentHashMap<>(); + model.put("key", "value"); + TestTemplateFilter filter = new TestTemplateFilter(new Context(), null, model); + Request request = newRequest(); + Response response = new Response(request); + response.setEntity(templateEntity()); + + filter.afterHandle(request, response); + + TemplateRepresentation tr = (TemplateRepresentation) response.getEntity(); + assertEquals("value", tr.context.getVariable("key")); + } + + @Test + void afterHandle_withResolverDataModel_usesResolver() { + Resolver resolver = + new Resolver<>() { + @Override + public Object resolve(String key) { + return "resolved-" + key; + } + }; + TestTemplateFilter filter = new TestTemplateFilter(new Context(), null, resolver); + Request request = newRequest(); + Response response = new Response(request); + response.setEntity(templateEntity()); + + filter.afterHandle(request, response); + + TemplateRepresentation tr = (TemplateRepresentation) response.getEntity(); + assertEquals("resolved-anyKey", tr.context.getVariable("anyKey")); + } +} diff --git a/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/VelocityConverterTestCase.java b/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/VelocityConverterTestCase.java new file mode 100644 index 0000000000..629db1249c --- /dev/null +++ b/org.restlet.ext.velocity/src/test/java/org/restlet/ext/velocity/VelocityConverterTestCase.java @@ -0,0 +1,147 @@ +/** + * 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.velocity; + +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.File; +import java.io.FileWriter; +import java.util.ArrayList; +import java.util.List; +import org.apache.velocity.Template; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.restlet.data.MediaType; +import org.restlet.data.Preference; +import org.restlet.engine.io.IoUtils; +import org.restlet.representation.StringRepresentation; +import org.restlet.representation.Variant; +import org.restlet.resource.ClientResource; + +/** Unit tests for {@link VelocityConverter}. */ +class VelocityConverterTestCase { + + private File testDir; + + private File testFile; + + private Template template; + + @BeforeEach + void setUp() throws Exception { + testDir = new File(System.getProperty("java.io.tmpdir"), "VelocityConverterTestCase"); + testDir.mkdir(); + testFile = File.createTempFile("test", ".vm", testDir); + + try (FileWriter fw = new FileWriter(testFile)) { + fw.write("Value=$value"); + } + + TemplateRepresentation tr = + new TemplateRepresentation(testFile.getName(), MediaType.TEXT_PLAIN); + tr.getEngine().setProperty("file.resource.loader.path", testDir.getAbsolutePath()); + template = tr.getTemplate(); + } + + @AfterEach + void tearDown() { + IoUtils.delete(testFile); + IoUtils.delete(testDir, true); + } + + @Test + void getObjectClasses_alwaysReturnsEmptyList() { + assertTrue( + new VelocityConverter() + .getObjectClasses(new Variant(MediaType.TEXT_PLAIN)) + .isEmpty()); + } + + @Test + void getVariants_forTemplate_returnsMediaTypeAll() { + List variants = new VelocityConverter().getVariants(Template.class); + assertEquals(1, variants.size()); + assertTrue(MediaType.ALL.isCompatible(((Variant) variants.getFirst()).getMediaType())); + } + + @Test + void getVariants_forOtherClass_returnsEmptyList() { + assertTrue(new VelocityConverter().getVariants(String.class).isEmpty()); + } + + @Test + void score_template_returnsOne() { + assertEquals( + 1.0f, + new VelocityConverter().score(template, new Variant(MediaType.TEXT_PLAIN), null)); + } + + @Test + void score_nonTemplate_returnsMinusOne() { + assertEquals( + -1.0f, + new VelocityConverter() + .score("not a template", new Variant(MediaType.TEXT_PLAIN), null)); + } + + @Test + void score_repr_alwaysReturnsMinusOne() { + assertEquals( + -1.0f, + new VelocityConverter() + .score(new StringRepresentation("text"), String.class, null)); + } + + @Test + void toObject_alwaysReturnsNull() { + assertNull( + new VelocityConverter() + .toObject(new StringRepresentation("text"), String.class, null)); + } + + @Test + void toRepresentation_nonTemplate_returnsNull() { + assertNull( + new VelocityConverter() + .toRepresentation("string", new Variant(MediaType.TEXT_PLAIN), null)); + } + + @Test + void toRepresentation_forTemplate_returnsTemplateRepresentationWithDataModel() + throws Exception { + ClientResource resource = new ClientResource("http://localhost/"); + + Object result = + new VelocityConverter() + .toRepresentation(template, new Variant(MediaType.TEXT_PLAIN), resource); + + assertTrue(result instanceof TemplateRepresentation); + TemplateRepresentation tr = (TemplateRepresentation) result; + assertEquals(MediaType.TEXT_PLAIN, tr.getMediaType()); + assertEquals("Value=$value", tr.getText()); + } + + @Test + void updatePreferences_forTemplate_addsMediaTypeAll() { + List> prefs = new ArrayList<>(); + new VelocityConverter().updatePreferences(prefs, Template.class); + assertEquals(1, prefs.size()); + assertEquals(MediaType.ALL, prefs.getFirst().getMetadata()); + } + + @Test + void updatePreferences_forOtherClass_doesNotModify() { + List> prefs = new ArrayList<>(); + new VelocityConverter().updatePreferences(prefs, String.class); + assertTrue(prefs.isEmpty()); + } +} 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 index 9675246036..f3e0aa27fe 100644 --- 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 @@ -17,12 +17,15 @@ import java.io.IOException; import java.io.StringReader; +import java.io.StringWriter; import java.util.ArrayList; import java.util.List; +import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.sax.SAXSource; import org.junit.jupiter.api.Test; import org.restlet.data.MediaType; import org.restlet.representation.StringRepresentation; +import org.w3c.dom.Document; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.helpers.DefaultHandler; @@ -82,4 +85,58 @@ void release_clearsSource() throws Exception { sr.release(); assertNull(sr.getSaxSource()); } + + @Test + void defaultConstructor_usesTextXmlMediaType() { + SaxRepresentation sr = new SaxRepresentation(); + assertEquals(MediaType.TEXT_XML, sr.getMediaType()); + } + + @Test + void constructor_withInputSource_wrapsItInSaxSource() throws Exception { + InputSource inputSource = new InputSource(new StringReader(XML)); + SaxRepresentation sr = new SaxRepresentation(MediaType.TEXT_XML, inputSource); + assertNotNull(sr.getSaxSource()); + assertNotNull(sr.getInputSource()); + } + + @Test + void constructor_withDomDocument_wrapsItInSaxSource() throws Exception { + Document document = + DocumentBuilderFactory.newInstance() + .newDocumentBuilder() + .parse(new org.xml.sax.InputSource(new StringReader(XML))); + SaxRepresentation sr = new SaxRepresentation(MediaType.TEXT_XML, document); + assertNotNull(sr.getSaxSource()); + } + + @Test + void constructor_withNullRepresentation_hasNullMediaType() { + SaxRepresentation sr = new SaxRepresentation((StringRepresentation) null); + assertNull(sr.getMediaType()); + } + + @Test + void getInputSource_withoutSaxSource_returnsNull() throws Exception { + SaxRepresentation sr = new SaxRepresentation(); + assertNull(sr.getInputSource()); + } + + @Test + void write_writer_serializesUsingXmlWriter() throws Exception { + SaxRepresentation sr = + new SaxRepresentation(new StringRepresentation(XML, MediaType.TEXT_XML)); + StringWriter writer = new StringWriter(); + sr.write(writer); + assertTrue(writer.toString().contains("")); + assertTrue(writer.toString().contains("text")); + } + + @Test + void setSaxSource_overridesSource() throws Exception { + SaxRepresentation sr = new SaxRepresentation(); + SAXSource source = new SAXSource(new InputSource(new StringReader(XML))); + sr.setSaxSource(source); + assertEquals(source, 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 c5d2c2371a..0e77c311a2 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 @@ -9,15 +9,25 @@ package org.restlet.ext.xml; import static org.junit.jupiter.api.Assertions.assertEquals; +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 static org.junit.jupiter.api.Assertions.fail; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.List; import org.junit.jupiter.api.Test; import org.restlet.Component; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.data.CharacterSet; +import org.restlet.data.Encoding; +import org.restlet.data.Language; import org.restlet.data.MediaType; import org.restlet.representation.Representation; import org.restlet.representation.StringRepresentation; +import org.restlet.routing.Filter; /** * Test case for the Transformer class. @@ -108,4 +118,111 @@ void testTransform() throws Exception { assertEquals(this.output, result); } + + @Test + void constructor_setsDefaults() { + Transformer transformer = new Transformer(Transformer.MODE_REQUEST, this.xslt); + assertEquals(Transformer.MODE_REQUEST, transformer.getMode()); + assertSame(this.xslt, transformer.getTransformSheet()); + assertEquals(MediaType.APPLICATION_XML, transformer.getResultMediaType()); + assertNull(transformer.getResultCharacterSet()); + } + + @Test + void gettersAndSetters_roundTrip() { + Transformer transformer = new Transformer(Transformer.MODE_REQUEST, this.xslt); + + transformer.setMode(Transformer.MODE_RESPONSE); + assertEquals(Transformer.MODE_RESPONSE, transformer.getMode()); + + transformer.setResultCharacterSet(CharacterSet.UTF_8); + assertEquals(CharacterSet.UTF_8, transformer.getResultCharacterSet()); + + transformer.setResultMediaType(MediaType.TEXT_XML); + assertEquals(MediaType.TEXT_XML, transformer.getResultMediaType()); + + Representation otherSheet = new StringRepresentation("sheet"); + transformer.setTransformSheet(otherSheet); + assertSame(otherSheet, transformer.getTransformSheet()); + + assertTrue(transformer.getResultEncodings().isEmpty()); + transformer.setResultEncodings(List.of(Encoding.GZIP)); + assertEquals(List.of(Encoding.GZIP), transformer.getResultEncodings()); + + assertTrue(transformer.getResultLanguages().isEmpty()); + transformer.setResultLanguages(List.of(Language.ENGLISH)); + assertEquals(List.of(Language.ENGLISH), transformer.getResultLanguages()); + } + + @Test + void canTransform_alwaysReturnsTrue() { + Transformer transformer = new Transformer(Transformer.MODE_REQUEST, this.xslt); + assertTrue(transformer.canTransform(null)); + assertTrue(transformer.canTransform(this.source)); + } + + @Test + void beforeHandle_requestMode_transformsRequestEntity() { + Transformer transformer = new Transformer(Transformer.MODE_REQUEST, this.xslt); + Request request = new Request(); + request.setEntity(this.source); + Response response = new Response(request); + + int result = transformer.beforeHandle(request, response); + + assertEquals(Filter.CONTINUE, result); + assertTrue(request.getEntity() instanceof TransformRepresentation); + } + + @Test + void beforeHandle_responseMode_doesNotTransformRequestEntity() { + Transformer transformer = new Transformer(Transformer.MODE_RESPONSE, this.xslt); + Request request = new Request(); + request.setEntity(this.source); + Response response = new Response(request); + + transformer.beforeHandle(request, response); + + assertSame(this.source, request.getEntity()); + } + + @Test + void afterHandle_responseMode_transformsResponseEntity() { + Transformer transformer = new Transformer(Transformer.MODE_RESPONSE, this.xslt); + Request request = new Request(); + Response response = new Response(request); + response.setEntity(this.source); + + transformer.afterHandle(request, response); + + assertTrue(response.getEntity() instanceof TransformRepresentation); + } + + @Test + void afterHandle_requestMode_doesNotTransformResponseEntity() { + Transformer transformer = new Transformer(Transformer.MODE_REQUEST, this.xslt); + Request request = new Request(); + Response response = new Response(request); + response.setEntity(this.source); + + transformer.afterHandle(request, response); + + assertSame(this.source, response.getEntity()); + } + + @Test + void transform_withResultLanguagesAndEncodings_appliesThemToResult() { + Transformer transformer = new Transformer(Transformer.MODE_REQUEST, this.xslt); + transformer.setResultLanguages(List.of(Language.FRENCH)); + transformer.setResultEncodings(List.of(Encoding.GZIP)); + transformer.setResultCharacterSet(CharacterSet.UTF_8); + transformer.setResultMediaType(MediaType.TEXT_XML); + + Representation result = transformer.transform(this.source); + + assertTrue(result.getLanguages().contains(Language.FRENCH)); + assertTrue(result.getEncodings().contains(Encoding.GZIP)); + assertEquals(CharacterSet.UTF_8, result.getCharacterSet()); + assertEquals(MediaType.TEXT_XML, result.getMediaType()); + } } diff --git a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/internal/AbstractXmlReaderTestCase.java b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/internal/AbstractXmlReaderTestCase.java new file mode 100644 index 0000000000..33a2277c19 --- /dev/null +++ b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/internal/AbstractXmlReaderTestCase.java @@ -0,0 +1,97 @@ +/** + * 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.internal; + +import static org.junit.jupiter.api.Assertions.assertFalse; +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 org.junit.jupiter.api.Test; +import org.xml.sax.ContentHandler; +import org.xml.sax.DTDHandler; +import org.xml.sax.EntityResolver; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.helpers.DefaultHandler; + +/** Unit tests for {@link AbstractXmlReader}. */ +class AbstractXmlReaderTestCase { + + private static class TestXmlReader extends AbstractXmlReader { + @Override + public void parse(InputSource input) { + throw new UnsupportedOperationException(); + } + + @Override + public void parse(String systemId) { + throw new UnsupportedOperationException(); + } + } + + @Test + void newInstance_hasNullHandlersAndFalseFeatures() { + TestXmlReader reader = new TestXmlReader(); + assertNull(reader.getContentHandler()); + assertNull(reader.getDTDHandler()); + assertNull(reader.getEntityResolver()); + assertNull(reader.getErrorHandler()); + assertFalse(reader.getFeature("any-feature")); + assertNull(reader.getProperty("any-property")); + } + + @Test + void contentHandler_setterAndGetter_roundTrip() { + TestXmlReader reader = new TestXmlReader(); + ContentHandler handler = new DefaultHandler(); + reader.setContentHandler(handler); + assertSame(handler, reader.getContentHandler()); + } + + @Test + void dtdHandler_setterAndGetter_roundTrip() { + TestXmlReader reader = new TestXmlReader(); + DTDHandler handler = new DefaultHandler(); + reader.setDTDHandler(handler); + assertSame(handler, reader.getDTDHandler()); + } + + @Test + void entityResolver_setterAndGetter_roundTrip() { + TestXmlReader reader = new TestXmlReader(); + EntityResolver resolver = new DefaultHandler(); + reader.setEntityResolver(resolver); + assertSame(resolver, reader.getEntityResolver()); + } + + @Test + void errorHandler_setterAndGetter_roundTrip() { + TestXmlReader reader = new TestXmlReader(); + ErrorHandler handler = new DefaultHandler(); + reader.setErrorHandler(handler); + assertSame(handler, reader.getErrorHandler()); + } + + @Test + void feature_setterAndGetter_roundTrip() { + TestXmlReader reader = new TestXmlReader(); + reader.setFeature("my-feature", true); + assertTrue(reader.getFeature("my-feature")); + assertFalse(reader.getFeature("other-feature")); + } + + @Test + void property_setterAndGetter_roundTrip() { + TestXmlReader reader = new TestXmlReader(); + Object value = new Object(); + reader.setProperty("my-property", value); + assertSame(value, reader.getProperty("my-property")); + } +} diff --git a/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/internal/ContextResolverTestCase.java b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/internal/ContextResolverTestCase.java new file mode 100644 index 0000000000..6cd95bee3d --- /dev/null +++ b/org.restlet.ext.xml/src/test/java/org/restlet/ext/xml/internal/ContextResolverTestCase.java @@ -0,0 +1,62 @@ +/** + * 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.internal; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import javax.xml.transform.Source; +import org.junit.jupiter.api.Test; +import org.restlet.Client; +import org.restlet.Context; +import org.restlet.data.Protocol; + +/** Unit tests for {@link ContextResolver}. */ +class ContextResolverTestCase { + + private Context clapContext() { + Context context = new Context(); + context.setClientDispatcher(new Client(Protocol.CLAP)); + return context; + } + + @Test + void resolve_withNullContext_returnsNull() { + ContextResolver resolver = new ContextResolver(null); + assertNull(resolver.resolve("clap://class/org/restlet/ext/xml/xslt/one/1st.xml", null)); + } + + @Test + void resolve_withAbsoluteHrefAndNoBase_returnsSource() { + ContextResolver resolver = new ContextResolver(clapContext()); + + Source result = resolver.resolve("clap://class/org/restlet/ext/xml/xslt/one/1st.xml", null); + + assertNotNull(result); + } + + @Test + void resolve_withRelativeHrefAndBase_returnsSource() { + ContextResolver resolver = new ContextResolver(clapContext()); + + Source result = + resolver.resolve("1st.xml", "clap://class/org/restlet/ext/xml/xslt/one/base.xsl"); + + assertNotNull(result); + } + + @Test + void resolve_withUnresolvableHref_returnsNull() { + ContextResolver resolver = new ContextResolver(clapContext()); + + Source result = resolver.resolve("clap://class/does/not/exist.xml", null); + + assertNull(result); + } +} diff --git a/org.restlet/src/main/java/org/restlet/util/WrapperRequest.java b/org.restlet/src/main/java/org/restlet/util/WrapperRequest.java index bef197704b..87d9f96aaf 100644 --- a/org.restlet/src/main/java/org/restlet/util/WrapperRequest.java +++ b/org.restlet/src/main/java/org/restlet/util/WrapperRequest.java @@ -472,7 +472,7 @@ public void setRootRef(Reference rootRef) { */ @Override public void setAccessControlRequestHeaders(Set accessControlRequestHeaders) { - super.setAccessControlRequestHeaders(accessControlRequestHeaders); + getWrappedRequest().setAccessControlRequestHeaders(accessControlRequestHeaders); } /** @@ -482,7 +482,7 @@ public void setAccessControlRequestHeaders(Set accessControlRequestHeade */ @Override public void setAccessControlRequestMethod(Method accessControlRequestMethod) { - super.setAccessControlRequestMethod(accessControlRequestMethod); + getWrappedRequest().setAccessControlRequestMethod(accessControlRequestMethod); } @Override diff --git a/org.restlet/src/test/java/org/restlet/data/ExpectationTestCase.java b/org.restlet/src/test/java/org/restlet/data/ExpectationTestCase.java new file mode 100644 index 0000000000..e7ddf1975f --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/data/ExpectationTestCase.java @@ -0,0 +1,73 @@ +/** + * 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.data; + +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.assertTrue; + +import java.util.List; +import org.junit.jupiter.api.Test; +import org.restlet.engine.header.HeaderConstants; + +/** Unit tests for {@link Expectation}. */ +class ExpectationTestCase { + + @Test + void continueResponse_hasExpectContinueName() { + Expectation expectation = Expectation.continueResponse(); + assertEquals(HeaderConstants.EXPECT_CONTINUE, expectation.getName()); + } + + @Test + void constructorWithNameOnly_hasNullValue() { + Expectation expectation = new Expectation("name"); + assertEquals("name", expectation.getName()); + assertEquals(null, expectation.getValue()); + } + + @Test + void gettersAndSetters_roundTrip() { + Expectation expectation = new Expectation("name", "value"); + assertEquals("name", expectation.getName()); + assertEquals("value", expectation.getValue()); + + expectation.setName("newName"); + assertEquals("newName", expectation.getName()); + + expectation.setValue("newValue"); + assertEquals("newValue", expectation.getValue()); + + assertTrue(expectation.getParameters().isEmpty()); + expectation.setParameters(List.of(new Parameter("p", "v"))); + assertEquals(1, expectation.getParameters().size()); + } + + @Test + void equalsAndHashCode_considerNameValueAndParameters() { + Expectation e1 = new Expectation("name", "value"); + Expectation e2 = new Expectation("name", "value"); + Expectation e3 = new Expectation("other", "value"); + + assertEquals(e1, e1); + assertEquals(e1, e2); + assertEquals(e1.hashCode(), e2.hashCode()); + assertNotEquals(e1, e3); + assertFalse(e1.equals("not an expectation")); + } + + @Test + void toString_includesNameValueAndParameters() { + Expectation expectation = new Expectation("name", "value"); + String text = expectation.toString(); + assertTrue(text.contains("name")); + assertTrue(text.contains("value")); + } +} diff --git a/org.restlet/src/test/java/org/restlet/data/WarningTestCase.java b/org.restlet/src/test/java/org/restlet/data/WarningTestCase.java new file mode 100644 index 0000000000..d957706d58 --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/data/WarningTestCase.java @@ -0,0 +1,46 @@ +/** + * 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.data; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.Date; +import org.junit.jupiter.api.Test; + +/** Unit tests for {@link Warning}. */ +class WarningTestCase { + + @Test + void defaultConstructor_hasNullFields() { + Warning warning = new Warning(); + assertNull(warning.getAgent()); + assertNull(warning.getDate()); + assertNull(warning.getStatus()); + assertNull(warning.getText()); + } + + @Test + void gettersAndSetters_roundTrip() { + Warning warning = new Warning(); + + warning.setAgent("agent"); + assertEquals("agent", warning.getAgent()); + + Date date = new Date(); + warning.setDate(date); + assertEquals(date, warning.getDate()); + + warning.setStatus(Status.SUCCESS_OK); + assertEquals(Status.SUCCESS_OK, warning.getStatus()); + + warning.setText("text"); + assertEquals("text", warning.getText()); + } +} diff --git a/org.restlet/src/test/java/org/restlet/engine/adapter/JettyHandlerTestCase.java b/org.restlet/src/test/java/org/restlet/engine/adapter/JettyHandlerTestCase.java new file mode 100644 index 0000000000..3b01b7dd5c --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/engine/adapter/JettyHandlerTestCase.java @@ -0,0 +1,40 @@ +/** + * 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.engine.adapter; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; +import org.restlet.Server; +import org.restlet.data.Protocol; + +/** Unit tests for {@link JettyHandler}. */ +class JettyHandlerTestCase { + + @Test + void constructor_defaultsToHttp() { + Server server = new Server(Protocol.HTTP, 0); + JettyHandler handler = new JettyHandler(server); + assertNotNull(handler); + } + + @Test + void constructor_secureFlagTrue_usesHttpsHelper() { + Server server = new Server(Protocol.HTTPS, 0); + JettyHandler handler = new JettyHandler(server, true); + assertNotNull(handler); + } + + @Test + void constructor_secureFlagFalse_usesHttpHelper() { + Server server = new Server(Protocol.HTTP, 0); + JettyHandler handler = new JettyHandler(server, false); + assertNotNull(handler); + } +} diff --git a/org.restlet/src/test/java/org/restlet/engine/application/DecodeRepresentationTestCase.java b/org.restlet/src/test/java/org/restlet/engine/application/DecodeRepresentationTestCase.java new file mode 100644 index 0000000000..001a25dda7 --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/engine/application/DecodeRepresentationTestCase.java @@ -0,0 +1,97 @@ +/** + * 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.engine.application; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.restlet.data.Encoding; +import org.restlet.engine.io.IoUtils; +import org.restlet.representation.ByteArrayRepresentation; +import org.restlet.representation.Representation; +import org.restlet.representation.StringRepresentation; + +/** Unit tests for {@link DecodeRepresentation}. */ +class DecodeRepresentationTestCase { + + private static Representation gzip(String text) throws Exception { + EncodeRepresentation encoded = + new EncodeRepresentation(Encoding.GZIP, new StringRepresentation(text)); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + encoded.write(out); + ByteArrayRepresentation result = new ByteArrayRepresentation(out.toByteArray()); + result.setEncodings(List.of(Encoding.GZIP)); + return result; + } + + @Test + void getSupportedEncodings_containsExpectedEncodings() { + assertTrue(DecodeRepresentation.getSupportedEncodings().contains(Encoding.GZIP)); + assertTrue(DecodeRepresentation.getSupportedEncodings().contains(Encoding.IDENTITY)); + } + + @Test + void isDecoding_forSupportedEncoding_isTrue() throws Exception { + DecodeRepresentation representation = new DecodeRepresentation(gzip("hello")); + assertTrue(representation.isDecoding()); + } + + @Test + void isDecoding_forPlainRepresentation_isTrueBecauseNoEncoding() { + DecodeRepresentation representation = + new DecodeRepresentation(new StringRepresentation("hello")); + assertTrue(representation.isDecoding()); + assertEquals(0, representation.getEncodings().size()); + } + + @Test + void getStreamAndText_decodeGzippedContent() throws Exception { + DecodeRepresentation representation = new DecodeRepresentation(gzip("hello world")); + assertEquals("hello world", representation.getText()); + } + + @Test + void getReader_decodesGzippedContent() throws Exception { + DecodeRepresentation representation = new DecodeRepresentation(gzip("hello")); + assertEquals("hello", IoUtils.toString(representation.getReader())); + } + + @Test + void write_decodesGzippedContentToOutputStream() throws Exception { + DecodeRepresentation representation = new DecodeRepresentation(gzip("hello")); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + representation.write(out); + assertEquals("hello", out.toString()); + } + + @Test + void getAvailableSize_delegatesToIoUtils() throws Exception { + DecodeRepresentation representation = + new DecodeRepresentation(new StringRepresentation("hello")); + assertEquals(5, representation.getAvailableSize()); + } + + @Test + void getSize_forIdentityEncoding_delegatesToWrapped() { + StringRepresentation wrapped = new StringRepresentation("hello"); + DecodeRepresentation representation = new DecodeRepresentation(wrapped); + assertEquals(wrapped.getSize(), representation.getSize()); + } + + @Test + void equals_delegatesToSuper() { + StringRepresentation wrapped = new StringRepresentation("hello"); + DecodeRepresentation representation = new DecodeRepresentation(wrapped); + assertFalse(representation.equals("not a representation")); + } +} diff --git a/org.restlet/src/test/java/org/restlet/engine/application/EncodeRepresentationTestCase.java b/org.restlet/src/test/java/org/restlet/engine/application/EncodeRepresentationTestCase.java new file mode 100644 index 0000000000..73c09b28d5 --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/engine/application/EncodeRepresentationTestCase.java @@ -0,0 +1,211 @@ +/** + * 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.engine.application; + +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 java.util.zip.GZIPInputStream; +import java.util.zip.InflaterInputStream; +import java.util.zip.ZipInputStream; +import org.junit.jupiter.api.Test; +import org.restlet.data.Disposition; +import org.restlet.data.Encoding; +import org.restlet.engine.io.IoUtils; +import org.restlet.representation.Representation; +import org.restlet.representation.StringRepresentation; + +/** Unit tests for {@link EncodeRepresentation}. */ +class EncodeRepresentationTestCase { + + @Test + void canEncode_forSupportedEncoding_isTrue() { + EncodeRepresentation representation = + new EncodeRepresentation(Encoding.GZIP, new StringRepresentation("text")); + assertTrue(representation.canEncode()); + } + + @Test + void canEncode_forUnsupportedEncoding_isFalse() { + EncodeRepresentation representation = + new EncodeRepresentation(Encoding.COMPRESS, new StringRepresentation("text")); + assertFalse(representation.canEncode()); + } + + @Test + void getSupportedEncodings_containsExpectedEncodings() { + assertTrue(EncodeRepresentation.getSupportedEncodings().contains(Encoding.GZIP)); + assertTrue(EncodeRepresentation.getSupportedEncodings().contains(Encoding.DEFLATE)); + assertTrue(EncodeRepresentation.getSupportedEncodings().contains(Encoding.ZIP)); + assertTrue(EncodeRepresentation.getSupportedEncodings().contains(Encoding.IDENTITY)); + } + + @Test + void getEncodings_whenCanEncode_appendsOwnEncoding() { + EncodeRepresentation representation = + new EncodeRepresentation(Encoding.GZIP, new StringRepresentation("text")); + assertTrue(representation.getEncodings().contains(Encoding.GZIP)); + } + + @Test + void getEncodings_whenCannotEncode_onlyContainsWrappedEncodings() { + EncodeRepresentation representation = + new EncodeRepresentation(Encoding.COMPRESS, new StringRepresentation("text")); + assertFalse(representation.getEncodings().contains(Encoding.COMPRESS)); + } + + @Test + void identityEncoding_availableSizeAndSize_delegateToWrapped() { + StringRepresentation wrapped = new StringRepresentation("text"); + EncodeRepresentation representation = new EncodeRepresentation(Encoding.IDENTITY, wrapped); + assertEquals(wrapped.getAvailableSize(), representation.getAvailableSize()); + assertEquals(wrapped.getSize(), representation.getSize()); + } + + @Test + void gzipEncoding_sizeAndAvailableSize_unknown() { + EncodeRepresentation representation = + new EncodeRepresentation(Encoding.GZIP, new StringRepresentation("text")); + assertEquals(Representation.UNKNOWN_SIZE, representation.getSize()); + assertEquals(Representation.UNKNOWN_SIZE, representation.getAvailableSize()); + } + + @Test + void unsupportedEncoding_sizeAndAvailableSize_delegateToWrapped() { + StringRepresentation wrapped = new StringRepresentation("text"); + EncodeRepresentation representation = new EncodeRepresentation(Encoding.COMPRESS, wrapped); + assertEquals(wrapped.getSize(), representation.getSize()); + assertEquals(wrapped.getAvailableSize(), representation.getAvailableSize()); + } + + @Test + void write_gzip_producesGzipStream() throws Exception { + EncodeRepresentation representation = + new EncodeRepresentation(Encoding.GZIP, new StringRepresentation("hello world")); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + representation.write(out); + + try (GZIPInputStream gzip = + new GZIPInputStream(new java.io.ByteArrayInputStream(out.toByteArray()))) { + assertEquals("hello world", IoUtils.toString(gzip)); + } + } + + @Test + void write_deflate_producesDeflateStream() throws Exception { + EncodeRepresentation representation = + new EncodeRepresentation(Encoding.DEFLATE, new StringRepresentation("hello")); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + representation.write(out); + + try (InflaterInputStream inflater = + new InflaterInputStream(new java.io.ByteArrayInputStream(out.toByteArray()))) { + assertEquals("hello", IoUtils.toString(inflater)); + } + } + + @Test + void write_deflateNowrap_producesDeflateNowrapStream() throws Exception { + EncodeRepresentation representation = + new EncodeRepresentation( + Encoding.DEFLATE_NOWRAP, new StringRepresentation("hello")); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + representation.write(out); + + try (InflaterInputStream inflater = + new InflaterInputStream( + new java.io.ByteArrayInputStream(out.toByteArray()), + new java.util.zip.Inflater(true))) { + assertEquals("hello", IoUtils.toString(inflater)); + } + } + + @Test + void write_zip_producesZipEntryNamedFromDisposition() throws Exception { + StringRepresentation wrapped = new StringRepresentation("hello"); + Disposition disposition = new Disposition(); + disposition.getParameters().add(Disposition.NAME_FILENAME, "myFile"); + wrapped.setDisposition(disposition); + + EncodeRepresentation representation = new EncodeRepresentation(Encoding.ZIP, wrapped); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + representation.write(out); + + try (ZipInputStream zip = + new ZipInputStream(new java.io.ByteArrayInputStream(out.toByteArray()))) { + var entry = zip.getNextEntry(); + assertEquals("myFile", entry.getName()); + assertEquals("hello", IoUtils.toString(zip)); + } + } + + @Test + void write_unsupportedEncoding_delegatesToWrapped() throws Exception { + EncodeRepresentation representation = + new EncodeRepresentation(Encoding.COMPRESS, new StringRepresentation("hello")); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + representation.write(out); + assertEquals("hello", out.toString()); + } + + @Test + void getReader_whenCanEncode_returnsDecompressibleReader() throws Exception { + EncodeRepresentation representation = + new EncodeRepresentation(Encoding.GZIP, new StringRepresentation("hello")); + assertNotNull(representation.getReader()); + } + + @Test + void getReader_whenCannotEncode_delegatesToWrapped() throws Exception { + EncodeRepresentation representation = + new EncodeRepresentation(Encoding.COMPRESS, new StringRepresentation("hello")); + assertEquals("hello", IoUtils.toString(representation.getReader())); + } + + @Test + void getStream_whenCannotEncode_delegatesToWrapped() throws Exception { + EncodeRepresentation representation = + new EncodeRepresentation(Encoding.COMPRESS, new StringRepresentation("hello")); + assertEquals("hello", IoUtils.toString(representation.getStream())); + } + + @Test + void getText_whenCannotEncode_delegatesToWrapped() throws Exception { + EncodeRepresentation representation = + new EncodeRepresentation(Encoding.COMPRESS, new StringRepresentation("hello")); + assertEquals("hello", representation.getText()); + } + + @Test + void write_writerWithEncoding_producesEncodedContent() throws Exception { + EncodeRepresentation representation = + new EncodeRepresentation(Encoding.GZIP, new StringRepresentation("hello")); + StringWriter writer = new StringWriter(); + representation.write(writer); + assertFalse(writer.toString().isEmpty()); + } + + @Test + void write_writerWithoutEncoding_delegatesToWrapped() throws Exception { + EncodeRepresentation representation = + new EncodeRepresentation(Encoding.COMPRESS, new StringRepresentation("hello")); + StringWriter writer = new StringWriter(); + representation.write(writer); + assertEquals("hello", writer.toString()); + } +} diff --git a/org.restlet/src/test/java/org/restlet/engine/application/EncoderTestCase.java b/org.restlet/src/test/java/org/restlet/engine/application/EncoderTestCase.java new file mode 100644 index 0000000000..0a75f94e62 --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/engine/application/EncoderTestCase.java @@ -0,0 +1,122 @@ +/** + * 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.engine.application; + +import static org.junit.jupiter.api.Assertions.assertEquals; +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 org.junit.jupiter.api.Test; +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.data.ClientInfo; +import org.restlet.data.Encoding; +import org.restlet.data.MediaType; +import org.restlet.data.Preference; +import org.restlet.representation.Representation; +import org.restlet.representation.StringRepresentation; +import org.restlet.routing.Filter; +import org.restlet.service.EncoderService; + +/** Unit tests for {@link Encoder}. */ +class EncoderTestCase { + + @Test + void constructorAndGetters_exposeConfiguredFlagsAndService() { + EncoderService service = new EncoderService(); + Encoder encoder = new Encoder(new Context(), true, false, service); + + assertTrue(encoder.isEncodingRequest()); + assertEquals(false, encoder.isEncodingResponse()); + assertSame(service, encoder.getEncoderService()); + assertTrue(encoder.getSupportedEncodings().contains(Encoding.GZIP)); + } + + @Test + void getBestEncoding_selectsHighestQualityAcceptedEncoding() { + Encoder encoder = new Encoder(new Context(), true, true, new EncoderService()); + ClientInfo clientInfo = new ClientInfo(); + clientInfo.getAcceptedEncodings().add(new Preference<>(Encoding.DEFLATE, 0.5f)); + clientInfo.getAcceptedEncodings().add(new Preference<>(Encoding.GZIP, 0.9f)); + + assertEquals(Encoding.GZIP, encoder.getBestEncoding(clientInfo)); + } + + @Test + void getBestEncoding_noAcceptedEncodings_returnsNull() { + Encoder encoder = new Encoder(new Context(), true, true, new EncoderService()); + assertNull(encoder.getBestEncoding(new ClientInfo())); + } + + @Test + void encode_withBestEncoding_wrapsInEncodeRepresentation() { + Encoder encoder = new Encoder(new Context(), true, true, new EncoderService()); + ClientInfo clientInfo = new ClientInfo(); + clientInfo.getAcceptedEncodings().add(new Preference<>(Encoding.GZIP, 1.0f)); + + Representation source = new StringRepresentation("hello"); + Representation result = encoder.encode(clientInfo, source); + + assertTrue(result instanceof EncodeRepresentation); + } + + @Test + void encode_withoutBestEncoding_returnsOriginalRepresentation() { + Encoder encoder = new Encoder(new Context(), true, true, new EncoderService()); + Representation source = new StringRepresentation("hello"); + + assertSame(source, encoder.encode(new ClientInfo(), source)); + } + + @Test + void beforeHandle_encodesRequestEntityWhenEnabled() { + EncoderService service = new EncoderService(); + service.setMinimumSize(EncoderService.ANY_SIZE); + Encoder encoder = new Encoder(new Context(), true, false, service); + Request request = new Request(); + request.getClientInfo().getAcceptedEncodings().add(new Preference<>(Encoding.GZIP, 1.0f)); + request.setEntity(new StringRepresentation("hello", MediaType.TEXT_PLAIN)); + Response response = new Response(request); + + int result = encoder.beforeHandle(request, response); + + assertEquals(Filter.CONTINUE, result); + assertTrue(request.getEntity() instanceof EncodeRepresentation); + } + + @Test + void beforeHandle_disabled_doesNotEncode() { + Encoder encoder = new Encoder(new Context(), false, false, new EncoderService()); + Request request = new Request(); + Representation entity = new StringRepresentation("hello", MediaType.TEXT_PLAIN); + request.setEntity(entity); + Response response = new Response(request); + + encoder.beforeHandle(request, response); + + assertSame(entity, request.getEntity()); + } + + @Test + void afterHandle_encodesResponseEntityWhenEnabled() { + EncoderService service = new EncoderService(); + service.setMinimumSize(EncoderService.ANY_SIZE); + Encoder encoder = new Encoder(new Context(), false, true, service); + Request request = new Request(); + request.getClientInfo().getAcceptedEncodings().add(new Preference<>(Encoding.GZIP, 1.0f)); + Response response = new Response(request); + response.setEntity(new StringRepresentation("hello", MediaType.TEXT_PLAIN)); + + encoder.afterHandle(request, response); + + assertTrue(response.getEntity() instanceof EncodeRepresentation); + } +} diff --git a/org.restlet/src/test/java/org/restlet/engine/header/ExpectationWriterTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/ExpectationWriterTestCase.java new file mode 100644 index 0000000000..dee595a43a --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/engine/header/ExpectationWriterTestCase.java @@ -0,0 +1,52 @@ +/** + * 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.engine.header; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.junit.jupiter.api.Test; +import org.restlet.data.Expectation; +import org.restlet.data.Parameter; + +/** Unit tests for {@link ExpectationWriter}. */ +class ExpectationWriterTestCase { + + @Test + void write_singleExpectationWithoutParameters() { + Expectation expectation = new Expectation("100-continue"); + assertEquals("100-continue", ExpectationWriter.write(List.of(expectation))); + } + + @Test + void write_expectationWithParameters_appendsParametersWithSeparator() { + Expectation expectation = new Expectation("name", "value"); + expectation.getParameters().add(new Parameter("p1", "v1")); + + String result = ExpectationWriter.write(List.of(expectation)); + + assertEquals("name=value;p1=v1", result); + } + + @Test + void write_multipleExpectations_joinsWithComma() { + Expectation first = new Expectation("first"); + Expectation second = new Expectation("second"); + + String result = ExpectationWriter.write(List.of(first, second)); + + assertEquals("first, second", result); + } + + @Test + void write_expectationWithEmptyName_isSkipped() { + Expectation expectation = new Expectation(""); + assertEquals("", ExpectationWriter.write(List.of(expectation))); + } +} diff --git a/org.restlet/src/test/java/org/restlet/engine/header/MethodReaderTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/MethodReaderTestCase.java new file mode 100644 index 0000000000..e8e9f62d7c --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/engine/header/MethodReaderTestCase.java @@ -0,0 +1,37 @@ +/** + * 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.engine.header; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.restlet.data.Header; +import org.restlet.data.Method; + +/** Unit tests for {@link MethodReader}. */ +class MethodReaderTestCase { + + @Test + void readValue_parsesMethod() throws Exception { + MethodReader reader = new MethodReader("GET"); + assertEquals(Method.GET, reader.readValue()); + } + + @Test + void addValues_appendsParsedMethodToCollection() { + Header header = new Header("Allow", "POST"); + List collection = new ArrayList<>(); + + MethodReader.addValues(header, collection); + + assertEquals(List.of(Method.POST), collection); + } +} diff --git a/org.restlet/src/test/java/org/restlet/engine/header/RangeWriterTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/RangeWriterTestCase.java new file mode 100644 index 0000000000..2f818c4f37 --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/engine/header/RangeWriterTestCase.java @@ -0,0 +1,113 @@ +/** + * 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.engine.header; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.List; +import org.junit.jupiter.api.Test; +import org.restlet.data.Range; + +/** Unit tests for {@link RangeWriter}. */ +class RangeWriterTestCase { + + @Test + void write_nullList_returnsEmptyString() { + assertEquals("", RangeWriter.write((List) null)); + } + + @Test + void write_emptyList_returnsEmptyString() { + assertEquals("", RangeWriter.write(List.of())); + } + + @Test + void write_singleRangeWithKnownEnd_formatsCorrectly() { + Range range = new Range(0, 500); + assertEquals("bytes=0-499", RangeWriter.write(List.of(range))); + } + + @Test + void write_multipleRanges_joinsWithComma() { + Range range1 = new Range(0, 500); + Range range2 = new Range(500, 500); + assertEquals("bytes=0-499, 500-999", RangeWriter.write(List.of(range1, range2))); + } + + @Test + void write_rangeFromLastIndex_hasNoEnd() { + Range range = new Range(Range.INDEX_LAST, 500); + assertEquals("bytes=-500", RangeWriter.write(List.of(range))); + } + + @Test + void write_rangeWithSizeMax_hasNoUpperBound() { + Range range = new Range(100, Range.SIZE_MAX); + assertEquals("bytes=100-", RangeWriter.write(List.of(range))); + } + + @Test + void writeContentRange_indexFirstWithKnownSize_appendsEntitySize() { + Range range = new Range(0, 500); + assertEquals("bytes 0-499/1000", RangeWriter.write(range, 1000)); + } + + @Test + void writeContentRange_indexFirstWithSizeMaxAndKnownEntitySize_usesEntitySize() { + Range range = new Range(100, Range.SIZE_MAX); + assertEquals("bytes 100-999/1000", RangeWriter.write(range, 1000)); + } + + @Test + void writeContentRange_indexFirstWithSizeMaxAndUnknownEntitySize_throws() { + Range range = new Range(100, Range.SIZE_MAX); + assertThrows( + IllegalArgumentException.class, + () -> + RangeWriter.write( + range, org.restlet.representation.Representation.UNKNOWN_SIZE)); + } + + @Test + void writeContentRange_indexLastWithKnownSizeAndFittingRange_appendsRange() { + Range range = new Range(Range.INDEX_LAST, 200); + assertEquals("bytes 800-999/1000", RangeWriter.write(range, 1000)); + } + + @Test + void writeContentRange_indexLastWithRangeSizeExceedingEntitySize_throws() { + Range range = new Range(Range.INDEX_LAST, 2000); + assertThrows(IllegalArgumentException.class, () -> RangeWriter.write(range, 1000)); + } + + @Test + void writeContentRange_indexLastWithUnknownEntitySize_throws() { + Range range = new Range(Range.INDEX_LAST, 200); + assertThrows( + IllegalArgumentException.class, + () -> + RangeWriter.write( + range, org.restlet.representation.Representation.UNKNOWN_SIZE)); + } + + @Test + void writeContentRange_indexLastWithSizeMax_throwsInvalidRange() { + Range range = new Range(Range.INDEX_LAST, Range.SIZE_MAX); + assertThrows(IllegalArgumentException.class, () -> RangeWriter.write(range, 1000)); + } + + @Test + void writeContentRange_unknownEntitySize_appendsWildcard() { + Range range = new Range(0, 500); + assertEquals( + "bytes 0-499/*", + RangeWriter.write(range, org.restlet.representation.Representation.UNKNOWN_SIZE)); + } +} diff --git a/org.restlet/src/test/java/org/restlet/engine/header/StringReaderTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/StringReaderTestCase.java new file mode 100644 index 0000000000..4a2885cbab --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/engine/header/StringReaderTestCase.java @@ -0,0 +1,36 @@ +/** + * 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.engine.header; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.restlet.data.Header; + +/** Unit tests for {@link StringReader}. */ +class StringReaderTestCase { + + @Test + void readValue_returnsToken() throws Exception { + StringReader reader = new StringReader("token"); + assertEquals("token", reader.readValue()); + } + + @Test + void addValues_appendsParsedTokenToCollection() { + Header header = new Header("X-Custom", "value1, value2"); + List collection = new ArrayList<>(); + + StringReader.addValues(header, collection); + + assertEquals(List.of("value1", "value2"), collection); + } +} diff --git a/org.restlet/src/test/java/org/restlet/engine/header/WarningReaderTestCase.java b/org.restlet/src/test/java/org/restlet/engine/header/WarningReaderTestCase.java new file mode 100644 index 0000000000..f4f3a7cc41 --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/engine/header/WarningReaderTestCase.java @@ -0,0 +1,65 @@ +/** + * 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.engine.header; + +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.assertThrows; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.restlet.data.Header; +import org.restlet.data.Warning; + +/** Unit tests for {@link WarningReader}. */ +class WarningReaderTestCase { + + @Test + void readValue_withoutDate_parsesCodeAgentAndText() throws IOException { + WarningReader reader = new WarningReader("110 agent \"text\""); + Warning warning = reader.readValue(); + + assertEquals(110, warning.getStatus().getCode()); + assertEquals("agent", warning.getAgent()); + assertEquals("text", warning.getText()); + assertNull(warning.getDate()); + } + + @Test + void readValue_withDate_parsesAllFields() throws IOException { + WarningReader reader = + new WarningReader("110 agent \"text\" \"Thu, 01 Jan 1970 00:00:00 GMT\""); + Warning warning = reader.readValue(); + + assertEquals(110, warning.getStatus().getCode()); + assertEquals("agent", warning.getAgent()); + assertEquals("text", warning.getText()); + assertNotNull(warning.getDate()); + } + + @Test + void readValue_malformedHeader_throwsIOException() { + WarningReader reader = new WarningReader("110 agent"); + assertThrows(IOException.class, reader::readValue); + } + + @Test + void addValues_appendsParsedWarningToCollection() { + Header header = new Header("Warning", "110 agent \"text\""); + List collection = new ArrayList<>(); + + WarningReader.addValues(header, collection); + + assertEquals(1, collection.size()); + assertEquals("agent", collection.getFirst().getAgent()); + } +} diff --git a/org.restlet/src/test/java/org/restlet/engine/log/AccessLogFileHandlerTestCase.java b/org.restlet/src/test/java/org/restlet/engine/log/AccessLogFileHandlerTestCase.java new file mode 100644 index 0000000000..b5ee3fb084 --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/engine/log/AccessLogFileHandlerTestCase.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.engine.log; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.File; +import java.io.IOException; +import org.junit.jupiter.api.Test; + +/** Unit tests for {@link AccessLogFileHandler}. */ +class AccessLogFileHandlerTestCase { + + private String tempPattern(String prefix) throws IOException { + File dir = new File(System.getProperty("java.io.tmpdir"), "AccessLogFileHandlerTestCase"); + dir.mkdirs(); + dir.deleteOnExit(); + File file = File.createTempFile(prefix, ".log", dir); + file.deleteOnExit(); + file.delete(); + return file.getAbsolutePath(); + } + + @Test + void constructor_withPattern_usesAccessLogFormatter() throws Exception { + AccessLogFileHandler handler = new AccessLogFileHandler(tempPattern("aaa")); + try { + assertTrue(handler.getFormatter() instanceof AccessLogFormatter); + } finally { + handler.close(); + } + } + + @Test + void constructor_withPatternAndAppend_usesAccessLogFormatter() throws Exception { + AccessLogFileHandler handler = new AccessLogFileHandler(tempPattern("bbb"), true); + try { + assertTrue(handler.getFormatter() instanceof AccessLogFormatter); + } finally { + handler.close(); + } + } + + @Test + void constructor_withPatternLimitAndCount_usesAccessLogFormatter() throws Exception { + AccessLogFileHandler handler = new AccessLogFileHandler(tempPattern("ccc"), 10000, 1); + try { + assertTrue(handler.getFormatter() instanceof AccessLogFormatter); + } finally { + handler.close(); + } + } + + @Test + void constructor_withPatternLimitCountAndAppend_usesAccessLogFormatter() throws Exception { + AccessLogFileHandler handler = + new AccessLogFileHandler(tempPattern("ddd"), 10000, 1, false); + try { + assertTrue(handler.getFormatter() instanceof AccessLogFormatter); + } finally { + handler.close(); + } + } +} diff --git a/org.restlet/src/test/java/org/restlet/engine/log/DefaultAccessLogFormatterTestCase.java b/org.restlet/src/test/java/org/restlet/engine/log/DefaultAccessLogFormatterTestCase.java new file mode 100644 index 0000000000..c7350a2b92 --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/engine/log/DefaultAccessLogFormatterTestCase.java @@ -0,0 +1,28 @@ +/** + * 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.engine.log; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +/** Unit tests for {@link DefaultAccessLogFormatter}. */ +class DefaultAccessLogFormatterTestCase { + + @Test + void getHead_containsSoftwareVersionAndFieldsHeader() { + String head = new DefaultAccessLogFormatter().getHead(null); + + assertTrue(head.contains("#Software: Restlet Framework")); + assertTrue(head.contains("#Version: 1.0")); + assertTrue(head.contains("#Date:")); + assertTrue(head.contains("#Fields:")); + assertTrue(head.contains("cs-method")); + } +} diff --git a/org.restlet/src/test/java/org/restlet/engine/log/IdentClientTestCase.java b/org.restlet/src/test/java/org/restlet/engine/log/IdentClientTestCase.java new file mode 100644 index 0000000000..bdf58013b7 --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/engine/log/IdentClientTestCase.java @@ -0,0 +1,38 @@ +/** + * 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.engine.log; + +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.Test; + +/** Unit tests for {@link IdentClient}. */ +class IdentClientTestCase { + + @Test + void constructor_withNullClientAddress_doesNothing() { + IdentClient client = new IdentClient(null, 1234, 80); + assertNull(client.getHostType()); + assertNull(client.getUserIdentifier()); + } + + @Test + void constructor_withInvalidPorts_doesNothing() { + IdentClient client = new IdentClient("127.0.0.1", -1, -1); + assertNull(client.getHostType()); + assertNull(client.getUserIdentifier()); + } + + @Test + void constructor_withUnreachableIdentServer_swallowsExceptionAndLeavesFieldsNull() { + IdentClient client = new IdentClient("127.0.0.1", 1234, 80); + assertNull(client.getHostType()); + assertNull(client.getUserIdentifier()); + } +} diff --git a/org.restlet/src/test/java/org/restlet/engine/log/LoggingThreadFactoryTestCase.java b/org.restlet/src/test/java/org/restlet/engine/log/LoggingThreadFactoryTestCase.java new file mode 100644 index 0000000000..d85e8d4563 --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/engine/log/LoggingThreadFactoryTestCase.java @@ -0,0 +1,74 @@ +/** + * 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.engine.log; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.logging.Handler; +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import org.junit.jupiter.api.Test; + +/** Unit tests for {@link LoggingThreadFactory}. */ +class LoggingThreadFactoryTestCase { + + @Test + void newThread_defaultConstructor_isNotDaemon() { + LoggingThreadFactory factory = new LoggingThreadFactory(Logger.getLogger("test")); + Thread thread = factory.newThread(() -> {}); + assertFalse(thread.isDaemon()); + assertTrue(thread.getName().startsWith("Restlet-")); + } + + @Test + void newThread_daemonTrue_createsDaemonThread() { + LoggingThreadFactory factory = new LoggingThreadFactory(Logger.getLogger("test"), true); + Thread thread = factory.newThread(() -> {}); + assertTrue(thread.isDaemon()); + } + + @Test + void uncaughtExceptionHandler_logsException() throws Exception { + Logger logger = Logger.getLogger("LoggingThreadFactoryTestCase"); + CountDownLatch latch = new CountDownLatch(1); + Handler captor = + new Handler() { + @Override + public void publish(LogRecord rec) { + latch.countDown(); + } + + @Override + public void flush() { + throw new UnsupportedOperationException(); + } + + @Override + public void close() { + throw new UnsupportedOperationException(); + } + }; + logger.addHandler(captor); + + LoggingThreadFactory factory = new LoggingThreadFactory(logger, true); + Thread thread = + factory.newThread( + () -> { + throw new RuntimeException("boom"); + }); + thread.start(); + thread.join(); + + assertTrue(latch.await(5, TimeUnit.SECONDS)); + logger.removeHandler(captor); + } +} diff --git a/org.restlet/src/test/java/org/restlet/engine/log/SimplerFormatterTestCase.java b/org.restlet/src/test/java/org/restlet/engine/log/SimplerFormatterTestCase.java new file mode 100644 index 0000000000..1b91b6c169 --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/engine/log/SimplerFormatterTestCase.java @@ -0,0 +1,44 @@ +/** + * 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.engine.log; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.logging.Level; +import java.util.logging.LogRecord; +import org.junit.jupiter.api.Test; + +/** Unit tests for {@link SimplerFormatter}. */ +class SimplerFormatterTestCase { + + @Test + void format_withoutThrowable_includesLevelLoggerAndMessage() { + LogRecord rec = new LogRecord(Level.INFO, "hello"); + rec.setLoggerName("my.logger"); + + String result = new SimplerFormatter().format(rec); + + assertTrue(result.contains(Level.INFO.getLocalizedName())); + assertTrue(result.contains("my.logger")); + assertTrue(result.contains("hello")); + } + + @Test + void format_withThrowable_appendsStackTrace() { + LogRecord rec = new LogRecord(Level.SEVERE, "boom"); + rec.setLoggerName("my.logger"); + rec.setThrown(new RuntimeException("failure")); + + String result = new SimplerFormatter().format(rec); + + assertTrue(result.contains("boom")); + assertTrue(result.contains("RuntimeException")); + assertTrue(result.contains("failure")); + } +} diff --git a/org.restlet/src/test/java/org/restlet/engine/ssl/WrapperSslServerSocketFactoryTestCase.java b/org.restlet/src/test/java/org/restlet/engine/ssl/WrapperSslServerSocketFactoryTestCase.java new file mode 100644 index 0000000000..c6d3dbe80f --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/engine/ssl/WrapperSslServerSocketFactoryTestCase.java @@ -0,0 +1,97 @@ +/** + * 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.engine.ssl; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.net.InetAddress; +import java.net.ServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import org.junit.jupiter.api.Test; + +/** Unit tests for {@link WrapperSslServerSocketFactory}. */ +class WrapperSslServerSocketFactoryTestCase { + + @Test + void gettersAndCipherSuites_delegateToWrappedFactory() { + DefaultSslContextFactory contextFactory = new DefaultSslContextFactory(); + SSLServerSocketFactory wrapped = + (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); + WrapperSslServerSocketFactory factory = + new WrapperSslServerSocketFactory(contextFactory, wrapped); + + assertSame(contextFactory, factory.getContextFactory()); + assertSame(wrapped, factory.getWrappedSocketFactory()); + assertTrue(factory.getDefaultCipherSuites().length > 0); + assertTrue(factory.getSupportedCipherSuites().length > 0); + } + + @Test + void createServerSocket_needClientAuth_configuresSocket() throws Exception { + DefaultSslContextFactory contextFactory = new DefaultSslContextFactory(); + contextFactory.setNeedClientAuthentication(true); + SSLServerSocketFactory wrapped = + (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); + WrapperSslServerSocketFactory factory = + new WrapperSslServerSocketFactory(contextFactory, wrapped); + + try (ServerSocket socket = factory.createServerSocket()) { + assertTrue(socket.isBound() || !socket.isBound()); + } + } + + @Test + void createServerSocket_withPort_wantClientAuthAndCipherSuites_configuresSocket() + throws Exception { + DefaultSslContextFactory contextFactory = new DefaultSslContextFactory(); + contextFactory.setWantClientAuthentication(true); + SSLServerSocketFactory wrapped = + (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); + contextFactory.setEnabledCipherSuites(wrapped.getSupportedCipherSuites()); + + WrapperSslServerSocketFactory factory = + new WrapperSslServerSocketFactory(contextFactory, wrapped); + + try (ServerSocket socket = factory.createServerSocket(0)) { + assertTrue(socket.getLocalPort() > 0); + } + } + + @Test + void createServerSocket_withPortAndBacklog_enabledProtocols_configuresSocket() + throws Exception { + DefaultSslContextFactory contextFactory = new DefaultSslContextFactory(); + SSLServerSocketFactory wrapped = + (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); + contextFactory.setEnabledProtocols(new String[] {"TLSv1.2", "TLSv1.3"}); + + WrapperSslServerSocketFactory factory = + new WrapperSslServerSocketFactory(contextFactory, wrapped); + + try (ServerSocket socket = factory.createServerSocket(0, 5)) { + assertTrue(socket.getLocalPort() > 0); + } + } + + @Test + void createServerSocket_withPortBacklogAndAddress_configuresSocket() throws Exception { + DefaultSslContextFactory contextFactory = new DefaultSslContextFactory(); + SSLServerSocketFactory wrapped = + (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); + + WrapperSslServerSocketFactory factory = + new WrapperSslServerSocketFactory(contextFactory, wrapped); + + try (ServerSocket socket = + factory.createServerSocket(0, 5, InetAddress.getLoopbackAddress())) { + assertTrue(socket.getLocalPort() > 0); + } + } +} diff --git a/org.restlet/src/test/java/org/restlet/engine/util/BeanInfoUtilsTestCase.java b/org.restlet/src/test/java/org/restlet/engine/util/BeanInfoUtilsTestCase.java new file mode 100644 index 0000000000..a05f846cef --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/engine/util/BeanInfoUtilsTestCase.java @@ -0,0 +1,62 @@ +/** + * 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.engine.util; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +import java.beans.BeanInfo; +import org.junit.jupiter.api.Test; + +/** Unit tests for {@link BeanInfoUtils}. */ +class BeanInfoUtilsTestCase { + + public static class SampleBean { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + public static class SampleException extends Exception { + private String code; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + } + + @Test + void getBeanInfo_forPlainClass_returnsBeanInfo() { + BeanInfo beanInfo = BeanInfoUtils.getBeanInfo(SampleBean.class); + assertNotNull(beanInfo); + } + + @Test + void getBeanInfo_forThrowableSubclass_returnsBeanInfo() { + BeanInfo beanInfo = BeanInfoUtils.getBeanInfo(SampleException.class); + assertNotNull(beanInfo); + } + + @Test + void getBeanInfo_secondCall_returnsCachedInstance() { + BeanInfo first = BeanInfoUtils.getBeanInfo(SampleBean.class); + BeanInfo second = BeanInfoUtils.getBeanInfo(SampleBean.class); + assertSame(first, second); + } +} diff --git a/org.restlet/src/test/java/org/restlet/engine/util/DefaultSaxHandlerTestCase.java b/org.restlet/src/test/java/org/restlet/engine/util/DefaultSaxHandlerTestCase.java new file mode 100644 index 0000000000..f26b220cd8 --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/engine/util/DefaultSaxHandlerTestCase.java @@ -0,0 +1,75 @@ +/** + * 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.engine.util; + +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.logging.Level; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.restlet.Context; +import org.xml.sax.InputSource; +import org.xml.sax.SAXParseException; + +/** Unit tests for {@link DefaultSaxHandler}. */ +class DefaultSaxHandlerTestCase { + + private Level previousLevel; + + @BeforeEach + void setUp() { + previousLevel = Context.getCurrentLogger().getLevel(); + Context.getCurrentLogger().setLevel(Level.CONFIG); + } + + @AfterEach + void tearDown() { + Context.getCurrentLogger().setLevel(previousLevel); + } + + private SAXParseException newException() { + return new SAXParseException("boom", "public-id", "system-id", 1, 2); + } + + @Test + void error_logsAndDoesNotThrow() throws Exception { + new DefaultSaxHandler().error(newException()); + } + + @Test + void fatalError_logs() { + new DefaultSaxHandler().fatalError(newException()); + } + + @Test + void warning_logs() throws Exception { + new DefaultSaxHandler().warning(newException()); + } + + @Test + void resolveEntity_delegatesToSuperAndLogs() throws Exception { + InputSource result = new DefaultSaxHandler().resolveEntity("public-id", "system-id"); + assertNull(result); + } + + @Test + void resolveResource_alwaysReturnsNull() { + var result = + new DefaultSaxHandler() + .resolveResource( + "type", "namespace-uri", "public-id", "system-id", "base-uri"); + assertNull(result); + } + + @Test + void skippedEntity_logs() throws Exception { + new DefaultSaxHandler().skippedEntity("entity-name"); + } +} diff --git a/org.restlet/src/test/java/org/restlet/engine/util/ListUtilsTestCase.java b/org.restlet/src/test/java/org/restlet/engine/util/ListUtilsTestCase.java new file mode 100644 index 0000000000..1991ad1dfc --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/engine/util/ListUtilsTestCase.java @@ -0,0 +1,44 @@ +/** + * 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.engine.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.List; +import org.junit.jupiter.api.Test; + +/** Unit tests for {@link ListUtils}. */ +class ListUtilsTestCase { + + @Test + void copySubList_returnsCopyOfRange() { + List source = List.of("a", "b", "c", "d"); + assertEquals(List.of("b", "c"), ListUtils.copySubList(source, 1, 2)); + } + + @Test + void copySubList_negativeFromIndex_throws() { + assertThrows( + IndexOutOfBoundsException.class, () -> ListUtils.copySubList(List.of("a"), -1, 0)); + } + + @Test + void copySubList_toIndexBeyondSize_throws() { + assertThrows( + IndexOutOfBoundsException.class, () -> ListUtils.copySubList(List.of("a"), 0, 5)); + } + + @Test + void copySubList_fromIndexGreaterThanToIndex_throws() { + assertThrows( + IllegalArgumentException.class, + () -> ListUtils.copySubList(List.of("a", "b"), 1, 0)); + } +} diff --git a/org.restlet/src/test/java/org/restlet/engine/util/MapResolverTestCase.java b/org.restlet/src/test/java/org/restlet/engine/util/MapResolverTestCase.java new file mode 100644 index 0000000000..86a4103429 --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/engine/util/MapResolverTestCase.java @@ -0,0 +1,31 @@ +/** + * 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.engine.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.Map; +import org.junit.jupiter.api.Test; + +/** Unit tests for {@link MapResolver}. */ +class MapResolverTestCase { + + @Test + void resolve_existingKey_returnsValue() { + MapResolver resolver = new MapResolver(Map.of("key", "value")); + assertEquals("value", resolver.resolve("key")); + } + + @Test + void resolve_missingKey_returnsNull() { + MapResolver resolver = new MapResolver(Map.of("key", "value")); + assertNull(resolver.resolve("missing")); + } +} diff --git a/org.restlet/src/test/java/org/restlet/engine/util/PoolTestCase.java b/org.restlet/src/test/java/org/restlet/engine/util/PoolTestCase.java new file mode 100644 index 0000000000..165695132b --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/engine/util/PoolTestCase.java @@ -0,0 +1,73 @@ +/** + * 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.engine.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.jupiter.api.Test; + +/** Unit tests for {@link Pool}. */ +class PoolTestCase { + + private static class CountingPool extends Pool { + private static final AtomicInteger COUNTER = new AtomicInteger(); + + CountingPool() { + super(); + } + + CountingPool(int initialSize) { + super(initialSize); + } + + @Override + protected Integer createObject() { + return COUNTER.incrementAndGet(); + } + } + + @Test + void checkoutWithoutCheckin_createsNewObjectsEachTime() { + CountingPool pool = new CountingPool(); + Integer first = pool.checkout(); + Integer second = pool.checkout(); + assertNotNull(first); + assertNotNull(second); + } + + @Test + void checkinThenCheckout_reusesObject() { + CountingPool pool = new CountingPool(); + Integer created = pool.checkout(); + pool.checkin(created); + assertEquals(created, pool.checkout()); + } + + @Test + void checkin_null_isIgnored() { + CountingPool pool = new CountingPool(); + pool.checkin(null); + assertNotNull(pool.checkout()); + } + + @Test + void constructorWithInitialSize_preCreatesObjects() { + CountingPool pool = new CountingPool(3); + assertEquals(3, pool.getStore().size()); + } + + @Test + void clear_emptiesTheStore() { + CountingPool pool = new CountingPool(2); + pool.clear(); + assertEquals(0, pool.getStore().size()); + } +} diff --git a/org.restlet/src/test/java/org/restlet/representation/BufferingRepresentationTestCase.java b/org.restlet/src/test/java/org/restlet/representation/BufferingRepresentationTestCase.java new file mode 100644 index 0000000000..9b06de3cbe --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/representation/BufferingRepresentationTestCase.java @@ -0,0 +1,72 @@ +/** + * 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.representation; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.StringWriter; +import org.junit.jupiter.api.Test; +import org.restlet.data.MediaType; +import org.restlet.engine.io.IoUtils; + +/** Unit tests for {@link BufferingRepresentation}. */ +class BufferingRepresentationTestCase { + + @Test + void getText_buffersAndReturnsWrappedText() throws Exception { + BufferingRepresentation representation = + new BufferingRepresentation( + new StringRepresentation("hello", MediaType.TEXT_PLAIN)); + + assertTrue(representation.isAvailable()); + assertEquals("hello", representation.getText()); + assertEquals(5, representation.getSize()); + assertEquals(5, representation.getAvailableSize()); + } + + @Test + void getStream_returnsBufferedBytes() throws Exception { + BufferingRepresentation representation = + new BufferingRepresentation(new StringRepresentation("hello")); + + assertEquals("hello", IoUtils.toString(representation.getStream())); + } + + @Test + void getReader_returnsBufferedCharacters() throws Exception { + BufferingRepresentation representation = + new BufferingRepresentation(new StringRepresentation("hello")); + + char[] buf = new char[5]; + representation.getReader().read(buf); + assertEquals("hello", new String(buf)); + } + + @Test + void write_outputStream_writesBufferedBytes() throws Exception { + BufferingRepresentation representation = + new BufferingRepresentation(new StringRepresentation("hello")); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + representation.write(out); + assertEquals("hello", out.toString()); + } + + @Test + void write_writer_writesBufferedText() throws Exception { + BufferingRepresentation representation = + new BufferingRepresentation(new StringRepresentation("hello")); + + StringWriter writer = new StringWriter(); + representation.write(writer); + assertEquals("hello", writer.toString()); + } +} diff --git a/org.restlet/src/test/java/org/restlet/representation/ByteArrayRepresentationTestCase.java b/org.restlet/src/test/java/org/restlet/representation/ByteArrayRepresentationTestCase.java new file mode 100644 index 0000000000..7601b9593d --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/representation/ByteArrayRepresentationTestCase.java @@ -0,0 +1,62 @@ +/** + * 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.representation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import org.restlet.data.MediaType; + +/** Unit tests for {@link ByteArrayRepresentation}. */ +class ByteArrayRepresentationTestCase { + + private static final byte[] DATA = "hello world".getBytes(); + + @Test + void constructor_byteArray() throws Exception { + assertEquals("hello world", new ByteArrayRepresentation(DATA).getText()); + } + + @Test + void constructor_byteArrayWithOffsetAndLength() throws Exception { + assertEquals("hello", new ByteArrayRepresentation(DATA, 0, 5).getText()); + } + + @Test + void constructor_byteArrayWithOffsetLengthAndMediaType() throws Exception { + ByteArrayRepresentation representation = + new ByteArrayRepresentation(DATA, 6, 5, MediaType.TEXT_PLAIN); + assertEquals("world", representation.getText()); + assertEquals(MediaType.TEXT_PLAIN, representation.getMediaType()); + } + + @Test + void constructor_byteArrayWithOffsetLengthMediaTypeAndExpectedSize() throws Exception { + ByteArrayRepresentation representation = + new ByteArrayRepresentation(DATA, 0, 5, MediaType.TEXT_PLAIN, 5); + assertEquals("hello", representation.getText()); + assertEquals(5, representation.getSize()); + } + + @Test + void constructor_byteArrayWithMediaType() throws Exception { + ByteArrayRepresentation representation = + new ByteArrayRepresentation(DATA, MediaType.TEXT_PLAIN); + assertEquals("hello world", representation.getText()); + assertEquals(MediaType.TEXT_PLAIN, representation.getMediaType()); + } + + @Test + void constructor_byteArrayWithMediaTypeAndExpectedSize() throws Exception { + ByteArrayRepresentation representation = + new ByteArrayRepresentation(DATA, MediaType.TEXT_PLAIN, DATA.length); + assertEquals("hello world", representation.getText()); + assertEquals(DATA.length, representation.getSize()); + } +} diff --git a/org.restlet/src/test/java/org/restlet/representation/ReaderRepresentationTestCase.java b/org.restlet/src/test/java/org/restlet/representation/ReaderRepresentationTestCase.java new file mode 100644 index 0000000000..9efde096d0 --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/representation/ReaderRepresentationTestCase.java @@ -0,0 +1,67 @@ +/** + * 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.representation; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.StringReader; +import java.io.StringWriter; +import org.junit.jupiter.api.Test; +import org.restlet.data.MediaType; + +/** Unit tests for {@link ReaderRepresentation}. */ +class ReaderRepresentationTestCase { + + @Test + void constructorWithReaderOnly_isAvailable() { + ReaderRepresentation representation = new ReaderRepresentation(new StringReader("text")); + assertTrue(representation.isAvailable()); + assertTrue(representation.isTransient()); + } + + @Test + void getText_readsFromWrappedReader() throws Exception { + ReaderRepresentation representation = + new ReaderRepresentation(new StringReader("hello"), MediaType.TEXT_PLAIN); + assertEquals("hello", representation.getText()); + } + + @Test + void getReader_returnsReaderOnceThenNull() throws Exception { + ReaderRepresentation representation = + new ReaderRepresentation(new StringReader("hello"), MediaType.TEXT_PLAIN, 5); + assertEquals(5, representation.getSize()); + assertTrue(representation.getReader() != null); + assertNull(representation.getReader()); + } + + @Test + void write_writer_copiesReaderContent() throws Exception { + ReaderRepresentation representation = new ReaderRepresentation(new StringReader("hello")); + StringWriter writer = new StringWriter(); + representation.write(writer); + assertEquals("hello", writer.toString()); + } + + @Test + void setReader_null_marksUnavailable() { + ReaderRepresentation representation = new ReaderRepresentation(new StringReader("hello")); + representation.setReader(null); + assertFalse(representation.isAvailable()); + } + + @Test + void release_closesReader() { + ReaderRepresentation representation = new ReaderRepresentation(new StringReader("hello")); + representation.release(); + } +} diff --git a/org.restlet/src/test/java/org/restlet/representation/WriterRepresentationTestCase.java b/org.restlet/src/test/java/org/restlet/representation/WriterRepresentationTestCase.java new file mode 100644 index 0000000000..d59724735d --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/representation/WriterRepresentationTestCase.java @@ -0,0 +1,58 @@ +/** + * 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.representation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.io.Writer; +import org.junit.jupiter.api.Test; +import org.restlet.data.MediaType; + +/** Unit tests for {@link WriterRepresentation}. */ +class WriterRepresentationTestCase { + + private static class TestWriterRepresentation extends WriterRepresentation { + TestWriterRepresentation(MediaType mediaType) { + super(mediaType); + } + + TestWriterRepresentation(MediaType mediaType, long expectedSize) { + super(mediaType, expectedSize); + } + + @Override + public void write(Writer writer) throws IOException { + writer.write("hello"); + } + } + + @Test + void constructor_withMediaType_hasUnknownSize() { + TestWriterRepresentation representation = + new TestWriterRepresentation(MediaType.TEXT_PLAIN); + assertEquals(Representation.UNKNOWN_SIZE, representation.getSize()); + } + + @Test + void constructor_withExpectedSize_setsSize() { + TestWriterRepresentation representation = + new TestWriterRepresentation(MediaType.TEXT_PLAIN, 5); + assertEquals(5, representation.getSize()); + } + + @Test + void getReader_readsWrittenContent() throws Exception { + TestWriterRepresentation representation = + new TestWriterRepresentation(MediaType.TEXT_PLAIN); + char[] buf = new char[5]; + representation.getReader().read(buf); + assertEquals("hello", new String(buf)); + } +} diff --git a/org.restlet/src/test/java/org/restlet/routing/ExtractorTestCase.java b/org.restlet/src/test/java/org/restlet/routing/ExtractorTestCase.java new file mode 100644 index 0000000000..32b916cab3 --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/routing/ExtractorTestCase.java @@ -0,0 +1,157 @@ +/** + * 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.routing; + +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.util.List; +import org.junit.jupiter.api.Test; +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.data.Cookie; +import org.restlet.data.MediaType; +import org.restlet.data.Parameter; +import org.restlet.representation.StringRepresentation; + +/** Unit tests for {@link Extractor}. */ +class ExtractorTestCase { + + @Test + void constructors_createUsableInstances() { + assertTrue(new Extractor() instanceof Extractor); + assertTrue(new Extractor(new Context()) instanceof Extractor); + assertTrue(new Extractor(new Context(), null) instanceof Extractor); + } + + @Test + void beforeHandle_withoutAnyExtractsConfigured_leavesAttributesEmpty() { + Extractor extractor = new Extractor(); + Request request = new Request(); + request.setResourceRef("http://localhost/test?foo=bar"); + Response response = new Response(request); + + int result = extractor.beforeHandle(request, response); + + assertEquals(Filter.CONTINUE, result); + assertTrue(request.getAttributes().isEmpty()); + } + + @Test + void beforeHandle_extractsFirstQueryValue() { + Extractor extractor = new Extractor(); + extractor.extractFromQuery("attr", "foo", true); + + Request request = new Request(); + request.setResourceRef("http://localhost/test?foo=bar1&foo=bar2"); + Response response = new Response(request); + + extractor.beforeHandle(request, response); + + assertEquals("bar1", request.getAttributes().get("attr")); + } + + @Test + void beforeHandle_extractsAllQueryValues() { + Extractor extractor = new Extractor(); + extractor.extractFromQuery("attr", "foo", false); + + Request request = new Request(); + request.setResourceRef("http://localhost/test?foo=bar1&foo=bar2"); + Response response = new Response(request); + + extractor.beforeHandle(request, response); + + @SuppressWarnings("unchecked") + List values = (List) request.getAttributes().get("attr"); + assertEquals(2, values.size()); + } + + @Test + void beforeHandle_missingQueryParameter_doesNotSetAttribute() { + Extractor extractor = new Extractor(); + extractor.extractFromQuery("attr", "missing", true); + + Request request = new Request(); + request.setResourceRef("http://localhost/test?foo=bar"); + Response response = new Response(request); + + extractor.beforeHandle(request, response); + + assertNull(request.getAttributes().get("attr")); + } + + @Test + void beforeHandle_extractsFirstEntityValue() { + Extractor extractor = new Extractor(); + extractor.extractFromEntity("attr", "foo", true); + + Request request = new Request(); + request.setMethod(org.restlet.data.Method.POST); + request.setEntity( + new StringRepresentation("foo=bar1&foo=bar2", MediaType.APPLICATION_WWW_FORM)); + Response response = new Response(request); + + extractor.beforeHandle(request, response); + + assertEquals("bar1", request.getAttributes().get("attr")); + } + + @Test + void beforeHandle_extractsAllEntityValues() { + Extractor extractor = new Extractor(); + extractor.extractFromEntity("attr", "foo", false); + + Request request = new Request(); + request.setMethod(org.restlet.data.Method.POST); + request.setEntity( + new StringRepresentation("foo=bar1&foo=bar2", MediaType.APPLICATION_WWW_FORM)); + Response response = new Response(request); + + extractor.beforeHandle(request, response); + + @SuppressWarnings("unchecked") + List values = (List) request.getAttributes().get("attr"); + assertEquals(2, values.size()); + } + + @Test + void beforeHandle_extractsFirstCookieValue() { + Extractor extractor = new Extractor(); + extractor.extractFromCookie("attr", "foo", true); + + Request request = new Request(); + request.getCookies().add(new Cookie("foo", "bar1")); + request.getCookies().add(new Cookie("foo", "bar2")); + Response response = new Response(request); + + extractor.beforeHandle(request, response); + + assertEquals("bar1", request.getAttributes().get("attr")); + } + + @Test + void beforeHandle_extractsAllCookieValues() { + Extractor extractor = new Extractor(); + extractor.extractFromCookie("attr", "foo", false); + + Request request = new Request(); + request.getCookies().add(new Cookie("foo", "bar1")); + request.getCookies().add(new Cookie("foo", "bar2")); + Response response = new Response(request); + + extractor.beforeHandle(request, response); + + @SuppressWarnings("unchecked") + List values = (List) request.getAttributes().get("attr"); + assertEquals(2, values.size()); + } +} diff --git a/org.restlet/src/test/java/org/restlet/security/CertificateAuthenticatorTestCase.java b/org.restlet/src/test/java/org/restlet/security/CertificateAuthenticatorTestCase.java new file mode 100644 index 0000000000..fc76b46cf8 --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/security/CertificateAuthenticatorTestCase.java @@ -0,0 +1,144 @@ +/** + * 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.security; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.File; +import java.io.FileInputStream; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.List; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.data.Status; + +/** Unit tests for {@link CertificateAuthenticator}. */ +class CertificateAuthenticatorTestCase { + + private static X509Certificate selfSignedCertificate; + + @BeforeAll + static void generateSelfSignedCertificate() throws Exception { + File keystoreFile = File.createTempFile("CertificateAuthenticatorTestCase", ".p12"); + keystoreFile.deleteOnExit(); + keystoreFile.delete(); + + ProcessBuilder pb = + new ProcessBuilder( + "keytool", + "-genkeypair", + "-alias", + "test", + "-keyalg", + "RSA", + "-keysize", + "2048", + "-validity", + "1", + "-storetype", + "PKCS12", + "-keystore", + keystoreFile.getAbsolutePath(), + "-storepass", + "changeit", + "-dname", + "CN=test-user, OU=Restlet, O=Restlet, L=Paris, ST=IDF, C=FR"); + pb.redirectErrorStream(true); + Process process = pb.start(); + process.getInputStream().readAllBytes(); + int exit = process.waitFor(); + org.junit.jupiter.api.Assumptions.assumeTrue(exit == 0, "keytool must be available"); + + KeyStore keyStore = KeyStore.getInstance("PKCS12"); + try (FileInputStream in = new FileInputStream(keystoreFile)) { + keyStore.load(in, "changeit".toCharArray()); + } + selfSignedCertificate = (X509Certificate) keyStore.getCertificate("test"); + keystoreFile.delete(); + } + + private Request requestWithCertificates(List certificates) { + Request request = new Request(); + request.getClientInfo().setCertificates(certificates); + return request; + } + + @Test + void authenticate_withoutCertificates_setsUnauthorizedStatus() { + CertificateAuthenticator authenticator = new CertificateAuthenticator(new Context()); + Request request = new Request(); + Response response = new Response(request); + + boolean result = authenticator.authenticate(request, response); + + assertFalse(result); + assertEquals(Status.CLIENT_ERROR_UNAUTHORIZED, response.getStatus()); + } + + @Test + void authenticate_withNonX509Certificate_setsUnauthorizedStatus() { + CertificateAuthenticator authenticator = new CertificateAuthenticator(new Context()); + Certificate nonX509 = + new Certificate("non-x509") { + @Override + public byte[] getEncoded() { + return new byte[0]; + } + + @Override + public void verify(java.security.PublicKey key) { + throw new UnsupportedOperationException(); + } + + @Override + public void verify(java.security.PublicKey key, String sigProvider) { + throw new UnsupportedOperationException(); + } + + @Override + public String toString() { + return "non-x509"; + } + + @Override + public java.security.PublicKey getPublicKey() { + return null; + } + }; + Request request = requestWithCertificates(List.of(nonX509)); + Response response = new Response(request); + + boolean result = authenticator.authenticate(request, response); + + assertFalse(result); + assertEquals(Status.CLIENT_ERROR_UNAUTHORIZED, response.getStatus()); + } + + @Test + void authenticate_withX509Certificate_setsUserAndPrincipal() { + org.junit.jupiter.api.Assumptions.assumeTrue(selfSignedCertificate != null); + + CertificateAuthenticator authenticator = new CertificateAuthenticator(new Context()); + Request request = requestWithCertificates(List.of(selfSignedCertificate)); + Response response = new Response(request); + + boolean result = authenticator.authenticate(request, response); + + assertTrue(result); + assertTrue(request.getClientInfo().getUser().getIdentifier().contains("test-user")); + assertEquals(1, request.getClientInfo().getPrincipals().size()); + } +} diff --git a/org.restlet/src/test/java/org/restlet/security/ConfidentialAuthorizerTestCase.java b/org.restlet/src/test/java/org/restlet/security/ConfidentialAuthorizerTestCase.java new file mode 100644 index 0000000000..436ff51583 --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/security/ConfidentialAuthorizerTestCase.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.security; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.data.Protocol; + +/** Unit tests for {@link ConfidentialAuthorizer}. */ +class ConfidentialAuthorizerTestCase { + + @Test + void authorize_confidentialRequest_returnsTrue() { + ConfidentialAuthorizer authorizer = new ConfidentialAuthorizer(); + Request request = new Request(); + request.setProtocol(Protocol.HTTPS); + Response response = new Response(request); + + assertTrue(authorizer.authorize(request, response)); + } + + @Test + void authorize_nonConfidentialRequest_returnsFalse() { + ConfidentialAuthorizer authorizer = new ConfidentialAuthorizer(); + Request request = new Request(); + request.setProtocol(Protocol.HTTP); + Response response = new Response(request); + + assertFalse(authorizer.authorize(request, response)); + } +} diff --git a/org.restlet/src/test/java/org/restlet/security/MethodAuthorizerTestCase.java b/org.restlet/src/test/java/org/restlet/security/MethodAuthorizerTestCase.java new file mode 100644 index 0000000000..08f3a538aa --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/security/MethodAuthorizerTestCase.java @@ -0,0 +1,109 @@ +/** + * 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.security; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.junit.jupiter.api.Test; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.data.Method; + +/** Unit tests for {@link MethodAuthorizer}. */ +class MethodAuthorizerTestCase { + + private Request requestWith(Method method, boolean authenticated) { + Request request = new Request(); + request.setMethod(method); + request.getClientInfo().setAuthenticated(authenticated); + return request; + } + + @Test + void defaultConstructor_hasEmptyMethodLists() { + MethodAuthorizer authorizer = new MethodAuthorizer(); + assertTrue(authorizer.getAnonymousMethods().isEmpty()); + assertTrue(authorizer.getAuthenticatedMethods().isEmpty()); + } + + @Test + void constructorWithIdentifier_setsIdentifier() { + MethodAuthorizer authorizer = new MethodAuthorizer("my-id"); + assertEquals("my-id", authorizer.getIdentifier()); + } + + @Test + void authorize_anonymousWithAllowedMethod_returnsTrue() { + MethodAuthorizer authorizer = new MethodAuthorizer(); + authorizer.setAnonymousMethods(List.of(Method.GET)); + + Request request = requestWith(Method.GET, false); + assertTrue(authorizer.authorize(request, new Response(request))); + } + + @Test + void authorize_anonymousWithDisallowedMethod_returnsFalse() { + MethodAuthorizer authorizer = new MethodAuthorizer(); + authorizer.setAnonymousMethods(List.of(Method.GET)); + + Request request = requestWith(Method.POST, false); + assertFalse(authorizer.authorize(request, new Response(request))); + } + + @Test + void authorize_authenticatedWithAllowedMethod_returnsTrue() { + MethodAuthorizer authorizer = new MethodAuthorizer(); + authorizer.setAuthenticatedMethods(List.of(Method.PUT)); + + Request request = requestWith(Method.PUT, true); + assertTrue(authorizer.authorize(request, new Response(request))); + } + + @Test + void authorize_authenticatedWithDisallowedMethod_returnsFalse() { + MethodAuthorizer authorizer = new MethodAuthorizer(); + authorizer.setAuthenticatedMethods(List.of(Method.PUT)); + + Request request = requestWith(Method.DELETE, true); + assertFalse(authorizer.authorize(request, new Response(request))); + } + + @Test + void setAnonymousMethods_withNull_clearsList() { + MethodAuthorizer authorizer = new MethodAuthorizer(); + authorizer.setAnonymousMethods(List.of(Method.GET)); + authorizer.setAnonymousMethods(null); + assertTrue(authorizer.getAnonymousMethods().isEmpty()); + } + + @Test + void setAnonymousMethods_withSameListInstance_isNoOp() { + MethodAuthorizer authorizer = new MethodAuthorizer(); + authorizer.setAnonymousMethods(authorizer.getAnonymousMethods()); + assertTrue(authorizer.getAnonymousMethods().isEmpty()); + } + + @Test + void setAuthenticatedMethods_withNull_clearsList() { + MethodAuthorizer authorizer = new MethodAuthorizer(); + authorizer.setAuthenticatedMethods(List.of(Method.PUT)); + authorizer.setAuthenticatedMethods(null); + assertTrue(authorizer.getAuthenticatedMethods().isEmpty()); + } + + @Test + void setAuthenticatedMethods_withSameListInstance_isNoOp() { + MethodAuthorizer authorizer = new MethodAuthorizer(); + authorizer.setAuthenticatedMethods(authorizer.getAuthenticatedMethods()); + assertTrue(authorizer.getAuthenticatedMethods().isEmpty()); + } +} diff --git a/org.restlet/src/test/java/org/restlet/service/CorsServiceTestCase.java b/org.restlet/src/test/java/org/restlet/service/CorsServiceTestCase.java new file mode 100644 index 0000000000..82ed25d1c2 --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/service/CorsServiceTestCase.java @@ -0,0 +1,82 @@ +/** + * 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.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Set; +import org.junit.jupiter.api.Test; +import org.restlet.Context; +import org.restlet.data.Method; +import org.restlet.engine.application.CorsFilter; +import org.restlet.routing.Filter; + +/** Unit tests for {@link CorsService}. */ +class CorsServiceTestCase { + + @Test + void defaultConstructor_isEnabled() { + CorsService service = new CorsService(); + assertTrue(service.isEnabled()); + assertEquals(Set.of("*"), service.getAllowedOrigins()); + assertTrue(service.isAllowingAllRequestedHeaders()); + assertFalse(service.isAllowedCredentials()); + assertFalse(service.isSkippingResourceForCorsOptions()); + assertEquals(-1, service.getMaxAge()); + } + + @Test + void gettersAndSetters_roundTrip() { + CorsService service = new CorsService(false); + assertFalse(service.isEnabled()); + + service.setAllowedCredentials(true); + assertTrue(service.isAllowedCredentials()); + + service.setAllowedHeaders(Set.of("X-Custom")); + assertEquals(Set.of("X-Custom"), service.getAllowedHeaders()); + + service.setAllowedOrigins(Set.of("http://example.com")); + assertEquals(Set.of("http://example.com"), service.getAllowedOrigins()); + + service.setAllowingAllRequestedHeaders(false); + assertFalse(service.isAllowingAllRequestedHeaders()); + + service.setDefaultAllowedMethods(Set.of(Method.GET)); + assertEquals(Set.of(Method.GET), service.getDefaultAllowedMethods()); + + service.setExposedHeaders(Set.of("X-Exposed")); + assertEquals(Set.of("X-Exposed"), service.getExposedHeaders()); + + service.setMaxAge(3600); + assertEquals(3600, service.getMaxAge()); + + service.setSkippingResourceForCorsOptions(true); + assertTrue(service.isSkippingResourceForCorsOptions()); + } + + @Test + void createInboundFilter_returnsConfiguredCorsFilter() { + CorsService service = new CorsService(); + service.setAllowedCredentials(true); + service.setAllowedOrigins(Set.of("http://example.com")); + service.setAllowingAllRequestedHeaders(false); + service.setAllowedHeaders(Set.of("X-Custom")); + service.setExposedHeaders(Set.of("X-Exposed")); + service.setSkippingResourceForCorsOptions(true); + service.setDefaultAllowedMethods(Set.of(Method.GET)); + service.setMaxAge(60); + + Filter filter = service.createInboundFilter(new Context()); + + assertTrue(filter instanceof CorsFilter); + } +} diff --git a/org.restlet/src/test/java/org/restlet/util/WrapperMapTestCase.java b/org.restlet/src/test/java/org/restlet/util/WrapperMapTestCase.java new file mode 100644 index 0000000000..9d85853019 --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/util/WrapperMapTestCase.java @@ -0,0 +1,56 @@ +/** + * 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.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.Test; + +/** Unit tests for {@link WrapperMap}. */ +class WrapperMapTestCase { + + @Test + void defaultConstructor_usesConcurrentHashMapDelegate() { + WrapperMap map = new WrapperMap<>(); + map.put("key", "value"); + assertEquals("value", map.get("key")); + } + + @Test + void delegatesAllMapOperations() { + Map delegate = new HashMap<>(); + WrapperMap map = new WrapperMap<>(delegate); + + assertTrue(map.isEmpty()); + map.put("key", "value"); + assertFalse(map.isEmpty()); + assertEquals(1, map.size()); + assertTrue(map.containsKey("key")); + assertTrue(map.containsValue("value")); + assertEquals("value", map.get("key")); + assertTrue(map.equals(delegate)); + assertEquals(delegate.hashCode(), map.hashCode()); + assertEquals(1, map.keySet().size()); + assertEquals(1, map.entrySet().size()); + assertEquals(1, map.values().size()); + + map.putAll(Map.of("key2", "value2")); + assertEquals(2, map.size()); + + map.remove("key2"); + assertEquals(1, map.size()); + + map.clear(); + assertTrue(map.isEmpty()); + } +} diff --git a/org.restlet/src/test/java/org/restlet/util/WrapperRequestTestCase.java b/org.restlet/src/test/java/org/restlet/util/WrapperRequestTestCase.java new file mode 100644 index 0000000000..dd699008ac --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/util/WrapperRequestTestCase.java @@ -0,0 +1,143 @@ +/** + * 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.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import java.util.Set; +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.Cookie; +import org.restlet.data.MediaType; +import org.restlet.data.Method; +import org.restlet.data.Protocol; +import org.restlet.data.Range; +import org.restlet.data.Reference; +import org.restlet.representation.StringRepresentation; + +/** Unit tests for {@link WrapperRequest}. */ +class WrapperRequestTestCase { + + private Request wrapped; + + private WrapperRequest wrapper; + + @BeforeEach + void setUp() { + wrapped = new Request(); + wrapper = new WrapperRequest(wrapped); + } + + @Test + void abort_delegatesToWrappedRequest() { + assertFalse(wrapper.abort()); + } + + @Test + void commit_delegatesToWrappedRequest() { + wrapper.commit(new Response(wrapped)); + } + + @Test + void attributesAndSimpleGetters_delegateToWrappedRequest() throws Exception { + wrapped.getAttributes().put("key", "value"); + assertSame(wrapped.getAttributes(), wrapper.getAttributes()); + + ChallengeResponse challengeResponse = new ChallengeResponse(ChallengeScheme.HTTP_BASIC); + wrapper.setChallengeResponse(challengeResponse); + assertSame(challengeResponse, wrapper.getChallengeResponse()); + assertSame(wrapped.getChallengeResponse(), wrapper.getChallengeResponse()); + + assertSame(wrapped.getClientInfo(), wrapper.getClientInfo()); + assertSame(wrapped.getConditions(), wrapper.getConditions()); + + wrapper.setCookies(wrapper.getCookies()); + wrapper.getCookies().add(new Cookie("foo", "bar")); + assertSame(wrapped.getCookies(), wrapper.getCookies()); + + wrapper.setEntity(new StringRepresentation("text")); + assertEquals("text", wrapper.getEntity().getText()); + + wrapper.setEntity("value", MediaType.TEXT_PLAIN); + assertEquals("value", wrapped.getEntity().getText()); + + wrapper.setHostRef("http://localhost"); + assertEquals("http://localhost", wrapper.getHostRef().toString()); + wrapper.setHostRef(new Reference("http://example.com")); + assertEquals("http://example.com", wrapped.getHostRef().toString()); + + wrapper.setMaxForwards(3); + assertEquals(3, wrapper.getMaxForwards()); + + wrapper.setMethod(Method.PUT); + assertEquals(Method.PUT, wrapper.getMethod()); + + wrapper.setOnResponse(null); + assertEquals(wrapped.getOnResponse(), wrapper.getOnResponse()); + + wrapper.setOriginalRef(new Reference("http://original")); + assertEquals("http://original", wrapper.getOriginalRef().toString()); + + wrapper.setProtocol(Protocol.HTTP); + assertEquals(Protocol.HTTP, wrapper.getProtocol()); + + ChallengeResponse proxyChallenge = new ChallengeResponse(ChallengeScheme.HTTP_BASIC); + wrapper.setProxyChallengeResponse(proxyChallenge); + assertSame(proxyChallenge, wrapper.getProxyChallengeResponse()); + + wrapper.setRanges(List.of(new Range(0, 10))); + assertEquals(1, wrapper.getRanges().size()); + + wrapper.setReferrerRef("http://referrer"); + assertEquals("http://referrer", wrapper.getReferrerRef().toString()); + wrapper.setReferrerRef(new Reference("http://referrer2")); + assertEquals("http://referrer2", wrapper.getReferrerRef().toString()); + + wrapper.setResourceRef("http://resource"); + assertEquals("http://resource", wrapper.getResourceRef().toString()); + wrapper.setResourceRef(new Reference("http://resource2")); + assertEquals("http://resource2", wrapper.getResourceRef().toString()); + + wrapper.setRootRef(new Reference("http://root")); + assertEquals("http://root", wrapper.getRootRef().toString()); + + wrapper.setAccessControlRequestHeaders(Set.of("X-Test")); + assertEquals(Set.of("X-Test"), wrapper.getAccessControlRequestHeaders()); + + wrapper.setAccessControlRequestMethod(Method.DELETE); + assertEquals(Method.DELETE, wrapper.getAccessControlRequestMethod()); + + assertFalse(wrapper.isAsynchronous()); + assertFalse(wrapper.isConfidential()); + assertTrue(wrapper.isEntityAvailable()); + assertTrue(wrapper.isExpectingResponse()); + assertTrue(wrapper.isSynchronous()); + + assertEquals(wrapped.toString(), wrapper.toString()); + } + + @Test + void setClientInfoConditionsCookies_delegateToWrappedRequest() { + var clientInfo = new org.restlet.data.ClientInfo(); + wrapper.setClientInfo(clientInfo); + assertSame(clientInfo, wrapped.getClientInfo()); + + var conditions = new org.restlet.data.Conditions(); + wrapper.setConditions(conditions); + assertSame(conditions, wrapped.getConditions()); + } +} diff --git a/org.restlet/src/test/java/org/restlet/util/WrapperResponseTestCase.java b/org.restlet/src/test/java/org/restlet/util/WrapperResponseTestCase.java new file mode 100644 index 0000000000..0ec3d80d7c --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/util/WrapperResponseTestCase.java @@ -0,0 +1,153 @@ +/** + * 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.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Date; +import java.util.List; +import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.data.AuthenticationInfo; +import org.restlet.data.ChallengeRequest; +import org.restlet.data.ChallengeScheme; +import org.restlet.data.Dimension; +import org.restlet.data.MediaType; +import org.restlet.data.Method; +import org.restlet.data.Reference; +import org.restlet.data.ServerInfo; +import org.restlet.data.Status; +import org.restlet.representation.StringRepresentation; + +/** Unit tests for {@link WrapperResponse}. */ +class WrapperResponseTestCase { + + private Request request; + + private Response wrapped; + + private WrapperResponse wrapper; + + @BeforeEach + void setUp() { + request = new Request(); + wrapped = new Response(request); + wrapper = new WrapperResponse(wrapped); + } + + @Test + void abortAndCommit_delegateToWrappedResponse() { + wrapper.abort(); + wrapper.commit(); + } + + @Test + void gettersAndSetters_delegateToWrappedResponse() throws Exception { + wrapper.setAge(42); + assertEquals(42, wrapper.getAge()); + + wrapper.setAllowedMethods(Set.of(Method.GET)); + assertEquals(Set.of(Method.GET), wrapper.getAllowedMethods()); + + wrapped.getAttributes().put("key", "value"); + assertSame(wrapped.getAttributes(), wrapper.getAttributes()); + + AuthenticationInfo authInfo = + new AuthenticationInfo("nextNonce", 1, "cnonce", "auth", "digest"); + wrapper.setAuthenticationInfo(authInfo); + assertSame(authInfo, wrapper.getAuthenticationInfo()); + + wrapper.setAutoCommitting(false); + assertFalse(wrapper.isAutoCommitting()); + + ChallengeRequest challengeRequest = new ChallengeRequest(ChallengeScheme.HTTP_BASIC); + wrapper.setChallengeRequests(List.of(challengeRequest)); + assertEquals(1, wrapper.getChallengeRequests().size()); + + wrapper.getCookieSettings().add(new org.restlet.data.CookieSetting("foo", "bar")); + assertSame(wrapped.getCookieSettings(), wrapper.getCookieSettings()); + + wrapper.setCommitted(true); + assertTrue(wrapper.isCommitted()); + + wrapper.setDimensions(Set.of(Dimension.MEDIA_TYPE)); + assertEquals(Set.of(Dimension.MEDIA_TYPE), wrapper.getDimensions()); + + wrapper.setEntity(new StringRepresentation("text")); + assertEquals("text", wrapper.getEntity().getText()); + wrapper.setEntity("value", MediaType.TEXT_PLAIN); + assertEquals("value", wrapped.getEntity().getText()); + + wrapper.setLocationRef("http://localhost/loc"); + assertEquals("http://localhost/loc", wrapper.getLocationRef().toString()); + wrapper.setLocationRef(new Reference("http://localhost/loc2")); + assertEquals("http://localhost/loc2", wrapper.getLocationRef().toString()); + + wrapper.setProxyChallengeRequests(List.of(challengeRequest)); + assertEquals(1, wrapper.getProxyChallengeRequests().size()); + + assertSame(request, wrapper.getRequest()); + wrapper.setRequest(request); + assertSame(request, wrapped.getRequest()); + wrapper.setRequest(new WrapperRequest(request)); + assertTrue(wrapped.getRequest() instanceof WrapperRequest); + + Date retryAfter = new Date(); + wrapper.setRetryAfter(retryAfter); + assertEquals(retryAfter, wrapper.getRetryAfter()); + + ServerInfo serverInfo = new ServerInfo(); + wrapper.setServerInfo(serverInfo); + assertSame(serverInfo, wrapper.getServerInfo()); + + wrapper.setStatus(Status.SUCCESS_ACCEPTED); + assertEquals(Status.SUCCESS_ACCEPTED, wrapper.getStatus()); + + wrapper.setStatus(Status.SUCCESS_OK, "custom message"); + assertEquals(Status.SUCCESS_OK, wrapper.getStatus()); + + wrapper.setStatus(Status.SERVER_ERROR_INTERNAL, new RuntimeException("boom")); + assertEquals(Status.SERVER_ERROR_INTERNAL, wrapper.getStatus()); + + wrapper.setStatus(Status.SERVER_ERROR_INTERNAL, new RuntimeException("boom"), "message"); + assertEquals(Status.SERVER_ERROR_INTERNAL, wrapper.getStatus()); + + assertFalse(wrapper.isConfidential()); + assertTrue(wrapper.isEntityAvailable()); + + assertEquals(wrapped.toString(), wrapper.toString()); + } + + @Test + void redirectMethods_delegateToWrappedResponse() { + wrapper.redirectPermanent("http://localhost/permanent"); + assertEquals(Status.REDIRECTION_PERMANENT, wrapped.getStatus()); + + wrapper.redirectPermanent(new Reference("http://localhost/permanent2")); + assertEquals(Status.REDIRECTION_PERMANENT, wrapped.getStatus()); + + wrapper.redirectSeeOther("http://localhost/other"); + assertEquals(Status.REDIRECTION_SEE_OTHER, wrapped.getStatus()); + + wrapper.redirectSeeOther(new Reference("http://localhost/other2")); + assertEquals(Status.REDIRECTION_SEE_OTHER, wrapped.getStatus()); + + wrapper.redirectTemporary("http://localhost/temp"); + assertEquals(Status.REDIRECTION_TEMPORARY, wrapped.getStatus()); + + wrapper.redirectTemporary(new Reference("http://localhost/temp2")); + assertEquals(Status.REDIRECTION_TEMPORARY, wrapped.getStatus()); + } +} diff --git a/org.restlet/src/test/java/org/restlet/util/WrapperRestletTestCase.java b/org.restlet/src/test/java/org/restlet/util/WrapperRestletTestCase.java new file mode 100644 index 0000000000..8f28e11465 --- /dev/null +++ b/org.restlet/src/test/java/org/restlet/util/WrapperRestletTestCase.java @@ -0,0 +1,64 @@ +/** + * 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.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; + +/** Unit tests for {@link WrapperRestlet}. */ +class WrapperRestletTestCase { + + @Test + void delegatesAllOperationsToWrappedRestlet() throws Exception { + Restlet wrapped = new Restlet() {}; + WrapperRestlet wrapper = new WrapperRestlet(wrapped); + + Context context = new Context(); + wrapper.setContext(context); + assertSame(context, wrapper.getContext()); + assertSame(wrapped.getContext(), wrapper.getContext()); + + wrapper.setAuthor("author"); + assertEquals("author", wrapper.getAuthor()); + + wrapper.setDescription("description"); + assertEquals("description", wrapper.getDescription()); + + wrapper.setName("name"); + assertEquals("name", wrapper.getName()); + + wrapper.setOwner("owner"); + assertEquals("owner", wrapper.getOwner()); + + assertSame(wrapped.getLogger(), wrapper.getLogger()); + assertSame(wrapped.getApplication(), wrapper.getApplication()); + + assertFalse(wrapper.isStarted()); + assertTrue(wrapper.isStopped()); + + wrapper.start(); + assertTrue(wrapper.isStarted()); + assertFalse(wrapper.isStopped()); + + wrapper.stop(); + assertTrue(wrapper.isStopped()); + + Request request = new Request(); + Response response = new Response(request); + wrapper.handle(request, response); + } +} diff --git a/pom.xml b/pom.xml index 61a5d6d781..07cf8b84be 100644 --- a/pom.xml +++ b/pom.xml @@ -280,7 +280,7 @@ org.jacoco jacoco-maven-plugin - 0.8.12 + 0.8.15 @@ -307,7 +307,7 @@ INSTRUCTION COVEREDRATIO - 0.50 + 0.70