Skip to content

Commit 8824b8b

Browse files
authored
chore: Add dotnet format validation (#33)
1 parent 5d69a97 commit 8824b8b

File tree

10 files changed

+452
-60
lines changed

10 files changed

+452
-60
lines changed

.editorconfig

Lines changed: 362 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Validate formatting
2+
3+
on:
4+
# Run on all PRs targeting main
5+
pull_request:
6+
branches:
7+
- main
8+
9+
jobs:
10+
validate-formatting:
11+
runs-on: ubuntu-latest
12+
permissions:
13+
contents: read
14+
15+
steps:
16+
- uses: actions/checkout@v6
17+
18+
- name: Setup .NET
19+
uses: actions/setup-dotnet@v5
20+
with:
21+
global-json-file: global.json
22+
23+
- name: 🧑‍🏫 Validate Formatting
24+
working-directory: ./src
25+
run: |
26+
dotnet format --verify-no-changes

src/Octopus.OpenFeature.Provider.Tests/OctopusFeatureContextProviderTests.cs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class OctopusFeatureContextProviderTests
1212
{
1313
CacheDuration = TimeSpan.FromSeconds(1)
1414
};
15-
15+
1616
class MockOctopusFeatureClient(FeatureToggles? featureToggles) : IOctopusFeatureClient
1717
{
1818
FeatureToggles? featureToggles = featureToggles;
@@ -37,7 +37,7 @@ public void ChangeToggles(FeatureToggles? featureToggles = null)
3737
public void WhenInstantiated_ProvidesAnEmptyEvaluationContext()
3838
{
3939
var provider = new OctopusFeatureContextProvider(configuration, new MockOctopusFeatureClient(null), NullLogger.Instance);
40-
40+
4141
var context = provider.GetEvaluationContext();
4242

4343
using var scope = new AssertionScope();
@@ -49,11 +49,11 @@ public void WhenInstantiated_ProvidesAnEmptyEvaluationContext()
4949
public async Task WhenInitialized_ProvidesRetrievedEvaluationContext()
5050
{
5151
byte[] contentHash = [0x01, 0x02, 0x03, 0x04];
52-
52+
5353
var client = new MockOctopusFeatureClient(new FeatureToggles(
5454
[new FeatureToggleEvaluation("Test Feature", "test-feature", true, [])],
5555
contentHash));
56-
56+
5757
var provider = new OctopusFeatureContextProvider(configuration, client, NullLogger.Instance);
5858
await provider.Initialize();
5959
var context = provider.GetEvaluationContext();
@@ -68,35 +68,35 @@ [new FeatureToggleEvaluation("Test Feature", "test-feature", true, [])],
6868
public async Task WhenInitialized_RefreshesCacheAfterCacheDurationExpires()
6969
{
7070
byte[] contentHash = [0x01, 0x02, 0x03, 0x04];
71-
71+
7272
var client = new MockOctopusFeatureClient(new FeatureToggles(
73-
[new FeatureToggleEvaluation("Test Feature", "test-feature", true, [])],
73+
[new FeatureToggleEvaluation("Test Feature", "test-feature", true, [])],
7474
contentHash));
75-
75+
7676
// Initialize the provider
7777
var provider = new OctopusFeatureContextProvider(configuration, client, NullLogger.Instance);
7878
await provider.Initialize();
79-
79+
8080
// Validate the initial state
8181
using var scope = new AssertionScope();
8282
var context = provider.GetEvaluationContext();
8383
context.ContentHash.Should().BeEquivalentTo(contentHash);
8484
context.Evaluate("test-feature", false, context: null).Value.Should().BeTrue();
85-
85+
8686
// Simulate a change in the available feature toggles
8787
client.ChangeToggles(new FeatureToggles(
8888
[new FeatureToggleEvaluation("Test Feature", "test-feature", false, [])],
8989
[0x01, 0x02, 0x03, 0x05]));
9090

9191
// Wait for the cache to expire
9292
await Task.Delay(TimeSpan.FromSeconds(5));
93-
93+
9494
// Validate the updated toggles are available
9595
context = provider.GetEvaluationContext();
9696
context.ContentHash.Should().BeEquivalentTo(new byte[] { 0x01, 0x02, 0x03, 0x05 });
9797
context.Evaluate("test-feature", false, context: null).Value.Should().BeFalse();
9898
}
99-
99+
100100
class AlwaysFailsFeatureClient : IOctopusFeatureClient
101101
{
102102
public Task<bool> HaveFeaturesChanged(byte[] contentHash, CancellationToken cancellationToken)
@@ -109,14 +109,14 @@ public Task<bool> HaveFeaturesChanged(byte[] contentHash, CancellationToken canc
109109
throw new Exception("Oops!");
110110
}
111111
}
112-
112+
113113
[Fact]
114114
public async Task WhenFeatureEvaluationRetrievalFails_LogsError()
115115
{
116116
var client = new AlwaysFailsFeatureClient();
117117
var logger = new FakeLogger();
118118
var provider = new OctopusFeatureContextProvider(configuration, client, logger);
119-
119+
120120
await provider.Initialize();
121121

122122
using var scope = new AssertionScope();
@@ -126,4 +126,4 @@ public async Task WhenFeatureEvaluationRetrievalFails_LogsError()
126126

127127
await provider.Shutdown();
128128
}
129-
}
129+
}

src/Octopus.OpenFeature.Provider.Tests/OctopusFeatureContextTests.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public void EvaluatesToTrue_IfFeatureIsContainedWithinTheSet_AndFeatureIsEnabled
2121

2222
result.Value.Should().BeTrue();
2323
}
24-
24+
2525
[Fact]
2626
public void WhenEvaluatedWithCasingDifferences_EvaluationIsInsensitiveToCase()
2727
{
@@ -35,7 +35,7 @@ public void WhenEvaluatedWithCasingDifferences_EvaluationIsInsensitiveToCase()
3535

3636
result.Value.Should().BeTrue();
3737
}
38-
38+
3939
[Fact]
4040
public void EvaluatesToFalse_IfFeatureIsContainedWithinTheSet_AndFeatureIsNotEnabled()
4141
{
@@ -49,7 +49,7 @@ public void EvaluatesToFalse_IfFeatureIsContainedWithinTheSet_AndFeatureIsNotEna
4949

5050
result.Value.Should().BeFalse();
5151
}
52-
52+
5353
[Fact]
5454
public void GivenAFlagKeyThatIsNotASlug_ReturnsFlagNotFound_AndEvaluatesToDefaultValue()
5555
{
@@ -64,7 +64,7 @@ public void GivenAFlagKeyThatIsNotASlug_ReturnsFlagNotFound_AndEvaluatesToDefaul
6464
result.ErrorType.Should().Be(ErrorType.FlagNotFound);
6565
result.Value.Should().BeTrue();
6666
}
67-
67+
6868
[Fact]
6969
public void EvaluatesToDefaultValue_IfFeatureIsNotContainedWithinSet()
7070
{
@@ -140,7 +140,7 @@ public void WhenAFeatureIsToggledOnForMultipleSegments_EvaluatesCorrectly()
140140
// A matching context value is present for each toggled segment
141141
context.Evaluate("testfeature", false, context: BuildContext([("license", "trial"), ("region", "us")])).Value
142142
.Should().BeTrue("when there is a matching context value for each toggled segment, the toggle should be enabled");
143-
143+
144144
// A context value is present for each toggled segment, but it is not toggled on for one of the supplied values
145145
context.Evaluate("testfeature", false, context: BuildContext([("license", "trial"), ("region", "eu")])).Value
146146
.Should().BeFalse("when there is a matching context value for each toggled segment, but the context value does not match the toggled segment, the toggle should be disabled");
@@ -149,7 +149,7 @@ public void WhenAFeatureIsToggledOnForMultipleSegments_EvaluatesCorrectly()
149149
context.Evaluate("testfeature", false,
150150
context: BuildContext([("license", "trial"), ("region", "us"), ("language", "english")])).Value.Should()
151151
.BeTrue("when extra context values are present, the toggle should still be enabled");
152-
152+
153153
// A context value is present for only one of the two toggled segments
154154
context.Evaluate("testfeature", false, context: BuildContext([("license", "trial")])).Value.Should()
155155
.BeFalse("when the context does not contain a value for all toggled segments, the toggle should be disabled");
@@ -163,7 +163,7 @@ public void WhenAFeatureIsToggledOnForMultipleSegments_EvaluatesCorrectly()
163163
// None specified
164164
context.Evaluate("testfeature", true, context: null).Value.Should().BeFalse("when no context values are present, and the feature is toggled on for a segment, the toggle should be disabled");
165165
}
166-
166+
167167
[Fact]
168168
public void
169169
WhenAFeatureIsToggledOnForASpecificSegment_ToleratesNullValuesInContext()
@@ -179,4 +179,4 @@ public void
179179
context.Evaluate("testfeature", false, context: BuildContext([("other", "segment")])).Value.Should().BeFalse();
180180
context.Evaluate("testfeature", false, context: null).Value.Should().BeFalse();
181181
}
182-
}
182+
}

