From 1d4609cde093fc6ecda6557774ac4e915060ab7e Mon Sep 17 00:00:00 2001 From: Tobias Ibounig Date: Fri, 19 Jun 2026 09:34:57 +0200 Subject: [PATCH] perf: return ImmutableContext.EMPTY when merging two empty contexts Signed-off-by: Tobias Ibounig --- .../dev/openfeature/sdk/ImmutableContext.java | 3 ++ .../openfeature/sdk/ImmutableContextTest.java | 35 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/main/java/dev/openfeature/sdk/ImmutableContext.java b/src/main/java/dev/openfeature/sdk/ImmutableContext.java index c8992db18..dd65538b8 100644 --- a/src/main/java/dev/openfeature/sdk/ImmutableContext.java +++ b/src/main/java/dev/openfeature/sdk/ImmutableContext.java @@ -88,6 +88,9 @@ public String getTargetingKey() { @Override public EvaluationContext merge(EvaluationContext overridingContext) { if (overridingContext == null || overridingContext.isEmpty()) { + if (this.isEmpty()) { + return ImmutableContext.EMPTY; + } return new ImmutableContext(this.asUnmodifiableMap()); } if (this.isEmpty()) { diff --git a/src/test/java/dev/openfeature/sdk/ImmutableContextTest.java b/src/test/java/dev/openfeature/sdk/ImmutableContextTest.java index a365b6fc2..08744fc3e 100644 --- a/src/test/java/dev/openfeature/sdk/ImmutableContextTest.java +++ b/src/test/java/dev/openfeature/sdk/ImmutableContextTest.java @@ -253,6 +253,41 @@ void nullTargetingKeyWithNullAttributesYieldsEmptyContext() { } } + @Nested + @DisplayName("ImmutableContext.merge() empty short-circuit") + class MergeEmpty { + + @Test + @DisplayName("merging two empty contexts returns the EMPTY singleton") + void mergingTwoEmptyContextsReturnsEmptySingleton() { + EvaluationContext result = new ImmutableContext().merge(new ImmutableContext()); + assertThat(result).isSameAs(ImmutableContext.EMPTY); + } + + @Test + @DisplayName("merging empty context with null returns the EMPTY singleton") + void mergingEmptyContextWithNullReturnsEmptySingleton() { + EvaluationContext result = new ImmutableContext().merge(null); + assertThat(result).isSameAs(ImmutableContext.EMPTY); + } + + @Test + @DisplayName("merging non-empty context with null does not return EMPTY") + void mergingNonEmptyContextWithNullDoesNotReturnEmpty() { + EvaluationContext result = new ImmutableContext("key").merge(null); + assertThat(result).isNotSameAs(ImmutableContext.EMPTY); + assertThat(result.getTargetingKey()).isEqualTo("key"); + } + + @Test + @DisplayName("merging non-empty context with empty override does not return EMPTY") + void mergingNonEmptyContextWithEmptyOverrideDoesNotReturnEmpty() { + EvaluationContext result = new ImmutableContext("key").merge(new ImmutableContext()); + assertThat(result).isNotSameAs(ImmutableContext.EMPTY); + assertThat(result.getTargetingKey()).isEqualTo("key"); + } + } + @Nested class Equals { ImmutableContext ctx = new ImmutableContext("c", Map.of("a", new Value("b")));