diff --git a/kuri-bind/src/commonTest/kotlin/org/dexpace/kuri/bind/BindOptionsTest.kt b/kuri-bind/src/commonTest/kotlin/org/dexpace/kuri/bind/BindOptionsTest.kt index 4b54a13..67e5793 100644 --- a/kuri-bind/src/commonTest/kotlin/org/dexpace/kuri/bind/BindOptionsTest.kt +++ b/kuri-bind/src/commonTest/kotlin/org/dexpace/kuri/bind/BindOptionsTest.kt @@ -21,4 +21,9 @@ class BindOptionsTest { assertFailsWith { BindOptions(maxDepth = 0) } assertFailsWith { BindOptions(maxDepth = 513) } } + + @Test + fun `accepts a maxDepth of exactly 512`() { + assertEquals(512, BindOptions(maxDepth = 512).maxDepth) + } } diff --git a/kuri-bind/src/jvmMain/kotlin/org/dexpace/kuri/bind/KuriBind.kt b/kuri-bind/src/jvmMain/kotlin/org/dexpace/kuri/bind/KuriBind.kt index c027460..3c131b7 100644 --- a/kuri-bind/src/jvmMain/kotlin/org/dexpace/kuri/bind/KuriBind.kt +++ b/kuri-bind/src/jvmMain/kotlin/org/dexpace/kuri/bind/KuriBind.kt @@ -248,7 +248,12 @@ private fun requireRoot( Profile.URI -> target::class.hasAnnotation() } if (!marked) { - throw KuriBindException("root ${target::class.simpleName} is not annotated @${profile.name.lowercase()}") + val markerName = + when (profile) { + Profile.URL -> "Url" + Profile.URI -> "Uri" + } + throw KuriBindException("root ${target::class.simpleName} is not annotated @$markerName") } } diff --git a/kuri-bind/src/jvmMain/kotlin/org/dexpace/kuri/bind/internal/BuilderSink.kt b/kuri-bind/src/jvmMain/kotlin/org/dexpace/kuri/bind/internal/BuilderSink.kt index 4121a92..2e4b932 100644 --- a/kuri-bind/src/jvmMain/kotlin/org/dexpace/kuri/bind/internal/BuilderSink.kt +++ b/kuri-bind/src/jvmMain/kotlin/org/dexpace/kuri/bind/internal/BuilderSink.kt @@ -18,8 +18,10 @@ private fun escapeLiteralPercent(text: String): String = text.replace("%", "%25" /** * The narrow set of builder operations the binder needs, abstracting the two profiles. * - * All profile-specific differences — userinfo split vs. verbatim-join, and fragment encoding — live - * in the concrete implementations rather than in the binder. Callers pass decoded (raw) values; each + * [UrlBuilderSink] and [UriBuilderSink] both split-encode-then-join userinfo, forwarding to their + * underlying builder's `username`/`password` setters identically; there is no verbatim-join path in + * either sink. Fragment encoding is likewise shared, implemented once as the [fragmentDecoded] default + * on this interface rather than overridden per profile. Callers pass decoded (raw) values; each * implementation decides how to encode before forwarding to the underlying builder. */ internal interface BuilderSink { diff --git a/kuri-bind/src/jvmTest/java/org/dexpace/kuri/bind/internal/JavaBean.java b/kuri-bind/src/jvmTest/java/org/dexpace/kuri/bind/internal/JavaBean.java index a54be6e..6a8fef6 100644 --- a/kuri-bind/src/jvmTest/java/org/dexpace/kuri/bind/internal/JavaBean.java +++ b/kuri-bind/src/jvmTest/java/org/dexpace/kuri/bind/internal/JavaBean.java @@ -8,9 +8,12 @@ import org.dexpace.kuri.bind.Scheme; /** - * A plain Java bean used to verify that {@code KotlinReflectMemberScanner} discovers annotated - * getter functions ({@code get*}/{@code is*}) for types that do not expose properties via - * Kotlin's {@code memberProperties}. The annotations live on the getters, not on the fields. + * A plain Java bean used to verify that {@code KotlinReflectMemberScanner} merges getter-level + * annotations onto the corresponding {@code memberProperties} entry. Kotlin-reflect exposes a + * {@code scheme}/{@code host} property for this bean, but with an empty annotation list — the + * {@code @Scheme}/{@code @Host} annotations live only on the getter functions — so discovery of them + * exercises the getter-to-property annotation-merge branch rather than reading annotations straight + * off the property. */ public final class JavaBean { private final String scheme; diff --git a/kuri-bind/src/jvmTest/kotlin/org/dexpace/kuri/bind/IntegrationTest.kt b/kuri-bind/src/jvmTest/kotlin/org/dexpace/kuri/bind/IntegrationTest.kt index 43ea906..f46f4a1 100644 --- a/kuri-bind/src/jvmTest/kotlin/org/dexpace/kuri/bind/IntegrationTest.kt +++ b/kuri-bind/src/jvmTest/kotlin/org/dexpace/kuri/bind/IntegrationTest.kt @@ -694,7 +694,7 @@ class IntegrationTest { @Test fun `rejects a uri root bound through the url entry point`() { val failure = assertFailsWith { KuriBind.toUrl(UriOnly()) } - assertTrue(failure.message.orEmpty().contains("@url")) + assertTrue(failure.message.orEmpty().contains("@Url")) } @Test diff --git a/kuri-bind/src/jvmTest/kotlin/org/dexpace/kuri/bind/KuriBindTest.kt b/kuri-bind/src/jvmTest/kotlin/org/dexpace/kuri/bind/KuriBindTest.kt index e434b65..194e0f8 100644 --- a/kuri-bind/src/jvmTest/kotlin/org/dexpace/kuri/bind/KuriBindTest.kt +++ b/kuri-bind/src/jvmTest/kotlin/org/dexpace/kuri/bind/KuriBindTest.kt @@ -85,7 +85,7 @@ class KuriBindTest { @Test fun `toUrl throws when the root lacks the url marker`() { val failure = assertFailsWith { KuriBind.toUrl(NoRootMarker("x")) } - assertEquals(true, failure.message?.contains("@url")) + assertEquals(true, failure.message?.contains("@Url")) } @Test @@ -98,7 +98,7 @@ class KuriBindTest { @Test fun `toUri throws when a url root is bound through the uri entry point`() { val failure = assertFailsWith { KuriBind.toUri(Absolute(host = "example.com")) } - assertEquals(true, failure.message?.contains("@uri")) + assertEquals(true, failure.message?.contains("@Uri")) } @Test diff --git a/kuri-bind/src/jvmTest/kotlin/org/dexpace/kuri/bind/internal/BuilderSinkTest.kt b/kuri-bind/src/jvmTest/kotlin/org/dexpace/kuri/bind/internal/BuilderSinkTest.kt index 8ca8b0f..68cbcee 100644 --- a/kuri-bind/src/jvmTest/kotlin/org/dexpace/kuri/bind/internal/BuilderSinkTest.kt +++ b/kuri-bind/src/jvmTest/kotlin/org/dexpace/kuri/bind/internal/BuilderSinkTest.kt @@ -43,7 +43,7 @@ class BuilderSinkTest { assertEquals("https", url.scheme) } - // UriBuilderSink — verbatim-join userinfo and fragment encoding + // UriBuilderSink — split-encode-then-join userinfo and fragment encoding @Test fun `uri sink joins userinfo with encoded parts and a literal colon`() {