src/Octopus.OpenFeature.Provider/OctopusFeatureClient.cs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
using System.Net;
2-
using Microsoft.Extensions.Logging;
32
using System.Text.Json;
3+
using Microsoft.Extensions.Logging;
44

55
namespace Octopus.OpenFeature.Provider;
66

77
public class FeatureToggles(FeatureToggleEvaluation[] evaluations, byte[] contentHash)
88
{
99
public FeatureToggleEvaluation[] Evaluations { get; } = evaluations;
10-
10+
1111
public byte[] ContentHash { get; } = contentHash;
1212
}
1313

1414
public class FeatureToggleEvaluation(string name, string slug, bool isEnabled, KeyValuePair<string, string>[] segments)
1515
{
1616
public string Name { get; } = name;
17-
17+
1818
public string Slug { get; } = slug;
19-
19+
2020
public bool IsEnabled { get; } = isEnabled;
21-
21+
2222
public KeyValuePair<string, string>[] Segments { get; } = segments;
2323
}
2424

@@ -58,22 +58,22 @@ public async Task<bool> HaveFeaturesChanged(byte[] contentHash, CancellationToke
5858
if (result is not null && result.IsSuccessStatusCode)
5959
{
6060
var rawResult = await result.Content.ReadAsStringAsync();
61-
61+
6262
hash = JsonSerializer.Deserialize<FeatureCheck>(rawResult, JsonSerializerOptions.Web);
6363
}
6464
}
6565
else
6666
{
6767
var result = await ExecuteWithRetry(async ct => await client.GetAsync($"api/featuretoggles/{configuration.ClientIdentifier}/check", ct), cancellationToken);
68-
68+
6969
if (result is not null && result.IsSuccessStatusCode)
7070
{
7171
var rawResult = await result.Content.ReadAsStringAsync();
72-
72+
7373
hash = JsonSerializer.Deserialize<FeatureCheck>(rawResult, JsonSerializerOptions.Web);
7474
}
7575
}
76-
76+
7777
if (hash is null)
7878
{
7979
logger.LogWarning("Failed to retrieve feature toggles after 3 retries. Previously retrieved feature toggle values will continue to be used.");
@@ -106,21 +106,21 @@ class FeatureCheck(byte[] contentHash)
106106

107107
if (configuration.ReleaseVersionOverride is not null)
108108
{
109-
client.DefaultRequestHeaders.Add(OctopusHttpHeaderNames.ReleaseVersion, configuration.ReleaseVersionOverride);
109+
client.DefaultRequestHeaders.Add(OctopusHttpHeaderNames.ReleaseVersion, configuration.ReleaseVersionOverride);
110110
}
111111

112112
HttpResponseMessage? response;
113113
if (configuration.IsV3ClientIdentifierSupplied())
114114
{
115115
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {configuration.ClientIdentifier}");
116-
116+
117117
response = await ExecuteWithRetry(async ct => await client.GetAsync("api/featuretoggles/v3/", ct), cancellationToken);
118118
}
119119
else
120120
{
121121
response = await ExecuteWithRetry(async ct => await client.GetAsync($"api/featuretoggles/v2/{configuration.ClientIdentifier}", ct), cancellationToken);
122122
}
123-
123+
124124
if (response is null or { StatusCode: HttpStatusCode.NotFound })
125125
{
126126
logger.LogWarning("Failed to retrieve feature toggles for client identifier {ClientIdentifier} from {OctoToggleUrl}", configuration.ClientIdentifier, configuration.ServerUri);
@@ -144,9 +144,9 @@ class FeatureCheck(byte[] contentHash)
144144
// If for any reason the v3 endpoint response contract starts to diverge from the v2 contract,
145145
// This code will need to update accordingly
146146
var result = await response.Content.ReadAsStringAsync();
147-
147+
148148
var evaluations = JsonSerializer.Deserialize<FeatureToggleEvaluation[]>(result, JsonSerializerOptions.Web);
149-
149+
150150
if (evaluations is null)
151151
{
152152
logger.LogWarning("Feature toggle response content from {OctoToggleUrl} was empty", configuration.ServerUri);
@@ -157,7 +157,7 @@ class FeatureCheck(byte[] contentHash)
157157

158158
return toggles;
159159
}
160-
160+
161161
async Task<T?> ExecuteWithRetry<T>(Func<CancellationToken, Task<T>> callback, CancellationToken cancellationToken)
162162
{
163163
var attempts = 0;

src/Octopus.OpenFeature.Provider/OctopusFeatureConfiguration.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,16 @@ namespace Octopus.OpenFeature.Provider;
66
public class OctopusFeatureConfiguration
77
{
88
const string DefaultServerUri = "https://features.octopus.com";
9-
9+
1010
public OctopusFeatureConfiguration(string clientIdentifier)
1111
{
1212
ClientIdentifier = clientIdentifier;
1313
var serverUri = Environment.GetEnvironmentVariable("OctoToggle__Url");
1414
ServerUri = serverUri is not null ? new Uri(serverUri) : new Uri(DefaultServerUri);
1515
}
16-
16+
1717
public Uri ServerUri { get; private set; }
18-
18+
1919
/// <summary>
2020
/// Overrides the application release version embedded in the ClientIdentifier
2121
/// </summary>
@@ -24,16 +24,16 @@ public OctopusFeatureConfiguration(string clientIdentifier)
2424
/// <summary>
2525
/// The ClientIdentifier provided by the Octopus variable Octopus.FeatureToggles.ClientIdentifier
2626
/// </summary>
27-
public string ClientIdentifier { get; set; }
28-
27+
public string ClientIdentifier { get; set; }
28+
2929
/// <summary>
3030
/// The amount of time between checks to see if new feature toggles are available
3131
/// The cache will be refreshed if new feature toggles are available
3232
/// </summary>
3333
public TimeSpan CacheDuration { get; set; } = TimeSpan.FromMinutes(1);
34-
34+
3535
public CancellationToken CancellationToken { get; set; } = CancellationToken.None;
36-
36+
3737
public ILoggerFactory LoggerFactory { get; set; } = NullLoggerFactory.Instance;
3838

3939
public bool IsV3ClientIdentifierSupplied()
@@ -42,4 +42,4 @@ public bool IsV3ClientIdentifierSupplied()
4242
var tokenSegments = ClientIdentifier.Split('.');
4343
return tokenSegments.Length == 3;
4444
}
45-
}
45+
}

src/Octopus.OpenFeature.Provider/OctopusFeatureContext.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,14 @@ public ResolutionDetails<bool> Evaluate(string slug, bool defaultValue, Evaluati
4646

4747
bool MatchesSegment(EvaluationContext? context, IEnumerable<KeyValuePair<string, string>> segments)
4848
{
49-
if (context == null) return false;
49+
if (context == null)
50+
{
51+
return false;
52+
}
5053

5154
var contextEntries = context.AsDictionary();
5255

53-
return segments.GroupBy(x => x.Key).All(group =>
56+
return segments.GroupBy(x => x.Key).All(group =>
5457
group.Any(segment =>
5558
contextEntries.Any(contextEntry =>
5659
contextEntry.Key.Equals(segment.Key, StringComparison.OrdinalIgnoreCase)
@@ -63,4 +66,4 @@ bool Evaluate(FeatureToggleEvaluation evaluation, EvaluationContext? context = n
6366
return evaluation.IsEnabled &&
6467
(evaluation.Segments.Length == 0 || MatchesSegment(context, evaluation.Segments));
6568
}
66-
}
69+
}

0 commit comments

Comments
 (0)