Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 105 additions & 51 deletions tests/MultiLock.Tests/LeadershipExtensionMethodsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,19 @@ public async Task Where_ShouldFilterEvents()
await service.WaitForLeadershipAsync(cts.Token);
await TestHelpers.WaitForConditionAsync(() => events.Count >= 1, TimeSpan.FromSeconds(5), cts.Token, eventsLock);

// Assert
events.ShouldHaveSingleItem();
events.ShouldAllBe(e => e.BecameLeader);

// Cleanup
// Cleanup - Cancel and wait for event task to complete before asserting
await cts.CancelAsync();
try { await eventTask; } catch (OperationCanceledException) { }
try { await eventTask; } catch (OperationCanceledException) { /* Expected during test cleanup; safe to ignore. */ }

// Assert - Take snapshot of events while holding lock to ensure thread-safety
LeadershipChangedEventArgs[] eventSnapshot;
lock (eventsLock)
{
eventSnapshot = events.ToArray();
}
eventSnapshot.ShouldHaveSingleItem();
eventSnapshot.ShouldAllBe(e => e.BecameLeader);

// ReSharper disable once MethodSupportsCancellation
await service.StopAsync();
await services.DisposeAsync();
Expand Down Expand Up @@ -133,12 +139,18 @@ public async Task Take_ShouldLimitNumberOfEvents()
await service.StopAsync(cts.Token);
await TestHelpers.WaitForConditionAsync(() => events.Count >= 2, TimeSpan.FromSeconds(5), cts.Token, eventsLock);

// Assert
events.Count.ShouldBe(2);

// Cleanup
// Cleanup - Cancel and wait for event task to complete before asserting
await cts.CancelAsync();
try { await eventTask; } catch (OperationCanceledException) { }
try { await eventTask; } catch (OperationCanceledException) { /* Expected during test cleanup; safe to ignore. */ }

// Assert - Take snapshot of events while holding lock to ensure thread-safety
LeadershipChangedEventArgs[] eventSnapshot;
lock (eventsLock)
{
eventSnapshot = events.ToArray();
}
eventSnapshot.Length.ShouldBe(2);

await services.DisposeAsync();
}

Expand Down Expand Up @@ -174,13 +186,19 @@ public async Task Skip_ShouldSkipEvents()
await service.StopAsync(cts.Token);
await TestHelpers.WaitForConditionAsync(() => events.Count >= 1, TimeSpan.FromSeconds(5), cts.Token, eventsLock);

// Assert
events.ShouldHaveSingleItem();
events.First().LostLeadership.ShouldBeTrue();

// Cleanup
// Cleanup - Cancel and wait for event task to complete before asserting
await cts.CancelAsync();
try { await eventTask; } catch (OperationCanceledException) { }
try { await eventTask; } catch (OperationCanceledException) { /* Expected during test cleanup; safe to ignore. */ }

// Assert - Take snapshot of events while holding lock to ensure thread-safety
LeadershipChangedEventArgs[] eventSnapshot;
lock (eventsLock)
{
eventSnapshot = events.ToArray();
}
eventSnapshot.ShouldHaveSingleItem();
eventSnapshot.First().LostLeadership.ShouldBeTrue();

await services.DisposeAsync();
}

Expand Down Expand Up @@ -217,12 +235,18 @@ public async Task DistinctUntilChanged_ShouldFilterConsecutiveDuplicates()
await service.StopAsync(cts.Token);
await TestHelpers.WaitForConditionAsync(() => events.Count >= 1, TimeSpan.FromSeconds(5), cts.Token, eventsLock);

// Assert
events.Count.ShouldBeGreaterThanOrEqualTo(1);

// Cleanup
// Cleanup - Cancel and wait for event task to complete before asserting
await cts.CancelAsync();
try { await eventTask; } catch (OperationCanceledException) { }
try { await eventTask; } catch (OperationCanceledException) { /* Expected during test cleanup; safe to ignore. */ }

// Assert - Take snapshot of events while holding lock to ensure thread-safety
LeadershipChangedEventArgs[] eventSnapshot;
lock (eventsLock)
{
eventSnapshot = events.ToArray();
}
eventSnapshot.Length.ShouldBeGreaterThanOrEqualTo(1);

await services.DisposeAsync();
}

Expand Down Expand Up @@ -454,12 +478,18 @@ public async Task Throttle_ShouldLimitEventRate()
await Task.Delay(TimeSpan.FromMilliseconds(250), cts.Token);
await TestHelpers.WaitForConditionAsync(() => events.Count >= 1, TimeSpan.FromSeconds(5), cts.Token, eventsLock);

// Assert
events.Count.ShouldBeGreaterThanOrEqualTo(1);

// Cleanup
// Cleanup - Cancel and wait for event task to complete before asserting
await cts.CancelAsync();
try { await eventTask; } catch (OperationCanceledException) { }
try { await eventTask; } catch (OperationCanceledException) { /* Expected during test cleanup; safe to ignore. */ }

