Skip to content

Commit 69a2c38

Browse files
committed
fixed tests for RetrievalAPi
1 parent 71aa167 commit 69a2c38

File tree

2 files changed

+289
-5
lines changed

2 files changed

+289
-5
lines changed

src/OpenFeature.Contrib.Providers.AwsAppConfig/AppConfigRetrievalApi.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,18 @@ public class AppConfigRetrievalApi: IRetrievalApi
5151
/// </summary>
5252
private readonly MemoryCacheEntryOptions _cacheOptions;
5353

54-
54+
5555
/// <summary>
5656
/// Initializes a new instance of the AppConfigRetrievalApi class.
5757
/// </summary>
5858
/// <param name="appConfigDataClient">The AWS AppConfig Data client used to interact with the AWS AppConfig service.</param>
59+
/// <param name="memoryCache">MemoryCache instance used for caching. If null, Default is instantiated.</param>
5960
/// <param name="cacheDuration">Optional duration for which items should be cached. Defaults to 5 minutes if not specified.</param>
6061
/// <exception cref="ArgumentNullException">Thrown when appConfigDataClient is null.</exception>
61-
public AppConfigRetrievalApi(IAmazonAppConfigData appConfigDataClient, TimeSpan? cacheDuration = null)
62+
public AppConfigRetrievalApi(IAmazonAppConfigData appConfigDataClient, IMemoryCache memoryCache, TimeSpan? cacheDuration = null)
6263
{
6364
_appConfigDataClient = appConfigDataClient ?? throw new ArgumentNullException(nameof(appConfigDataClient));
64-
_memoryCache = new MemoryCache(new MemoryCacheOptions());
65+
_memoryCache = memoryCache ?? new MemoryCache(new MemoryCacheOptions());
6566

6667
// Default cache duration of 60 minutes if not specified
6768
_cacheOptions = new MemoryCacheEntryOptions()
@@ -79,9 +80,12 @@ public AppConfigRetrievalApi(IAmazonAppConfigData appConfigDataClient, TimeSpan?
7980
/// If AWS returns an empty configuration, it indicates no changes from the previous configuration,
8081
/// and the cached value will be returned if available.
8182
/// </remarks>
82-
/// <exception cref="Exception">Thrown when unable to connect to AWS or retrieve configuration.</exception>
83+
/// <exception cref="ArgumentException">Thrown when the provided profile is invalid.</exception>
84+
/// <exception cref="AmazonAppConfigDataException">Thrown when unable to connect to AWS or retrieve configuration.</exception>
8385
public async Task<GetLatestConfigurationResponse>GetLatestConfigurationAsync(FeatureFlagProfile profile)
8486
{
87+
if(!profile.IsValid) throw new ArgumentException("Invalid Feature Flag configuration profile");
88+
8589
var configKey = BuildConfigurationKey(profile);
8690
var sessionKey = BuildSessionKey(profile);
8791

@@ -164,7 +168,7 @@ public void Dispose()
164168
/// </remarks>
165169
private async Task<string> GetSessionToken(FeatureFlagProfile profile)
166170
{
167-
if(!profile.IsValid) throw new ArgumentException("Invalid Feature Flag configuration profile");
171+
if(!profile.IsValid) throw new ArgumentException("Invalid Feature Flag configuration profile");
168172

169173
return await _memoryCache.GetOrCreateAsync(BuildSessionKey(profile), async entry =>
170174
{
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
using Xunit;
2+
using Moq;
3+
using System;
4+
using OpenFeature.Model;
5+
using Amazon.AppConfigData;
6+
using Amazon.AppConfigData.Model;
7+
using System.Text;
8+
using OpenFeature.Contrib.Providers.AwsAppConfig;
9+
using System.Threading;
10+
using System.Threading.Tasks;
11+
using System.Collections.Generic;
12+
using System.IO;
13+
using Microsoft.Extensions.Caching.Memory;
14+
public class AppConfigRetrievalApiTests
15+
{
16+
private readonly Mock<IAmazonAppConfigData> _appConfigClientMock;
17+
private readonly IMemoryCache _memoryCache;
18+
private readonly AppConfigRetrievalApi _retrievalApi;
19+
private readonly string _jsonContent;
20+
21+
public AppConfigRetrievalApiTests()
22+
{
23+
_appConfigClientMock = new Mock<IAmazonAppConfigData>();
24+
_memoryCache = new MemoryCache(new MemoryCacheOptions());
25+
_retrievalApi = new AppConfigRetrievalApi(_appConfigClientMock.Object, _memoryCache);
26+
_jsonContent = System.IO.File.ReadAllText("test-data.json");
27+
}
28+
29+
[Fact]
30+
public async Task GetConfiguration_WhenSuccessful_ReturnsConfiguration()
31+
{
32+
// Arrange
33+
var profile = new FeatureFlagProfile{
34+
ApplicationIdentifier = "testApp",
35+
EnvironmentIdentifier = "testEnv",
36+
ConfigurationProfileIdentifier = "testConfig"
37+
};
38+
var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(_jsonContent));
39+
40+
var response = new GetLatestConfigurationResponse
41+
{
42+
Configuration = memoryStream,
43+
NextPollConfigurationToken = "nextToken"
44+
};
45+
46+
_appConfigClientMock
47+
.Setup(x => x.StartConfigurationSessionAsync(
48+
It.IsAny<StartConfigurationSessionRequest>(),
49+
It.IsAny<CancellationToken>()))
50+
.ReturnsAsync(new StartConfigurationSessionResponse{InitialConfigurationToken="initialToken"});
51+
52+
_appConfigClientMock
53+
.Setup(x => x.GetLatestConfigurationAsync(
54+
It.IsAny<GetLatestConfigurationRequest>(),
55+
It.IsAny<CancellationToken>()))
56+
.ReturnsAsync(response);
57+
58+
59+
60+
// Act
61+
var result = await _retrievalApi.GetLatestConfigurationAsync(profile);
62+
63+
// Assert
64+
Assert.NotNull(result);
65+
Assert.Equal(_jsonContent, await new StreamReader(result.Configuration).ReadToEndAsync());
66+
}
67+
68+
[Fact]
69+
public async Task GetConfiguration_WhenSuccessful_SetCorrectNextPollToken()
70+
{
71+
// Arrange
72+
var profile = new FeatureFlagProfile{
73+
ApplicationIdentifier = "testApp",
74+
EnvironmentIdentifier = "testEnv",
75+
ConfigurationProfileIdentifier = "testConfig"
76+
};
77+
var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(_jsonContent));
78+
79+
var response = new GetLatestConfigurationResponse
80+
{
81+
Configuration = memoryStream,
82+
NextPollConfigurationToken = "nextToken"
83+
};
84+
85+
_appConfigClientMock
86+
.Setup(x => x.StartConfigurationSessionAsync(
87+
It.IsAny<StartConfigurationSessionRequest>(),
88+
It.IsAny<CancellationToken>()))
89+
.ReturnsAsync(new StartConfigurationSessionResponse{InitialConfigurationToken="initialToken"});
90+
91+
_appConfigClientMock
92+
.Setup(x => x.GetLatestConfigurationAsync(
93+
It.IsAny<GetLatestConfigurationRequest>(),
94+
It.IsAny<CancellationToken>()))
95+
.ReturnsAsync(response);
96+
97+
98+
99+
// Act
100+
var result = await _retrievalApi.GetLatestConfigurationAsync(profile);
101+
102+
// Assert
103+
Assert.Equal("nextToken", result.NextPollConfigurationToken);
104+
// Verify that correct sessionToken is set for Next polling.
105+
Assert.Equal(result.NextPollConfigurationToken, _memoryCache.Get<string>($"session_token_{profile}"));
106+
}
107+
108+
[Fact]
109+
public async Task GetConfiguration_WhenSuccessful_CalledWithCorrectInitialToken()
110+
{
111+
// Arrange
112+
var profile = new FeatureFlagProfile{
113+
ApplicationIdentifier = "testApp",
114+
EnvironmentIdentifier = "testEnv",
115+
ConfigurationProfileIdentifier = "testConfig"
116+
};
117+
var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(_jsonContent));
118+
119+
var response = new GetLatestConfigurationResponse
120+
{
121+
Configuration = memoryStream,
122+
NextPollConfigurationToken = "nextToken"
123+
};
124+
125+
_appConfigClientMock
126+
.Setup(x => x.StartConfigurationSessionAsync(
127+
It.IsAny<StartConfigurationSessionRequest>(),
128+
It.IsAny<CancellationToken>()))
129+
.ReturnsAsync(new StartConfigurationSessionResponse{InitialConfigurationToken="initialToken"});
130+
131+
_appConfigClientMock
132+
.Setup(x => x.GetLatestConfigurationAsync(
133+
It.IsAny<GetLatestConfigurationRequest>(),
134+
It.IsAny<CancellationToken>()))
135+
.ReturnsAsync(response);
136+
137+
138+
139+
// Act
140+
var result = await _retrievalApi.GetLatestConfigurationAsync(profile);
141+
142+
// Assert
143+
_appConfigClientMock.Verify(x => x.GetLatestConfigurationAsync(
144+
It.Is<GetLatestConfigurationRequest>(r => r.ConfigurationToken == "initialToken"),
145+
It.IsAny<CancellationToken>()),
146+
Times.Once);
147+
}
148+
149+
[Theory]
150+
[InlineData(null, "env", "config")]
151+
[InlineData("app", null, "config")]
152+
[InlineData("app", "env", null)]
153+
public async Task GetConfiguration_WithNullParameters_ThrowsArgumentNullException(
154+
string application,
155+
string environment,
156+
string configuration)
157+
{
158+
// Arrange
159+
var profile = new FeatureFlagProfile
160+
{
161+
ApplicationIdentifier = application,
162+
EnvironmentIdentifier = environment,
163+
ConfigurationProfileIdentifier = configuration
164+
};
165+
166+
// Act & Assert
167+
await Assert.ThrowsAsync<ArgumentException>(() =>
168+
_retrievalApi.GetLatestConfigurationAsync(profile));
169+
}
170+
171+
[Fact]
172+
public async Task GetConfiguration_WhenServiceThrows_PropagatesException()
173+
{
174+
// Arrange
175+
var profile = new FeatureFlagProfile{
176+
ApplicationIdentifier = "testApp",
177+
EnvironmentIdentifier = "testEnv",
178+
ConfigurationProfileIdentifier = "testConfig"
179+
};
180+
181+
_appConfigClientMock
182+
.Setup(x => x.StartConfigurationSessionAsync(
183+
It.IsAny<StartConfigurationSessionRequest>(),
184+
It.IsAny<CancellationToken>()))
185+
.ReturnsAsync(new StartConfigurationSessionResponse{InitialConfigurationToken="initialToken"});
186+
187+
_appConfigClientMock
188+
.Setup(x => x.GetLatestConfigurationAsync(
189+
It.IsAny<GetLatestConfigurationRequest>(),
190+
It.IsAny<CancellationToken>()))
191+
.ThrowsAsync(new AmazonAppConfigDataException("Test exception"));
192+
193+
// Act & Assert
194+
await Assert.ThrowsAsync<AmazonAppConfigDataException>(() =>
195+
_retrievalApi.GetLatestConfigurationAsync(profile));
196+
}
197+
198+
[Fact]
199+
public async Task GetConfiguration_VerifiesCorrectParametersPassedToClient()
200+
{
201+
// Arrange
202+
var profile = new FeatureFlagProfile{
203+
ApplicationIdentifier = "testApp",
204+
EnvironmentIdentifier = "testEnv",
205+
ConfigurationProfileIdentifier = "testConfig"
206+
};
207+
208+
var response = new GetLatestConfigurationResponse
209+
{
210+
Configuration = new MemoryStream(),
211+
NextPollConfigurationToken = "nextToken"
212+
};
213+
214+
_appConfigClientMock
215+
.Setup(x => x.StartConfigurationSessionAsync(
216+
It.IsAny<StartConfigurationSessionRequest>(),
217+
It.IsAny<CancellationToken>()))
218+
.ReturnsAsync(new StartConfigurationSessionResponse{InitialConfigurationToken="initialToken"});
219+
220+
_appConfigClientMock
221+
.Setup(x => x.GetLatestConfigurationAsync(
222+
It.IsAny<GetLatestConfigurationRequest>(),
223+
It.IsAny<CancellationToken>()))
224+
.ReturnsAsync(response);
225+
226+
// Act
227+
await _retrievalApi.GetLatestConfigurationAsync(profile);
228+
229+
// Assert
230+
_appConfigClientMock.Verify(x => x.GetLatestConfigurationAsync(
231+
It.Is<GetLatestConfigurationRequest>(r =>
232+
r.ConfigurationToken == "initialToken"),
233+
It.IsAny<CancellationToken>()),
234+
Times.Once);
235+
}
236+
237+
[Fact]
238+
public async Task GetConfiguration_WhenCalledSecondTime_UsesNextPollConfigToken()
239+
{
240+
// Arrange
241+
var profile = new FeatureFlagProfile{
242+
ApplicationIdentifier = "testApp",
243+
EnvironmentIdentifier = "testEnv",
244+
ConfigurationProfileIdentifier = "testConfig"
245+
};
246+
var response = new GetLatestConfigurationResponse
247+
{
248+
Configuration = new MemoryStream(),
249+
NextPollConfigurationToken = "nextToken"
250+
};
251+
252+
_appConfigClientMock
253+
.Setup(x => x.StartConfigurationSessionAsync(
254+
It.IsAny<StartConfigurationSessionRequest>(),
255+
It.IsAny<CancellationToken>()))
256+
.ReturnsAsync(new StartConfigurationSessionResponse{InitialConfigurationToken="initialToken"});
257+
258+
_appConfigClientMock
259+
.Setup(x => x.GetLatestConfigurationAsync(
260+
It.IsAny<GetLatestConfigurationRequest>(),
261+
It.IsAny<CancellationToken>()))
262+
.ReturnsAsync(response);
263+
264+
// Act
265+
await _retrievalApi.GetLatestConfigurationAsync(profile);
266+
267+
await _retrievalApi.GetLatestConfigurationAsync(profile);
268+
269+
// Assert
270+
_appConfigClientMock.Verify(x => x.GetLatestConfigurationAsync(
271+
It.Is<GetLatestConfigurationRequest>(r => r.ConfigurationToken == "initialToken"),
272+
It.IsAny<CancellationToken>()),
273+
Times.Once);
274+
275+
_appConfigClientMock.Verify(x => x.GetLatestConfigurationAsync(
276+
It.Is<GetLatestConfigurationRequest>(r => r.ConfigurationToken == "nextToken"),
277+
It.IsAny<CancellationToken>()),
278+
Times.Once);
279+
}
280+
}

0 commit comments

Comments
 (0)