From 7914a8320aa8d08318b4ebef9e6de583e2a7ec9c Mon Sep 17 00:00:00 2001 From: OmarAlJarrah Date: Mon, 15 Jun 2026 22:30:08 +0300 Subject: [PATCH 1/3] feat: resolve HTTP log level from a caller-supplied env var Add HttpLogLevel.fromEnv(key, source, default) so a log level can be driven from an environment variable without the toolkit baking in a variable name. The caller supplies both the key (e.g. a generated client passes its own product's variable) and the source, reusing the existing Configuration env-source seam so the lookup is injectable and tests never touch the real process environment. Parsing is tolerant: values match the enum names case-insensitively and after trimming surrounding whitespace. An unset key, an empty value (which Configuration treats as absent), or an unrecognized value falls back to the supplied default, which itself defaults to NONE so logging stays off unless explicitly opted into. --- sdk-core/api/sdk-core.api | 9 ++ .../core/http/pipeline/steps/HttpLogLevel.kt | 39 ++++++++ .../http/pipeline/steps/HttpLogLevelTest.kt | 98 +++++++++++++++++++ 3 files changed, 146 insertions(+) create mode 100644 sdk-core/src/test/kotlin/org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevelTest.kt diff --git a/sdk-core/api/sdk-core.api b/sdk-core/api/sdk-core.api index de3a5d14..e4f4a429 100644 --- a/sdk-core/api/sdk-core.api +++ b/sdk-core/api/sdk-core.api @@ -829,13 +829,22 @@ public final class org/dexpace/sdk/core/http/pipeline/steps/HttpInstrumentationO public final class org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel : java/lang/Enum { public static final field BODY_AND_HEADERS Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel; + public static final field Companion Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel$Companion; public static final field HEADERS Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel; public static final field NONE Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel; + public static final fun fromEnv (Ljava/lang/String;Lorg/dexpace/sdk/core/config/Configuration;)Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel; + public static final fun fromEnv (Ljava/lang/String;Lorg/dexpace/sdk/core/config/Configuration;Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel;)Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel; public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel; public static fun values ()[Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel; } +public final class org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel$Companion { + public final fun fromEnv (Ljava/lang/String;Lorg/dexpace/sdk/core/config/Configuration;)Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel; + public final fun fromEnv (Ljava/lang/String;Lorg/dexpace/sdk/core/config/Configuration;Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel;)Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel; + public static synthetic fun fromEnv$default (Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel$Companion;Ljava/lang/String;Lorg/dexpace/sdk/core/config/Configuration;Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel;ILjava/lang/Object;)Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel; +} + public final class org/dexpace/sdk/core/http/pipeline/steps/HttpRedirectCondition { public fun (Lorg/dexpace/sdk/core/http/response/Response;ILjava/util/Set;)V public final fun component1 ()Lorg/dexpace/sdk/core/http/response/Response; diff --git a/sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel.kt b/sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel.kt index 5e75cf26..e8aeb247 100644 --- a/sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel.kt +++ b/sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel.kt @@ -7,6 +7,9 @@ package org.dexpace.sdk.core.http.pipeline.steps +import org.dexpace.sdk.core.config.Configuration +import java.util.Locale + /** * Granularity of HTTP logging emitted by [InstrumentationStep]. * @@ -30,4 +33,40 @@ public enum class HttpLogLevel { * See [HttpInstrumentationOptions] for the streaming and async-completion-thread caveats. */ BODY_AND_HEADERS, + ; + + public companion object { + /** + * Resolves a log level from the environment-variable [key], reading through the testable + * env-source seam of [source]. + * + * The SDK is a toolkit, not a product, so it deliberately bakes in **no** default key — + * the caller (e.g. a generated client) supplies its own product's variable name, and + * [source] supplies the lookup. Pass a [Configuration] built with + * [org.dexpace.sdk.core.config.ConfigurationBuilder.envSource] to inject a hermetic env + * in tests; in production a [Configuration] backed by `System.getenv` is the natural fit. + * + * Parsing is tolerant: the resolved value is matched against the enum names + * case-insensitively and after trimming surrounding whitespace (so `headers`, + * `HEADERS`, and ` Headers ` all resolve to [HEADERS]). When the key is unset (or set + * to an empty string, which [Configuration] treats as absent) or holds an unrecognized + * value, [default] is returned. [default] itself defaults to [NONE] — logging stays off + * unless explicitly opted into. + */ + @JvmStatic + @JvmOverloads + public fun fromEnv( + key: String, + source: Configuration, + default: HttpLogLevel = NONE, + ): HttpLogLevel { + val raw = source.get(key) ?: return default + return when (raw.trim().uppercase(Locale.US)) { + "NONE" -> NONE + "HEADERS" -> HEADERS + "BODY_AND_HEADERS" -> BODY_AND_HEADERS + else -> default + } + } + } } diff --git a/sdk-core/src/test/kotlin/org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevelTest.kt b/sdk-core/src/test/kotlin/org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevelTest.kt new file mode 100644 index 00000000..dc0e4aeb --- /dev/null +++ b/sdk-core/src/test/kotlin/org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevelTest.kt @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2026 dexpace and Omar Aljarrah + * + * Licensed under the MIT License. See LICENSE in the project root. + * SPDX-License-Identifier: MIT + */ + +package org.dexpace.sdk.core.http.pipeline.steps + +import org.dexpace.sdk.core.config.ConfigurationBuilder +import kotlin.test.Test +import kotlin.test.assertEquals + +class HttpLogLevelTest { + /** Builds a [org.dexpace.sdk.core.config.Configuration] whose env layer returns [entries] and nothing else. */ + private fun configWithEnv(vararg entries: Pair) = + ConfigurationBuilder() + .envSource { name -> entries.firstOrNull { it.first == name }?.second } + .propsSource { null } + .build() + + // ----- Known value resolves (case-insensitive) ----- + + @Test + fun `known key resolves to the matching level`() { + val cfg = configWithEnv("MY_PRODUCT_LOG_LEVEL" to "HEADERS") + assertEquals( + HttpLogLevel.HEADERS, + HttpLogLevel.fromEnv("MY_PRODUCT_LOG_LEVEL", cfg, HttpLogLevel.NONE), + ) + } + + @Test + fun `value resolves case-insensitively`() { + val cfg = configWithEnv("MY_PRODUCT_LOG_LEVEL" to "body_and_headers") + assertEquals( + HttpLogLevel.BODY_AND_HEADERS, + HttpLogLevel.fromEnv("MY_PRODUCT_LOG_LEVEL", cfg, HttpLogLevel.NONE), + ) + } + + @Test + fun `value resolves with surrounding whitespace trimmed`() { + val cfg = configWithEnv("MY_PRODUCT_LOG_LEVEL" to " Headers ") + assertEquals( + HttpLogLevel.HEADERS, + HttpLogLevel.fromEnv("MY_PRODUCT_LOG_LEVEL", cfg, HttpLogLevel.NONE), + ) + } + + @Test + fun `each level name round-trips`() { + HttpLogLevel.entries.forEach { level -> + val cfg = configWithEnv("LL" to level.name) + assertEquals(level, HttpLogLevel.fromEnv("LL", cfg, HttpLogLevel.NONE)) + } + } + + // ----- Unset key falls back to the supplied default ----- + + @Test + fun `unset key returns the supplied default`() { + val cfg = configWithEnv() // nothing set + assertEquals( + HttpLogLevel.HEADERS, + HttpLogLevel.fromEnv("MY_PRODUCT_LOG_LEVEL", cfg, HttpLogLevel.HEADERS), + ) + } + + @Test + fun `empty env value returns the supplied default`() { + // An empty env var (EXAMPLE=) is treated as absent by Configuration, so the default applies. + val cfg = configWithEnv("MY_PRODUCT_LOG_LEVEL" to "") + assertEquals( + HttpLogLevel.BODY_AND_HEADERS, + HttpLogLevel.fromEnv("MY_PRODUCT_LOG_LEVEL", cfg, HttpLogLevel.BODY_AND_HEADERS), + ) + } + + // ----- Unrecognized value falls back to the supplied default ----- + + @Test + fun `unrecognized value returns the supplied default`() { + val cfg = configWithEnv("MY_PRODUCT_LOG_LEVEL" to "VERBOSE") + assertEquals( + HttpLogLevel.NONE, + HttpLogLevel.fromEnv("MY_PRODUCT_LOG_LEVEL", cfg, HttpLogLevel.NONE), + ) + } + + // ----- Default of the default-defaulted overload ----- + + @Test + fun `default parameter is NONE when omitted`() { + val cfg = configWithEnv() // nothing set + assertEquals(HttpLogLevel.NONE, HttpLogLevel.fromEnv("MY_PRODUCT_LOG_LEVEL", cfg)) + } +} From 64e5a05822d1560c6b5647f52c688029660f88bd Mon Sep 17 00:00:00 2001 From: OmarAlJarrah Date: Tue, 16 Jun 2026 22:22:46 +0300 Subject: [PATCH 2/3] docs: clarify HttpLogLevel.fromEnv layering and use enum-driven lookup The fromEnv KDoc described env-only resolution, but the value is read through Configuration.get, which layers override -> env var -> normalized system property -> default. Note this so callers aren't surprised when a system property supplies the value. Replace the hardcoded when over level-name string literals with a lookup over HttpLogLevel.entries so a newly added enum constant resolves without a matching branch edit. Add a test pinning that a whitespace-only value falls back to the supplied default. --- .../core/http/pipeline/steps/HttpLogLevel.kt | 18 ++++++++++-------- .../http/pipeline/steps/HttpLogLevelTest.kt | 11 +++++++++++ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel.kt b/sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel.kt index e8aeb247..7c5bf867 100644 --- a/sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel.kt +++ b/sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel.kt @@ -37,8 +37,14 @@ public enum class HttpLogLevel { public companion object { /** - * Resolves a log level from the environment-variable [key], reading through the testable - * env-source seam of [source]. + * Resolves a log level from the configuration [key], reading through the testable + * config seam of [source]. + * + * Despite the name, resolution is not env-only: [source] is consulted via + * [Configuration.get], which applies the full layering — explicit override -> environment + * variable -> normalized system property -> default. So the value may legitimately come + * from an override or from a system property (e.g. the key `MY_PRODUCT_LOG_LEVEL` also + * matches the `my.product.log.level` system property), not strictly the environment. * * The SDK is a toolkit, not a product, so it deliberately bakes in **no** default key — * the caller (e.g. a generated client) supplies its own product's variable name, and @@ -61,12 +67,8 @@ public enum class HttpLogLevel { default: HttpLogLevel = NONE, ): HttpLogLevel { val raw = source.get(key) ?: return default - return when (raw.trim().uppercase(Locale.US)) { - "NONE" -> NONE - "HEADERS" -> HEADERS - "BODY_AND_HEADERS" -> BODY_AND_HEADERS - else -> default - } + val name = raw.trim().uppercase(Locale.US) + return entries.firstOrNull { it.name == name } ?: default } } } diff --git a/sdk-core/src/test/kotlin/org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevelTest.kt b/sdk-core/src/test/kotlin/org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevelTest.kt index dc0e4aeb..40b4d909 100644 --- a/sdk-core/src/test/kotlin/org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevelTest.kt +++ b/sdk-core/src/test/kotlin/org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevelTest.kt @@ -77,6 +77,17 @@ class HttpLogLevelTest { ) } + @Test + fun `whitespace-only value returns the supplied default`() { + // Configuration only treats an exactly-empty env string as absent, so a whitespace-only + // value is returned as-is; fromEnv's own trim collapses it to "" and falls to the default. + val cfg = configWithEnv("MY_PRODUCT_LOG_LEVEL" to " ") + assertEquals( + HttpLogLevel.HEADERS, + HttpLogLevel.fromEnv("MY_PRODUCT_LOG_LEVEL", cfg, HttpLogLevel.HEADERS), + ) + } + // ----- Unrecognized value falls back to the supplied default ----- @Test From 6e41c1581e1675aa97582bef793f1053fac66c78 Mon Sep 17 00:00:00 2001 From: OmarAlJarrah Date: Tue, 16 Jun 2026 23:07:47 +0300 Subject: [PATCH 3/3] refactor: rename HttpLogLevel.fromEnv to fromConfiguration and test the non-env layers The factory resolves through the full Configuration layering (override -> env -> system property), not just the environment, so fromEnv was a misnomer that the KDoc had to walk back. Rename it to fromConfiguration to match the existing ProxyOptions.fromConfiguration(Configuration) factory and tighten the doc accordingly. Add tests covering resolution from the system-property layer and an explicit override winning over a conflicting env value, which the prior env-only suite never exercised despite the documented layering. --- sdk-core/api/sdk-core.api | 10 ++-- .../core/http/pipeline/steps/HttpLogLevel.kt | 15 +++--- .../http/pipeline/steps/HttpLogLevelTest.kt | 52 +++++++++++++++---- 3 files changed, 54 insertions(+), 23 deletions(-) diff --git a/sdk-core/api/sdk-core.api b/sdk-core/api/sdk-core.api index e4f4a429..1c5f1c77 100644 --- a/sdk-core/api/sdk-core.api +++ b/sdk-core/api/sdk-core.api @@ -832,17 +832,17 @@ public final class org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel : java/ public static final field Companion Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel$Companion; public static final field HEADERS Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel; public static final field NONE Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel; - public static final fun fromEnv (Ljava/lang/String;Lorg/dexpace/sdk/core/config/Configuration;)Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel; - public static final fun fromEnv (Ljava/lang/String;Lorg/dexpace/sdk/core/config/Configuration;Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel;)Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel; + public static final fun fromConfiguration (Ljava/lang/String;Lorg/dexpace/sdk/core/config/Configuration;)Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel; + public static final fun fromConfiguration (Ljava/lang/String;Lorg/dexpace/sdk/core/config/Configuration;Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel;)Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel; public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel; public static fun values ()[Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel; } public final class org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel$Companion { - public final fun fromEnv (Ljava/lang/String;Lorg/dexpace/sdk/core/config/Configuration;)Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel; - public final fun fromEnv (Ljava/lang/String;Lorg/dexpace/sdk/core/config/Configuration;Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel;)Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel; - public static synthetic fun fromEnv$default (Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel$Companion;Ljava/lang/String;Lorg/dexpace/sdk/core/config/Configuration;Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel;ILjava/lang/Object;)Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel; + public final fun fromConfiguration (Ljava/lang/String;Lorg/dexpace/sdk/core/config/Configuration;)Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel; + public final fun fromConfiguration (Ljava/lang/String;Lorg/dexpace/sdk/core/config/Configuration;Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel;)Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel; + public static synthetic fun fromConfiguration$default (Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel$Companion;Ljava/lang/String;Lorg/dexpace/sdk/core/config/Configuration;Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel;ILjava/lang/Object;)Lorg/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel; } public final class org/dexpace/sdk/core/http/pipeline/steps/HttpRedirectCondition { diff --git a/sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel.kt b/sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel.kt index 7c5bf867..7ae22378 100644 --- a/sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel.kt +++ b/sdk-core/src/main/kotlin/org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevel.kt @@ -37,14 +37,13 @@ public enum class HttpLogLevel { public companion object { /** - * Resolves a log level from the configuration [key], reading through the testable - * config seam of [source]. + * Resolves a log level by looking [key] up in [source]. * - * Despite the name, resolution is not env-only: [source] is consulted via - * [Configuration.get], which applies the full layering — explicit override -> environment - * variable -> normalized system property -> default. So the value may legitimately come - * from an override or from a system property (e.g. the key `MY_PRODUCT_LOG_LEVEL` also - * matches the `my.product.log.level` system property), not strictly the environment. + * [source] is consulted via [Configuration.get], so the value goes through the full + * layering — explicit override -> environment variable -> normalized system property -> + * default. The value may come from any of those layers: the key `MY_PRODUCT_LOG_LEVEL` + * also matches the `my.product.log.level` system property, and an explicit override wins + * over both. * * The SDK is a toolkit, not a product, so it deliberately bakes in **no** default key — * the caller (e.g. a generated client) supplies its own product's variable name, and @@ -61,7 +60,7 @@ public enum class HttpLogLevel { */ @JvmStatic @JvmOverloads - public fun fromEnv( + public fun fromConfiguration( key: String, source: Configuration, default: HttpLogLevel = NONE, diff --git a/sdk-core/src/test/kotlin/org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevelTest.kt b/sdk-core/src/test/kotlin/org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevelTest.kt index 40b4d909..00b6874b 100644 --- a/sdk-core/src/test/kotlin/org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevelTest.kt +++ b/sdk-core/src/test/kotlin/org/dexpace/sdk/core/http/pipeline/steps/HttpLogLevelTest.kt @@ -26,7 +26,7 @@ class HttpLogLevelTest { val cfg = configWithEnv("MY_PRODUCT_LOG_LEVEL" to "HEADERS") assertEquals( HttpLogLevel.HEADERS, - HttpLogLevel.fromEnv("MY_PRODUCT_LOG_LEVEL", cfg, HttpLogLevel.NONE), + HttpLogLevel.fromConfiguration("MY_PRODUCT_LOG_LEVEL", cfg, HttpLogLevel.NONE), ) } @@ -35,7 +35,7 @@ class HttpLogLevelTest { val cfg = configWithEnv("MY_PRODUCT_LOG_LEVEL" to "body_and_headers") assertEquals( HttpLogLevel.BODY_AND_HEADERS, - HttpLogLevel.fromEnv("MY_PRODUCT_LOG_LEVEL", cfg, HttpLogLevel.NONE), + HttpLogLevel.fromConfiguration("MY_PRODUCT_LOG_LEVEL", cfg, HttpLogLevel.NONE), ) } @@ -44,7 +44,7 @@ class HttpLogLevelTest { val cfg = configWithEnv("MY_PRODUCT_LOG_LEVEL" to " Headers ") assertEquals( HttpLogLevel.HEADERS, - HttpLogLevel.fromEnv("MY_PRODUCT_LOG_LEVEL", cfg, HttpLogLevel.NONE), + HttpLogLevel.fromConfiguration("MY_PRODUCT_LOG_LEVEL", cfg, HttpLogLevel.NONE), ) } @@ -52,10 +52,42 @@ class HttpLogLevelTest { fun `each level name round-trips`() { HttpLogLevel.entries.forEach { level -> val cfg = configWithEnv("LL" to level.name) - assertEquals(level, HttpLogLevel.fromEnv("LL", cfg, HttpLogLevel.NONE)) + assertEquals(level, HttpLogLevel.fromConfiguration("LL", cfg, HttpLogLevel.NONE)) } } + // ----- Resolution honors the full Configuration layering, not just env ----- + + @Test + fun `value resolves from the system-property layer when the env is unset`() { + // Env unset; the property is looked up under the normalized name (MY_PRODUCT_LOG_LEVEL + // -> my.product.log.level). The resolved value must still feed the level parsing. + val cfg = + ConfigurationBuilder() + .envSource { null } + .propsSource { name -> if (name == "my.product.log.level") "HEADERS" else null } + .build() + assertEquals( + HttpLogLevel.HEADERS, + HttpLogLevel.fromConfiguration("MY_PRODUCT_LOG_LEVEL", cfg, HttpLogLevel.NONE), + ) + } + + @Test + fun `explicit override wins over a conflicting env value`() { + // Override is the top layer; it must take precedence over the env entry for the same key. + val cfg = + ConfigurationBuilder() + .put("MY_PRODUCT_LOG_LEVEL", "BODY_AND_HEADERS") + .envSource { name -> if (name == "MY_PRODUCT_LOG_LEVEL") "HEADERS" else null } + .propsSource { null } + .build() + assertEquals( + HttpLogLevel.BODY_AND_HEADERS, + HttpLogLevel.fromConfiguration("MY_PRODUCT_LOG_LEVEL", cfg, HttpLogLevel.NONE), + ) + } + // ----- Unset key falls back to the supplied default ----- @Test @@ -63,7 +95,7 @@ class HttpLogLevelTest { val cfg = configWithEnv() // nothing set assertEquals( HttpLogLevel.HEADERS, - HttpLogLevel.fromEnv("MY_PRODUCT_LOG_LEVEL", cfg, HttpLogLevel.HEADERS), + HttpLogLevel.fromConfiguration("MY_PRODUCT_LOG_LEVEL", cfg, HttpLogLevel.HEADERS), ) } @@ -73,18 +105,18 @@ class HttpLogLevelTest { val cfg = configWithEnv("MY_PRODUCT_LOG_LEVEL" to "") assertEquals( HttpLogLevel.BODY_AND_HEADERS, - HttpLogLevel.fromEnv("MY_PRODUCT_LOG_LEVEL", cfg, HttpLogLevel.BODY_AND_HEADERS), + HttpLogLevel.fromConfiguration("MY_PRODUCT_LOG_LEVEL", cfg, HttpLogLevel.BODY_AND_HEADERS), ) } @Test fun `whitespace-only value returns the supplied default`() { // Configuration only treats an exactly-empty env string as absent, so a whitespace-only - // value is returned as-is; fromEnv's own trim collapses it to "" and falls to the default. + // value is returned as-is; fromConfiguration's own trim collapses it to "" and falls to the default. val cfg = configWithEnv("MY_PRODUCT_LOG_LEVEL" to " ") assertEquals( HttpLogLevel.HEADERS, - HttpLogLevel.fromEnv("MY_PRODUCT_LOG_LEVEL", cfg, HttpLogLevel.HEADERS), + HttpLogLevel.fromConfiguration("MY_PRODUCT_LOG_LEVEL", cfg, HttpLogLevel.HEADERS), ) } @@ -95,7 +127,7 @@ class HttpLogLevelTest { val cfg = configWithEnv("MY_PRODUCT_LOG_LEVEL" to "VERBOSE") assertEquals( HttpLogLevel.NONE, - HttpLogLevel.fromEnv("MY_PRODUCT_LOG_LEVEL", cfg, HttpLogLevel.NONE), + HttpLogLevel.fromConfiguration("MY_PRODUCT_LOG_LEVEL", cfg, HttpLogLevel.NONE), ) } @@ -104,6 +136,6 @@ class HttpLogLevelTest { @Test fun `default parameter is NONE when omitted`() { val cfg = configWithEnv() // nothing set - assertEquals(HttpLogLevel.NONE, HttpLogLevel.fromEnv("MY_PRODUCT_LOG_LEVEL", cfg)) + assertEquals(HttpLogLevel.NONE, HttpLogLevel.fromConfiguration("MY_PRODUCT_LOG_LEVEL", cfg)) } }