diff --git a/EventStore.Client.sln.DotSettings b/EventStore.Client.sln.DotSettings index 2eda35eca..16d970453 100644 --- a/EventStore.Client.sln.DotSettings +++ b/EventStore.Client.sln.DotSettings @@ -2,8 +2,11 @@ 1000 3000 5000 + DO_NOT_SHOW + DO_NOT_SHOW DO_NOT_SHOW DO_NOT_SHOW + DO_NOT_SHOW True @@ -23,30 +26,36 @@ &lt;inspection_tool class="UnnecessaryReturnJS" enabled="false" level="WARNING" enabled_by_default="false" /&gt; &lt;/profile&gt;</IDEA_SETTINGS><RIDER_SETTINGS>&lt;profile&gt; &lt;Language id="CSS"&gt; - &lt;Reformat&gt;true&lt;/Reformat&gt; &lt;Rearrange&gt;true&lt;/Rearrange&gt; + &lt;Reformat&gt;true&lt;/Reformat&gt; &lt;/Language&gt; &lt;Language id="EditorConfig"&gt; &lt;Reformat&gt;true&lt;/Reformat&gt; &lt;/Language&gt; &lt;Language id="HTML"&gt; - &lt;Reformat&gt;true&lt;/Reformat&gt; &lt;OptimizeImports&gt;false&lt;/OptimizeImports&gt; &lt;Rearrange&gt;true&lt;/Rearrange&gt; + &lt;Reformat&gt;true&lt;/Reformat&gt; &lt;/Language&gt; &lt;Language id="HTTP Request"&gt; &lt;Reformat&gt;true&lt;/Reformat&gt; &lt;/Language&gt; + &lt;Language id="Handlebars"&gt; + &lt;Reformat&gt;true&lt;/Reformat&gt; + &lt;/Language&gt; &lt;Language id="Ini"&gt; &lt;Reformat&gt;true&lt;/Reformat&gt; &lt;/Language&gt; &lt;Language id="JSON"&gt; &lt;Reformat&gt;true&lt;/Reformat&gt; &lt;/Language&gt; - &lt;Language id="JavaScript"&gt; + &lt;Language id="Jade"&gt; &lt;Reformat&gt;true&lt;/Reformat&gt; + &lt;/Language&gt; + &lt;Language id="JavaScript"&gt; &lt;OptimizeImports&gt;false&lt;/OptimizeImports&gt; &lt;Rearrange&gt;true&lt;/Rearrange&gt; + &lt;Reformat&gt;true&lt;/Reformat&gt; &lt;/Language&gt; &lt;Language id="Markdown"&gt; &lt;Reformat&gt;true&lt;/Reformat&gt; @@ -64,9 +73,9 @@ &lt;Reformat&gt;true&lt;/Reformat&gt; &lt;/Language&gt; &lt;Language id="XML"&gt; - &lt;Reformat&gt;true&lt;/Reformat&gt; &lt;OptimizeImports&gt;false&lt;/OptimizeImports&gt; &lt;Rearrange&gt;true&lt;/Rearrange&gt; + &lt;Reformat&gt;true&lt;/Reformat&gt; &lt;/Language&gt; &lt;Language id="yaml"&gt; &lt;Reformat&gt;true&lt;/Reformat&gt; @@ -77,6 +86,8 @@ EventStore ExpressionBody + Implicit + Implicit ExpressionBody ExpressionBody BaseClass @@ -133,6 +144,7 @@ IF_OWNER_IS_SINGLE_LINE IF_OWNER_IS_SINGLE_LINE True + END_OF_LINE True True CHOP_IF_LONG diff --git a/test/EventStore.Client.PersistentSubscriptions.Tests/Bugs/Issue_1125.cs b/test/EventStore.Client.PersistentSubscriptions.Tests/Bugs/Issue_1125.cs index d85900b59..54fa95077 100644 --- a/test/EventStore.Client.PersistentSubscriptions.Tests/Bugs/Issue_1125.cs +++ b/test/EventStore.Client.PersistentSubscriptions.Tests/Bugs/Issue_1125.cs @@ -67,7 +67,8 @@ await _fixture.Client.CreateToStreamAsync( completed.TrySetException(new Exception($"{dr}")); }, userCredentials - )) { + ) + ) { for (var i = 0; i < eventCount; i++) await _fixture.StreamsClient.AppendToStreamAsync( streamName, diff --git a/test/EventStore.Client.Tests.Common/Extensions/EnumerableTaskExtensions.cs b/test/EventStore.Client.Tests.Common/Extensions/EnumerableTaskExtensions.cs index d8e9332d6..fd62d7b4c 100644 --- a/test/EventStore.Client.Tests.Common/Extensions/EnumerableTaskExtensions.cs +++ b/test/EventStore.Client.Tests.Common/Extensions/EnumerableTaskExtensions.cs @@ -3,9 +3,9 @@ namespace EventStore.Client.Tests; public static class EnumerableTaskExtensions { - [DebuggerStepThrough] - public static Task WhenAll(this IEnumerable source) => Task.WhenAll(source); + [DebuggerStepThrough] + public static Task WhenAll(this IEnumerable source) => Task.WhenAll(source); - [DebuggerStepThrough] - public static Task WhenAll(this IEnumerable> source) => Task.WhenAll(source); + [DebuggerStepThrough] + public static Task WhenAll(this IEnumerable> source) => Task.WhenAll(source); } \ No newline at end of file diff --git a/test/EventStore.Client.Tests.Common/Extensions/EventStoreClientExtensions.cs b/test/EventStore.Client.Tests.Common/Extensions/EventStoreClientExtensions.cs index 91767b1cb..e1475bc16 100644 --- a/test/EventStore.Client.Tests.Common/Extensions/EventStoreClientExtensions.cs +++ b/test/EventStore.Client.Tests.Common/Extensions/EventStoreClientExtensions.cs @@ -4,18 +4,21 @@ namespace EventStore.Client.Tests; public static class EventStoreClientExtensions { - public static Task CreateUserWithRetry( - this EventStoreUserManagementClient client, string loginName, string fullName, string[] groups, string password, - UserCredentials? userCredentials = null, CancellationToken cancellationToken = default - ) => - Policy.Handle() - .WaitAndRetryAsync(200, _ => FromMilliseconds(100)) - .ExecuteAsync( - ct => client.CreateUserAsync( - loginName, fullName, groups, password, - userCredentials: userCredentials, - cancellationToken: ct - ), - cancellationToken - ); + public static Task CreateUserWithRetry( + this EventStoreUserManagementClient client, string loginName, string fullName, string[] groups, string password, + UserCredentials? userCredentials = null, CancellationToken cancellationToken = default + ) => + Policy.Handle() + .WaitAndRetryAsync(200, _ => FromMilliseconds(100)) + .ExecuteAsync( + ct => client.CreateUserAsync( + loginName, + fullName, + groups, + password, + userCredentials: userCredentials, + cancellationToken: ct + ), + cancellationToken + ); } \ No newline at end of file diff --git a/test/EventStore.Client.Tests.Common/Extensions/EventStoreClientWarmupExtensions.cs b/test/EventStore.Client.Tests.Common/Extensions/EventStoreClientWarmupExtensions.cs index 7bc1b26a3..1da963738 100644 --- a/test/EventStore.Client.Tests.Common/Extensions/EventStoreClientWarmupExtensions.cs +++ b/test/EventStore.Client.Tests.Common/Extensions/EventStoreClientWarmupExtensions.cs @@ -1,85 +1,89 @@ namespace EventStore.Client.Tests; public static class EventStoreClientWarmupExtensions { - public static Task WarmUp(this EventStoreClient client) => - DatabaseWarmup.TryExecuteOnce(client, async ct => { - // if we can read from $users then we know that - // 1. the users exist - // 2. we are connected to leader if we require it - var users = await client - .ReadStreamAsync( - direction: Direction.Forwards, - streamName: "$users", - revision: StreamPosition.Start, - maxCount: 1, - userCredentials: TestCredentials.Root, - cancellationToken: ct) - .ToArrayAsync(ct); + public static Task WarmUp(this EventStoreClient client) => + DatabaseWarmup.TryExecuteOnce( + client, + async ct => { + // if we can read from $users then we know that + // 1. the users exist + // 2. we are connected to leader if we require it + var users = await client + .ReadStreamAsync( + direction: Direction.Forwards, + streamName: "$users", + revision: StreamPosition.Start, + maxCount: 1, + userCredentials: TestCredentials.Root, + cancellationToken: ct + ) + .ToArrayAsync(ct); - if (users.Length == 0) - throw new ("System is not ready yet..."); + if (users.Length == 0) + throw new("System is not ready yet..."); - // the read from leader above is not enough to guarantee the next write goes to leader - _ = await client.AppendToStreamAsync( - streamName: "warmup", - expectedState: StreamState.Any, - eventData: Enumerable.Empty(), - userCredentials: TestCredentials.Root, - cancellationToken: ct - ); - }); + // the read from leader above is not enough to guarantee the next write goes to leader + _ = await client.AppendToStreamAsync( + streamName: "warmup", + expectedState: StreamState.Any, + eventData: Enumerable.Empty(), + userCredentials: TestCredentials.Root, + cancellationToken: ct + ); + } + ); - public static Task WarmUp(this EventStoreOperationsClient client) => - DatabaseWarmup.TryExecuteOnce( - client, - async ct => { - await client.RestartPersistentSubscriptions( - userCredentials: TestCredentials.Root, - cancellationToken: ct - ); - } - ); + public static Task WarmUp(this EventStoreOperationsClient client) => + DatabaseWarmup.TryExecuteOnce( + client, + async ct => { + await client.RestartPersistentSubscriptions( + userCredentials: TestCredentials.Root, + cancellationToken: ct + ); + } + ); - public static Task WarmUp(this EventStorePersistentSubscriptionsClient client) => - DatabaseWarmup.TryExecuteOnce( - client, - async ct => { - var id = Guid.NewGuid(); - await client.CreateToStreamAsync( - streamName: $"warmup-stream-{id}", - groupName: $"warmup-group-{id}", - settings: new(), - userCredentials: TestCredentials.Root, - cancellationToken: ct - ); - } - ); + public static Task WarmUp(this EventStorePersistentSubscriptionsClient client) => + DatabaseWarmup.TryExecuteOnce( + client, + async ct => { + var id = Guid.NewGuid(); + await client.CreateToStreamAsync( + streamName: $"warmup-stream-{id}", + groupName: $"warmup-group-{id}", + settings: new(), + userCredentials: TestCredentials.Root, + cancellationToken: ct + ); + } + ); - public static Task WarmUp(this EventStoreProjectionManagementClient client) => - DatabaseWarmup.TryExecuteOnce( - client, - async ct => { - _ = await client - .ListAllAsync( - userCredentials: TestCredentials.Root, - cancellationToken: ct - ) - .Take(1) - .ToArrayAsync(ct); + public static Task WarmUp(this EventStoreProjectionManagementClient client) => + DatabaseWarmup.TryExecuteOnce( + client, + async ct => { + _ = await client + .ListAllAsync( + userCredentials: TestCredentials.Root, + cancellationToken: ct + ) + .Take(1) + .ToArrayAsync(ct); - // await client.RestartSubsystemAsync(userCredentials: TestCredentials.Root, cancellationToken: ct); - } - ); + // await client.RestartSubsystemAsync(userCredentials: TestCredentials.Root, cancellationToken: ct); + } + ); - public static Task WarmUp(this EventStoreUserManagementClient client) => - DatabaseWarmup.TryExecuteOnce( - client, - async ct => _ = await client - .ListAllAsync( - userCredentials: TestCredentials.Root, - cancellationToken: ct - ) - .Take(1) - .ToArrayAsync(ct) - ); + public static Task WarmUp(this EventStoreUserManagementClient client) => + DatabaseWarmup.TryExecuteOnce( + client, + async ct => _ = await client + .ListAllAsync( + userCredentials: TestCredentials.Root, + cancellationToken: ct + ) + .Take(1) + .ToArrayAsync(ct) + ); } \ No newline at end of file diff --git a/test/EventStore.Client.Tests.Common/Extensions/OperatingSystemExtensions.cs b/test/EventStore.Client.Tests.Common/Extensions/OperatingSystemExtensions.cs index a53237543..1889b084d 100644 --- a/test/EventStore.Client.Tests.Common/Extensions/OperatingSystemExtensions.cs +++ b/test/EventStore.Client.Tests.Common/Extensions/OperatingSystemExtensions.cs @@ -1,7 +1,7 @@ namespace EventStore.Client; public static class OperatingSystemExtensions { - public static bool IsWindows(this OperatingSystem operatingSystem) => - operatingSystem.Platform != PlatformID.Unix - && operatingSystem.Platform != PlatformID.MacOSX; + public static bool IsWindows(this OperatingSystem operatingSystem) => + operatingSystem.Platform != PlatformID.Unix + && operatingSystem.Platform != PlatformID.MacOSX; } \ No newline at end of file diff --git a/test/EventStore.Client.Tests.Common/Extensions/ReadOnlyMemoryExtensions.cs b/test/EventStore.Client.Tests.Common/Extensions/ReadOnlyMemoryExtensions.cs index 4a99c3762..49ffd1cd9 100644 --- a/test/EventStore.Client.Tests.Common/Extensions/ReadOnlyMemoryExtensions.cs +++ b/test/EventStore.Client.Tests.Common/Extensions/ReadOnlyMemoryExtensions.cs @@ -3,26 +3,26 @@ namespace EventStore.Client; public static class ReadOnlyMemoryExtensions { - public static Position ParsePosition(this ReadOnlyMemory json) { - using var doc = JsonDocument.Parse(json); + public static Position ParsePosition(this ReadOnlyMemory json) { + using var doc = JsonDocument.Parse(json); - var checkPoint = doc.RootElement.GetString(); - if (checkPoint is null) - throw new("Unable to parse Position, data is missing!"); + var checkPoint = doc.RootElement.GetString(); + if (checkPoint is null) + throw new("Unable to parse Position, data is missing!"); - if (Position.TryParse(checkPoint, out var position) && position.HasValue) - return position.Value; + if (Position.TryParse(checkPoint, out var position) && position.HasValue) + return position.Value; - throw new("Unable to parse Position, invalid data!"); - } + throw new("Unable to parse Position, invalid data!"); + } - public static StreamPosition ParseStreamPosition(this ReadOnlyMemory json) { - using var doc = JsonDocument.Parse(json); + public static StreamPosition ParseStreamPosition(this ReadOnlyMemory json) { + using var doc = JsonDocument.Parse(json); - var checkPoint = doc.RootElement.GetString(); - if (checkPoint is null) - throw new("Unable to parse Position, data is missing!"); + var checkPoint = doc.RootElement.GetString(); + if (checkPoint is null) + throw new("Unable to parse Position, data is missing!"); - return StreamPosition.FromInt64(int.Parse(checkPoint)); - } + return StreamPosition.FromInt64(int.Parse(checkPoint)); + } } \ No newline at end of file diff --git a/test/EventStore.Client.Tests.Common/Extensions/TaskExtensions.cs b/test/EventStore.Client.Tests.Common/Extensions/TaskExtensions.cs index cb6d15f5d..7558c3547 100644 --- a/test/EventStore.Client.Tests.Common/Extensions/TaskExtensions.cs +++ b/test/EventStore.Client.Tests.Common/Extensions/TaskExtensions.cs @@ -1,31 +1,33 @@ using System.Diagnostics; -namespace EventStore.Client; +namespace EventStore.Client; public static class TaskExtensions { - public static Task WithTimeout(this Task task, TimeSpan timeout) - => task.WithTimeout(Convert.ToInt32(timeout.TotalMilliseconds)); - - public static async Task WithTimeout(this Task task, int timeoutMs = 10000) { - if (Debugger.IsAttached) { - timeoutMs = -1; - } - - if (await Task.WhenAny(task, Task.Delay(timeoutMs)) != task) - throw new TimeoutException("Timed out waiting for task"); - await task; - } - - public static Task WithTimeout(this Task task, TimeSpan timeout) - => task.WithTimeout(Convert.ToInt32(timeout.TotalMilliseconds)); - - public static async Task WithTimeout(this Task task, int timeoutMs = 10000) { - if (Debugger.IsAttached) { - timeoutMs = -1; - } - - if (await Task.WhenAny(task, Task.Delay(timeoutMs)) == task) - return await task; - throw new TimeoutException("Timed out waiting for task"); - } + public static Task WithTimeout(this Task task, TimeSpan timeout) + => task.WithTimeout(Convert.ToInt32(timeout.TotalMilliseconds)); + + public static async Task WithTimeout(this Task task, int timeoutMs = 10000) { + if (Debugger.IsAttached) { + timeoutMs = -1; + } + + if (await Task.WhenAny(task, Task.Delay(timeoutMs)) != task) + throw new TimeoutException("Timed out waiting for task"); + + await task; + } + + public static Task WithTimeout(this Task task, TimeSpan timeout) + => task.WithTimeout(Convert.ToInt32(timeout.TotalMilliseconds)); + + public static async Task WithTimeout(this Task task, int timeoutMs = 10000) { + if (Debugger.IsAttached) { + timeoutMs = -1; + } + + if (await Task.WhenAny(task, Task.Delay(timeoutMs)) == task) + return await task; + + throw new TimeoutException("Timed out waiting for task"); + } } \ No newline at end of file diff --git a/test/EventStore.Client.Tests.Common/Extensions/WithExtension.cs b/test/EventStore.Client.Tests.Common/Extensions/WithExtension.cs index 8ffc7f58c..b2ea3dbac 100644 --- a/test/EventStore.Client.Tests.Common/Extensions/WithExtension.cs +++ b/test/EventStore.Client.Tests.Common/Extensions/WithExtension.cs @@ -3,60 +3,60 @@ namespace EventStore.Client.Tests; public static class WithExtension { - [DebuggerStepThrough] - public static T With(this T instance, Action action, bool when = true) { - if (when) - action(instance); - - return instance; - } - - [DebuggerStepThrough] - public static T With(this T instance, Func action, bool when = true) => when ? action(instance) : instance; - - [DebuggerStepThrough] - public static T With(this T instance, Action action, Func when) { - if (when is null) - throw new ArgumentNullException(nameof(when)); - - if (when(instance)) - action(instance); - - return instance; - } - - [DebuggerStepThrough] - public static TR WithResult(this T instance, Func action) { - if (action is null) - throw new ArgumentNullException(nameof(action)); - - return action(instance); - } - - [DebuggerStepThrough] - public static T With(this T instance, Func action, Func when) { - if (when is null) - throw new ArgumentNullException(nameof(when)); - - return when(instance) ? action(instance) : instance; - } - - [DebuggerStepThrough] - public static T With(this T instance, Action action, Func when) { - if (when is null) - throw new ArgumentNullException(nameof(when)); - - if (when()) - action(instance); - - return instance; - } - - [DebuggerStepThrough] - public static T With(this T instance, Func action, Func when) { - if (when is null) - throw new ArgumentNullException(nameof(when)); - - return when() ? action(instance) : instance; - } + [DebuggerStepThrough] + public static T With(this T instance, Action action, bool when = true) { + if (when) + action(instance); + + return instance; + } + + [DebuggerStepThrough] + public static T With(this T instance, Func action, bool when = true) => when ? action(instance) : instance; + + [DebuggerStepThrough] + public static T With(this T instance, Action action, Func when) { + if (when is null) + throw new ArgumentNullException(nameof(when)); + + if (when(instance)) + action(instance); + + return instance; + } + + [DebuggerStepThrough] + public static TR WithResult(this T instance, Func action) { + if (action is null) + throw new ArgumentNullException(nameof(action)); + + return action(instance); + } + + [DebuggerStepThrough] + public static T With(this T instance, Func action, Func when) { + if (when is null) + throw new ArgumentNullException(nameof(when)); + + return when(instance) ? action(instance) : instance; + } + + [DebuggerStepThrough] + public static T With(this T instance, Action action, Func when) { + if (when is null) + throw new ArgumentNullException(nameof(when)); + + if (when()) + action(instance); + + return instance; + } + + [DebuggerStepThrough] + public static T With(this T instance, Func action, Func when) { + if (when is null) + throw new ArgumentNullException(nameof(when)); + + return when() ? action(instance) : instance; + } } \ No newline at end of file diff --git a/test/EventStore.Client.Tests.Common/Facts/SupportsPSToAllFact.cs b/test/EventStore.Client.Tests.Common/Facts/SupportsPSToAllFact.cs index 3679d8588..9200aaf46 100644 --- a/test/EventStore.Client.Tests.Common/Facts/SupportsPSToAllFact.cs +++ b/test/EventStore.Client.Tests.Common/Facts/SupportsPSToAllFact.cs @@ -1,19 +1,19 @@ namespace EventStore.Client.Tests; public class SupportsPSToAll { - const int SupportedFromMajorVersion = 21; + const int SupportedFromMajorVersion = 21; - static readonly string SkipMessage = $"Persistent Subscriptions to $all are not supported on" - + $" {EventStoreTestServer.Version?.ToString(3) ?? "unknown"}"; + static readonly string SkipMessage = $"Persistent Subscriptions to $all are not supported on" + + $" {EventStoreTestServer.Version?.ToString(3) ?? "unknown"}"; - public static bool No => !Yes; - public static bool Yes => (EventStoreTestServer.Version?.Major ?? int.MaxValue) >= SupportedFromMajorVersion; + public static bool No => !Yes; + public static bool Yes => (EventStoreTestServer.Version?.Major ?? int.MaxValue) >= SupportedFromMajorVersion; - public class FactAttribute : Regression.FactAttribute { - public FactAttribute() : base(SupportedFromMajorVersion, SkipMessage) { } - } + public class FactAttribute : Regression.FactAttribute { + public FactAttribute() : base(SupportedFromMajorVersion, SkipMessage) { } + } - public class TheoryAttribute : Regression.TheoryAttribute { - public TheoryAttribute() : base(SupportedFromMajorVersion, SkipMessage) { } - } + public class TheoryAttribute : Regression.TheoryAttribute { + public TheoryAttribute() : base(SupportedFromMajorVersion, SkipMessage) { } + } } \ No newline at end of file diff --git a/test/EventStore.Client.Tests.Common/Fakers/TestUserFaker.cs b/test/EventStore.Client.Tests.Common/Fakers/TestUserFaker.cs index 224a35261..1aca4aac1 100644 --- a/test/EventStore.Client.Tests.Common/Fakers/TestUserFaker.cs +++ b/test/EventStore.Client.Tests.Common/Fakers/TestUserFaker.cs @@ -1,56 +1,53 @@ namespace EventStore.Client.Tests; public class TestUser { - public UserDetails Details { get; set; } = default!; - public UserCredentials? Credentials { get; set; } = default!; + public UserDetails Details { get; set; } = default!; + public UserCredentials? Credentials { get; set; } = default!; - public string LoginName { get; set; } = null!; - public string FullName { get; set; } = null!; - public string[] Groups { get; set; } = null!; - public string Password { get; set; } = null!; + public string LoginName { get; set; } = null!; + public string FullName { get; set; } = null!; + public string[] Groups { get; set; } = null!; + public string Password { get; set; } = null!; - public override string ToString() => $"{LoginName} Credentials({Credentials?.Username ?? "null"})"; + public override string ToString() => $"{LoginName} Credentials({Credentials?.Username ?? "null"})"; } public sealed class TestUserFaker : Faker { - internal static TestUserFaker Instance => new(); - - TestUserFaker() { - RuleFor(x => x.LoginName, f => f.Person.UserName); - RuleFor(x => x.FullName, f => f.Person.FullName); - RuleFor(x => x.Groups, f => f.Lorem.Words()); - RuleFor(x => x.Password, () => PasswordGenerator.GenerateSimplePassword()); - RuleFor(x => x.Credentials, (_, user) => new(user.LoginName, user.Password)); - RuleFor(x => x.Details, (_, user) => new(user.LoginName, user.FullName, user.Groups, disabled: false, dateLastUpdated: default)); - } - - public TestUser WithValidCredentials() => Generate(); - - public TestUser WithNoCredentials() => - Instance - .FinishWith((_, x) => x.Credentials = null) - .Generate(); - - public TestUser WithInvalidCredentials(bool wrongLoginName = true, bool wrongPassword = true) => - Instance - .FinishWith( - (f, x) => x.Credentials = new( - wrongLoginName ? "wrong-username" : x.LoginName, - wrongPassword ? "wrong-password" : x.Password - ) - ) - .Generate(); - - public TestUser WithNonAsciiPassword() => - Instance - .RuleFor(x => x.Password, () => PasswordGenerator.GeneratePassword()) - .RuleFor(x => x.Credentials, (_, user) => new (user.LoginName, user.Password)) - .Generate(); + internal static TestUserFaker Instance => new(); + + TestUserFaker() { + RuleFor(x => x.LoginName, f => f.Person.UserName); + RuleFor(x => x.FullName, f => f.Person.FullName); + RuleFor(x => x.Groups, f => f.Lorem.Words()); + RuleFor(x => x.Password, () => PasswordGenerator.GenerateSimplePassword()); + RuleFor(x => x.Credentials, (_, user) => new(user.LoginName, user.Password)); + RuleFor(x => x.Details, (_, user) => new(user.LoginName, user.FullName, user.Groups, disabled: false, dateLastUpdated: default)); + } + + public TestUser WithValidCredentials() => Generate(); + + public TestUser WithNoCredentials() => + Instance + .FinishWith((_, x) => x.Credentials = null) + .Generate(); + + public TestUser WithInvalidCredentials(bool wrongLoginName = true, bool wrongPassword = true) => + Instance + .FinishWith( + (f, x) => x.Credentials = new( + wrongLoginName ? "wrong-username" : x.LoginName, + wrongPassword ? "wrong-password" : x.Password + ) + ) + .Generate(); + + public TestUser WithNonAsciiPassword() => + Instance + .RuleFor(x => x.Password, () => PasswordGenerator.GeneratePassword()) + .RuleFor(x => x.Credentials, (_, user) => new(user.LoginName, user.Password)) + .Generate(); } public static partial class Fakers { - public static TestUserFaker Users => TestUserFaker.Instance; -} - - - + public static TestUserFaker Users => TestUserFaker.Instance; +} \ No newline at end of file diff --git a/test/EventStore.Client.Tests.Common/Fixtures/Base/EventStoreClientFixtureBase.cs b/test/EventStore.Client.Tests.Common/Fixtures/Base/EventStoreClientFixtureBase.cs index b6f92ee57..2a5076437 100644 --- a/test/EventStore.Client.Tests.Common/Fixtures/Base/EventStoreClientFixtureBase.cs +++ b/test/EventStore.Client.Tests.Common/Fixtures/Base/EventStoreClientFixtureBase.cs @@ -28,7 +28,7 @@ protected EventStoreClientFixtureBase( IDictionary? env = null, bool noDefaultCredentials = false ) { _disposables = new List(); - + ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; var connectionString = GlobalEnvironment.UseCluster ? ConnectionStringCluster : ConnectionStringSingle; diff --git a/test/EventStore.Client.Tests.Common/Fixtures/CertificatesManager.cs b/test/EventStore.Client.Tests.Common/Fixtures/CertificatesManager.cs index 1affd2068..03a28bb1d 100644 --- a/test/EventStore.Client.Tests.Common/Fixtures/CertificatesManager.cs +++ b/test/EventStore.Client.Tests.Common/Fixtures/CertificatesManager.cs @@ -28,10 +28,10 @@ await GenerateCertificates( certificateDirectory.FullName, "A node certificate & key file have been generated in the '/tmp/node' directory.", "create-node", - "-ca-certificate", "/tmp/ca/ca.crt", - "-ca-key", "/tmp/ca/ca.key", + "-ca-certificate", "/tmp/ca/ca.crt", + "-ca-key", "/tmp/ca/ca.key", "-out", "/tmp/node", - "-ip-addresses", "127.0.0.1", + "-ip-addresses", "127.0.0.1", "-dns-names", "localhost" ); diff --git a/test/EventStore.Client.Tests.Common/Fixtures/DatabaseWarmup.cs b/test/EventStore.Client.Tests.Common/Fixtures/DatabaseWarmup.cs index 6441f95cf..50e056011 100644 --- a/test/EventStore.Client.Tests.Common/Fixtures/DatabaseWarmup.cs +++ b/test/EventStore.Client.Tests.Common/Fixtures/DatabaseWarmup.cs @@ -9,80 +9,77 @@ namespace EventStore.Client.Tests; static class DatabaseWarmup where T : EventStoreClientBase { - static readonly InterlockedBoolean Completed = new InterlockedBoolean(); - static readonly SemaphoreSlim Semaphore = new(1, 1); - static readonly ILogger Logger = Log.ForContext(SourceContextPropertyName, typeof(T).Name); - - static readonly TimeSpan ExecutionTimeout = FromSeconds(90); - static readonly TimeSpan RediscoverTimeout = FromSeconds(30); + static readonly InterlockedBoolean Completed = new InterlockedBoolean(); + static readonly SemaphoreSlim Semaphore = new(1, 1); + static readonly ILogger Logger = Log.ForContext(SourceContextPropertyName, typeof(T).Name); - static readonly IEnumerable DefaultBackoffDelay = Backoff.DecorrelatedJitterBackoffV2( - medianFirstRetryDelay: FromMilliseconds(100), - retryCount: 100, - fastFirst: false - ); - - // static DatabaseWarmup() { - // AppDomain.CurrentDomain.DomainUnload += (_, _) => Completed.Set(false); - // } + static readonly TimeSpan ExecutionTimeout = FromSeconds(90); + static readonly TimeSpan RediscoverTimeout = FromSeconds(30); - public static async Task TryExecuteOnce(T client, Func action, CancellationToken cancellationToken = default) { - await TryExecuteOld(client, action, cancellationToken); + static readonly IEnumerable DefaultBackoffDelay = Backoff.DecorrelatedJitterBackoffV2( + medianFirstRetryDelay: FromMilliseconds(100), + retryCount: 100, + fastFirst: false + ); - // await Semaphore.WaitAsync(cancellationToken); - // - // try { - // if (!Completed.EnsureCalledOnce()) { - // Logger.Warning("*** Warmup started ***"); - // await TryExecuteOld(client, action, cancellationToken); - // Logger.Warning("*** Warmup completed ***"); - // } - // else { - // Logger.Information("*** Warmup skipped ***"); - // } - // } - // finally { - // Semaphore.Release(); - // } - } - - static async Task TryExecute(EventStoreClientBase client, Func action, CancellationToken cancellationToken) { - var retry = Policy - .Handle() - .WaitAndRetryAsync(DefaultBackoffDelay); + public static async Task TryExecuteOnce(T client, Func action, CancellationToken cancellationToken = default) { + await Semaphore.WaitAsync(cancellationToken); - var rediscover = Policy.TimeoutAsync( - RediscoverTimeout, - async (_, _, _) => { - Logger.Warning("*** Warmup triggering rediscovery ***"); - try { - await client.RediscoverAsync(); - } - catch { - // ignored - } - } - ); - - var policy = Policy - .TimeoutAsync(ExecutionTimeout) - .WrapAsync(rediscover.WrapAsync(retry)); + try { + if (!Completed.EnsureCalledOnce()) { + Logger.Warning("*** Warmup started ***"); + await TryExecute(client, action, cancellationToken); + Logger.Warning("*** Warmup completed ***"); + } + else { + Logger.Information("*** Warmup skipped ***"); + } + } + finally { + Semaphore.Release(); + } + } + + static async Task TryExecute(EventStoreClientBase client, Func action, CancellationToken cancellationToken) { + var retry = Policy + .Handle() + .WaitAndRetryAsync(DefaultBackoffDelay); + + var rediscover = Policy.TimeoutAsync( + RediscoverTimeout, + async (_, _, _) => { + Logger.Warning("*** Warmup triggering rediscovery ***"); + try { + await client.RediscoverAsync(); + } + catch { + // ignored + } + } + ); + + var policy = Policy + .TimeoutAsync(ExecutionTimeout) + .WrapAsync(rediscover.WrapAsync(retry)); - await policy.ExecuteAsync(async ct => { - try { - await action(ct).ConfigureAwait(false); - } - catch (Exception ex) when (ex is not OperationCanceledException) { - // grpc throws a rpcexception when you cancel the token (which we convert into - // invalid operation) - but polly expects operationcancelledexception or it wont - // call onTimeoutAsync. so raise that here. - ct.ThrowIfCancellationRequested(); - throw; - } - }, cancellationToken); - } - - // This executes `warmup` with some somewhat subtle retry logic: + await policy.ExecuteAsync( + async ct => { + try { + await action(ct).ConfigureAwait(false); + } + catch (Exception ex) when (ex is not OperationCanceledException) { + // grpc throws a rpcexception when you cancel the token (which we convert into + // invalid operation) - but polly expects operationcancelledexception or it wont + // call onTimeoutAsync. so raise that here. + ct.ThrowIfCancellationRequested(); + throw; + } + }, + cancellationToken + ); + } + + // This executes `warmup` with some somewhat subtle retry logic: // execute the `warmup`. // if it succeeds we are done. // if it throws an exception, wait a short time (100ms) and try again. @@ -93,7 +90,7 @@ await policy.ExecuteAsync(async ct => { // eventually give up retrying. public static Task TryExecuteOld(EventStoreClientBase client, Func action, CancellationToken cancellationToken) { const string retryCountKey = "retryCount"; - + return Policy.Handle() .WaitAndRetryAsync( retryCount: 200, @@ -101,14 +98,15 @@ public static Task TryExecuteOld(EventStoreClientBase client, Func { }) + onRetry: (ex, slept, context) => { } + ) .WrapAsync( Policy.TimeoutAsync( timeoutProvider: context => { // decide how long to allow for the call (including discovery if it is pending) var retryCount = (int)context[retryCountKey]; var retryMs = retryCount * 100; - retryMs = Math.Max(retryMs, 100); // wait at least + retryMs = Math.Max(retryMs, 100); // wait at least retryMs = Math.Min(retryMs, 2000); // wait at most return FromMilliseconds(retryMs); }, @@ -116,7 +114,9 @@ public static Task TryExecuteOld(EventStoreClientBase client, Func { try { diff --git a/test/EventStore.Client.Tests.Common/Fixtures/EventStoreFixture.Helpers.cs b/test/EventStore.Client.Tests.Common/Fixtures/EventStoreFixture.Helpers.cs index c571e9d7d..960434c33 100644 --- a/test/EventStore.Client.Tests.Common/Fixtures/EventStoreFixture.Helpers.cs +++ b/test/EventStore.Client.Tests.Common/Fixtures/EventStoreFixture.Helpers.cs @@ -1,52 +1,50 @@ -using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Text; namespace EventStore.Client.Tests; -[SuppressMessage("Performance", "CA1822:Mark members as static")] public partial class EventStoreFixture { - const string TestEventType = "-"; - - public T NewClient(Action configure) where T : EventStoreClientBase, new() => - (T)Activator.CreateInstance(typeof(T), new object?[] { ClientSettings.With(configure) })!; - - public string GetStreamName([CallerMemberName] string? testMethod = null) => - $"{GetType().DeclaringType?.Name}.{testMethod ?? "unknown"}"; - - public IEnumerable CreateTestEvents(int count = 1, string? type = null, int metadataSize = 1) => - Enumerable.Range(0, count).Select(index => CreateTestEvent(index, type ?? TestEventType, metadataSize)); - - protected static EventData CreateTestEvent(int index) => CreateTestEvent(index, TestEventType, 1); - - protected static EventData CreateTestEvent(int index, string type, int metadataSize) => - new( - Uuid.NewUuid(), - type, - Encoding.UTF8.GetBytes($$"""{"x":{{index}}}"""), - Encoding.UTF8.GetBytes($"\"{new string('$', metadataSize)}\"") - ); - - public async Task CreateTestUser(bool withoutGroups = true, bool useUserCredentials = false) { - var result = await CreateTestUsers(1, withoutGroups, useUserCredentials); - return result.First(); - } - - public Task CreateTestUsers(int count = 3, bool withoutGroups = true, bool useUserCredentials = false) => - Fakers.Users - .RuleFor(x => x.Groups, f => withoutGroups ? Array.Empty() : f.Lorem.Words()) - .Generate(count) - .Select( - async user => { - await Users.CreateUserAsync( - user.LoginName, - user.FullName, - user.Groups, - user.Password, - userCredentials: useUserCredentials ? user.Credentials : TestCredentials.Root - ); - - return user; - } - ).WhenAll(); + const string TestEventType = "-"; + + public T NewClient(Action configure) where T : EventStoreClientBase, new() => + (T)Activator.CreateInstance(typeof(T), new object?[] { ClientSettings.With(configure) })!; + + public string GetStreamName([CallerMemberName] string? testMethod = null) => + $"{GetType().DeclaringType?.Name}.{testMethod ?? "unknown"}"; + + public IEnumerable CreateTestEvents(int count = 1, string? type = null, int metadataSize = 1) => + Enumerable.Range(0, count).Select(index => CreateTestEvent(index, type ?? TestEventType, metadataSize)); + + protected static EventData CreateTestEvent(int index) => CreateTestEvent(index, TestEventType, 1); + + protected static EventData CreateTestEvent(int index, string type, int metadataSize) => + new( + Uuid.NewUuid(), + type, + Encoding.UTF8.GetBytes($$"""{"x":{{index}}}"""), + Encoding.UTF8.GetBytes($"\"{new string('$', metadataSize)}\"") + ); + + public async Task CreateTestUser(bool withoutGroups = true, bool useUserCredentials = false) { + var result = await CreateTestUsers(1, withoutGroups, useUserCredentials); + return result.First(); + } + + public Task CreateTestUsers(int count = 3, bool withoutGroups = true, bool useUserCredentials = false) => + Fakers.Users + .RuleFor(x => x.Groups, f => withoutGroups ? Array.Empty() : f.Lorem.Words()) + .Generate(count) + .Select( + async user => { + await Users.CreateUserAsync( + user.LoginName, + user.FullName, + user.Groups, + user.Password, + userCredentials: useUserCredentials ? user.Credentials : TestCredentials.Root + ); + + return user; + } + ).WhenAll(); } \ No newline at end of file diff --git a/test/EventStore.Client.Tests.Common/Fixtures/EventStoreFixture.cs b/test/EventStore.Client.Tests.Common/Fixtures/EventStoreFixture.cs index 403481727..63e65403a 100644 --- a/test/EventStore.Client.Tests.Common/Fixtures/EventStoreFixture.cs +++ b/test/EventStore.Client.Tests.Common/Fixtures/EventStoreFixture.cs @@ -18,7 +18,7 @@ this with { ) }; - public EventStoreFixtureOptions WithoutDefaultCredentials() => + public EventStoreFixtureOptions WithoutDefaultCredentials() => this with { ClientSettings = ClientSettings.With(x => x.DefaultCredentials = null) }; } @@ -30,12 +30,12 @@ public partial class EventStoreFixture : IAsyncLifetime, IAsyncDisposable { static EventStoreFixture() { Logging.Initialize(); Logger = Log.ForContext(); - + ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; } public EventStoreFixture() : this(options => options) { } - + protected EventStoreFixture(ConfigureFixture configure) { // TODO SS: should I verify the certificates exist here? if (GlobalEnvironment.UseExternalServer) { @@ -54,7 +54,7 @@ protected EventStoreFixture(ConfigureFixture configure) { } List TestRuns { get; } = new(); - + public ITestService Service { get; } public EventStoreFixtureOptions Options { get; } @@ -63,7 +63,7 @@ protected EventStoreFixture(ConfigureFixture configure) { public EventStoreProjectionManagementClient Projections { get; private set; } = null!; public EventStorePersistentSubscriptionsClient PersistentSubscriptions { get; private set; } = null!; public EventStoreOperationsClient Operations { get; private set; } = null!; - + public Func OnSetup { get; set; } = () => Task.CompletedTask; public Func OnTearDown { get; set; } = () => Task.CompletedTask; @@ -82,14 +82,14 @@ protected EventStoreFixture(ConfigureFixture configure) { DefaultCredentials = Options.ClientSettings.DefaultCredentials, DefaultDeadline = Options.ClientSettings.DefaultDeadline }; - + public void CaptureTestRun(ITestOutputHelper outputHelper) { var testRunId = Logging.CaptureLogs(outputHelper); TestRuns.Add(testRunId); Logger.Information(">>> Test Run {testRunId} {Operation} <<<", testRunId, "starting"); Service.ReportStatus(); } - + async Task WarmUp() { Logger.Information("*** !!! Warming up database !!! ***"); @@ -118,7 +118,7 @@ public async Task InitializeAsync() { await OnSetup(); } - + public async Task DisposeAsync() { try { await OnTearDown(); @@ -134,14 +134,4 @@ public async Task DisposeAsync() { } async ValueTask IAsyncDisposable.DisposeAsync() => await DisposeAsync(); - -} - -/// -/// The clients dont have default credentials set. -/// -public class InsecureClientTestFixture() : EventStoreFixture(x => x.WithoutDefaultCredentials()); - -public class RunInMemoryTestFixture() : EventStoreFixture(x => x.RunInMemory()); - -public class RunProjectionsTestFixture() : EventStoreFixture(x => x.RunProjections()); \ No newline at end of file +} \ No newline at end of file diff --git a/test/EventStore.Client.Tests.Common/Fixtures/EventStoreTestCluster.cs b/test/EventStore.Client.Tests.Common/Fixtures/EventStoreTestCluster.cs index 67a5cbd0b..d0f0bbe88 100644 --- a/test/EventStore.Client.Tests.Common/Fixtures/EventStoreTestCluster.cs +++ b/test/EventStore.Client.Tests.Common/Fixtures/EventStoreTestCluster.cs @@ -8,7 +8,7 @@ namespace EventStore.Client.Tests; public class EventStoreTestCluster(EventStoreFixtureOptions options) : TestCompositeService { EventStoreFixtureOptions Options { get; } = options; - public static EventStoreFixtureOptions DefaultOptions() { + public static EventStoreFixtureOptions DefaultOptions() { const string connString = "esdb://localhost:2113,localhost:2112,localhost:2111?tls=true&tlsVerifyCert=false"; var defaultSettings = EventStoreClientSettings @@ -28,27 +28,27 @@ public static EventStoreFixtureOptions DefaultOptions() { ["EVENTSTORE_STREAM_EXISTENCE_FILTER_SIZE"] = "10000", ["EVENTSTORE_STREAM_INFO_CACHE_CAPACITY"] = "10000", ["EVENTSTORE_ENABLE_ATOM_PUB_OVER_HTTP"] = "true" // why true? - }; - + }; + return new(defaultSettings, defaultEnvironment); } - + protected override CompositeBuilder Configure() { var env = Options.Environment.Select(pair => $"{pair.Key}={pair.Value}").ToArray(); - var builder = new Builder() - .UseContainer() - .FromComposeFile("docker-compose.yml") - .ServiceName("esdb-test-cluster") - .WithEnvironment(env) - .RemoveOrphans() - .NoRecreate() - .KeepRunning(); - - return builder; - } - - protected override async Task OnServiceStarted() { - await Service.WaitUntilNodesAreHealthy("esdb-node", TimeSpan.FromSeconds(60)); - } + var builder = new Builder() + .UseContainer() + .FromComposeFile("docker-compose.yml") + .ServiceName("esdb-test-cluster") + .WithEnvironment(env) + .RemoveOrphans() + .NoRecreate() + .KeepRunning(); + + return builder; + } + + 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 1d9717da1..ef15b2106 100644 --- a/test/EventStore.Client.Tests.Common/Fixtures/EventStoreTestNode.cs +++ b/test/EventStore.Client.Tests.Common/Fixtures/EventStoreTestNode.cs @@ -37,15 +37,16 @@ public static EventStoreFixtureOptions DefaultOptions() { // ["EVENTSTORE_NODE_PORT_ADVERTISE_AS"] = port, // ["EVENTSTORE_NODE_PORT_ADVERTISE_AS"] = port, //["EVENTSTORE_ADVERTISE_NODE_PORT_TO_CLIENT_AS"] = port, - ["EVENTSTORE_ENABLE_EXTERNAL_TCP"] = "false", - ["EVENTSTORE_MEM_DB"] = "true", - ["EVENTSTORE_CHUNK_SIZE"] = (1024 * 1024).ToString(), - ["EVENTSTORE_CERTIFICATE_FILE"] = "/etc/eventstore/certs/node/node.crt", - ["EVENTSTORE_CERTIFICATE_PRIVATE_KEY_FILE"] = "/etc/eventstore/certs/node/node.key", - ["EVENTSTORE_STREAM_EXISTENCE_FILTER_SIZE"] = "10000", - ["EVENTSTORE_STREAM_INFO_CACHE_CAPACITY"] = "10000", - ["EVENTSTORE_ENABLE_ATOM_PUB_OVER_HTTP"] = "true", - ["EVENTSTORE_DISABLE_LOG_FILE"] = "true" + //["EVENTSTORE_HTTP_PORT"] = port, + ["EVENTSTORE_ENABLE_EXTERNAL_TCP"] = "false", + ["EVENTSTORE_MEM_DB"] = "true", + ["EVENTSTORE_CHUNK_SIZE"] = (1024 * 1024).ToString(), + ["EVENTSTORE_CERTIFICATE_FILE"] = "/etc/eventstore/certs/node/node.crt", + ["EVENTSTORE_CERTIFICATE_PRIVATE_KEY_FILE"] = "/etc/eventstore/certs/node/node.key", + ["EVENTSTORE_STREAM_EXISTENCE_FILTER_SIZE"] = "10000", + ["EVENTSTORE_STREAM_INFO_CACHE_CAPACITY"] = "10000", + ["EVENTSTORE_ENABLE_ATOM_PUB_OVER_HTTP"] = "false", + ["EVENTSTORE_DISABLE_LOG_FILE"] = "true" }; return new(defaultSettings, defaultEnvironment); @@ -54,8 +55,8 @@ public static EventStoreFixtureOptions DefaultOptions() { protected override ContainerBuilder Configure() { var env = Options.Environment.Select(pair => $"{pair.Key}={pair.Value}").ToArray(); - var port = Options.ClientSettings.ConnectivitySettings.Address.Port; - var certsPath = Path.Combine(Environment.CurrentDirectory, "certs"); + var port = Options.ClientSettings.ConnectivitySettings.Address.Port; + var certsPath = Path.Combine(Environment.CurrentDirectory, "certs"); //var containerName = $"esbd-dotnet-test-{port}-{Guid.NewGuid().ToString()[30..]}"; var containerName = "es-client-dotnet-test"; @@ -69,7 +70,7 @@ protected override ContainerBuilder Configure() { .MountVolume(certsPath, "/etc/eventstore/certs", MountType.ReadOnly) .ExposePort(port, 2113) .WaitForMessageInLog("'admin' user added to $users.", TimeSpan.FromSeconds(60)); - //.Wait("", (svc, count) => CheckConnection()); + //.Wait("", (svc, count) => CheckConnection()); // .KeepContainer() // .WaitForHealthy(TimeSpan.FromSeconds(60)); //.WaitForMessageInLog($"========== [\"0.0.0.0:{port}\"] IS LEADER... SPARTA!") @@ -77,29 +78,6 @@ protected override ContainerBuilder Configure() { //.WaitForMessageInLog("'admin' user added to $users."); } - int CheckConnection() { - using var http = new HttpClient( - new SocketsHttpHandler { - SslOptions = { RemoteCertificateValidationCallback = delegate { return true; } } - } - ) { - BaseAddress = Options.ClientSettings.ConnectivitySettings.Address - }; - - return Policy.Handle() - .WaitAndRetryAsync(300, retryCount => TimeSpan.FromMilliseconds(100)) - .ExecuteAsync( - async () => { - using var response = await http.GetAsync("/health/live", CancellationToken.None); - - if (response.StatusCode >= HttpStatusCode.BadRequest) - throw new FluentDockerException($"Health check failed with status code: {response.StatusCode}."); - - return 0; - } - ).GetAwaiter().GetResult(); - } - protected override async Task OnServiceStarted() { using var http = new HttpClient( new SocketsHttpHandler { @@ -108,7 +86,7 @@ protected override async Task OnServiceStarted() { ) { BaseAddress = Options.ClientSettings.ConnectivitySettings.Address }; - + await Policy.Handle() .WaitAndRetryAsync(200, retryCount => TimeSpan.FromMilliseconds(100)) .ExecuteAsync( diff --git a/test/EventStore.Client.Tests.Common/Fixtures/InsecureClientTestFixture.cs b/test/EventStore.Client.Tests.Common/Fixtures/InsecureClientTestFixture.cs new file mode 100644 index 000000000..a462d370a --- /dev/null +++ b/test/EventStore.Client.Tests.Common/Fixtures/InsecureClientTestFixture.cs @@ -0,0 +1,6 @@ +namespace EventStore.Client.Tests; + +/// +/// The clients dont have default credentials set. +/// +public class InsecureClientTestFixture() : EventStoreFixture(x => x.WithoutDefaultCredentials()); \ No newline at end of file diff --git a/test/EventStore.Client.Tests.Common/Fixtures/RunInMemoryTestFixture.cs b/test/EventStore.Client.Tests.Common/Fixtures/RunInMemoryTestFixture.cs new file mode 100644 index 000000000..335a14174 --- /dev/null +++ b/test/EventStore.Client.Tests.Common/Fixtures/RunInMemoryTestFixture.cs @@ -0,0 +1,3 @@ +namespace EventStore.Client.Tests; + +public class RunInMemoryTestFixture() : EventStoreFixture(x => x.RunInMemory()); \ No newline at end of file diff --git a/test/EventStore.Client.Tests.Common/Fixtures/RunProjectionsTestFixture.cs b/test/EventStore.Client.Tests.Common/Fixtures/RunProjectionsTestFixture.cs new file mode 100644 index 000000000..cc9c103c3 --- /dev/null +++ b/test/EventStore.Client.Tests.Common/Fixtures/RunProjectionsTestFixture.cs @@ -0,0 +1,3 @@ +namespace EventStore.Client.Tests; + +public class RunProjectionsTestFixture() : EventStoreFixture(x => x.RunProjections()); \ No newline at end of file diff --git a/test/EventStore.Client.Tests.Common/FluentDocker/FluentDockerBuilderExtensions.cs b/test/EventStore.Client.Tests.Common/FluentDocker/FluentDockerBuilderExtensions.cs index e400cc0e2..782ab7696 100644 --- a/test/EventStore.Client.Tests.Common/FluentDocker/FluentDockerBuilderExtensions.cs +++ b/test/EventStore.Client.Tests.Common/FluentDocker/FluentDockerBuilderExtensions.cs @@ -7,18 +7,18 @@ namespace EventStore.Client.Tests.FluentDocker; public static class FluentDockerBuilderExtensions { - public static CompositeBuilder OverrideConfiguration(this CompositeBuilder compositeBuilder, Action configure) { - configure(GetInternalConfig(compositeBuilder)); - return compositeBuilder; + public static CompositeBuilder OverrideConfiguration(this CompositeBuilder compositeBuilder, Action configure) { + configure(GetInternalConfig(compositeBuilder)); + return compositeBuilder; - static DockerComposeConfig GetInternalConfig(CompositeBuilder compositeBuilder) => - (DockerComposeConfig)typeof(CompositeBuilder) - .GetField("_config", BindingFlags.NonPublic | BindingFlags.Instance)! - .GetValue(compositeBuilder)!; - } + static DockerComposeConfig GetInternalConfig(CompositeBuilder compositeBuilder) => + (DockerComposeConfig)typeof(CompositeBuilder) + .GetField("_config", BindingFlags.NonPublic | BindingFlags.Instance)! + .GetValue(compositeBuilder)!; + } - public static DockerComposeConfig Configuration(this ICompositeService service) => - (DockerComposeConfig)typeof(DockerComposeCompositeService) - .GetProperty("Config", BindingFlags.NonPublic | BindingFlags.Instance)! - .GetValue(service)!; + public static DockerComposeConfig Configuration(this ICompositeService service) => + (DockerComposeConfig)typeof(DockerComposeCompositeService) + .GetProperty("Config", BindingFlags.NonPublic | BindingFlags.Instance)! + .GetValue(service)!; } \ 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 7211af0ed..595ab06d7 100644 --- a/test/EventStore.Client.Tests.Common/FluentDocker/FluentDockerServiceExtensions.cs +++ b/test/EventStore.Client.Tests.Common/FluentDocker/FluentDockerServiceExtensions.cs @@ -5,40 +5,40 @@ namespace EventStore.Client.Tests.FluentDocker; public static class FluentDockerServiceExtensions { - static readonly TimeSpan DefaultRetryDelay = TimeSpan.FromMilliseconds(100); - - public static async Task WaitUntilNodesAreHealthy(this IContainerService service, CancellationToken cancellationToken) { - while (true) { - var config = service.GetConfiguration(true); - var status = config?.State?.Health?.Status; - - if (status is HealthState.Healthy) return; - - if (cancellationToken.IsCancellationRequested) - throw new FluentDockerException($"Wait for healthy expired for container {service.Id}"); - - // ReSharper disable once MethodSupportsCancellation - await Task.Delay(DefaultRetryDelay); - } - } - - public static async ValueTask WaitUntilNodesAreHealthy(this IContainerService service, TimeSpan timeout) { - using var cts = new CancellationTokenSource(timeout); - await WaitUntilNodesAreHealthy(service, cts.Token); - } - - public static async Task WaitUntilNodesAreHealthy(this ICompositeService service, IEnumerable services, CancellationToken cancellationToken) { - var nodes = service.Containers.Where(x => services.Contains(x.Name)); - await Parallel.ForEachAsync(nodes, cancellationToken, async (node, ct) => await node.WaitUntilNodesAreHealthy(ct)); - } - - public static async Task WaitUntilNodesAreHealthy(this ICompositeService service, string serviceNamePrefix, CancellationToken cancellationToken) { - var nodes = service.Containers.Where(x => x.Name.StartsWith(serviceNamePrefix)); - await Parallel.ForEachAsync(nodes, cancellationToken, async (node, ct) => await node.WaitUntilNodesAreHealthy(ct)); - } - - public static async Task WaitUntilNodesAreHealthy(this ICompositeService service, string serviceNamePrefix, TimeSpan timeout) { - using var cts = new CancellationTokenSource(timeout); - await WaitUntilNodesAreHealthy(service, serviceNamePrefix, cts.Token); - } + static readonly TimeSpan DefaultRetryDelay = TimeSpan.FromMilliseconds(100); + + public static async Task WaitUntilNodesAreHealthy(this IContainerService service, CancellationToken cancellationToken) { + while (true) { + var config = service.GetConfiguration(true); + var status = config?.State?.Health?.Status; + + if (status is HealthState.Healthy) return; + + if (cancellationToken.IsCancellationRequested) + throw new FluentDockerException($"Wait for healthy expired for container {service.Id}"); + + // ReSharper disable once MethodSupportsCancellation + await Task.Delay(DefaultRetryDelay); + } + } + + public static async ValueTask WaitUntilNodesAreHealthy(this IContainerService service, TimeSpan timeout) { + using var cts = new CancellationTokenSource(timeout); + await WaitUntilNodesAreHealthy(service, cts.Token); + } + + public static async Task WaitUntilNodesAreHealthy(this ICompositeService service, IEnumerable services, CancellationToken cancellationToken) { + var nodes = service.Containers.Where(x => services.Contains(x.Name)); + await Parallel.ForEachAsync(nodes, cancellationToken, async (node, ct) => await node.WaitUntilNodesAreHealthy(ct)); + } + + public static async Task WaitUntilNodesAreHealthy(this ICompositeService service, string serviceNamePrefix, CancellationToken cancellationToken) { + var nodes = service.Containers.Where(x => x.Name.StartsWith(serviceNamePrefix)); + await Parallel.ForEachAsync(nodes, cancellationToken, async (node, ct) => await node.WaitUntilNodesAreHealthy(ct)); + } + + 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 924b890f1..2fb3e805c 100644 --- a/test/EventStore.Client.Tests.Common/FluentDocker/TestBypassService.cs +++ b/test/EventStore.Client.Tests.Common/FluentDocker/TestBypassService.cs @@ -5,57 +5,57 @@ namespace EventStore.Client.Tests.FluentDocker; public class TestBypassService : TestService { - protected override BypassBuilder Configure() => throw new NotImplementedException(); - - public override async Task Start() { - try { - await OnServiceStarted(); - } - catch (Exception ex) { - throw new FluentDockerException($"{nameof(OnServiceStarted)} execution error", ex); - } - } - - public override async Task Stop() { - try { - await OnServiceStop(); - } - catch (Exception ex) { - throw new FluentDockerException($"{nameof(OnServiceStop)} execution error", ex); - } - } - - public override ValueTask DisposeAsync() => ValueTask.CompletedTask; + protected override BypassBuilder Configure() => throw new NotImplementedException(); + + public override async Task Start() { + try { + await OnServiceStarted(); + } + catch (Exception ex) { + throw new FluentDockerException($"{nameof(OnServiceStarted)} execution error", ex); + } + } + + public override async Task Stop() { + try { + await OnServiceStop(); + } + catch (Exception ex) { + throw new FluentDockerException($"{nameof(OnServiceStop)} execution error", ex); + } + } + + public override ValueTask DisposeAsync() => ValueTask.CompletedTask; } public sealed class BypassService : IService { - public string Name { get; } = nameof(BypassService); - public ServiceRunningState State { get; } = ServiceRunningState.Unknown; + public string Name { get; } = nameof(BypassService); + public ServiceRunningState State { get; } = ServiceRunningState.Unknown; - public void Dispose() { } + public void Dispose() { } - public void Start() { } + public void Start() { } - public void Pause() { } + public void Pause() { } - public void Stop() { } + public void Stop() { } - public void Remove(bool force = false) { } + public void Remove(bool force = false) { } - public IService AddHook(ServiceRunningState state, Action hook, string? uniqueName = null) => this; + public IService AddHook(ServiceRunningState state, Action hook, string? uniqueName = null) => this; - public IService RemoveHook(string uniqueName) => this; + public IService RemoveHook(string uniqueName) => this; - public event ServiceDelegates.StateChange? StateChange; + public event ServiceDelegates.StateChange? StateChange; - void OnStateChange(StateChangeEventArgs evt) => StateChange?.Invoke(this, evt); + void OnStateChange(StateChangeEventArgs evt) => StateChange?.Invoke(this, evt); } public sealed class BypassBuilder : BaseBuilder { - BypassBuilder(IBuilder? parent) : base(parent) { } - public BypassBuilder() : this(null) { } + BypassBuilder(IBuilder? parent) : base(parent) { } + public BypassBuilder() : this(null) { } - public override BypassService Build() => new BypassService(); + public override BypassService Build() => new BypassService(); - protected override IBuilder InternalCreate() => this; + 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 b404f82db..2f99c99fd 100644 --- a/test/EventStore.Client.Tests.Common/FluentDocker/TestService.cs +++ b/test/EventStore.Client.Tests.Common/FluentDocker/TestService.cs @@ -8,10 +8,10 @@ namespace EventStore.Client.Tests.FluentDocker; public interface ITestService : IAsyncDisposable { - Task Start(); - Task Stop(); + Task Start(); + Task Stop(); - void ReportStatus(); + void ReportStatus(); } /// @@ -19,127 +19,126 @@ public interface ITestService : IAsyncDisposable { /// Required to avoid failures on creating the networks the containers are attached to. /// sealed class TestServiceGatekeeper { - static readonly SemaphoreSlim Semaphore = new(1, 1); + static readonly SemaphoreSlim Semaphore = new(1, 1); - public static Task Wait() => Semaphore.WaitAsync(); - public static void Next() => Semaphore.Release(); + public static Task Wait() => Semaphore.WaitAsync(); + public static void Next() => Semaphore.Release(); } public abstract class TestService : ITestService where TService : IService where TBuilder : BaseBuilder { - ILogger Logger { get; } - - public TestService() => Logger = Log.ForContext(SourceContextPropertyName, GetType().Name); - - protected TService Service { get; private set; } = default!; - - INetworkService? Network { get; set; } = null!; - - public virtual async Task Start() { - Logger.Information("Container service starting"); - - //await TestServiceGatekeeper.Wait(); - - try { - var builder = Configure(); - - Service = builder.Build(); - - // // for some reason fluent docker does not always create the network - // // before the service is started, so we do it manually here - // if (Service is IContainerService service) { - // var cfg = service.GetConfiguration(true); - // - // Network = Fd - // .UseNetwork(cfg.Name) - // .IsInternal() - // .Build() - // .Attach(service, true); - // - // Logger.Information("Created network {Network}", Network.Name); - // } - - try { - Service.Start(); - Logger.Information("Container service started"); - } - catch (Exception ex) { - throw new FluentDockerException("Failed to start container service", ex); - - } - - try { - await OnServiceStarted(); - } - catch (Exception ex) { - throw new FluentDockerException($"{nameof(OnServiceStarted)} execution error", ex); - } - } - finally { - //TestServiceGatekeeper.Next(); - } - } - - public virtual async Task Stop() { - try { - await OnServiceStop(); - } - catch (Exception ex) { - throw new FluentDockerException($"{nameof(OnServiceStop)} execution error", ex); - } - - try { - Service.Stop(); - } - catch (Exception ex) { - throw new FluentDockerException("Failed to stop container service", ex); - } - } - - public void ReportStatus() { - if (Service is IContainerService containerService) { - ReportContainerStatus(containerService); - } - - if (Service is ICompositeService compose) { + ILogger Logger { get; } + + public TestService() => Logger = Log.ForContext(SourceContextPropertyName, GetType().Name); + + protected TService Service { get; private set; } = default!; + + INetworkService? Network { get; set; } = null!; + + public virtual async Task Start() { + Logger.Information("Container service starting"); + + //await TestServiceGatekeeper.Wait(); + + try { + var builder = Configure(); + + Service = builder.Build(); + + // // for some reason fluent docker does not always create the network + // // before the service is started, so we do it manually here + // if (Service is IContainerService service) { + // var cfg = service.GetConfiguration(true); + // + // Network = Fd + // .UseNetwork(cfg.Name) + // .IsInternal() + // .Build() + // .Attach(service, true); + // + // Logger.Information("Created network {Network}", Network.Name); + // } + + try { + Service.Start(); + Logger.Information("Container service started"); + } + catch (Exception ex) { + throw new FluentDockerException("Failed to start container service", ex); + } + + try { + await OnServiceStarted(); + } + catch (Exception ex) { + throw new FluentDockerException($"{nameof(OnServiceStarted)} execution error", ex); + } + } + finally { + //TestServiceGatekeeper.Next(); + } + } + + public virtual async Task Stop() { + try { + await OnServiceStop(); + } + catch (Exception ex) { + throw new FluentDockerException($"{nameof(OnServiceStop)} execution error", ex); + } + + try { + Service.Stop(); + } + catch (Exception ex) { + throw new FluentDockerException("Failed to stop container service", ex); + } + } + + public void ReportStatus() { + if (Service is IContainerService containerService) { + ReportContainerStatus(containerService); + } + + if (Service is ICompositeService compose) { foreach (var container in compose.Containers) { ReportContainerStatus(container); } } - return; - - void ReportContainerStatus(IContainerService service) { - var cfg = service.GetConfiguration(true); - Logger.Information("Container {Name} {State} Ports: {Ports}", service.Name, service.State, cfg.Config.ExposedPorts.Keys); - } - - // var docker = Fd.Hosts().Discover().FirstOrDefault(x => x.IsNative || x.Name == "default")!; - } - - public virtual ValueTask DisposeAsync() { - try { - Network?.Dispose(); - - try { - Service.Dispose(); - } - catch { - // ignored - } - - /*if (Service.State != ServiceRunningState.Unknown) { - Service.Dispose(); - }*/ - } - catch (Exception ex) { - throw new FluentDockerException("Failed to dispose of container service", ex); - } - - return ValueTask.CompletedTask; - } - - protected abstract TBuilder Configure(); - - protected virtual Task OnServiceStarted() => Task.CompletedTask; - protected virtual Task OnServiceStop() => Task.CompletedTask; + return; + + void ReportContainerStatus(IContainerService service) { + var cfg = service.GetConfiguration(true); + Logger.Information("Container {Name} {State} Ports: {Ports}", service.Name, service.State, cfg.Config.ExposedPorts.Keys); + } + + // var docker = Fd.Hosts().Discover().FirstOrDefault(x => x.IsNative || x.Name == "default")!; + } + + public virtual ValueTask DisposeAsync() { + try { + Network?.Dispose(); + + try { + Service.Dispose(); + } + catch { + // ignored + } + + /*if (Service.State != ServiceRunningState.Unknown) { + Service.Dispose(); + }*/ + } + catch (Exception ex) { + throw new FluentDockerException("Failed to dispose of container service", ex); + } + + return ValueTask.CompletedTask; + } + + 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 313dc76d8..563ff4cb6 100644 --- a/test/EventStore.Client.Tests.Common/GlobalEnvironment.cs +++ b/test/EventStore.Client.Tests.Common/GlobalEnvironment.cs @@ -6,7 +6,7 @@ namespace EventStore.Client.Tests; public static class GlobalEnvironment { static GlobalEnvironment() { EnsureDefaults(Application.Configuration); - + UseCluster = Application.Configuration.GetValue("ES_USE_CLUSTER"); UseExternalServer = Application.Configuration.GetValue("ES_USE_EXTERNAL_SERVER"); DockerImage = Application.Configuration.GetValue("ES_DOCKER_IMAGE")!; @@ -44,7 +44,7 @@ static void EnsureDefaults(IConfiguration configuration) { public static bool UseExternalServer { get; } public static string DockerImage { get; } public static string DbLogFormat { get; } - + #region . Obsolete . //[Obsolete("Use the EventStoreFixture instead so you don't have to use this method.", false)] @@ -73,6 +73,6 @@ public static IDictionary GetEnvironmentVariables(IDictionary /// Interlocked support for boolean values /// public class InterlockedBoolean { - int _value; - - /// - /// Initializes a new instance of - /// - /// initial value - public InterlockedBoolean(bool initialValue = false) => _value = initialValue ? 1 : 0; - - /// - /// Current value - /// - public bool CurrentValue => _value == 1; - - /// - /// Sets a new value - /// - /// new value - /// the original value before any operation was performed - public bool Set(bool newValue) { - var oldValue = Interlocked.Exchange(ref _value, newValue ? 1 : 0); - return oldValue == 1; - } - - /// - /// Compares the current value and the comparand for equality and, if they are equal, - /// replaces the current value with the new value in an atomic/thread-safe operation. - /// - /// new value - /// value to compare the current value with - /// the original value before any operation was performed - public bool CompareExchange(bool newValue, bool comparand) { - var oldValue = Interlocked.CompareExchange(ref _value, newValue ? 1 : 0, comparand ? 1 : 0); - return oldValue == 1; - } - - public bool EnsureCalledOnce() => CompareExchange(true, false); + int _value; + + /// + /// Initializes a new instance of + /// + /// initial value + public InterlockedBoolean(bool initialValue = false) => _value = initialValue ? 1 : 0; + + /// + /// Current value + /// + public bool CurrentValue => _value == 1; + + /// + /// Sets a new value + /// + /// new value + /// the original value before any operation was performed + public bool Set(bool newValue) { + var oldValue = Interlocked.Exchange(ref _value, newValue ? 1 : 0); + return oldValue == 1; + } + + /// + /// Compares the current value and the comparand for equality and, if they are equal, + /// replaces the current value with the new value in an atomic/thread-safe operation. + /// + /// new value + /// value to compare the current value with + /// the original value before any operation was performed + public bool CompareExchange(bool newValue, bool comparand) { + var oldValue = Interlocked.CompareExchange(ref _value, newValue ? 1 : 0, comparand ? 1 : 0); + return oldValue == 1; + } + + public bool EnsureCalledOnce() => CompareExchange(true, false); } \ No newline at end of file diff --git a/test/EventStore.Client.Tests.Common/Logging.cs b/test/EventStore.Client.Tests.Common/Logging.cs index 156199742..0d5fc435c 100644 --- a/test/EventStore.Client.Tests.Common/Logging.cs +++ b/test/EventStore.Client.Tests.Common/Logging.cs @@ -12,71 +12,69 @@ static class Logging { static readonly Subject LogEventSubject = new(); static readonly ConcurrentDictionary Subscriptions = new(); - static readonly string DefaultTemplate; static readonly MessageTemplateTextFormatter DefaultFormatter; - - static Logging() { - DefaultTemplate = "[{Timestamp:HH:mm:ss.fff} {Level:u3}] ({ThreadId:000}) {SourceContext} {Message}{NewLine}{Exception}"; - DefaultFormatter = new(DefaultTemplate); - - Log.Logger = new LoggerConfiguration() - .ReadFrom.Configuration(Application.Configuration) - .WriteTo.Observers(x => x.Subscribe(LogEventSubject.OnNext)) - .CreateLogger(); - - #if GRPC_CORE + + static Logging() { + DefaultFormatter = new("[{Timestamp:HH:mm:ss.fff} {Level:u3}] ({ThreadId:000}) {SourceContext} {Message}{NewLine}{Exception}"); + + Log.Logger = new LoggerConfiguration() + .ReadFrom.Configuration(Application.Configuration) + .WriteTo.Observers(x => x.Subscribe(LogEventSubject.OnNext)) + .CreateLogger(); + +#if GRPC_CORE GrpcEnvironment.SetLogger(new GrpcCoreSerilogLogger(Log.Logger.ForContext())); - #endif - - Ductus.FluentDocker.Services.Logging.Enabled(); - - AppDomain.CurrentDomain.DomainUnload += (_, _) => Log.CloseAndFlush(); - } - - public static void Initialize() { } // triggers static ctor - - /// - /// Captures logs for the duration of the test run. - /// +#endif + + Ductus.FluentDocker.Services.Logging.Enabled(); + + AppDomain.CurrentDomain.DomainUnload += (_, _) => Log.CloseAndFlush(); + } + + public static void Initialize() { } // triggers static ctor + + /// + /// Captures logs for the duration of the test run. + /// static Guid CaptureLogs(Action write, Guid testRunId = default) { - if (testRunId == default) - testRunId = Guid.NewGuid(); - - var callContextData = new AsyncLocal { Value = testRunId }; - var testRunIdProperty = new LogEventProperty("TestRunId", new ScalarValue(testRunId)); - - var subscription = LogEventSubject - .Where(_ => callContextData.Value.Equals(testRunId)) - .Subscribe(WriteLogEvent()); - - Subscriptions.TryAdd(testRunId, subscription); - - return testRunId; - - Action WriteLogEvent() => - logEvent => { - logEvent.AddOrUpdateProperty(testRunIdProperty); - using var writer = new StringWriter(); - DefaultFormatter.Format(logEvent, writer); - write(writer.ToString().Trim()); - }; + if (testRunId == default) + testRunId = Guid.NewGuid(); + + var callContextData = new AsyncLocal { Value = testRunId }; + var testRunIdProperty = new LogEventProperty("TestRunId", new ScalarValue(testRunId)); + + var subscription = LogEventSubject + .Where(_ => callContextData.Value.Equals(testRunId)) + .Subscribe(WriteLogEvent()); + + Subscriptions.TryAdd(testRunId, subscription); + + return testRunId; + + Action WriteLogEvent() => + logEvent => { + logEvent.AddOrUpdateProperty(testRunIdProperty); + using var writer = new StringWriter(); + DefaultFormatter.Format(logEvent, writer); + write(writer.ToString().Trim()); + }; + } + + public static Guid CaptureLogs(ITestOutputHelper outputHelper, Guid testRunId = default) => + CaptureLogs(outputHelper.WriteLine, testRunId); + + public static Guid CaptureLogs(IMessageSink sink, Guid testRunId = default) => + CaptureLogs(msg => sink.OnMessage(new DiagnosticMessage(msg)), testRunId); + + public static void ReleaseLogs(Guid captureId) { + if (!Subscriptions.TryRemove(captureId, out var subscription)) + return; + + try { + subscription.Dispose(); + } + catch { + // ignored + } } - - public static Guid CaptureLogs(ITestOutputHelper outputHelper, Guid testRunId = default) => - CaptureLogs(outputHelper.WriteLine, testRunId); - - public static Guid CaptureLogs(IMessageSink sink, Guid testRunId = default) => - CaptureLogs(msg => sink.OnMessage(new DiagnosticMessage(msg)), testRunId); - - public static void ReleaseLogs(Guid captureId) { - if (!Subscriptions.TryRemove(captureId, out var subscription)) - return; - - try { - subscription.Dispose(); - } - catch { - // ignored - } - } } \ No newline at end of file diff --git a/test/EventStore.Client.Tests.Common/PasswordGenerator.cs b/test/EventStore.Client.Tests.Common/PasswordGenerator.cs index 997b05f6e..298600529 100644 --- a/test/EventStore.Client.Tests.Common/PasswordGenerator.cs +++ b/test/EventStore.Client.Tests.Common/PasswordGenerator.cs @@ -3,64 +3,64 @@ namespace EventStore.Client.Tests; static class PasswordGenerator { - static PasswordGenerator() { - Random = new(); - AsciiChars = GenerateAsciiCharacters(); - NonAsciiChars = GenerateNonAsciiCharacters(); - } - - static Random Random { get; } - static string AsciiChars { get; } - static string NonAsciiChars { get; } - - static string GenerateAsciiCharacters() { - var builder = new StringBuilder(); - for (var i = 32; i < 127; i++) - builder.Append((char)i); - - return builder.ToString(); - } - - static string GenerateNonAsciiCharacters() { - var builder = new StringBuilder(); - for (var i = 127; i < 65535; i++) - builder.Append((char)i); - - return builder.ToString(); - } - - public static string GeneratePassword(int length = 8, int minNonAsciiChars = 1) { - if (length < minNonAsciiChars || length <= 0 || minNonAsciiChars < 0) - throw new ArgumentException("Invalid input parameters."); - - var password = new char[length]; - - // Generate the required number of non-ASCII characters - for (var i = 0; i < minNonAsciiChars; i++) - password[i] = NonAsciiChars[Random.Next(NonAsciiChars.Length)]; - - // Generate the remaining characters - for (var i = minNonAsciiChars; i < length; i++) - password[i] = AsciiChars[Random.Next(AsciiChars.Length)]; - - // Shuffle the characters to randomize the password - for (var i = length - 1; i > 0; i--) { - var j = Random.Next(i + 1); - (password[i], password[j]) = (password[j], password[i]); - } - - return new(password); - } - - public static string GenerateSimplePassword(int length = 8) { - if (length <= 0) - throw new ArgumentException("Invalid input parameters."); - - var password = new char[length]; - - for (var i = 0; i < length; i++) - password[i] = AsciiChars[Random.Next(AsciiChars.Length)]; - - return new(password); - } + static PasswordGenerator() { + Random = new(); + AsciiChars = GenerateAsciiCharacters(); + NonAsciiChars = GenerateNonAsciiCharacters(); + } + + static Random Random { get; } + static string AsciiChars { get; } + static string NonAsciiChars { get; } + + static string GenerateAsciiCharacters() { + var builder = new StringBuilder(); + for (var i = 32; i < 127; i++) + builder.Append((char)i); + + return builder.ToString(); + } + + static string GenerateNonAsciiCharacters() { + var builder = new StringBuilder(); + for (var i = 127; i < 65535; i++) + builder.Append((char)i); + + return builder.ToString(); + } + + public static string GeneratePassword(int length = 8, int minNonAsciiChars = 1) { + if (length < minNonAsciiChars || length <= 0 || minNonAsciiChars < 0) + throw new ArgumentException("Invalid input parameters."); + + var password = new char[length]; + + // Generate the required number of non-ASCII characters + for (var i = 0; i < minNonAsciiChars; i++) + password[i] = NonAsciiChars[Random.Next(NonAsciiChars.Length)]; + + // Generate the remaining characters + for (var i = minNonAsciiChars; i < length; i++) + password[i] = AsciiChars[Random.Next(AsciiChars.Length)]; + + // Shuffle the characters to randomize the password + for (var i = length - 1; i > 0; i--) { + var j = Random.Next(i + 1); + (password[i], password[j]) = (password[j], password[i]); + } + + return new(password); + } + + public static string GenerateSimplePassword(int length = 8) { + if (length <= 0) + throw new ArgumentException("Invalid input parameters."); + + var password = new char[length]; + + for (var i = 0; i < length; i++) + password[i] = AsciiChars[Random.Next(AsciiChars.Length)]; + + return new(password); + } } \ No newline at end of file diff --git a/test/EventStore.Client.Tests.Common/TestCredentials.cs b/test/EventStore.Client.Tests.Common/TestCredentials.cs index 7530f124b..a489cd13d 100644 --- a/test/EventStore.Client.Tests.Common/TestCredentials.cs +++ b/test/EventStore.Client.Tests.Common/TestCredentials.cs @@ -1,9 +1,9 @@ -namespace EventStore.Client.Tests; +namespace EventStore.Client.Tests; public static class TestCredentials { - public static readonly UserCredentials Root = new("admin", "changeit"); - public static readonly UserCredentials TestUser1 = new("user1", "pa$$1"); - public static readonly UserCredentials TestUser2 = new("user2", "pa$$2"); - public static readonly UserCredentials TestAdmin = new("adm", "admpa$$"); - public static readonly UserCredentials TestBadUser = new("badlogin", "badpass"); + public static readonly UserCredentials Root = new("admin", "changeit"); + public static readonly UserCredentials TestUser1 = new("user1", "pa$$1"); + public static readonly UserCredentials TestUser2 = new("user2", "pa$$2"); + public static readonly UserCredentials TestAdmin = new("adm", "admpa$$"); + public static readonly UserCredentials TestBadUser = new("badlogin", "badpass"); } \ No newline at end of file