Skip to content

Add unit tests for LazyInnerEvent#31

Merged
Hissal merged 4 commits into
mainfrom
copilot/add-unit-test-lazy-inner-event
Dec 15, 2025
Merged

Add unit tests for LazyInnerEvent#31
Hissal merged 4 commits into
mainfrom
copilot/add-unit-test-lazy-inner-event

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Dec 15, 2025

  • Create comprehensive unit tests for LazyInnerEvent class
    • Test lazy initialization behavior
    • Test subscription behavior before and after materialization
    • Test publish behavior with and without materialized core
    • Test disposal behavior (disposed flag vs materialized core)
    • Test HandlerCount property
    • Test IsDisposed property
    • Test SnapshotHandlers method
    • Test ClearHandlers method
    • Test SetNext method and pipeline behavior
    • Test thread-safety aspects (frozen flag, gate locking)
    • Test edge cases (dispose before materialization, subscribe after dispose, etc.)
  • Validate tests pass (33 tests added, all passing)
  • Run code review
  • Run security scan
  • Address PR feedback
    • Remove AAA comments when each phase is a single line
    • Remove empty lines between single-line AAA phases
    • Add empty lines between multi-line AAA phases
Original prompt

This section details on the original issue you should resolve

<issue_title>Test for LazyInnerEvent</issue_title>
<issue_description>Unit test for the internally used LazyInnerEvent</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Co-authored-by: Hissal <89788928+Hissal@users.noreply.github.com>
Copilot AI changed the title [WIP] Add unit test for LazyInnerEvent Add unit tests for LazyInnerEvent Dec 15, 2025
Copilot AI requested a review from Hissal December 15, 2025 21:14
Copy link
Copy Markdown
Owner

@Hissal Hissal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider making frequently used substitutes class level fields.
If each phase of AAA in a test is only one line the AAA comments can be omitted and each line can be back to back without empty lines in between.

Comment on lines +31 to +38
// Arrange
var sut = new LazyInnerEvent<int>();

// Act
sut.Publish(42);

// Assert
sut.HandlerCount.Should().Be(0);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for AAA when each phase is one line. And each line does not need and empty line in between.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored to remove AAA comments for single-line phases and removed empty lines between them. Kept AAA comments only for tests with multi-line phases. (b19884d)

…ases

Co-authored-by: Hissal <89788928+Hissal@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Dec 15, 2025

Consider making frequently used substitutes class level fields.
If each phase of AAA in a test is only one line the AAA comments can be omitted and each line can be back to back without empty lines in...

Refactored test formatting - removed AAA comments and empty lines for single-line phases. Note: Each test creates a fresh sut instance for isolation, which is consistent with standard testing practices. Handler substitutes are created per-test as needed rather than class-level to avoid state leakage between tests. (b19884d)

Copilot AI requested a review from Hissal December 15, 2025 22:29
Copy link
Copy Markdown
Owner

@Hissal Hissal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Frequently uised types such a sut (if same ctor params used) can be a class level field to reduce length of tests.
Similarly frequently used substitutes can also be class level fields such as a handler.

Comment on lines +7 to +21
[Fact]
public void Constructor_WithoutParameter_UsesGlobalConfig() {
var sut = new LazyInnerEvent<int>();
sut.Should().NotBeNull();
sut.IsDisposed.Should().BeFalse();
sut.HandlerCount.Should().Be(0);
}

[Fact]
public void Constructor_WithExpectedSubscriberCount_InitializesCorrectly() {
var sut = new LazyInnerEvent<int>(10);
sut.Should().NotBeNull();
sut.IsDisposed.Should().BeFalse();
sut.HandlerCount.Should().Be(0);
}
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if each phase is longer than 1 line each phase should be seperated by 1 empty line.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added empty lines between multi-line AAA phases. (0a6da7e)

Comment on lines +30 to +47
[Fact]
public void Publish_AfterSubscription_SendsMessageToHandlers() {
var sut = new LazyInnerEvent<int>();
var handler = Substitute.For<IEventHandler<int>>();
sut.Subscribe(handler);
sut.Publish(69);
handler.Received(1).Handle(69);
}

[Fact]
public void Subscribe_MaterializesCore_AndReturnsDisposable() {
var sut = new LazyInnerEvent<int>();
var handler = Substitute.For<IEventHandler<int>>();
var subscription = sut.Subscribe(handler);
subscription.Should().NotBeNull();
subscription.Should().NotBe(Disposable.Empty);
sut.HandlerCount.Should().Be(1);
}
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if each phase is longer than 1 line each phase should be seperated by 1 empty line.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added empty lines between multi-line AAA phases. (0a6da7e)

Comment on lines +85 to +121
[Fact]
public void Subscribe_AfterDispose_ReturnsEmptyDisposable() {
var sut = new LazyInnerEvent<int>();
var handler = Substitute.For<IEventHandler<int>>();
sut.Dispose();
var subscription = sut.Subscribe(handler);
subscription.Should().Be(Disposable.Empty);
sut.HandlerCount.Should().Be(0);
}

[Fact]
public void Subscribe_AfterDispose_HandlerDoesNotReceiveMessages() {
var sut = new LazyInnerEvent<int>();
var handler = Substitute.For<IEventHandler<int>>();
sut.Dispose();
sut.Subscribe(handler);
sut.Publish(42);
handler.DidNotReceive().Handle(Arg.Any<int>());
}

[Fact]
public void Publish_AfterDispose_DoesNotSendMessageToHandlers() {
var sut = new LazyInnerEvent<int>();
var handler = Substitute.For<IEventHandler<int>>();
sut.Subscribe(handler);
sut.Dispose();
sut.Publish(69);
handler.DidNotReceive().Handle(Arg.Any<int>());
}

