From c1c11f4775453a18b956d66556014962952339a3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 7 Nov 2025 18:09:49 +0000 Subject: [PATCH 01/10] Initial plan From d29045a794b62bd9339c35a4661fea5e2690975e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 7 Nov 2025 18:17:29 +0000 Subject: [PATCH 02/10] Add User-Agent header to all HTTP requests - Created SdkVersion utility class to retrieve SDK version at runtime - Added User-Agent header to FlagsmithClient.GetJson method - Added User-Agent header to AnalyticsProcessor.Flush method - Added comprehensive tests for User-Agent header functionality - Format: flagsmith-dotnet-sdk/8.0.2 (or unknown if version cannot be determined) Co-authored-by: khvn26 <979078+khvn26@users.noreply.github.com> --- Flagsmith.Client.Test/UserAgentTest.cs | 146 ++++++++++++++++++ .../AnalyticsProcessor.cs | 3 +- Flagsmith.FlagsmithClient/FlagsmithClient.cs | 3 +- Flagsmith.FlagsmithClient/SdkVersion.cs | 38 +++++ 4 files changed, 188 insertions(+), 2 deletions(-) create mode 100644 Flagsmith.Client.Test/UserAgentTest.cs create mode 100644 Flagsmith.FlagsmithClient/SdkVersion.cs diff --git a/Flagsmith.Client.Test/UserAgentTest.cs b/Flagsmith.Client.Test/UserAgentTest.cs new file mode 100644 index 0000000..39ae07d --- /dev/null +++ b/Flagsmith.Client.Test/UserAgentTest.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Moq; +using Xunit; + +namespace Flagsmith.FlagsmithClientTest +{ + public class UserAgentTest + { + [Fact] + public async Task TestUserAgentHeaderIsSentInGetEnvironmentFlags() + { + // Given + HttpRequestMessage capturedRequest = null; + + var httpClientMock = new Mock(); + httpClientMock.Setup(x => x.SendAsync(It.IsAny(), It.IsAny())) + .Callback((request, token) => + { + capturedRequest = request; + }) + .ReturnsAsync(new HttpResponseMessage + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent(Fixtures.ApiFlagResponse) + }); + + var config = new FlagsmithConfiguration + { + EnvironmentKey = Fixtures.ApiKey, + HttpClient = httpClientMock.Object + }; + + var client = new FlagsmithClient(config); + + // When + await client.GetEnvironmentFlags(); + + // Then + Assert.NotNull(capturedRequest); + Assert.True(capturedRequest.Headers.Contains("User-Agent")); + + var userAgentValues = capturedRequest.Headers.GetValues("User-Agent").ToList(); + Assert.Single(userAgentValues); + + var userAgent = userAgentValues[0]; + Assert.StartsWith("flagsmith-dotnet-sdk/", userAgent); + + // Verify it's either a version number or "unknown" + var versionPart = userAgent.Substring("flagsmith-dotnet-sdk/".Length); + Assert.True(versionPart == "unknown" || versionPart.Contains("."), + $"User-Agent should be 'flagsmith-dotnet-sdk/version' or 'flagsmith-dotnet-sdk/unknown', but was '{userAgent}'"); + } + + [Fact] + public async Task TestUserAgentHeaderIsSentInGetIdentityFlags() + { + // Given + HttpRequestMessage capturedRequest = null; + + var httpClientMock = new Mock(); + httpClientMock.Setup(x => x.SendAsync(It.IsAny(), It.IsAny())) + .Callback((request, token) => + { + capturedRequest = request; + }) + .ReturnsAsync(new HttpResponseMessage + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent(Fixtures.ApiIdentityResponse) + }); + + var config = new FlagsmithConfiguration + { + EnvironmentKey = Fixtures.ApiKey, + HttpClient = httpClientMock.Object + }; + + var client = new FlagsmithClient(config); + + // When + await client.GetIdentityFlags("test-identity"); + + // Then + Assert.NotNull(capturedRequest); + Assert.True(capturedRequest.Headers.Contains("User-Agent")); + + var userAgentValues = capturedRequest.Headers.GetValues("User-Agent").ToList(); + Assert.Single(userAgentValues); + Assert.StartsWith("flagsmith-dotnet-sdk/", userAgentValues[0]); + } + + [Fact] + public void TestSdkVersionReturnsConsistentValue() + { + // When + var userAgent1 = SdkVersion.GetUserAgent(); + var userAgent2 = SdkVersion.GetUserAgent(); + + // Then + Assert.Equal(userAgent1, userAgent2); + Assert.StartsWith("flagsmith-dotnet-sdk/", userAgent1); + } + + [Fact] + public async Task TestUserAgentHeaderIsSentInAnalyticsFlush() + { + // Given + HttpRequestMessage capturedRequest = null; + + var httpClientMock = new Mock(); + httpClientMock.Setup(x => x.SendAsync(It.IsAny(), It.IsAny())) + .Callback((request, token) => + { + capturedRequest = request; + }) + .ReturnsAsync(new HttpResponseMessage + { + StatusCode = HttpStatusCode.OK + }); + + var analyticsProcessor = new AnalyticsProcessor( + httpClientMock.Object, + Fixtures.ApiKey, + Fixtures.ApiUrl + ); + + // When + await analyticsProcessor.TrackFeature("test_feature"); + await analyticsProcessor.Flush(); + + // Then + Assert.NotNull(capturedRequest); + Assert.True(capturedRequest.Headers.Contains("User-Agent")); + + var userAgentValues = capturedRequest.Headers.GetValues("User-Agent").ToList(); + Assert.Single(userAgentValues); + Assert.StartsWith("flagsmith-dotnet-sdk/", userAgentValues[0]); + } + } +} diff --git a/Flagsmith.FlagsmithClient/AnalyticsProcessor.cs b/Flagsmith.FlagsmithClient/AnalyticsProcessor.cs index a4e0436..99f4201 100644 --- a/Flagsmith.FlagsmithClient/AnalyticsProcessor.cs +++ b/Flagsmith.FlagsmithClient/AnalyticsProcessor.cs @@ -75,7 +75,8 @@ private async Task FlushWithoutLock() { Headers = { - { "X-Environment-Key", _EnvironmentKey } + { "X-Environment-Key", _EnvironmentKey }, + { "User-Agent", SdkVersion.GetUserAgent() } }, Content = new StringContent(analyticsJson, Encoding.UTF8, "application/json") }; diff --git a/Flagsmith.FlagsmithClient/FlagsmithClient.cs b/Flagsmith.FlagsmithClient/FlagsmithClient.cs index 55ccf8d..d4c28f6 100644 --- a/Flagsmith.FlagsmithClient/FlagsmithClient.cs +++ b/Flagsmith.FlagsmithClient/FlagsmithClient.cs @@ -196,7 +196,8 @@ private async Task GetJson(HttpMethod method, string url, string? body = { Headers = { - { "X-Environment-Key", _config.EnvironmentKey } + { "X-Environment-Key", _config.EnvironmentKey }, + { "User-Agent", SdkVersion.GetUserAgent() } } }; _config.CustomHeaders?.ForEach(kvp => request.Headers.Add(kvp.Key, kvp.Value)); diff --git a/Flagsmith.FlagsmithClient/SdkVersion.cs b/Flagsmith.FlagsmithClient/SdkVersion.cs new file mode 100644 index 0000000..8eb9071 --- /dev/null +++ b/Flagsmith.FlagsmithClient/SdkVersion.cs @@ -0,0 +1,38 @@ +#nullable enable + +using System.Reflection; + +namespace Flagsmith +{ + /// + /// Provides SDK version information for User-Agent header + /// + public static class SdkVersion + { + private static string? _version; + + /// + /// Gets the SDK version in the format "flagsmith-dotnet-sdk/version" + /// + public static string GetUserAgent() + { + if (_version == null) + { + var assembly = typeof(SdkVersion).Assembly; + var version = assembly.GetName().Version; + + if (version != null && version.Major > 0) + { + // Use only Major.Minor.Build (e.g., 8.0.2) + _version = $"flagsmith-dotnet-sdk/{version.Major}.{version.Minor}.{version.Build}"; + } + else + { + _version = "flagsmith-dotnet-sdk/unknown"; + } + } + + return _version; + } + } +} From eaf7b3a13162f075a9ad6d3221b63e4e59a44027 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 7 Nov 2025 18:19:08 +0000 Subject: [PATCH 03/10] Fix SdkVersion to handle edge cases properly - Remove version.Major > 0 check to support 0.x.x versions - Check if Build >= 0 before including it in version string - Fall back to Major.Minor format if Build is -1 Co-authored-by: khvn26 <979078+khvn26@users.noreply.github.com> --- Flagsmith.FlagsmithClient/SdkVersion.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Flagsmith.FlagsmithClient/SdkVersion.cs b/Flagsmith.FlagsmithClient/SdkVersion.cs index 8eb9071..0b843b9 100644 --- a/Flagsmith.FlagsmithClient/SdkVersion.cs +++ b/Flagsmith.FlagsmithClient/SdkVersion.cs @@ -21,10 +21,17 @@ public static string GetUserAgent() var assembly = typeof(SdkVersion).Assembly; var version = assembly.GetName().Version; - if (version != null && version.Major > 0) + if (version != null) { - // Use only Major.Minor.Build (e.g., 8.0.2) - _version = $"flagsmith-dotnet-sdk/{version.Major}.{version.Minor}.{version.Build}"; + // Use Major.Minor.Build if Build >= 0, otherwise use Major.Minor + if (version.Build >= 0) + { + _version = $"flagsmith-dotnet-sdk/{version.Major}.{version.Minor}.{version.Build}"; + } + else + { + _version = $"flagsmith-dotnet-sdk/{version.Major}.{version.Minor}"; + } } else { From e6e4736839a49b178d6a16c0455a82e8176513e6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 7 Nov 2025 18:20:07 +0000 Subject: [PATCH 04/10] Make SdkVersion thread-safe and fix build number check - Use Lazy for thread-safe initialization - Change condition from >= 0 to > -1 for proper build number check - Simplify code structure Co-authored-by: khvn26 <979078+khvn26@users.noreply.github.com> --- Flagsmith.FlagsmithClient/SdkVersion.cs | 43 ++++++++++++------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/Flagsmith.FlagsmithClient/SdkVersion.cs b/Flagsmith.FlagsmithClient/SdkVersion.cs index 0b843b9..e3a5f74 100644 --- a/Flagsmith.FlagsmithClient/SdkVersion.cs +++ b/Flagsmith.FlagsmithClient/SdkVersion.cs @@ -1,5 +1,6 @@ #nullable enable +using System; using System.Reflection; namespace Flagsmith @@ -9,37 +10,35 @@ namespace Flagsmith /// public static class SdkVersion { - private static string? _version; - - /// - /// Gets the SDK version in the format "flagsmith-dotnet-sdk/version" - /// - public static string GetUserAgent() + private static readonly Lazy _version = new Lazy(() => { - if (_version == null) + var assembly = typeof(SdkVersion).Assembly; + var version = assembly.GetName().Version; + + if (version != null) { - var assembly = typeof(SdkVersion).Assembly; - var version = assembly.GetName().Version; - - if (version != null) + // Use Major.Minor.Build if Build > -1, otherwise use Major.Minor + if (version.Build > -1) { - // Use Major.Minor.Build if Build >= 0, otherwise use Major.Minor - if (version.Build >= 0) - { - _version = $"flagsmith-dotnet-sdk/{version.Major}.{version.Minor}.{version.Build}"; - } - else - { - _version = $"flagsmith-dotnet-sdk/{version.Major}.{version.Minor}"; - } + return $"flagsmith-dotnet-sdk/{version.Major}.{version.Minor}.{version.Build}"; } else { - _version = "flagsmith-dotnet-sdk/unknown"; + return $"flagsmith-dotnet-sdk/{version.Major}.{version.Minor}"; } } + else + { + return "flagsmith-dotnet-sdk/unknown"; + } + }); - return _version; + /// + /// Gets the SDK version in the format "flagsmith-dotnet-sdk/version" + /// + public static string GetUserAgent() + { + return _version.Value; } } } From 33e3630739492cecc0634e91ce02a596bf45c6b3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 7 Nov 2025 18:22:47 +0000 Subject: [PATCH 05/10] Fix nullable reference warnings in tests - Use null! instead of nullable annotation for consistency with test codebase - Eliminates compiler warnings while maintaining null safety Co-authored-by: khvn26 <979078+khvn26@users.noreply.github.com> --- Flagsmith.Client.Test/UserAgentTest.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Flagsmith.Client.Test/UserAgentTest.cs b/Flagsmith.Client.Test/UserAgentTest.cs index 39ae07d..b63e416 100644 --- a/Flagsmith.Client.Test/UserAgentTest.cs +++ b/Flagsmith.Client.Test/UserAgentTest.cs @@ -16,7 +16,7 @@ public class UserAgentTest public async Task TestUserAgentHeaderIsSentInGetEnvironmentFlags() { // Given - HttpRequestMessage capturedRequest = null; + HttpRequestMessage capturedRequest = null!; var httpClientMock = new Mock(); httpClientMock.Setup(x => x.SendAsync(It.IsAny(), It.IsAny())) @@ -61,7 +61,7 @@ public async Task TestUserAgentHeaderIsSentInGetEnvironmentFlags() public async Task TestUserAgentHeaderIsSentInGetIdentityFlags() { // Given - HttpRequestMessage capturedRequest = null; + HttpRequestMessage capturedRequest = null!; var httpClientMock = new Mock(); httpClientMock.Setup(x => x.SendAsync(It.IsAny(), It.IsAny())) @@ -111,7 +111,7 @@ public void TestSdkVersionReturnsConsistentValue() public async Task TestUserAgentHeaderIsSentInAnalyticsFlush() { // Given - HttpRequestMessage capturedRequest = null; + HttpRequestMessage capturedRequest = null!; var httpClientMock = new Mock(); httpClientMock.Setup(x => x.SendAsync(It.IsAny(), It.IsAny())) From c03ba97fcb73829882e7d58ba52e67e4d85fcf27 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 7 Nov 2025 18:30:50 +0000 Subject: [PATCH 06/10] Run dotnet format to apply code style formatting Applied standard .NET code formatting to fix whitespace and indentation Co-authored-by: khvn26 <979078+khvn26@users.noreply.github.com> --- Flagsmith.Client.Test/Fixtures.cs | 3 ++- Flagsmith.Client.Test/UserAgentTest.cs | 26 ++++++++++++------------- Flagsmith.Engine/Engine.cs | 3 ++- Flagsmith.FlagsmithClient/SdkVersion.cs | 2 +- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/Flagsmith.Client.Test/Fixtures.cs b/Flagsmith.Client.Test/Fixtures.cs index 6cd17af..5a3c5a6 100644 --- a/Flagsmith.Client.Test/Fixtures.cs +++ b/Flagsmith.Client.Test/Fixtures.cs @@ -169,7 +169,8 @@ public static string ApiFlagResponseWithTenFlags environment = 1, }; flags.Add(flag); - }; + } + ; var json = JsonConvert.SerializeObject(flags); // Return the JSON string representation of the flags list return json; diff --git a/Flagsmith.Client.Test/UserAgentTest.cs b/Flagsmith.Client.Test/UserAgentTest.cs index b63e416..32dfcb7 100644 --- a/Flagsmith.Client.Test/UserAgentTest.cs +++ b/Flagsmith.Client.Test/UserAgentTest.cs @@ -17,7 +17,7 @@ public async Task TestUserAgentHeaderIsSentInGetEnvironmentFlags() { // Given HttpRequestMessage capturedRequest = null!; - + var httpClientMock = new Mock(); httpClientMock.Setup(x => x.SendAsync(It.IsAny(), It.IsAny())) .Callback((request, token) => @@ -35,7 +35,7 @@ public async Task TestUserAgentHeaderIsSentInGetEnvironmentFlags() EnvironmentKey = Fixtures.ApiKey, HttpClient = httpClientMock.Object }; - + var client = new FlagsmithClient(config); // When @@ -44,16 +44,16 @@ public async Task TestUserAgentHeaderIsSentInGetEnvironmentFlags() // Then Assert.NotNull(capturedRequest); Assert.True(capturedRequest.Headers.Contains("User-Agent")); - + var userAgentValues = capturedRequest.Headers.GetValues("User-Agent").ToList(); Assert.Single(userAgentValues); - + var userAgent = userAgentValues[0]; Assert.StartsWith("flagsmith-dotnet-sdk/", userAgent); - + // Verify it's either a version number or "unknown" var versionPart = userAgent.Substring("flagsmith-dotnet-sdk/".Length); - Assert.True(versionPart == "unknown" || versionPart.Contains("."), + Assert.True(versionPart == "unknown" || versionPart.Contains("."), $"User-Agent should be 'flagsmith-dotnet-sdk/version' or 'flagsmith-dotnet-sdk/unknown', but was '{userAgent}'"); } @@ -62,7 +62,7 @@ public async Task TestUserAgentHeaderIsSentInGetIdentityFlags() { // Given HttpRequestMessage capturedRequest = null!; - + var httpClientMock = new Mock(); httpClientMock.Setup(x => x.SendAsync(It.IsAny(), It.IsAny())) .Callback((request, token) => @@ -80,7 +80,7 @@ public async Task TestUserAgentHeaderIsSentInGetIdentityFlags() EnvironmentKey = Fixtures.ApiKey, HttpClient = httpClientMock.Object }; - + var client = new FlagsmithClient(config); // When @@ -89,7 +89,7 @@ public async Task TestUserAgentHeaderIsSentInGetIdentityFlags() // Then Assert.NotNull(capturedRequest); Assert.True(capturedRequest.Headers.Contains("User-Agent")); - + var userAgentValues = capturedRequest.Headers.GetValues("User-Agent").ToList(); Assert.Single(userAgentValues); Assert.StartsWith("flagsmith-dotnet-sdk/", userAgentValues[0]); @@ -112,7 +112,7 @@ public async Task TestUserAgentHeaderIsSentInAnalyticsFlush() { // Given HttpRequestMessage capturedRequest = null!; - + var httpClientMock = new Mock(); httpClientMock.Setup(x => x.SendAsync(It.IsAny(), It.IsAny())) .Callback((request, token) => @@ -125,8 +125,8 @@ public async Task TestUserAgentHeaderIsSentInAnalyticsFlush() }); var analyticsProcessor = new AnalyticsProcessor( - httpClientMock.Object, - Fixtures.ApiKey, + httpClientMock.Object, + Fixtures.ApiKey, Fixtures.ApiUrl ); @@ -137,7 +137,7 @@ public async Task TestUserAgentHeaderIsSentInAnalyticsFlush() // Then Assert.NotNull(capturedRequest); Assert.True(capturedRequest.Headers.Contains("User-Agent")); - + var userAgentValues = capturedRequest.Headers.GetValues("User-Agent").ToList(); Assert.Single(userAgentValues); Assert.StartsWith("flagsmith-dotnet-sdk/", userAgentValues[0]); diff --git a/Flagsmith.Engine/Engine.cs b/Flagsmith.Engine/Engine.cs index 2b28057..8316702 100644 --- a/Flagsmith.Engine/Engine.cs +++ b/Flagsmith.Engine/Engine.cs @@ -70,7 +70,8 @@ public Dictionary GetIdentityFeatureStatesMappi } featureStates[feature] = featureState; - }; + } + ; } identity.IdentityFeatures?.ForEach(x => { diff --git a/Flagsmith.FlagsmithClient/SdkVersion.cs b/Flagsmith.FlagsmithClient/SdkVersion.cs index e3a5f74..450dd68 100644 --- a/Flagsmith.FlagsmithClient/SdkVersion.cs +++ b/Flagsmith.FlagsmithClient/SdkVersion.cs @@ -14,7 +14,7 @@ public static class SdkVersion { var assembly = typeof(SdkVersion).Assembly; var version = assembly.GetName().Version; - + if (version != null) { // Use Major.Minor.Build if Build > -1, otherwise use Major.Minor From cab12bd73333a2c2ecd0209b63f7feb09c82e1b9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 7 Nov 2025 18:48:57 +0000 Subject: [PATCH 07/10] Address code review feedback and add SdkVersion tests - Improved assertion in UserAgentTest to directly compare with SdkVersion.GetUserAgent() - Removed TestSdkVersionReturnsConsistentValue test from UserAgentTest as it was redundant - Created dedicated SdkVersionTest class with comprehensive tests for SdkVersion functionality: - TestGetUserAgentReturnsCorrectFormat: Verifies format starts with "flagsmith-dotnet-sdk/" - TestGetUserAgentReturnsConsistentValue: Ensures consistent values across multiple calls - TestGetUserAgentContainsVersionOrUnknown: Validates version part is either "unknown" or contains version number Co-authored-by: khvn26 <979078+khvn26@users.noreply.github.com> --- Flagsmith.Client.Test/SdkVersionTest.cs | 42 +++++++++++++++++++++++++ Flagsmith.Client.Test/UserAgentTest.cs | 19 +---------- 2 files changed, 43 insertions(+), 18 deletions(-) create mode 100644 Flagsmith.Client.Test/SdkVersionTest.cs diff --git a/Flagsmith.Client.Test/SdkVersionTest.cs b/Flagsmith.Client.Test/SdkVersionTest.cs new file mode 100644 index 0000000..aa6d878 --- /dev/null +++ b/Flagsmith.Client.Test/SdkVersionTest.cs @@ -0,0 +1,42 @@ +using Xunit; + +namespace Flagsmith.FlagsmithClientTest +{ + public class SdkVersionTest + { + [Fact] + public void TestGetUserAgentReturnsCorrectFormat() + { + // When + var userAgent = SdkVersion.GetUserAgent(); + + // Then + Assert.StartsWith("flagsmith-dotnet-sdk/", userAgent); + } + + [Fact] + public void TestGetUserAgentReturnsConsistentValue() + { + // When + var userAgent1 = SdkVersion.GetUserAgent(); + var userAgent2 = SdkVersion.GetUserAgent(); + + // Then + Assert.Equal(userAgent1, userAgent2); + } + + [Fact] + public void TestGetUserAgentContainsVersionOrUnknown() + { + // When + var userAgent = SdkVersion.GetUserAgent(); + var versionPart = userAgent.Substring("flagsmith-dotnet-sdk/".Length); + + // Then + Assert.True( + versionPart == "unknown" || versionPart.Contains("."), + $"Version part should be 'unknown' or contain a dot, but was '{versionPart}'" + ); + } + } +} diff --git a/Flagsmith.Client.Test/UserAgentTest.cs b/Flagsmith.Client.Test/UserAgentTest.cs index 32dfcb7..312e1b3 100644 --- a/Flagsmith.Client.Test/UserAgentTest.cs +++ b/Flagsmith.Client.Test/UserAgentTest.cs @@ -49,12 +49,7 @@ public async Task TestUserAgentHeaderIsSentInGetEnvironmentFlags() Assert.Single(userAgentValues); var userAgent = userAgentValues[0]; - Assert.StartsWith("flagsmith-dotnet-sdk/", userAgent); - - // Verify it's either a version number or "unknown" - var versionPart = userAgent.Substring("flagsmith-dotnet-sdk/".Length); - Assert.True(versionPart == "unknown" || versionPart.Contains("."), - $"User-Agent should be 'flagsmith-dotnet-sdk/version' or 'flagsmith-dotnet-sdk/unknown', but was '{userAgent}'"); + Assert.Equal(SdkVersion.GetUserAgent(), userAgent); } [Fact] @@ -95,18 +90,6 @@ public async Task TestUserAgentHeaderIsSentInGetIdentityFlags() Assert.StartsWith("flagsmith-dotnet-sdk/", userAgentValues[0]); } - [Fact] - public void TestSdkVersionReturnsConsistentValue() - { - // When - var userAgent1 = SdkVersion.GetUserAgent(); - var userAgent2 = SdkVersion.GetUserAgent(); - - // Then - Assert.Equal(userAgent1, userAgent2); - Assert.StartsWith("flagsmith-dotnet-sdk/", userAgent1); - } - [Fact] public async Task TestUserAgentHeaderIsSentInAnalyticsFlush() { From a337abbc1acb4716cfbe7313b48ec862cc2cfe52 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 7 Nov 2025 18:51:32 +0000 Subject: [PATCH 08/10] Add explicit using Flagsmith directive to test files Added using Flagsmith; to SdkVersionTest.cs and UserAgentTest.cs for clarity and better code organization Co-authored-by: khvn26 <979078+khvn26@users.noreply.github.com> --- Flagsmith.Client.Test/SdkVersionTest.cs | 1 + Flagsmith.Client.Test/UserAgentTest.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/Flagsmith.Client.Test/SdkVersionTest.cs b/Flagsmith.Client.Test/SdkVersionTest.cs index aa6d878..555497b 100644 --- a/Flagsmith.Client.Test/SdkVersionTest.cs +++ b/Flagsmith.Client.Test/SdkVersionTest.cs @@ -1,3 +1,4 @@ +using Flagsmith; using Xunit; namespace Flagsmith.FlagsmithClientTest diff --git a/Flagsmith.Client.Test/UserAgentTest.cs b/Flagsmith.Client.Test/UserAgentTest.cs index 312e1b3..54820bd 100644 --- a/Flagsmith.Client.Test/UserAgentTest.cs +++ b/Flagsmith.Client.Test/UserAgentTest.cs @@ -5,6 +5,7 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Flagsmith; using Moq; using Xunit; From a5fca4841fbb23109cc1236f1bdfce08dc9647b7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 12:57:52 +0000 Subject: [PATCH 09/10] Switch from Assembly-based version to hard-coded constant with release-please - Replace Assembly reflection with hard-coded version constant - Add x-release-please markers for automatic version updates - Update release-please config to include SdkVersion.cs and SdkVersionTest.cs as extra files - Add test to verify expected version matches with release-please markers - Simplify SdkVersion implementation (no longer needs Lazy or reflection) Co-authored-by: khvn26 <979078+khvn26@users.noreply.github.com> --- Flagsmith.Client.Test/SdkVersionTest.cs | 20 ++++++++++++++--- Flagsmith.FlagsmithClient/SdkVersion.cs | 30 ++++--------------------- release-please-config.json | 4 +++- 3 files changed, 24 insertions(+), 30 deletions(-) diff --git a/Flagsmith.Client.Test/SdkVersionTest.cs b/Flagsmith.Client.Test/SdkVersionTest.cs index 555497b..6983e5e 100644 --- a/Flagsmith.Client.Test/SdkVersionTest.cs +++ b/Flagsmith.Client.Test/SdkVersionTest.cs @@ -5,6 +5,20 @@ namespace Flagsmith.FlagsmithClientTest { public class SdkVersionTest { + [Fact] + public void TestGetUserAgentReturnsExpectedVersion() + { + // x-release-please-start-version + string expectedVersion = "8.0.2"; + // x-release-please-end + + // When + var userAgent = SdkVersion.GetUserAgent(); + + // Then + Assert.Equal($"flagsmith-dotnet-sdk/{expectedVersion}", userAgent); + } + [Fact] public void TestGetUserAgentReturnsCorrectFormat() { @@ -27,7 +41,7 @@ public void TestGetUserAgentReturnsConsistentValue() } [Fact] - public void TestGetUserAgentContainsVersionOrUnknown() + public void TestGetUserAgentContainsVersion() { // When var userAgent = SdkVersion.GetUserAgent(); @@ -35,8 +49,8 @@ public void TestGetUserAgentContainsVersionOrUnknown() // Then Assert.True( - versionPart == "unknown" || versionPart.Contains("."), - $"Version part should be 'unknown' or contain a dot, but was '{versionPart}'" + versionPart.Contains("."), + $"Version part should contain a dot, but was '{versionPart}'" ); } } diff --git a/Flagsmith.FlagsmithClient/SdkVersion.cs b/Flagsmith.FlagsmithClient/SdkVersion.cs index 450dd68..dc50d9e 100644 --- a/Flagsmith.FlagsmithClient/SdkVersion.cs +++ b/Flagsmith.FlagsmithClient/SdkVersion.cs @@ -1,8 +1,5 @@ #nullable enable -using System; -using System.Reflection; - namespace Flagsmith { /// @@ -10,35 +7,16 @@ namespace Flagsmith /// public static class SdkVersion { - private static readonly Lazy _version = new Lazy(() => - { - var assembly = typeof(SdkVersion).Assembly; - var version = assembly.GetName().Version; - - if (version != null) - { - // Use Major.Minor.Build if Build > -1, otherwise use Major.Minor - if (version.Build > -1) - { - return $"flagsmith-dotnet-sdk/{version.Major}.{version.Minor}.{version.Build}"; - } - else - { - return $"flagsmith-dotnet-sdk/{version.Major}.{version.Minor}"; - } - } - else - { - return "flagsmith-dotnet-sdk/unknown"; - } - }); + // x-release-please-start-version + private const string Version = "8.0.2"; + // x-release-please-end /// /// Gets the SDK version in the format "flagsmith-dotnet-sdk/version" /// public static string GetUserAgent() { - return _version.Value; + return $"flagsmith-dotnet-sdk/{Version}"; } } } diff --git a/release-please-config.json b/release-please-config.json index 727affa..bd97ccf 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -15,7 +15,9 @@ "type": "xml", "path": "Flagsmith.FlagsmithClient/Flagsmith.FlagsmithClient.csproj", "xpath": "//Project/PropertyGroup/Version" - } + }, + "Flagsmith.FlagsmithClient/SdkVersion.cs", + "Flagsmith.Client.Test/SdkVersionTest.cs" ] } }, From d8c68d44ee21689aab9c1ba34352caca4c86265c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 13:10:26 +0000 Subject: [PATCH 10/10] Remove redundant tests and use consistent assertions - Removed redundant tests from SdkVersionTest: TestGetUserAgentReturnsCorrectFormat, TestGetUserAgentReturnsConsistentValue, TestGetUserAgentContainsVersion - Updated UserAgentTest assertions to be consistent across all tests (using Assert.Equal with SdkVersion.GetUserAgent()) - All tests now use the same assertion pattern for better maintainability Co-authored-by: khvn26 <979078+khvn26@users.noreply.github.com> --- Flagsmith.Client.Test/SdkVersionTest.cs | 35 ------------------------- Flagsmith.Client.Test/UserAgentTest.cs | 8 ++++-- 2 files changed, 6 insertions(+), 37 deletions(-) diff --git a/Flagsmith.Client.Test/SdkVersionTest.cs b/Flagsmith.Client.Test/SdkVersionTest.cs index 6983e5e..62dbd7c 100644 --- a/Flagsmith.Client.Test/SdkVersionTest.cs +++ b/Flagsmith.Client.Test/SdkVersionTest.cs @@ -18,40 +18,5 @@ public void TestGetUserAgentReturnsExpectedVersion() // Then Assert.Equal($"flagsmith-dotnet-sdk/{expectedVersion}", userAgent); } - - [Fact] - public void TestGetUserAgentReturnsCorrectFormat() - { - // When - var userAgent = SdkVersion.GetUserAgent(); - - // Then - Assert.StartsWith("flagsmith-dotnet-sdk/", userAgent); - } - - [Fact] - public void TestGetUserAgentReturnsConsistentValue() - { - // When - var userAgent1 = SdkVersion.GetUserAgent(); - var userAgent2 = SdkVersion.GetUserAgent(); - - // Then - Assert.Equal(userAgent1, userAgent2); - } - - [Fact] - public void TestGetUserAgentContainsVersion() - { - // When - var userAgent = SdkVersion.GetUserAgent(); - var versionPart = userAgent.Substring("flagsmith-dotnet-sdk/".Length); - - // Then - Assert.True( - versionPart.Contains("."), - $"Version part should contain a dot, but was '{versionPart}'" - ); - } } } diff --git a/Flagsmith.Client.Test/UserAgentTest.cs b/Flagsmith.Client.Test/UserAgentTest.cs index 54820bd..14b2346 100644 --- a/Flagsmith.Client.Test/UserAgentTest.cs +++ b/Flagsmith.Client.Test/UserAgentTest.cs @@ -88,7 +88,9 @@ public async Task TestUserAgentHeaderIsSentInGetIdentityFlags() var userAgentValues = capturedRequest.Headers.GetValues("User-Agent").ToList(); Assert.Single(userAgentValues); - Assert.StartsWith("flagsmith-dotnet-sdk/", userAgentValues[0]); + + var userAgent = userAgentValues[0]; + Assert.Equal(SdkVersion.GetUserAgent(), userAgent); } [Fact] @@ -124,7 +126,9 @@ public async Task TestUserAgentHeaderIsSentInAnalyticsFlush() var userAgentValues = capturedRequest.Headers.GetValues("User-Agent").ToList(); Assert.Single(userAgentValues); - Assert.StartsWith("flagsmith-dotnet-sdk/", userAgentValues[0]); + + var userAgent = userAgentValues[0]; + Assert.Equal(SdkVersion.GetUserAgent(), userAgent); } } }