From 264d8c988a522b44ae8b8d086d990fb8dfa870e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Tue, 10 Dec 2024 08:37:21 +0000 Subject: [PATCH 01/11] Changing API client and added Context Propagator. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- src/OpenFeature/Api.cs | 63 ++++++++++++++++++- .../Model/ITransactionContextPropagator.cs | 26 ++++++++ 2 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 src/OpenFeature/Model/ITransactionContextPropagator.cs diff --git a/src/OpenFeature/Api.cs b/src/OpenFeature/Api.cs index fae9916b..f778c659 100644 --- a/src/OpenFeature/Api.cs +++ b/src/OpenFeature/Api.cs @@ -22,6 +22,8 @@ public sealed class Api : IEventBus private EventExecutor _eventExecutor = new EventExecutor(); private ProviderRepository _repository = new ProviderRepository(); private readonly ConcurrentStack _hooks = new ConcurrentStack(); + private ITransactionContextPropagator? _transactionContextPropagator; + private object _transactionContextPropagatorLock = new(); /// The reader/writer locks are not disposed because the singleton instance should never be disposed. private readonly ReaderWriterLockSlim _evaluationContextLock = new ReaderWriterLockSlim(); @@ -47,6 +49,7 @@ public async Task SetProviderAsync(FeatureProvider featureProvider) { this._eventExecutor.RegisterDefaultFeatureProvider(featureProvider); await this._repository.SetProviderAsync(featureProvider, this.GetContext(), this.AfterInitialization, this.AfterError).ConfigureAwait(false); + } /// @@ -85,7 +88,6 @@ public FeatureProvider GetProvider() /// Gets the feature provider with given domain /// /// An identifier which logically binds clients with providers - /// A provider associated with the given domain, if domain is empty or doesn't /// have a corresponding provider the default provider will be returned public FeatureProvider GetProvider(string domain) @@ -109,7 +111,6 @@ public FeatureProvider GetProvider(string domain) /// assigned to it the default provider will be returned /// /// An identifier which logically binds clients with providers - /// Metadata assigned to provider public Metadata? GetProviderMetadata(string domain) => this.GetProvider(domain).GetMetadata(); @@ -218,6 +219,64 @@ public EvaluationContext GetContext() } } + /// + /// Return the transaction context propagator. + /// + /// the registered transaction context propagator + public ITransactionContextPropagator? GetTransactionContextPropagator() + { + return this._transactionContextPropagator; + } + + /// + /// Sets the transaction context propagator. + /// + /// the transaction context propagator to be registered + /// Transaction context propagator cannot be null + public void SetTransactionContextPropagator(ITransactionContextPropagator transactionContextPropagator) + { + if (transactionContextPropagator == null) + { + throw new ArgumentNullException(nameof(transactionContextPropagator), + "Transaction context propagator cannot be null"); + } + + lock (this._transactionContextPropagatorLock) + { + this._transactionContextPropagator = transactionContextPropagator; + } + } + + /// + /// Returns the currently defined transaction context using the registered transaction context propagator. + /// + /// The current transaction context + public EvaluationContext? GetTransactionContext() + { + return this._transactionContextPropagator?.GetTransactionContext(); + } + + /// + /// Sets the transaction context using the registered transaction context propagator. + /// + /// The to set + /// Transaction context propagator is not set. + /// Evaluation context cannot be null + public void SetTransactionContext(EvaluationContext evaluationContext) + { + if (evaluationContext == null) + { + throw new ArgumentNullException(nameof(evaluationContext), "Evaluation context cannot be null"); + } + + if (this._transactionContextPropagator == null) + { + throw new InvalidOperationException("Transaction context propagator is not set"); + } + + this._transactionContextPropagator?.SetTransactionContext(evaluationContext); + } + /// /// /// Shut down and reset the current status of OpenFeature API. diff --git a/src/OpenFeature/Model/ITransactionContextPropagator.cs b/src/OpenFeature/Model/ITransactionContextPropagator.cs new file mode 100644 index 00000000..3fbc43c9 --- /dev/null +++ b/src/OpenFeature/Model/ITransactionContextPropagator.cs @@ -0,0 +1,26 @@ +namespace OpenFeature.Model; + +/// +/// is responsible for persisting a transactional context +/// for the duration of a single transaction. +/// Examples of potential transaction specific context include: a user id, user agent, IP. +/// Transaction context is merged with evaluation context prior to flag evaluation. +/// +/// +/// The precedence of merging context can be seen in +/// the specification. +/// +public interface ITransactionContextPropagator +{ + /// + /// Returns the currently defined transaction context using the registered transaction context propagator. + /// + /// The current transaction context + EvaluationContext GetTransactionContext(); + + /// + /// Sets the transaction context. + /// + /// The transaction context to be set + void SetTransactionContext(EvaluationContext evaluationContext); +} From 6a870ffa99dbd3367f155ae0b28188a096b03a9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Wed, 18 Dec 2024 10:35:34 +0000 Subject: [PATCH 02/11] Adding NoOpTransactionContextPropagator. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- src/OpenFeature/Api.cs | 8 ++++---- .../NoOpTransactionContextPropagator.cs | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 src/OpenFeature/NoOpTransactionContextPropagator.cs diff --git a/src/OpenFeature/Api.cs b/src/OpenFeature/Api.cs index f778c659..547d0216 100644 --- a/src/OpenFeature/Api.cs +++ b/src/OpenFeature/Api.cs @@ -22,7 +22,7 @@ public sealed class Api : IEventBus private EventExecutor _eventExecutor = new EventExecutor(); private ProviderRepository _repository = new ProviderRepository(); private readonly ConcurrentStack _hooks = new ConcurrentStack(); - private ITransactionContextPropagator? _transactionContextPropagator; + private ITransactionContextPropagator _transactionContextPropagator = new NoOpTransactionContextPropagator(); private object _transactionContextPropagatorLock = new(); /// The reader/writer locks are not disposed because the singleton instance should never be disposed. @@ -223,7 +223,7 @@ public EvaluationContext GetContext() /// Return the transaction context propagator. /// /// the registered transaction context propagator - public ITransactionContextPropagator? GetTransactionContextPropagator() + public ITransactionContextPropagator GetTransactionContextPropagator() { return this._transactionContextPropagator; } @@ -253,7 +253,7 @@ public void SetTransactionContextPropagator(ITransactionContextPropagator transa /// The current transaction context public EvaluationContext? GetTransactionContext() { - return this._transactionContextPropagator?.GetTransactionContext(); + return this._transactionContextPropagator.GetTransactionContext(); } /// @@ -274,7 +274,7 @@ public void SetTransactionContext(EvaluationContext evaluationContext) throw new InvalidOperationException("Transaction context propagator is not set"); } - this._transactionContextPropagator?.SetTransactionContext(evaluationContext); + this._transactionContextPropagator.SetTransactionContext(evaluationContext); } /// diff --git a/src/OpenFeature/NoOpTransactionContextPropagator.cs b/src/OpenFeature/NoOpTransactionContextPropagator.cs new file mode 100644 index 00000000..ad56693f --- /dev/null +++ b/src/OpenFeature/NoOpTransactionContextPropagator.cs @@ -0,0 +1,16 @@ +using OpenFeature.Model; + +namespace OpenFeature; + +internal class NoOpTransactionContextPropagator : ITransactionContextPropagator +{ + public EvaluationContext GetTransactionContext() + { + throw new System.NotImplementedException(); + } + + public void SetTransactionContext(EvaluationContext evaluationContext) + { + throw new System.NotImplementedException(); + } +} From 8bcfc99b6c11181bddee1f73332d3d589bfeae4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Wed, 18 Dec 2024 14:12:51 +0000 Subject: [PATCH 03/11] Making field readonly. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- src/OpenFeature/Api.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenFeature/Api.cs b/src/OpenFeature/Api.cs index 547d0216..cd8aa3ab 100644 --- a/src/OpenFeature/Api.cs +++ b/src/OpenFeature/Api.cs @@ -23,7 +23,7 @@ public sealed class Api : IEventBus private ProviderRepository _repository = new ProviderRepository(); private readonly ConcurrentStack _hooks = new ConcurrentStack(); private ITransactionContextPropagator _transactionContextPropagator = new NoOpTransactionContextPropagator(); - private object _transactionContextPropagatorLock = new(); + private readonly object _transactionContextPropagatorLock = new(); /// The reader/writer locks are not disposed because the singleton instance should never be disposed. private readonly ReaderWriterLockSlim _evaluationContextLock = new ReaderWriterLockSlim(); From 50613fedbbce484d00097f2f868abf1cac70cd3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Wed, 18 Dec 2024 14:14:18 +0000 Subject: [PATCH 04/11] Added an empty context return. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- src/OpenFeature/NoOpTransactionContextPropagator.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/OpenFeature/NoOpTransactionContextPropagator.cs b/src/OpenFeature/NoOpTransactionContextPropagator.cs index ad56693f..70f57cdc 100644 --- a/src/OpenFeature/NoOpTransactionContextPropagator.cs +++ b/src/OpenFeature/NoOpTransactionContextPropagator.cs @@ -6,11 +6,10 @@ internal class NoOpTransactionContextPropagator : ITransactionContextPropagator { public EvaluationContext GetTransactionContext() { - throw new System.NotImplementedException(); + return EvaluationContext.Empty; } public void SetTransactionContext(EvaluationContext evaluationContext) { - throw new System.NotImplementedException(); } } From b67b659c5092c1faeb69317d4bc212893a73c8d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Wed, 18 Dec 2024 14:25:10 +0000 Subject: [PATCH 05/11] Adding unit tests. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- src/OpenFeature/Api.cs | 5 -- test/OpenFeature.Tests/OpenFeatureTests.cs | 53 ++++++++++++++++++++++ 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/OpenFeature/Api.cs b/src/OpenFeature/Api.cs index cd8aa3ab..83ad12cc 100644 --- a/src/OpenFeature/Api.cs +++ b/src/OpenFeature/Api.cs @@ -269,11 +269,6 @@ public void SetTransactionContext(EvaluationContext evaluationContext) throw new ArgumentNullException(nameof(evaluationContext), "Evaluation context cannot be null"); } - if (this._transactionContextPropagator == null) - { - throw new InvalidOperationException("Transaction context propagator is not set"); - } - this._transactionContextPropagator.SetTransactionContext(evaluationContext); } diff --git a/test/OpenFeature.Tests/OpenFeatureTests.cs b/test/OpenFeature.Tests/OpenFeatureTests.cs index acc53b61..ae703a8d 100644 --- a/test/OpenFeature.Tests/OpenFeatureTests.cs +++ b/test/OpenFeature.Tests/OpenFeatureTests.cs @@ -1,3 +1,4 @@ +using System; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading.Tasks; @@ -244,5 +245,57 @@ public async Task OpenFeature_Should_Allow_Multiple_Client_Mapping() (await client1.GetBooleanValueAsync("test", false)).Should().BeTrue(); (await client2.GetBooleanValueAsync("test", false)).Should().BeFalse(); } + + [Fact] + public void SetTransactionContextPropagator_ShouldThrowArgumentNullException_WhenNullPropagatorIsPassed() + { + // Arrange + var api = Api.Instance; + + // Act & Assert + Assert.Throws(() => api.SetTransactionContextPropagator(null!)); + } + + [Fact] + public void SetTransactionContextPropagator_ShouldSetPropagator_WhenValidPropagatorIsPassed() + { + // Arrange + var api = Api.Instance; + var mockPropagator = Substitute.For(); + + // Act + api.SetTransactionContextPropagator(mockPropagator); + + // Assert + Assert.Equal(mockPropagator, api.GetTransactionContextPropagator()); + } + + [Fact] + public void SetTransactionContext_ShouldThrowArgumentNullException_WhenEvaluationContextIsNull() + { + // Arrange + var api = Api.Instance; + + // Act & Assert + Assert.Throws(() => api.SetTransactionContext(null!)); + } + + [Fact] + public void SetTransactionContext_ShouldSetTransactionContext_WhenValidEvaluationContextIsProvided() + { + // Arrange + var api = Api.Instance; + var evaluationContext = EvaluationContext.Empty; + var mockPropagator = Substitute.For(); + api.SetTransactionContextPropagator(mockPropagator); + + // Act + api.SetTransactionContext(evaluationContext); + var result = api.GetTransactionContext(); + + // Assert + mockPropagator.Received().SetTransactionContext(evaluationContext); + Assert.Equal(evaluationContext, result); + } } } From 02f93ddf40a32a8c25ecff4803a45f4cbe1ece31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Wed, 18 Dec 2024 14:30:05 +0000 Subject: [PATCH 06/11] Fix unit test. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- test/OpenFeature.Tests/OpenFeatureTests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/OpenFeature.Tests/OpenFeatureTests.cs b/test/OpenFeature.Tests/OpenFeatureTests.cs index ae703a8d..4d27be87 100644 --- a/test/OpenFeature.Tests/OpenFeatureTests.cs +++ b/test/OpenFeature.Tests/OpenFeatureTests.cs @@ -287,7 +287,9 @@ public void SetTransactionContext_ShouldSetTransactionContext_WhenValidEvaluatio var api = Api.Instance; var evaluationContext = EvaluationContext.Empty; var mockPropagator = Substitute.For(); + mockPropagator.GetTransactionContext().Returns(evaluationContext); api.SetTransactionContextPropagator(mockPropagator); + api.SetTransactionContext(evaluationContext); // Act api.SetTransactionContext(evaluationContext); From 8d452647947b741fae87cf5c1b4b0fda7562a79e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Wed, 18 Dec 2024 15:11:19 +0000 Subject: [PATCH 07/11] Adding clear transaction context to shutdown. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- src/OpenFeature/Api.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/OpenFeature/Api.cs b/src/OpenFeature/Api.cs index 83ad12cc..dc19498d 100644 --- a/src/OpenFeature/Api.cs +++ b/src/OpenFeature/Api.cs @@ -288,6 +288,7 @@ public async Task ShutdownAsync() { this._evaluationContext = EvaluationContext.Empty; this._hooks.Clear(); + this._transactionContextPropagator = new NoOpTransactionContextPropagator(); // TODO: make these lazy to avoid extra allocations on the common cleanup path? this._eventExecutor = new EventExecutor(); From d9ed7302606d8d74cc02041759b68aae738759b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Thu, 19 Dec 2024 14:38:47 +0000 Subject: [PATCH 08/11] Merging transaction context at evaluation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- src/OpenFeature/Api.cs | 2 +- src/OpenFeature/OpenFeatureClient.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/OpenFeature/Api.cs b/src/OpenFeature/Api.cs index dc19498d..15dfc39e 100644 --- a/src/OpenFeature/Api.cs +++ b/src/OpenFeature/Api.cs @@ -251,7 +251,7 @@ public void SetTransactionContextPropagator(ITransactionContextPropagator transa /// Returns the currently defined transaction context using the registered transaction context propagator. /// /// The current transaction context - public EvaluationContext? GetTransactionContext() + public EvaluationContext GetTransactionContext() { return this._transactionContextPropagator.GetTransactionContext(); } diff --git a/src/OpenFeature/OpenFeatureClient.cs b/src/OpenFeature/OpenFeatureClient.cs index e774c6b5..47654ff5 100644 --- a/src/OpenFeature/OpenFeatureClient.cs +++ b/src/OpenFeature/OpenFeatureClient.cs @@ -221,6 +221,7 @@ private async Task> EvaluateFlagAsync( evaluationContextBuilder.Merge(evaluationContext); evaluationContextBuilder.Merge(this.GetContext()); evaluationContextBuilder.Merge(context); + evaluationContextBuilder.Merge(Api.Instance.GetTransactionContext()); var allHooks = new List() .Concat(Api.Instance.GetHooks()) From 9f75a44ba0e63d3d76cbaea6b1e8077236010149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Thu, 19 Dec 2024 14:57:50 +0000 Subject: [PATCH 09/11] Adding an AsyncLocalTransactionContextPropagator. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- .../AsyncLocalTransactionContextPropagator.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/OpenFeature/AsyncLocalTransactionContextPropagator.cs diff --git a/src/OpenFeature/AsyncLocalTransactionContextPropagator.cs b/src/OpenFeature/AsyncLocalTransactionContextPropagator.cs new file mode 100644 index 00000000..7aec1c91 --- /dev/null +++ b/src/OpenFeature/AsyncLocalTransactionContextPropagator.cs @@ -0,0 +1,25 @@ +using System.Threading; +using OpenFeature.Model; + +namespace OpenFeature; + +/// +/// It is a no-op implementation of +/// It uses the to store the transaction context. +/// +public sealed class AsyncLocalTransactionContextPropagator : ITransactionContextPropagator +{ + private readonly AsyncLocal _transactionContext = new(); + + /// + public EvaluationContext GetTransactionContext() + { + return this._transactionContext.Value ?? EvaluationContext.Empty; + } + + /// + public void SetTransactionContext(EvaluationContext evaluationContext) + { + this._transactionContext.Value = evaluationContext; + } +} From 4146f0e88e3800e24f1543658d0eb9bc06f05286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Fri, 20 Dec 2024 08:30:18 +0000 Subject: [PATCH 10/11] Adding unit tests. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- ...cLocalTransactionContextPropagatorTests.cs | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 test/OpenFeature.Tests/AsyncLocalTransactionContextPropagatorTests.cs diff --git a/test/OpenFeature.Tests/AsyncLocalTransactionContextPropagatorTests.cs b/test/OpenFeature.Tests/AsyncLocalTransactionContextPropagatorTests.cs new file mode 100644 index 00000000..ae44aa4b --- /dev/null +++ b/test/OpenFeature.Tests/AsyncLocalTransactionContextPropagatorTests.cs @@ -0,0 +1,55 @@ +using OpenFeature.Model; +using Xunit; + +namespace OpenFeature.Tests; + +public class AsyncLocalTransactionContextPropagatorTests +{ + [Fact] + public void GetTransactionContext_ReturnsEmpty_WhenNoContextIsSet() + { + // Arrange + var propagator = new AsyncLocalTransactionContextPropagator(); + + // Act + var context = propagator.GetTransactionContext(); + + // Assert + Assert.Equal(EvaluationContext.Empty, context); + } + + [Fact] + public void SetTransactionContext_SetsAndGetsContextCorrectly() + { + // Arrange + var propagator = new AsyncLocalTransactionContextPropagator(); + var evaluationContext = EvaluationContext.Empty; + + // Act + propagator.SetTransactionContext(evaluationContext); + var context = propagator.GetTransactionContext(); + + // Assert + Assert.Equal(evaluationContext, context); + } + + [Fact] + public void SetTransactionContext_OverridesPreviousContext() + { + // Arrange + var propagator = new AsyncLocalTransactionContextPropagator(); + + var initialContext = EvaluationContext.Builder() + .Set("initial", "yes") + .Build(); + var newContext = EvaluationContext.Empty; + + // Act + propagator.SetTransactionContext(initialContext); + propagator.SetTransactionContext(newContext); + var context = propagator.GetTransactionContext(); + + // Assert + Assert.Equal(newContext, context); + } +} From 64f5c68186008cbb9361bb796fe67b07c18c468f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Silva?= <2493377+askpt@users.noreply.github.com> Date: Fri, 20 Dec 2024 08:33:31 +0000 Subject: [PATCH 11/11] Adding missing tests. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Silva <2493377+askpt@users.noreply.github.com> --- test/OpenFeature.Tests/OpenFeatureTests.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/OpenFeature.Tests/OpenFeatureTests.cs b/test/OpenFeature.Tests/OpenFeatureTests.cs index 4d27be87..b2c6d9de 100644 --- a/test/OpenFeature.Tests/OpenFeatureTests.cs +++ b/test/OpenFeature.Tests/OpenFeatureTests.cs @@ -299,5 +299,20 @@ public void SetTransactionContext_ShouldSetTransactionContext_WhenValidEvaluatio mockPropagator.Received().SetTransactionContext(evaluationContext); Assert.Equal(evaluationContext, result); } + + [Fact] + public void GetTransactionContext_ShouldReturnEmptyEvaluationContext_WhenNoPropagatorIsSet() + { + // Arrange + var api = Api.Instance; + var context = EvaluationContext.Builder().Set("status", "not-ready").Build(); + api.SetTransactionContext(context); + + // Act + var result = api.GetTransactionContext(); + + // Assert + Assert.Equal(EvaluationContext.Empty, result); + } } }