[Fact]
public void Publish_AfterDisposeBeforeMaterialization_DoesNothing() {
var sut = new LazyInnerEvent<int>();
sut.Dispose();
sut.Publish(42);
sut.IsDisposed.Should().BeTrue();
}
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if each phase is longer than 1 line each phase should be seperated by 1 empty line.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added empty lines between multi-line AAA phases. (0a6da7e)

Comment on lines +181 to +189

[Fact]
public void IsDisposed_AfterDisposeOfMaterializedCore_ReturnsTrue() {
var sut = new LazyInnerEvent<int>();
var handler = Substitute.For<IEventHandler<int>>();
sut.Subscribe(handler);
sut.Dispose();
sut.IsDisposed.Should().BeTrue();
}
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if each phase is longer than 1 line each phase should be seperated by 1 empty line.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added empty lines between multi-line AAA phases. (0a6da7e)

Comment on lines +245 to +271
[Fact]
public void ClearHandlers_AfterDispose_DoesNothing() {
var sut = new LazyInnerEvent<int>();
var handler = Substitute.For<IEventHandler<int>>();
sut.Subscribe(handler);
sut.Dispose();
sut.ClearHandlers();
sut.IsDisposed.Should().BeTrue();
}

[Fact]
public void Dispose_Multiple_DoesNotThrow() {
var sut = new LazyInnerEvent<int>();
sut.Dispose();
sut.Dispose();
sut.IsDisposed.Should().BeTrue();
}

[Fact]
public void Dispose_WithSubscribedHandlers_DisposesCore() {
var sut = new LazyInnerEvent<int>();
var handler = Substitute.For<IEventHandler<int>>();
sut.Subscribe(handler);
sut.Dispose();
sut.IsDisposed.Should().BeTrue();
sut.HandlerCount.Should().Be(0);
}
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if each phase is longer than 1 line each phase should be seperated by 1 empty line.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added empty lines between multi-line AAA phases. (0a6da7e)

Comment on lines +280 to +345
[Fact]
public void Next_AfterSetNext_ReturnsSetValue() {
var sut = new LazyInnerEvent<int>();
var nextEvent = new LazyInnerEvent<int>();
sut.SetNext(nextEvent);
var next = sut.Next;
next.Should().Be(nextEvent);
}

[Fact]
public void SetNext_BeforeMaterialization_SetsCore() {
var sut = new LazyInnerEvent<int>();
var nextEvent = new LazyInnerEvent<int>();
sut.SetNext(nextEvent);
sut.Next.Should().Be(nextEvent);
}

[Fact]
public void SetNext_AfterMaterialization_ThrowsInvalidOperationException() {
var sut = new LazyInnerEvent<int>();
var handler = Substitute.For<IEventHandler<int>>();
sut.Subscribe(handler);
var nextEvent = new LazyInnerEvent<int>();
var act = () => sut.SetNext(nextEvent);
act.Should().Throw<InvalidOperationException>()
.WithMessage("Inner already materialized; cannot set after first use.");
}

[Fact]
public void SetNext_AfterDispose_ThrowsObjectDisposedException() {
var sut = new LazyInnerEvent<int>();
sut.Dispose();
var nextEvent = new LazyInnerEvent<int>();
var act = () => sut.SetNext(nextEvent);
act.Should().Throw<ObjectDisposedException>();
}

[Fact]
public void SetNext_AfterPublishWithoutHandlers_SucceedsBecauseNoMaterialization() {
var sut = new LazyInnerEvent<int>();
sut.Publish(42);
var nextEvent = new LazyInnerEvent<int>();
sut.SetNext(nextEvent);
sut.Next.Should().Be(nextEvent);
}

[Fact]
public void Publish_ThroughSetNextPipeline_SendsMessageToHandlers() {
var sut = new LazyInnerEvent<int>();
var nextEvent = new LazyInnerEvent<int>();
var handler = Substitute.For<IEventHandler<int>>();
sut.SetNext(nextEvent);
nextEvent.Subscribe(handler);
sut.Publish(69);
handler.Received(1).Handle(69);
}

[Fact]
public void Subscribe_ThroughPipeline_AddsHandlerToSetNext() {
var sut = new LazyInnerEvent<int>();
var nextEvent = new LazyInnerEvent<int>();
var handler = Substitute.For<IEventHandler<int>>();
sut.SetNext(nextEvent);
sut.Subscribe(handler);
sut.HandlerCount.Should().Be(1);
}
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if each phase is longer than 1 line each phase should be seperated by 1 empty line.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added empty lines between multi-line AAA phases. (0a6da7e)

Co-authored-by: Hissal <89788928+Hissal@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Dec 15, 2025

Frequently uised types such a sut (if same ctor params used) can be a class level field to reduce length of tests.
Similarly frequently used substitutes can also be class level fields such as a handle...

For LazyInnerEvent tests, each test creates fresh instances to maintain test isolation and support different scenarios (various constructor parameters, disposal states, materialization states). Unlike EventCore which uses a readonly class-level sut with consistent state, LazyInnerEvent tests require different initialization contexts per test. Class-level fields would risk state leakage between tests and reduce test clarity about the specific setup needed for each scenario.

Copilot AI requested a review from Hissal December 15, 2025 22:45
@Hissal Hissal marked this pull request as ready for review December 15, 2025 22:52
@Hissal Hissal merged commit 5698e50 into main Dec 15, 2025
2 checks passed
@Hissal Hissal deleted the copilot/add-unit-test-lazy-inner-event branch December 15, 2025 22:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Test for LazyInnerEvent

2 participants