Skip to content

Commit 1cfac50

Browse files
authored
chore: Don't create a static field in a generic class (#1555)
1 parent d6cd3dc commit 1cfac50

File tree

5 files changed

+101
-41
lines changed

5 files changed

+101
-41
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
namespace DotNet.Testcontainers.Configurations
2+
{
3+
using System.Text.Json;
4+
5+
internal static class DefaultJsonSerializerOptions
6+
{
7+
static DefaultJsonSerializerOptions()
8+
{
9+
Instance.Converters.Add(new JsonOrderedKeysConverter());
10+
}
11+
12+
public static JsonSerializerOptions Instance { get; }
13+
= new JsonSerializerOptions();
14+
}
15+
}

src/Testcontainers/Configurations/Commons/JsonIgnoreRuntimeResourceLabels.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@ namespace DotNet.Testcontainers.Configurations
88

99
internal sealed class JsonIgnoreRuntimeResourceLabels : JsonOrderedKeysConverter
1010
{
11-
private static readonly ISet<string> IgnoreLabels = new HashSet<string> { ResourceReaper.ResourceReaperSessionLabel, TestcontainersClient.TestcontainersVersionLabel, TestcontainersClient.TestcontainersSessionIdLabel };
11+
private static readonly ISet<string> IgnoreLabels = new HashSet<string>
12+
{
13+
ResourceReaper.ResourceReaperSessionLabel,
14+
TestcontainersClient.TestcontainersVersionLabel,
15+
TestcontainersClient.TestcontainersSessionIdLabel,
16+
};
1217

1318
public override void Write(Utf8JsonWriter writer, IReadOnlyDictionary<string, string> value, JsonSerializerOptions options)
1419
{
1520
var labels = value.Where(label => !IgnoreLabels.Contains(label.Key)).ToDictionary(label => label.Key, label => label.Value);
16-
1721
base.Write(writer, labels, options);
1822
}
1923
}

src/Testcontainers/Configurations/Commons/ResourceConfiguration.cs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,6 @@ namespace DotNet.Testcontainers.Configurations
1414
[PublicAPI]
1515
public class ResourceConfiguration<TCreateResourceEntity> : IResourceConfiguration<TCreateResourceEntity>
1616
{
17-
private static readonly JsonSerializerOptions JsonSerializerOptions;
18-
19-
static ResourceConfiguration()
20-
{
21-
JsonSerializerOptions = new JsonSerializerOptions { Converters = { new JsonOrderedKeysConverter() } };
22-
}
23-
2417
/// <summary>
2518
/// Initializes a new instance of the <see cref="ResourceConfiguration{TCreateResourceEntity}" /> class.
2619
/// </summary>
@@ -95,7 +88,7 @@ protected ResourceConfiguration(IResourceConfiguration<TCreateResourceEntity> ol
9588
/// <inheritdoc />
9689
public virtual string GetReuseHash()
9790
{
98-
var jsonUtf8Bytes = JsonSerializer.SerializeToUtf8Bytes(this, GetType(), JsonSerializerOptions);
91+
var jsonUtf8Bytes = JsonSerializer.SerializeToUtf8Bytes(this, GetType(), DefaultJsonSerializerOptions.Instance);
9992

10093
#if NET6_0_OR_GREATER
10194
return Convert.ToBase64String(SHA1.HashData(jsonUtf8Bytes));

tests/Testcontainers.Platform.Linux.Tests/ReusableResourceTest.cs

Lines changed: 70 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -104,37 +104,76 @@ public sealed class EqualTest
104104
{
105105
[Fact]
106106
[Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))]
107-
public void ForSameConfigurationCreatedInDifferentOrder()
107+
public void ForKnownConfiguration()
108108
{
109-
var env1 = new Dictionary<string, string>
110-
{
111-
["keyA"] = "valueA",
112-
["keyB"] = "valueB",
113-
};
114-
var env2 = new Dictionary<string, string>
115-
{
116-
["keyB"] = "valueB",
117-
["keyA"] = "valueA",
118-
};
119-
var hash1 = new ReuseHashContainerBuilder().WithEnvironment(env1).WithLabel("labelA", "A").WithLabel("labelB", "B").GetReuseHash();
120-
var hash2 = new ReuseHashContainerBuilder().WithEnvironment(env2).WithLabel("labelB", "B").WithLabel("labelA", "A").GetReuseHash();
121-
Assert.Equal(hash1, hash2);
109+
// Given
110+
var env = new Dictionary<string, string>();
111+
env["keyA"] = "valueA";
112+
env["keyB"] = "valueB";
113+
114+
// When
115+
var hash = new ReuseHashContainerBuilder()
116+
.WithEnvironment(env)
117+
.WithLabel("labelA", "A")
118+
.WithLabel("labelB", "B")
119+
.GetReuseHash();
120+
121+
// Then
122+
123+
// The hash is calculated from the minified JSON. For readability, the JSON
124+
// shown below is formatted. `Dtj7Jx6NVlbDUnA3vmH1nNZw+o8=` is the
125+
// Base64-encoded SHA-1 hash for this JSON (minified):
126+
//
127+
// {
128+
// "Image": null,
129+
// "Name": null,
130+
// "Entrypoint": null,
131+
// "Command": [],
132+
// "Environments": {
133+
// "keyA": "valueA",
134+
// "keyB": "valueB"
135+
// },
136+
// "ExposedPorts": {},
137+
// "PortBindings": {},
138+
// "NetworkAliases": [],
139+
// "Labels": {
140+
// "labelA": "A",
141+
// "labelB": "B",
142+
// "org.testcontainers": "true",
143+
// "org.testcontainers.lang": "dotnet"
144+
// }
145+
// }
146+
Assert.Equal("Dtj7Jx6NVlbDUnA3vmH1nNZw+o8=", hash);
122147
}
123148

124149
[Fact]
125150
[Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))]
126-
public void ForGivenConfiguration()
151+
public void ForSameConfigurationInDifferentOrder()
127152
{
128-
var env = new Dictionary<string, string>
129-
{
130-
["keyB"] = "valueB",
131-
["keyA"] = "valueA",
132-
};
133-
var hash = new ReuseHashContainerBuilder().WithEnvironment(env).WithLabel("labelB", "B").WithLabel("labelA", "A").GetReuseHash();
134-
135-
// 50MEP+vnxEkQFo5PrndJ7oKOfh8= is the base64 encoded SHA1 of this JSON:
136-
// {"Image":null,"Name":null,"Entrypoint":null,"Command":[],"Environments":{"keyA":"valueA","keyB":"valueB"},"ExposedPorts":{},"PortBindings":{},"NetworkAliases":[],"ExtraHosts":[],"Labels":{"labelA":"A","labelB":"B","org.testcontainers":"true","org.testcontainers.lang":"dotnet"}}
137-
Assert.Equal("50MEP+vnxEkQFo5PrndJ7oKOfh8=", hash);
153+
// Given
154+
var env1 = new Dictionary<string, string>();
155+
env1["keyA"] = "valueA";
156+
env1["keyB"] = "valueB";
157+
158+
var env2 = new Dictionary<string, string>();
159+
env2["keyB"] = "valueB";
160+
env2["keyA"] = "valueA";
161+
162+
// When
163+
var hash1 = new ReuseHashContainerBuilder()
164+
.WithEnvironment(env1)
165+
.WithLabel("labelA", "A")
166+
.WithLabel("labelB", "B")
167+
.GetReuseHash();
168+
169+
var hash2 = new ReuseHashContainerBuilder()
170+
.WithEnvironment(env2)
171+
.WithLabel("labelB", "B")
172+
.WithLabel("labelA", "A")
173+
.GetReuseHash();
174+
175+
// Then
176+
Assert.Equal(hash1, hash2);
138177
}
139178
}
140179