// Assert - Take snapshot of events while holding lock to ensure thread-safety
LeadershipChangedEventArgs[] eventSnapshot;
lock (eventsLock)
{
eventSnapshot = events.ToArray();
}
eventSnapshot.Length.ShouldBeGreaterThanOrEqualTo(1);

await services.DisposeAsync();
}

Expand Down Expand Up @@ -499,12 +529,18 @@ public async Task Debounce_ShouldDelayEvents()
await Task.Delay(TimeSpan.FromMilliseconds(150), cts.Token);
await TestHelpers.WaitForConditionAsync(() => events.Count >= 1, TimeSpan.FromSeconds(5), cts.Token, eventsLock);

// Assert
events.Count.ShouldBeGreaterThanOrEqualTo(1);

// Cleanup
// Cleanup - Cancel and wait for event task to complete before asserting
await cts.CancelAsync();
try { await eventTask; } catch (OperationCanceledException) { }
try { await eventTask; } catch (OperationCanceledException) { /* Expected during test cleanup; safe to ignore. */ }

// Assert - Take snapshot of events while holding lock to ensure thread-safety
LeadershipChangedEventArgs[] eventSnapshot;
lock (eventsLock)
{
eventSnapshot = events.ToArray();
}
eventSnapshot.Length.ShouldBeGreaterThanOrEqualTo(1);

await services.DisposeAsync();
}

Expand Down Expand Up @@ -538,13 +574,19 @@ public async Task TakeUntilLeader_ShouldStopWhenBecomingLeader()
await service.WaitForLeadershipAsync(cts.Token);
await TestHelpers.WaitForConditionAsync(() => events.Any(e => e.BecameLeader), TimeSpan.FromSeconds(5), cts.Token, eventsLock);

// Assert
events.ShouldContain(e => e.BecameLeader);
events.Last().BecameLeader.ShouldBeTrue();

// Cleanup
// Cleanup - Cancel and wait for event task to complete before asserting
await cts.CancelAsync();
try { await eventTask; } catch (OperationCanceledException) { }
try { await eventTask; } catch (OperationCanceledException) { /* Expected during test cleanup; safe to ignore. */ }

// Assert - Take snapshot of events while holding lock to ensure thread-safety
LeadershipChangedEventArgs[] eventSnapshot;
lock (eventsLock)
{
eventSnapshot = events.ToArray();
}
eventSnapshot.ShouldContain(e => e.BecameLeader);
eventSnapshot.Last().BecameLeader.ShouldBeTrue();

// ReSharper disable once MethodSupportsCancellation
await service.StopAsync();
await services.DisposeAsync();
Expand Down Expand Up @@ -585,13 +627,19 @@ public async Task WhileLeader_ShouldContinueWhileLeader()
await service.StopAsync(cts.Token);
await TestHelpers.WaitForConditionAsync(() => !service.IsLeader, TimeSpan.FromSeconds(5), cts.Token);

// Assert
events.ShouldContain(e => e.BecameLeader);
events.ShouldAllBe(e => e.CurrentStatus.IsLeader || e.LostLeadership);

// Cleanup
// Cleanup - Cancel and wait for event task to complete before asserting
await cts.CancelAsync();
try { await eventTask; } catch (OperationCanceledException) { }
try { await eventTask; } catch (OperationCanceledException) { /* Expected during test cleanup; safe to ignore. */ }

// Assert - Take snapshot of events while holding lock to ensure thread-safety
LeadershipChangedEventArgs[] eventSnapshot;
lock (eventsLock)
{
eventSnapshot = events.ToArray();
}
eventSnapshot.ShouldContain(e => e.BecameLeader);
eventSnapshot.ShouldAllBe(e => e.CurrentStatus.IsLeader || e.LostLeadership);

await services.DisposeAsync();
}

Expand Down Expand Up @@ -737,14 +785,20 @@ public async Task CombinedExtensions_ShouldWorkTogether()
await service.StopAsync(cts.Token);
await TestHelpers.WaitForConditionAsync(() => events.Count >= 2, TimeSpan.FromSeconds(5), cts.Token, eventsLock);

// Assert
events.Count.ShouldBe(2);
events.ShouldContain(e => e.BecameLeader);
events.ShouldContain(e => e.LostLeadership);

// Cleanup
// Cleanup - Cancel and wait for event task to complete before asserting
await cts.CancelAsync();
try { await eventTask; } catch (OperationCanceledException) { }
try { await eventTask; } catch (OperationCanceledException) { /* Expected during test cleanup; safe to ignore. */ }

// Assert - Take snapshot of events while holding lock to ensure thread-safety
LeadershipChangedEventArgs[] eventSnapshot;
lock (eventsLock)
{
eventSnapshot = events.ToArray();
}
eventSnapshot.Length.ShouldBe(2);
eventSnapshot.ShouldContain(e => e.BecameLeader);
eventSnapshot.ShouldContain(e => e.LostLeadership);

await services.DisposeAsync();
}
}