Add MicCheck.Client library with FeatureClient.IsEnabledAsync

Client-side SDK for checking feature flag state against the flags API,
with unit tests mocking HttpMessageHandler.
This commit is contained in:
James Wampler
2026-07-09 12:48:21 -07:00
parent 8262cd2f61
commit 3eebef56a0
7 changed files with 184 additions and 0 deletions
@@ -0,0 +1,79 @@
using System.Net;
using System.Text;
using Moq;
using Moq.Protected;
using NUnit.Framework;
namespace MicCheck.Client.Tests.Unit;
[TestFixture]
public class FeatureClientTests
{
[Test]
public async Task WhenFeatureIsEnabled_ThenIsEnabledAsyncReturnsTrue()
{
var client = CreateClient(HttpStatusCode.OK, """
[{"id":1,"feature":{"id":1,"name":"my-feature","type":"STANDARD"},"enabled":true,"featureStateValue":null}]
""");
var result = await client.IsEnabledAsync("my-feature");
Assert.That(result, Is.True);
}
[Test]
public async Task WhenFeatureIsDisabled_ThenIsEnabledAsyncReturnsFalse()
{
var client = CreateClient(HttpStatusCode.OK, """
[{"id":1,"feature":{"id":1,"name":"my-feature","type":"STANDARD"},"enabled":false,"featureStateValue":null}]
""");
var result = await client.IsEnabledAsync("my-feature");
Assert.That(result, Is.False);
}
[Test]
public async Task WhenFeatureNameDiffersOnlyByCase_ThenIsEnabledAsyncStillMatches()
{
var client = CreateClient(HttpStatusCode.OK, """
[{"id":1,"feature":{"id":1,"name":"My-Feature","type":"STANDARD"},"enabled":true,"featureStateValue":null}]
""");
var result = await client.IsEnabledAsync("my-feature");
Assert.That(result, Is.True);
}
[Test]
public async Task WhenFeatureIsNotFound_ThenIsEnabledAsyncReturnsProvidedDefaultValue()
{
var client = CreateClient(HttpStatusCode.OK, "[]");
var result = await client.IsEnabledAsync("missing-feature", defaultValue: true);
Assert.That(result, Is.True);
}
[Test]
public void WhenFeatureNameIsEmpty_ThenIsEnabledAsyncThrowsArgumentException()
{
var client = CreateClient(HttpStatusCode.OK, "[]");
Assert.That(async () => await client.IsEnabledAsync(" "), Throws.ArgumentException);
}
private static FeatureClient CreateClient(HttpStatusCode responseStatus, string responseBody)
{
var handler = new Mock<HttpMessageHandler>();
handler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage(responseStatus)
{
Content = new StringContent(responseBody, Encoding.UTF8, "application/json")
});
var httpClient = new HttpClient(handler.Object) { BaseAddress = new Uri("https://example.com") };
return new FeatureClient(httpClient);
}
}
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
<IsPackable>false</IsPackable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="NUnit" Version="4.5.1" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../../src/MicCheck.Client/MicCheck.Client.csproj" />
</ItemGroup>
</Project>