diff --git a/Directory.Build.props b/Directory.Build.props
index eecbb8220..5c261f63f 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,6 +1,6 @@
- net6.0;net7.0;net8.0
+ net48;net6.0;net7.0;net8.0trueenableenable
@@ -16,4 +16,9 @@
2.59.02.59.0
+
+
+
+
+
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 1921131a9..c12d9449c 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -1,5 +1,5 @@
-
+ EventStore.Client
@@ -12,8 +12,8 @@
-
-
+
+
@@ -30,8 +30,8 @@
-
-
+
+
@@ -43,11 +43,11 @@
allruntime; build; native; contentfiles; analyzers
-
+
-
+
diff --git a/src/EventStore.Client.Common/EpochExtensions.cs b/src/EventStore.Client.Common/EpochExtensions.cs
index d62bdb44c..db59e620d 100644
--- a/src/EventStore.Client.Common/EpochExtensions.cs
+++ b/src/EventStore.Client.Common/EpochExtensions.cs
@@ -1,7 +1,23 @@
namespace EventStore.Client;
static class EpochExtensions {
+#if NET
static readonly DateTime UnixEpoch = DateTime.UnixEpoch;
+#else
+ const long TicksPerMillisecond = 10000;
+ const long TicksPerSecond = TicksPerMillisecond * 1000;
+ const long TicksPerMinute = TicksPerSecond * 60;
+ const long TicksPerHour = TicksPerMinute * 60;
+ const long TicksPerDay = TicksPerHour * 24;
+ const int DaysPerYear = 365;
+ const int DaysPer4Years = DaysPerYear * 4 + 1;
+ const int DaysPer100Years = DaysPer4Years * 25 - 1;
+ const int DaysPer400Years = DaysPer100Years * 4 + 1;
+ const int DaysTo1970 = DaysPer400Years * 4 + DaysPer100Years * 3 + DaysPer4Years * 17 + DaysPerYear;
+ const long UnixEpochTicks = DaysTo1970 * TicksPerDay;
+
+ static readonly DateTime UnixEpoch = new(UnixEpochTicks, DateTimeKind.Utc);
+#endif
public static DateTime FromTicksSinceEpoch(this long value) => new(UnixEpoch.Ticks + value, DateTimeKind.Utc);
diff --git a/src/EventStore.Client.Common/Shims/Index.cs b/src/EventStore.Client.Common/Shims/Index.cs
new file mode 100644
index 000000000..67af4a05d
--- /dev/null
+++ b/src/EventStore.Client.Common/Shims/Index.cs
@@ -0,0 +1,131 @@
+#if !NET
+using System.Runtime.CompilerServices;
+
+namespace System;
+
+/// Represent a type can be used to index a collection either from the start or the end.
+///
+/// Index is used by the C# compiler to support the new index syntax
+///
+/// int[] someArray = new int[5] { 1, 2, 3, 4, 5 } ;
+/// int lastElement = someArray[^1]; // lastElement = 5
+///
+///
+internal readonly struct Index : IEquatable
+{
+ private readonly int _value;
+
+ /// Construct an Index using a value and indicating if the index is from the start or from the end.
+ /// The index value. it has to be zero or positive number.
+ /// Indicating if the index is from the start or from the end.
+ ///
+ /// If the Index constructed from the end, index value 1 means pointing at the last element and index value 0 means pointing at beyond last element.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Index(int value, bool fromEnd = false)
+ {
+ if (value < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative");
+ }
+
+ if (fromEnd)
+ _value = ~value;
+ else
+ _value = value;
+ }
+
+ // The following private constructors mainly created for perf reason to avoid the checks
+ private Index(int value)
+ {
+ _value = value;
+ }
+
+ /// Create an Index pointing at first element.
+ public static Index Start => new Index(0);
+
+ /// Create an Index pointing at beyond last element.
+ public static Index End => new Index(~0);
+
+ /// Create an Index from the start at the position indicated by the value.
+ /// The index value from the start.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Index FromStart(int value)
+ {
+ if (value < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative");
+ }
+
+ return new Index(value);
+ }
+
+ /// Create an Index from the end at the position indicated by the value.
+ /// The index value from the end.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Index FromEnd(int value)
+ {
+ if (value < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), "value must be non-negative");
+ }
+
+ return new Index(~value);
+ }
+
+ /// Returns the index value.
+ public int Value
+ {
+ get
+ {
+ if (_value < 0)
+ return ~_value;
+ else
+ return _value;
+ }
+ }
+
+ /// Indicates whether the index is from the start or the end.
+ public bool IsFromEnd => _value < 0;
+
+ /// Calculate the offset from the start using the giving collection length.
+ /// The length of the collection that the Index will be used with. length has to be a positive value
+ ///
+ /// For performance reason, we don't validate the input length parameter and the returned offset value against negative values.
+ /// we don't validate either the returned offset is greater than the input length.
+ /// It is expected Index will be used with collections which always have non negative length/count. If the returned offset is negative and
+ /// then used to index a collection will get out of range exception which will be same affect as the validation.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int GetOffset(int length)
+ {
+ int offset = _value;
+ if (IsFromEnd)
+ {
+ // offset = length - (~value)
+ // offset = length + (~(~value) + 1)
+ // offset = length + value + 1
+
+ offset += length + 1;
+ }
+ return offset;
+ }
+
+ /// Indicates whether the current Index object is equal to another object of the same type.
+ /// An object to compare with this object
+ public override bool Equals(object? value) => value is Index && _value == ((Index)value)._value;
+
+ /// Indicates whether the current Index object is equal to another Index object.
+ /// An object to compare with this object
+ public bool Equals(Index other) => _value == other._value;
+
+ /// Returns the hash code for this instance.
+ public override int GetHashCode() => _value;
+
+ /// Converts integer number to an Index.
+ public static implicit operator Index(int value) => FromStart(value);
+
+ /// Converts the value of the current Index object to its equivalent string representation.
+ public override string ToString() => IsFromEnd ? $"^{((uint)Value)}" : ((uint)Value).ToString();
+}
+#endif
diff --git a/src/EventStore.Client.Common/Shims/IsExternalInit.cs b/src/EventStore.Client.Common/Shims/IsExternalInit.cs
new file mode 100644
index 000000000..a77ccc3c3
--- /dev/null
+++ b/src/EventStore.Client.Common/Shims/IsExternalInit.cs
@@ -0,0 +1,9 @@
+#if !NET
+using System.ComponentModel;
+
+// ReSharper disable once CheckNamespace
+namespace System.Runtime.CompilerServices;
+
+[EditorBrowsable(EditorBrowsableState.Never)]
+internal class IsExternalInit{}
+#endif
diff --git a/src/EventStore.Client.Common/Shims/Range.cs b/src/EventStore.Client.Common/Shims/Range.cs
new file mode 100644
index 000000000..9d4b88f2f
--- /dev/null
+++ b/src/EventStore.Client.Common/Shims/Range.cs
@@ -0,0 +1,79 @@
+#if !NET
+using System.Runtime.CompilerServices;
+
+namespace System;
+
+/// Represent a range has start and end indexes.
+///
+/// Range is used by the C# compiler to support the range syntax.
+///
+/// int[] someArray = new int[5] { 1, 2, 3, 4, 5 };
+/// int[] subArray1 = someArray[0..2]; // { 1, 2 }
+/// int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 }
+///
+///
+internal readonly struct Range : IEquatable
+{
+ /// Represent the inclusive start index of the Range.
+ public Index Start { get; }
+
+ /// Represent the exclusive end index of the Range.
+ public Index End { get; }
+
+ /// Construct a Range object using the start and end indexes.
+ /// Represent the inclusive start index of the range.
+ /// Represent the exclusive end index of the range.
+ public Range(Index start, Index end)
+ {
+ Start = start;
+ End = end;
+ }
+
+ /// Indicates whether the current Range object is equal to another object of the same type.
+ /// An object to compare with this object
+ public override bool Equals(object? value) =>
+ value is Range r &&
+ r.Start.Equals(Start) &&
+ r.End.Equals(End);
+
+ /// Indicates whether the current Range object is equal to another Range object.
+ /// An object to compare with this object
+ public bool Equals(Range other) => other.Start.Equals(Start) && other.End.Equals(End);
+
+ /// Returns the hash code for this instance.
+ public override int GetHashCode() => Start.GetHashCode() * 31 + End.GetHashCode();
+
+ /// Converts the value of the current Range object to its equivalent string representation.
+ public override string ToString() => $"{Start}..{End}";
+
+ /// Create a Range object starting from start index to the end of the collection.
+ public static Range StartAt(Index start) => new(start, Index.End);
+
+ /// Create a Range object starting from first element in the collection to the end Index.
+ public static Range EndAt(Index end) => new(Index.Start, end);
+
+ /// Create a Range object starting from first element to the end.
+ public static Range All => new(Index.Start, Index.End);
+
+ /// Calculate the start offset and length of range object using a collection length.
+ /// The length of the collection that the range will be used with. length has to be a positive value.
+ ///
+ /// For performance reason, we don't validate the input length parameter against negative values.
+ /// It is expected Range will be used with collections which always have non negative length/count.
+ /// We validate the range is inside the length scope though.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public (int Offset, int Length) GetOffsetAndLength(int length)
+ {
+ int start = Start.GetOffset(length);
+ int end = End.GetOffset(length);
+
+ if ((uint)end > (uint)length || (uint)start > (uint)end)
+ {
+ throw new ArgumentOutOfRangeException(nameof(length));
+ }
+
+ return (start, end - start);
+ }
+}
+#endif
diff --git a/src/EventStore.Client.Common/Shims/TaskCompletionSource.cs b/src/EventStore.Client.Common/Shims/TaskCompletionSource.cs
new file mode 100644
index 000000000..e7e88a97f
--- /dev/null
+++ b/src/EventStore.Client.Common/Shims/TaskCompletionSource.cs
@@ -0,0 +1,8 @@
+#if !NET
+namespace System.Threading.Tasks;
+
+internal class TaskCompletionSource : TaskCompletionSource
diff --git a/test/EventStore.Client.Streams.Tests/Subscriptions/reconnection.cs b/test/EventStore.Client.Streams.Tests/Subscriptions/reconnection.cs
index df9162130..db9601d4f 100644
--- a/test/EventStore.Client.Streams.Tests/Subscriptions/reconnection.cs
+++ b/test/EventStore.Client.Streams.Tests/Subscriptions/reconnection.cs
@@ -6,7 +6,7 @@ namespace EventStore.Client.Streams.Tests.Subscriptions;
[Trait("Category", "Subscriptions")]
public class @reconnection(ITestOutputHelper output, ReconnectionFixture fixture) : EventStoreTests(output, fixture) {
[Theory]
- [InlineData(4, 1000, 0, 15000)]
+ [InlineData(4, 5000, 0, 30000)]
public async Task when_the_connection_is_lost(int expectedNumberOfEvents, int reconnectDelayMs, int serviceRestartDelayMs, int testTimeoutMs) {
using var cancellator = new CancellationTokenSource().With(x => x.CancelAfter(testTimeoutMs));
diff --git a/test/EventStore.Client.Tests.Common/ApplicationInfo.cs b/test/EventStore.Client.Tests.Common/ApplicationInfo.cs
index 8a7a1ec7a..0120c21b4 100644
--- a/test/EventStore.Client.Tests.Common/ApplicationInfo.cs
+++ b/test/EventStore.Client.Tests.Common/ApplicationInfo.cs
@@ -25,7 +25,7 @@ static Application() {
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", true)
- .AddJsonFile($"appsettings.{Environment}.json", true) // Accept default naming convention
+ .AddJsonFile($"appsettings.{Environment}.json", true) // Accept default naming convention
.AddJsonFile($"appsettings.{Environment.ToLowerInvariant()}.json", true) // Linux is case sensitive
.AddEnvironmentVariables();
@@ -33,7 +33,7 @@ static Application() {
WriteLine($"APP: {Environment} configuration loaded "
+ $"with {Configuration.AsEnumerable().Count()} entries "
- + $"from {builder.Sources.Count} sources.");
+ + $"from {builder.Sources.Count()} sources.");
IsDevelopment = IsEnvironment(Environments.Development);
IsStaging = IsEnvironment(Environments.Staging);
@@ -65,4 +65,4 @@ public static class OperatingSystem {
public static bool IsMacOS() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
public static bool IsLinux() => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
}
-}
\ No newline at end of file
+}
diff --git a/test/EventStore.Client.Tests.Common/EventStore.Client.Tests.Common.csproj b/test/EventStore.Client.Tests.Common/EventStore.Client.Tests.Common.csproj
index ba1a806f8..f666f3871 100644
--- a/test/EventStore.Client.Tests.Common/EventStore.Client.Tests.Common.csproj
+++ b/test/EventStore.Client.Tests.Common/EventStore.Client.Tests.Common.csproj
@@ -4,38 +4,40 @@
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
certs\%(RecursiveDir)/%(FileName)%(Extension)
@@ -65,6 +67,6 @@
-
+
diff --git a/test/EventStore.Client.Tests.Common/Fixtures/Base/EventStoreClientFixtureBase.cs b/test/EventStore.Client.Tests.Common/Fixtures/Base/EventStoreClientFixtureBase.cs
index 2a5076437..522495198 100644
--- a/test/EventStore.Client.Tests.Common/Fixtures/Base/EventStoreClientFixtureBase.cs
+++ b/test/EventStore.Client.Tests.Common/Fixtures/Base/EventStoreClientFixtureBase.cs
@@ -14,7 +14,7 @@ namespace EventStore.Client;
public abstract class EventStoreClientFixtureBase : IAsyncLifetime {
public const string TestEventType = "-";
- const string ConnectionStringSingle = "esdb://admin:changeit@localhost:2113/?tlsVerifyCert=false";
+ const string ConnectionStringSingle = "esdb://admin:changeit@localhost:2113/?tls=true&tlsVerifyCert=false";
const string ConnectionStringCluster = "esdb://admin:changeit@localhost:2113,localhost:2112,localhost:2111?tls=true&tlsVerifyCert=false";
static readonly Subject LogEventSubject = new();
@@ -88,9 +88,6 @@ static void ConfigureLogging() {
.WriteTo.Seq("http://localhost:5341/", period: TimeSpan.FromMilliseconds(1));
Log.Logger = loggerConfiguration.CreateLogger();
-#if GRPC_CORE
- GrpcEnvironment.SetLogger(new GrpcCoreSerilogLogger(Log.Logger.ForContext()));
-#endif
AppDomain.CurrentDomain.DomainUnload += (_, e) => Log.CloseAndFlush();
}
@@ -147,4 +144,4 @@ public void CaptureLogs(ITestOutputHelper testOutputHelper) {
_disposables.Add(subscription);
}
-}
\ No newline at end of file
+}
diff --git a/test/EventStore.Client.Tests.Common/Fixtures/Base/EventStoreTestServer.cs b/test/EventStore.Client.Tests.Common/Fixtures/Base/EventStoreTestServer.cs
index 772946347..fb7e5f601 100644
--- a/test/EventStore.Client.Tests.Common/Fixtures/Base/EventStoreTestServer.cs
+++ b/test/EventStore.Client.Tests.Common/Fixtures/Base/EventStoreTestServer.cs
@@ -1,4 +1,5 @@
using System.Net;
+using System.Net.Http;
using Ductus.FluentDocker.Builders;
using Ductus.FluentDocker.Extensions;
using Ductus.FluentDocker.Model.Builders;
@@ -24,13 +25,19 @@ public EventStoreTestServer(
_hostCertificatePath = hostCertificatePath;
VerifyCertificatesExist();
- _httpClient = new(
- new SocketsHttpHandler {
- SslOptions = { RemoteCertificateValidationCallback = delegate { return true; } }
- }
- ) {
- BaseAddress = address
+#if NET
+ _httpClient = new HttpClient(new SocketsHttpHandler {
+ SslOptions = {RemoteCertificateValidationCallback = delegate { return true; }}
+ }) {
+ BaseAddress = address,
};
+#else
+ _httpClient = new HttpClient(new WinHttpHandler {
+ ServerCertificateValidationCallback = delegate { return true; }
+ }) {
+ BaseAddress = address,
+ };
+#endif
var env = new Dictionary {
["EVENTSTORE_DB_LOG_FORMAT"] = "V2",
@@ -46,8 +53,8 @@ public EventStoreTestServer(
["EVENTSTORE_DISABLE_LOG_FILE"] = "true"
};
- foreach (var (key, value) in envOverrides ?? Enumerable.Empty>())
- env[key] = value;
+ foreach (var val in envOverrides ?? Enumerable.Empty>())
+ env[val.Key] = val.Value;
_eventStore = new Builder()
.UseContainer()
@@ -89,7 +96,7 @@ public ValueTask DisposeAsync() {
_httpClient?.Dispose();
_eventStore?.Dispose();
- return ValueTask.CompletedTask;
+ return new ValueTask(Task.CompletedTask);
}
static Version GetVersion() {
@@ -125,4 +132,4 @@ void VerifyCertificatesExist() {
$"Could not locate the certificates file {file} needed to run EventStoreDB. Please run the 'gencert' tool at the root of the repository."
);
}
-}
\ No newline at end of file
+}
diff --git a/test/EventStore.Client.Tests.Common/Fixtures/Base/EventStoreTestServerCluster.cs b/test/EventStore.Client.Tests.Common/Fixtures/Base/EventStoreTestServerCluster.cs
index e9317c88e..ceb263e15 100644
--- a/test/EventStore.Client.Tests.Common/Fixtures/Base/EventStoreTestServerCluster.cs
+++ b/test/EventStore.Client.Tests.Common/Fixtures/Base/EventStoreTestServerCluster.cs
@@ -1,4 +1,5 @@
using System.Net;
+using System.Net.Http;
using Ductus.FluentDocker.Builders;
using Ductus.FluentDocker.Common;
using Ductus.FluentDocker.Services;
@@ -21,13 +22,19 @@ public EventStoreTestServerCluster(
_eventStoreCluster = BuildCluster(envOverrides);
- _httpClient = new(
- new SocketsHttpHandler {
- SslOptions = { RemoteCertificateValidationCallback = delegate { return true; } }
- }
- ) {
- BaseAddress = address
+#if NET
+ _httpClient = new HttpClient(new SocketsHttpHandler {
+ SslOptions = {RemoteCertificateValidationCallback = delegate { return true; }}
+ }) {
+ BaseAddress = address,
};
+#else
+ _httpClient = new HttpClient(new WinHttpHandler {
+ ServerCertificateValidationCallback = delegate { return true; }
+ }) {
+ BaseAddress = address,
+ };
+#endif
}
public async Task StartAsync(CancellationToken cancellationToken = default) {
@@ -83,4 +90,4 @@ ICompositeService BuildCluster(IDictionary? envOverrides = null)
.RemoveOrphans()
.Build();
}
-}
\ No newline at end of file
+}
diff --git a/test/EventStore.Client.Tests.Common/Fixtures/Base/EventStoreTestServerExternal.cs b/test/EventStore.Client.Tests.Common/Fixtures/Base/EventStoreTestServerExternal.cs
index 1b6ff3492..19b866a63 100644
--- a/test/EventStore.Client.Tests.Common/Fixtures/Base/EventStoreTestServerExternal.cs
+++ b/test/EventStore.Client.Tests.Common/Fixtures/Base/EventStoreTestServerExternal.cs
@@ -4,5 +4,5 @@ public class EventStoreTestServerExternal : IEventStoreTestServer {
public Task StartAsync(CancellationToken cancellationToken = default) => Task.CompletedTask;
public void Stop() { }
- public ValueTask DisposeAsync() => ValueTask.CompletedTask;
-}
\ No newline at end of file
+ public ValueTask DisposeAsync() => new ValueTask(Task.CompletedTask);
+}
diff --git a/test/EventStore.Client.Tests.Common/Fixtures/EventStoreTestCluster.cs b/test/EventStore.Client.Tests.Common/Fixtures/EventStoreTestCluster.cs
index f97b852e2..ad8246843 100644
--- a/test/EventStore.Client.Tests.Common/Fixtures/EventStoreTestCluster.cs
+++ b/test/EventStore.Client.Tests.Common/Fixtures/EventStoreTestCluster.cs
@@ -24,7 +24,6 @@ public static EventStoreFixtureOptions DefaultOptions() {
["EVENTSTORE_INT_TCP_PORT"] = "1112",
["EVENTSTORE_HTTP_PORT"] = "2113",
["EVENTSTORE_DISCOVER_VIA_DNS"] = "false",
- ["EVENTSTORE_ENABLE_EXTERNAL_TCP"] = "false",
["EVENTSTORE_STREAM_EXISTENCE_FILTER_SIZE"] = "10000",
["EVENTSTORE_STREAM_INFO_CACHE_CAPACITY"] = "10000"
};
@@ -50,4 +49,4 @@ protected override CompositeBuilder Configure() {
protected override async Task OnServiceStarted() {
await Service.WaitUntilNodesAreHealthy("esdb-node", TimeSpan.FromSeconds(60));
}
-}
\ No newline at end of file
+}
diff --git a/test/EventStore.Client.Tests.Common/Fixtures/EventStoreTestNode.cs b/test/EventStore.Client.Tests.Common/Fixtures/EventStoreTestNode.cs
index 46ae316da..e767b3bae 100644
--- a/test/EventStore.Client.Tests.Common/Fixtures/EventStoreTestNode.cs
+++ b/test/EventStore.Client.Tests.Common/Fixtures/EventStoreTestNode.cs
@@ -1,4 +1,5 @@
using System.Net;
+using System.Net.Http;
using System.Net.Sockets;
using Ductus.FluentDocker.Builders;
using Ductus.FluentDocker.Common;
@@ -31,7 +32,6 @@ public static EventStoreFixtureOptions DefaultOptions() {
.With(x => x.ConnectivitySettings.DiscoveryInterval = FromSeconds(1));
var defaultEnvironment = new Dictionary(GlobalEnvironment.Variables) {
- ["EVENTSTORE_ENABLE_EXTERNAL_TCP"] = "false",
["EVENTSTORE_MEM_DB"] = "true",
["EVENTSTORE_CHUNK_SIZE"] = (1024 * 1024).ToString(),
["EVENTSTORE_CERTIFICATE_FILE"] = "/etc/eventstore/certs/node/node.crt",
@@ -82,7 +82,11 @@ protected override ContainerBuilder Configure() {
protected override async Task OnServiceStarted() {
using var http = new HttpClient(
+#if NET
new SocketsHttpHandler { SslOptions = { RemoteCertificateValidationCallback = delegate { return true; } } }
+#else
+ new WinHttpHandler { ServerCertificateValidationCallback = delegate { return true; } }
+#endif
) {
BaseAddress = Options.ClientSettings.ConnectivitySettings.Address
};
@@ -132,7 +136,11 @@ public async Task GetNextAvailablePort(TimeSpan delay = default) {
await Task.Delay(delay);
}
finally {
+#if NET
if (socket.Connected) await socket.DisconnectAsync(true);
+#else
+ if (socket.Connected) socket.Disconnect(true);
+#endif
}
}
}
@@ -142,4 +150,4 @@ public async Task GetNextAvailablePort(TimeSpan delay = default) {
}
public int NextAvailablePort => GetNextAvailablePort(FromMilliseconds(100)).GetAwaiter().GetResult();
-}
\ No newline at end of file
+}
diff --git a/test/EventStore.Client.Tests.Common/FluentDocker/FluentDockerServiceExtensions.cs b/test/EventStore.Client.Tests.Common/FluentDocker/FluentDockerServiceExtensions.cs
index e763edd35..c4773cede 100644
--- a/test/EventStore.Client.Tests.Common/FluentDocker/FluentDockerServiceExtensions.cs
+++ b/test/EventStore.Client.Tests.Common/FluentDocker/FluentDockerServiceExtensions.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
+
using Ductus.FluentDocker.Common;
using Ductus.FluentDocker.Model.Containers;
using Ductus.FluentDocker.Services;
@@ -29,17 +31,32 @@ public static async ValueTask WaitUntilNodesAreHealthy(this IContainerService se
public static async Task WaitUntilNodesAreHealthy(this ICompositeService service, IEnumerable services, CancellationToken cancellationToken) {
var nodes = service.Containers.Where(x => services.Contains(x.Name));
-
+#if NET
await Parallel.ForEachAsync(nodes, cancellationToken, async (node, ct) => await node.WaitUntilNodesAreHealthy(ct));
+#else
+ Parallel.ForEach(
+ nodes,
+ node => { node.WaitUntilNodesAreHealthy(cancellationToken).GetAwaiter().GetResult(); }
+ );
+#endif
}
public static async Task WaitUntilNodesAreHealthy(this ICompositeService service, string serviceNamePrefix, CancellationToken cancellationToken) {
var nodes = service.Containers.Where(x => x.Name.StartsWith(serviceNamePrefix));
+
+#if NET
await Parallel.ForEachAsync(nodes, cancellationToken, async (node, ct) => await node.WaitUntilNodesAreHealthy(ct));
+#else
+ Parallel.ForEach(
+ nodes,
+ node => { node.WaitUntilNodesAreHealthy(cancellationToken).GetAwaiter().GetResult(); }
+ );
+#endif
}
public static async Task WaitUntilNodesAreHealthy(this ICompositeService service, string serviceNamePrefix, TimeSpan timeout) {
using var cts = new CancellationTokenSource(timeout);
+
await WaitUntilNodesAreHealthy(service, serviceNamePrefix, cts.Token);
}
-}
\ No newline at end of file
+}
diff --git a/test/EventStore.Client.Tests.Common/FluentDocker/TestBypassService.cs b/test/EventStore.Client.Tests.Common/FluentDocker/TestBypassService.cs
index 2fb3e805c..3505eb9af 100644
--- a/test/EventStore.Client.Tests.Common/FluentDocker/TestBypassService.cs
+++ b/test/EventStore.Client.Tests.Common/FluentDocker/TestBypassService.cs
@@ -25,7 +25,7 @@ public override async Task Stop() {
}
}
- public override ValueTask DisposeAsync() => ValueTask.CompletedTask;
+ public override ValueTask DisposeAsync() => new ValueTask();
}
public sealed class BypassService : IService {
@@ -58,4 +58,4 @@ public BypassBuilder() : this(null) { }
public override BypassService Build() => new BypassService();
protected override IBuilder InternalCreate() => this;
-}
\ No newline at end of file
+}
diff --git a/test/EventStore.Client.Tests.Common/FluentDocker/TestService.cs b/test/EventStore.Client.Tests.Common/FluentDocker/TestService.cs
index 026e0a12c..890f4e9b6 100644
--- a/test/EventStore.Client.Tests.Common/FluentDocker/TestService.cs
+++ b/test/EventStore.Client.Tests.Common/FluentDocker/TestService.cs
@@ -1,6 +1,7 @@
using Ductus.FluentDocker.Builders;
using Ductus.FluentDocker.Common;
using Ductus.FluentDocker.Services;
+using Google.Protobuf.WellKnownTypes;
using Serilog;
using static Serilog.Core.Constants;
@@ -10,10 +11,10 @@ public interface ITestService : IAsyncDisposable {
Task Start();
Task Stop();
Task Restart(TimeSpan delay);
-
- Task Restart() => Restart(TimeSpan.Zero);
-
- void ReportStatus();
+
+ Task Restart();
+
+ void ReportStatus();
}
public abstract class TestService : ITestService where TService : IService where TBuilder : BaseBuilder {
@@ -25,6 +26,11 @@ public abstract class TestService : ITestService where TServ
INetworkService? Network { get; set; } = null!;
+ public Task Restart()
+ {
+ return Restart(TimeSpan.Zero);
+ }
+
public virtual async Task Start() {
Logger.Information("Container service starting");
@@ -63,7 +69,7 @@ public virtual async Task Stop() {
throw new FluentDockerException("Failed to stop container service", ex);
}
}
-
+
public virtual async Task Restart(TimeSpan delay) {
try {
try {
@@ -73,9 +79,9 @@ public virtual async Task Restart(TimeSpan delay) {
catch (Exception ex) {
throw new FluentDockerException("Failed to stop container service", ex);
}
-
+
await Task.Delay(delay);
-
+
Logger.Information("Container service starting...");
try {
@@ -132,11 +138,11 @@ public virtual ValueTask DisposeAsync() {
throw new FluentDockerException("Failed to dispose of container service", ex);
}
- return ValueTask.CompletedTask;
+ return default;
}
protected abstract TBuilder Configure();
protected virtual Task OnServiceStarted() => Task.CompletedTask;
protected virtual Task OnServiceStop() => Task.CompletedTask;
-}
\ No newline at end of file
+}
diff --git a/test/EventStore.Client.Tests.Common/GlobalEnvironment.cs b/test/EventStore.Client.Tests.Common/GlobalEnvironment.cs
index f2e1e69eb..74f8838da 100644
--- a/test/EventStore.Client.Tests.Common/GlobalEnvironment.cs
+++ b/test/EventStore.Client.Tests.Common/GlobalEnvironment.cs
@@ -52,11 +52,11 @@ public static IDictionary GetEnvironmentVariables(IDictionary>()) {
- if (key.StartsWith("EVENTSTORE") && !SharedEnv.Contains(key))
- throw new Exception($"Add {key} to shared.env and _sharedEnv to pass it to the cluster containers");
+ foreach (var @override in overrides ?? Enumerable.Empty>()) {
+ if (@override.Key.StartsWith("EVENTSTORE") && !SharedEnv.Contains(@override.Key))
+ throw new Exception($"Add {@override.Key} to shared.env and _sharedEnv to pass it to the cluster containers");
- env[key] = value;
+ env[@override.Key] = @override.Value;
}
return env;
@@ -73,4 +73,4 @@ public static IDictionary GetEnvironmentVariables(IDictionary(handler);
if (!tlsVerifyCert) {
Assert.NotNull(socketsHandler.SslOptions.RemoteCertificateValidationCallback);
@@ -128,6 +130,16 @@ public void tls_verify_cert(bool tlsVerifyCert) {
else {
Assert.Null(socketsHandler.SslOptions.RemoteCertificateValidationCallback);
}
+#else
+ var socketsHandler = Assert.IsType(handler);
+ if (!tlsVerifyCert) {
+ Assert.NotNull(socketsHandler.ServerCertificateValidationCallback);
+ Assert.True(socketsHandler.ServerCertificateValidationCallback!.Invoke(null!, default!,
+ default!, default));
+ } else {
+ Assert.Null(socketsHandler.ServerCertificateValidationCallback);
+ }
+#endif
}
#endif
@@ -141,10 +153,15 @@ public void infinite_grpc_timeouts() {
using var handler = result.CreateHttpMessageHandler?.Invoke();
+#if NET
var socketsHandler = Assert.IsType(handler);
-
Assert.Equal(System.Threading.Timeout.InfiniteTimeSpan, socketsHandler.KeepAlivePingTimeout);
Assert.Equal(System.Threading.Timeout.InfiniteTimeSpan, socketsHandler.KeepAlivePingDelay);
+#else
+ var winHttpHandler = Assert.IsType(handler);
+ Assert.Equal(System.Threading.Timeout.InfiniteTimeSpan, winHttpHandler.TcpKeepAliveTime);
+ Assert.Equal(System.Threading.Timeout.InfiniteTimeSpan, winHttpHandler.TcpKeepAliveInterval);
+#endif
}
[Fact]
@@ -360,13 +377,19 @@ static string GetKeyValuePairs(
settings.DefaultDeadline.Value.TotalMilliseconds.ToString()
);
-#if !GRPC_CORE
if (settings.CreateHttpMessageHandler != null) {
using var handler = settings.CreateHttpMessageHandler.Invoke();
+#if NET
if (handler is SocketsHttpHandler socketsHttpHandler &&
socketsHttpHandler.SslOptions.RemoteCertificateValidationCallback != null)
pairs.Add("tlsVerifyCert", "false");
}
+#else
+ if (handler is WinHttpHandler winHttpHandler &&
+ winHttpHandler.ServerCertificateValidationCallback != null) {
+ pairs.Add("tlsVerifyCert", "false");
+ }
+ }
#endif
return string.Join("&", pairs.Select(pair => $"{getKey?.Invoke(pair.Key) ?? pair.Key}={pair.Value}"));
@@ -467,6 +490,7 @@ public bool Equals(EventStoreClientOperationOptions? x, EventStoreClientOperatio
return x.GetType() == y.GetType();
}
- public int GetHashCode(EventStoreClientOperationOptions obj) => System.HashCode.Combine(obj.ThrowOnAppendFailure);
+ public int GetHashCode(EventStoreClientOperationOptions obj) =>
+ System.HashCode.Combine(obj.ThrowOnAppendFailure);
}
-}
\ No newline at end of file
+}
diff --git a/test/EventStore.Client.Tests/EventStore.Client.Tests.csproj b/test/EventStore.Client.Tests/EventStore.Client.Tests.csproj
index 0bd9cef57..494e5e243 100644
--- a/test/EventStore.Client.Tests/EventStore.Client.Tests.csproj
+++ b/test/EventStore.Client.Tests/EventStore.Client.Tests.csproj
@@ -1,27 +1,32 @@
-
+
-
+
-
+
-
+
-
+
+
+
+
+
-
-
- allruntime; build; native; contentfiles; analyzers
+
+
+
+
diff --git a/test/EventStore.Client.Tests/GrpcServerCapabilitiesClientTests.cs b/test/EventStore.Client.Tests/GrpcServerCapabilitiesClientTests.cs
index 9173f83b5..2b13cf13b 100644
--- a/test/EventStore.Client.Tests/GrpcServerCapabilitiesClientTests.cs
+++ b/test/EventStore.Client.Tests/GrpcServerCapabilitiesClientTests.cs
@@ -1,3 +1,4 @@
+#if NET
using System.Net;
using EventStore.Client.ServerFeatures;
using Grpc.Core;
@@ -97,4 +98,5 @@ class FakeServerFeatures : ServerFeatures.ServerFeatures.ServerFeaturesBase {
public override Task GetSupportedMethods(Empty request, ServerCallContext context) => Task.FromResult(_supportedMethods);
}
-}
\ No newline at end of file
+}
+#endif
diff --git a/test/EventStore.Client.UserManagement.Tests/EventStore.Client.UserManagement.Tests.csproj b/test/EventStore.Client.UserManagement.Tests/EventStore.Client.UserManagement.Tests.csproj
index d4b52e67f..abaf5e7bf 100644
--- a/test/EventStore.Client.UserManagement.Tests/EventStore.Client.UserManagement.Tests.csproj
+++ b/test/EventStore.Client.UserManagement.Tests/EventStore.Client.UserManagement.Tests.csproj
@@ -4,9 +4,9 @@
EventStore.Client.Tests
-
+
-
+
\ No newline at end of file