@@ -226,7 +265,12 @@ public void EnabledCleanUpThrowsException()
226265
private sealed class ReuseHashContainerBuilder : ContainerBuilder<ReuseHashContainerBuilder, DockerContainer, ContainerConfiguration>
227266
{
228267
public ReuseHashContainerBuilder() : this(new ContainerConfiguration())
229-
=> DockerResourceConfiguration = Init().DockerResourceConfiguration;
268+
{
269+
// By default, the constructor calls `Init()`, which sets up the default builder
270+
// configurations, including ones for the port forwarding container if it's running.
271+
// To avoid applying those settings during tests, this class intentionally doesn't
272+
// call `Init()`.
273+
}
230274

231275
private ReuseHashContainerBuilder(ContainerConfiguration configuration) : base(configuration)
232276
=> DockerResourceConfiguration = configuration;

tests/Testcontainers.Platform.Linux.Tests/WaitStrategyModeTest.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,13 @@ namespace Testcontainers.Tests;
22

33
public abstract class WaitStrategyModeTest : IAsyncLifetime
44
{
5-
private const string Message = "Hello, World!";
6-
75
private readonly IContainer _container;
86

97
private WaitStrategyModeTest(WaitStrategyMode waitStrategyMode)
108
{
119
_container = new ContainerBuilder()
1210
.WithImage(CommonImages.Alpine)
13-
.WithEntrypoint("/bin/sh", "-c")
14-
.WithCommand("echo " + Message)
15-
.WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged(Message, o => o.WithMode(waitStrategyMode)))
11+
.WithWaitStrategy(Wait.ForUnixContainer().AddCustomWaitStrategy(new WaitUntil(), o => o.WithMode(waitStrategyMode)))
1612
.Build();
1713
}
1814

@@ -68,4 +64,12 @@ public async Task StartAsyncShouldSucceedWhenContainerIsNotRunning()
6864
Assert.Null(exception);
6965
}
7066
}
67+
68+
private sealed class WaitUntil : IWaitUntil
69+
{
70+
public Task<bool> UntilAsync(IContainer container)
71+
{
72+
return Task.FromResult(TestcontainersStates.Exited.Equals(container.State));
73+
}
74+
}
7175
}

0 commit comments

Comments
 (0)