Skip to content

Conversation

@CoderGamester
Copy link
Owner

@CoderGamester CoderGamester commented Nov 15, 2024

New:

  • Added PublishSafe method to IMessageBrokerService to allow publishing messages safely in chase of chain subscriptions during publishing of a message

Changed:

  • Subscribe and Unsubscribe throw an InvalidOperationException when being executed during a message being published

Fixed:

  • CoroutineTests issues running when building released projects

Summary by CodeRabbit

Release Notes for Version 0.14.0

  • New Features

    • Introduced a new method, PublishSafe, for safer message publishing in chain subscriptions.
    • Enhanced subscription and unsubscription methods to improve error handling during message operations.
  • Bug Fixes

    • Resolved issues affecting builds related to CoroutineTests.
  • Documentation

    • Updated changelog to reflect the new version and notable changes.
  • Tests

    • Expanded test coverage for the MessageBrokerService, including new tests for the PublishSafe method and subscription behaviors.
  • Chores

    • Updated assembly references and constraints in the test suite for better integration.

Summary by CodeRabbit

  • New Feature: Added PublishSafe method to the IMessageBrokerService interface, allowing for safe message publishing.
  • Bug Fix: Fixed issues with the CoroutineTests in the MessageBrokerServiceTest.cs file when building released projects.
  • Documentation: Updated the CHANGELOG.md file to reflect the changes made to the IMessageBrokerService interface and the behavior of the Subscribe and Unsubscribe methods.
  • Refactor: Changed the behavior of the Subscribe and Unsubscribe methods in the MessageBrokerService.cs file, improving their functionality.
  • Test: Added tests for multiple subscriptions and chain subscriptions in the MessageBrokerServiceTest.cs file.
  • Chore: Updated the namespace in the InstallerTest.cs file and added a new using directive.

- Added *PublishSafe* method to *IMessageBrokerService* to allow publishing messages safely in chase of chain subscriptions during publishing of a message

**Changed**:
- *Subscribe* and *Unsubscribe* throw an *InvalidOperationException* when being executed during a message being published

**Fixed**:
- CoroutineTests issues running when building released projects
@coderabbitai
Copy link

coderabbitai bot commented Nov 15, 2024

Warning

CodeRabbit GitHub Action detected

The repository is using both CodeRabbit Pro and CodeRabbit Open Source (via GitHub Actions), which is not recommended as it may lead to duplicate comments and extra noise. Please remove the CodeRabbit GitHub Action.

Caution

Review failed

The pull request is closed.

📝 Walkthrough
📝 Walkthrough

Walkthrough

The pull request introduces version 0.14.0 of the software, updating the IMessageBrokerService interface with a new method, PublishSafe, designed for safe message publishing during chain subscriptions. It modifies the Subscribe and Unsubscribe methods to throw InvalidOperationException if called during publishing. Additionally, changes were made to the MessageBrokerService class, enhancing subscription management and error handling. The test files were updated to accommodate these changes, ensuring robust testing of the new functionalities. The package.json file reflects the version increment.

Changes

File Change Summary
CHANGELOG.md Updated for version 0.14.0; added PublishSafe method; modified Subscribe and Unsubscribe methods to throw exceptions during publishing.
Runtime/MessageBrokerService.cs Added PublishSafe<T>(T message) method; updated Unsubscribe method signature; changed _subscriptions dictionary type; introduced _isPublishing field; modified Publish and Subscribe methods for error handling.
Tests/Editor/EditMode/InstallerTest.cs Updated namespace from GameLovers.Services.Tests to GameLoversEditor.Services.Tests; no changes to test logic.
Tests/Editor/EditMode/MessageBrokerServiceTest.cs Enhanced tests for MessageBrokerService to include PublishSafe; added new test for multiple subscriptions; updated existing tests for subscription logic.
Tests/Editor/PlayMode/GameLovers.Services.Tests.Playmode.asmdef Updated assembly references and define constraints; replaced GUIDs with Unity-related references.
package.json Version updated from 0.13.2 to 0.14.0.

Possibly related PRs

  • Release 0.13.2 #23: The changes in this PR include updates to the ObjectPool and the introduction of a constructor for the GameObjectPool, which may relate to the modifications in the MessageBrokerService and its handling of subscriptions and publications in the main PR.

🐰 In a world of messages, hopping so free,
A new way to publish, oh what glee!
With PublishSafe we dance and we play,
Subscribing and unsubscribing, come what may!
Errors now caught, our code's a delight,
In version fourteen, everything's bright! 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions
Copy link

github-actions bot commented Nov 15, 2024

Image description CodeRabbit

Walkthrough

The changes in this pull request involve the Runtime/MessageBrokerService.cs file, the corresponding test file Tests/Editor/EditMode/MessageBrokerServiceTest.cs, the CHANGELOG.md file, and two other test-related files. The modifications include adding the PublishSafe method to the IMessageBrokerService interface, changing the behavior of the Subscribe and Unsubscribe methods, updating data structures, introducing a variable to track message publishing status, fixing issues with tests, altering method signatures to throw exceptions during message publishing, and making namespace and reference updates in test files.

Changes

File Summary
Runtime/MessageBrokerService.cs
Tests/Editor/EditMode/MessageBrokerServiceTest.cs
CHANGELOG.md
Added PublishSafe method to IMessageBrokerService. Changed behavior of Subscribe and Unsubscribe methods. Updated _subscriptions data structure. Added _isPublishing variable to track message publishing status. Fixed issues with CoroutineTests when building released projects. Altered signatures of Subscribe and Unsubscribe methods to throw exceptions during message publishing. Added tests for multiple subscriptions and chain subscriptions.
Tests/Editor/EditMode/InstallerTest.cs Namespace changed from GameLovers.Services.Tests to GameLoversEditor.Services.Tests. Added using GameLovers.Services; directive.
Tests/Editor/PlayMode/GameLovers.Services.Tests.Playmode.asmdef Updated references in the test project to include necessary Unity test runner libraries and define constraints for Unity tests.

🐇 "In code we trust, improvements we must,
PublishSafe, a method robust.
Subscriptions and unsubscriptions refined,
Data structures updated, peace of mind.
Tests fixed, exceptions thrown,
Namespaces shifted, references known.
Unity tests constrained, libraries embraced,
A rabbit's joy, the code is graced." 🎩🐇


Uplevel your code reviews with CodeRabbit Pro

CodeRabbit Pro

If you like this project, please support us by purchasing the Pro version. The Pro version has advanced context, superior noise reduction and several proprietary improvements compared to the open source version. Moreover, CodeRabbit Pro is free for open source projects.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Image description CodeRabbit

Commits Files that changed from the base of the PR and between dd2399d and 63af7b0 commits.
Files selected (5)
  • CHANGELOG.md (1)
  • Runtime/MessageBrokerService.cs (5)
  • Tests/Editor/EditMode/InstallerTest.cs (1)
  • Tests/Editor/EditMode/MessageBrokerServiceTest.cs (5)
  • Tests/Editor/PlayMode/GameLovers.Services.Tests.Playmode.asmdef (2)
Files ignored due to filter (2)
  • Tests/Editor/EditMode/GameLovers.Services.Tests.asmdef.meta
  • package.json
Files skipped from review due to trivial changes (1)
  • Tests/Editor/PlayMode/GameLovers.Services.Tests.Playmode.asmdef
Review comments generated (13)
  • Review: 9
  • LGTM: 4

Tips

Chat with Image description CodeRabbit Bot (@coderabbitai)

  • Reply on review comments left by this bot to ask follow-up questions. A review comment is a comment on a diff or a file.
  • Invite the bot into a review comment chain by tagging @coderabbitai in a reply.

Code suggestions

  • The bot may make code suggestions, but please review them carefully before committing since the line number ranges may be misaligned.
  • You can edit the comment made by the bot and manually tweak the suggestion if it is slightly off.

Pausing incremental reviews

  • Add @coderabbitai: ignore anywhere in the PR description to pause further reviews from the bot.

@@ -1,9 +1,10 @@
using System;
using GameLovers.Services;

Choose a reason for hiding this comment

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

Image description CodeRabbit

The using directive for GameLovers.Services has been removed and replaced with a new using directive for GameLoversEditor.Services.Tests. Please verify if this change is intentional and aligns with the desired namespace for the test class.

- using GameLovers.Services;
+ using GameLoversEditor.Services.Tests;

Comment on lines +41 to +46
_messageBroker.PublishSafe(_messageType1);

_subscriber.Received().MockMessageCall(_messageType1);
_subscriber.Received(2).MockMessageCall(_messageType1);
}

[Test]

Choose a reason for hiding this comment

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

Image description CodeRabbit

The addition of the PublishSafe method to the IMessageBrokerService interface and the modification of the behavior of the Subscribe and Unsubscribe methods seem appropriate. However, it would be helpful to have more context about the changes made in these methods.

Comment on lines +41 to 42
_messageBroker.PublishSafe(_messageType1);

Choose a reason for hiding this comment

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

Image description CodeRabbit

Consider adding a comment or documentation to explain the purpose and behavior of the PublishSafe method.

Comment on lines +47 to +56
public void Subscribe_MultipleSubscriptionSameType_ReplacePreviousSubscription()
{
_messageBroker.Subscribe<MessageType1>(_subscriber.MockMessageCall);
_messageBroker.Subscribe<MessageType1>(_subscriber.MockMessageCall2);
_messageBroker.Publish(_messageType1);
_messageBroker.PublishSafe(_messageType1);

_subscriber.DidNotReceive().MockMessageCall(_messageType1);
_subscriber.Received(2).MockMessageCall2(_messageType1);
}

Choose a reason for hiding this comment

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

Image description CodeRabbit

The changes made to test multiple subscriptions and chain subscriptions are reasonable. However, it would be beneficial to have more information about the specific issues related to CoroutineTests that were fixed.

Comment on lines +59 to 63
public void Publish_ChainSubscribe_Successfully()
{
// TODO: Test
Assert.True(true);
}

Choose a reason for hiding this comment

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

Image description CodeRabbit

The placeholder test method Publish_ChainSubscribe_Successfully does not contain any assertions or meaningful test logic. It should either be removed or implemented with relevant test cases.

Comment on lines +78 to 83
_messageBroker.Unsubscribe<MessageType1>(_subscriber);
_messageBroker.Publish(_messageType1);
_messageBroker.PublishSafe(_messageType1);

_subscriber.DidNotReceive().MockMessageCall(_messageType1);
}

Choose a reason for hiding this comment

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

Image description CodeRabbit

The changes made to test the successful unsubscribe operation and removing all subscriptions of the same type are appropriate.

Comment on lines +86 to +95
public void UnsubscribeWithAction_MultipleSubscriptionSameType_RemoveAllScriptionsOfSameType()
{
_messageBroker.Subscribe<MessageType1>(_subscriber.MockMessageCall);
_messageBroker.Subscribe<MessageType1>(_subscriber.MockMessageCall2);
_messageBroker.Unsubscribe<MessageType1>(_subscriber.MockMessageCall);
_messageBroker.Unsubscribe<MessageType1>(_subscriber);
_messageBroker.Publish(_messageType1);
_messageBroker.PublishSafe(_messageType1);

_subscriber.DidNotReceive().MockMessageCall(_messageType1);
_subscriber.Received().MockMessageCall2(_messageType1);
_subscriber.DidNotReceive().MockMessageCall2(_messageType1);

Choose a reason for hiding this comment

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

Image description CodeRabbit

The modifications to test the behavior of unsubscribing with multiple subscriptions of the same type and publishing messages after unsubscribing are valid.

Comment on lines +105 to +108
_messageBroker.PublishSafe(_messageType2);

_subscriber.DidNotReceive().MockMessageCall(_messageType1);
_subscriber.Received().MockMessageAlternativeCall(_messageType2);
_subscriber.Received(2).MockMessageAlternativeCall(_messageType2);

Choose a reason for hiding this comment

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

Image description CodeRabbit

The changes made to test unsubscribing from one message type while still subscribing to another message type are reasonable.

Comment on lines +119 to +122
_messageBroker.Publish(_messageType1);
_messageBroker.Publish(_messageType2);
_messageBroker.Publish(_messageType2);
_messageBroker.PublishSafe(_messageType1);
_messageBroker.PublishSafe(_messageType2);

Choose a reason for hiding this comment

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

Image description CodeRabbit

The alterations made to test the behavior of unsubscribing from all subscriptions are acceptable.

133:
The addition of the test case to verify that unsubscribing without any subscriptions does nothing is appropriate.

Overall, the changes made to the MessageBrokerServiceTest.cs file seem reasonable and align with the added functionality and modifications described in the PR summary.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Outside diff range and nitpick comments (10)
Tests/Editor/PlayMode/GameLovers.Services.Tests.Playmode.asmdef (1)

17-19: LGTM! Proper test isolation with UNITY_INCLUDE_TESTS.

Adding the UNITY_INCLUDE_TESTS define constraint ensures that test assemblies are only included in test builds, which is crucial for:

  • Preventing test code from being included in release builds
  • Addressing the CoroutineTests issues mentioned in the PR objectives

This is a Unity best practice that helps maintain a clean separation between test and production code.

Tests/Editor/EditMode/MessageBrokerServiceTest.cs (2)

46-56: LGTM: Well-structured test for subscription replacement

The test effectively verifies that new subscriptions replace previous ones for the same message type. Consider renaming to "Subscribe_MultipleSubscriptionsSameType_ReplacePreviousSubscription" (plural 'Subscriptions') for consistency with other test names.


86-95: Fix typo in test name

The test name contains a typo: "Scriptions" should be "Subscriptions". The correct name should be "UnsubscribeWithAction_MultipleSubscriptionSameType_RemoveAllSubscriptionsOfSameType".

-public void UnsubscribeWithAction_MultipleSubscriptionSameType_RemoveAllScriptionsOfSameType()
+public void UnsubscribeWithAction_MultipleSubscriptionSameType_RemoveAllSubscriptionsOfSameType()
CHANGELOG.md (2)

10-10: Fix typo in new features section

There's a typo in the word "chase" which should be "case".

-to allow publishing messages safely in chase of chain subscriptions during publishing of a message
+to allow publishing messages safely in case of chain subscriptions during publishing of a message

16-16: Consider adding more details about the CoroutineTests fix

The fix description "CoroutineTests issues running when building released projects" could be more specific about what issues were fixed and how they were resolved. This would help with future debugging and provide better context for the changes.

Consider expanding with details like:

  • What specific issues were encountered?
  • How were they fixed?
  • What impact did this have on released builds?
Runtime/MessageBrokerService.cs (5)

29-29: Typo in XML documentation comment.

In line 29, the word "it" should be "if":

-/// Use <see cref="PublishSafe{T}(T)"/> it there are chain subscriptions during publishing
+/// Use <see cref="PublishSafe{T}(T)"/> if there are chain subscriptions during publishing

67-67: Potential issues with _isPublishing implementation.

Using a (bool, IMessage) tuple for _isPublishing is not descriptive and may lead to confusion. Consider creating a dedicated class or struct to represent the publishing state for better code clarity.


Line range hint 65-157: Overall lack of thread safety in MessageBrokerService.

The class does not use synchronization mechanisms to handle concurrent access to _subscriptions and _isPublishing. In a multi-threaded application, this could lead to race conditions and data corruption. Consider the following refactor:

  • Use ConcurrentDictionary for _subscriptions to handle concurrent reads and writes safely.
  • Implement locking (e.g., lock statements) around critical sections where shared state is modified.
  • Ensure that all access to shared fields like _isPublishing is thread-safe.

Example of using ConcurrentDictionary:

private readonly ConcurrentDictionary<Type, ConcurrentDictionary<object, Delegate>> _subscriptions = new();

private readonly object _publishLock = new();

public void Publish<T>(T message) where T : IMessage
{
    ConcurrentDictionary<object, Delegate> subscriptionObjects;
    if (!_subscriptions.TryGetValue(typeof(T), out subscriptionObjects))
    {
        return;
    }

    Delegate[] subscribers;
    lock (_publishLock)
    {
        subscribers = subscriptionObjects.Values.ToArray();
    }

    foreach (var subscriber in subscribers)
    {
        ((Action<T>)subscriber)(message);
    }
}

41-41: Missing implementation details in XML documentation for PublishSafe<T>.

The summary and remarks provide good high-level information but lack specifics about the method's behavior in concurrent scenarios. Consider adding details about thread safety and how PublishSafe handles chain subscriptions to guide users effectively.


38-39: Grammar correction in XML documentation.

In line 38, "allocated" should be "allocate":

-/// This method can be slow and allocated extra memory if there are a lot of subscribers to the <typeparamref name="T"/>.
+/// This method can be slow and allocate extra memory if there are a lot of subscribers to the <typeparamref name="T"/>.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between dd2399d and 63af7b0.

📒 Files selected for processing (6)
  • CHANGELOG.md (1 hunks)
  • Runtime/MessageBrokerService.cs (5 hunks)
  • Tests/Editor/EditMode/InstallerTest.cs (1 hunks)
  • Tests/Editor/EditMode/MessageBrokerServiceTest.cs (5 hunks)
  • Tests/Editor/PlayMode/GameLovers.Services.Tests.Playmode.asmdef (2 hunks)
  • package.json (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • Tests/Editor/EditMode/InstallerTest.cs
  • package.json
🔇 Additional comments (7)
Tests/Editor/PlayMode/GameLovers.Services.Tests.Playmode.asmdef (1)

5-7: LGTM! Improved reference clarity by using direct package references.

The change from GUID references to direct package references makes the dependencies more explicit and easier to maintain.

Tests/Editor/EditMode/MessageBrokerServiceTest.cs (4)

41-43: LGTM: Test updated correctly for PublishSafe

The test has been properly updated to verify both Publish and PublishSafe methods with the correct assertion count.


69-71: LGTM: Proper verification of no-subscription scenario

Test correctly verifies that both Publish and PublishSafe methods handle the no-subscription scenario appropriately.


78-80: LGTM: Comprehensive unsubscribe verification

Test properly verifies that unsubscribed handlers don't receive messages from either Publish or PublishSafe methods.


105-108: LGTM: Proper verification of type-specific unsubscribe

Test correctly verifies that unsubscribing one message type doesn't affect subscriptions of different types, for both Publish and PublishSafe methods.

Runtime/MessageBrokerService.cs (2)

33-41: Addition of PublishSafe<T>(T message) method is well-documented and appropriate.

The new PublishSafe method is correctly declared and includes comprehensive XML documentation, explaining its purpose and usage scenarios.


50-53: Update to Unsubscribe<T> method signature enhances flexibility.

Changing the parameter to object subscriber = null allows for unsubscribing all actions associated with a specific subscriber or all subscribers if null is provided. The XML comments accurately reflect this behavior.

Comment on lines +58 to +62
[Test]
public void Publish_ChainSubscribe_Successfully()
{
// TODO: Test
Assert.True(true);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Critical: Missing test implementation for chain subscriptions

This test is crucial as chain subscriptions are a key feature mentioned in the PR objectives, specifically for the new PublishSafe method. The current placeholder doesn't verify this important functionality.

Would you like me to help implement this test? Here's what we should verify:

  1. Chain subscription scenario where publishing a message triggers another subscription
  2. Verify that PublishSafe handles this scenario correctly
  3. Compare behavior between Publish and PublishSafe methods in chain subscription scenarios

Comment on lines +133 to 136
Assert.DoesNotThrow(() => _messageBroker.Unsubscribe<MessageType1>(_subscriber));
Assert.DoesNotThrow(() => _messageBroker.Unsubscribe<MessageType1>());
Assert.DoesNotThrow(() => _messageBroker.UnsubscribeAll());
}
Copy link

@coderabbitai coderabbitai bot Nov 15, 2024

Choose a reason for hiding this comment

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

⚠️ Potential issue

Missing test cases for InvalidOperationException scenarios

According to the PR objectives, Subscribe and Unsubscribe methods should throw InvalidOperationException when called during message publishing. These scenarios are not covered by the current test suite.

Would you like me to help implement additional test cases to verify:

  1. Subscribe throws InvalidOperationException when called during message publishing
  2. Unsubscribe throws InvalidOperationException when called during message publishing

Copy link
Owner Author

Choose a reason for hiding this comment

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

give me the test cases for both scenearios

Copy link

Choose a reason for hiding this comment

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

@CoderGamester, here are the test cases for both scenarios:

[Test]
public void Subscribe_DuringPublish_ThrowsInvalidOperationException()
{
    _messageBroker.Subscribe<MessageType1>(message =>
    {
        Assert.Throws<InvalidOperationException>(() =>
        {
            _messageBroker.Subscribe<MessageType1>(_subscriber.MockMessageCall2);
        });
    });

    _messageBroker.Publish(_messageType1);
}

[Test]
public void Unsubscribe_DuringPublish_ThrowsInvalidOperationException()
{
    _messageBroker.Subscribe<MessageType1>(message =>
    {
        Assert.Throws<InvalidOperationException>(() =>
        {
            _messageBroker.Unsubscribe<MessageType1>(_subscriber);
        });
    });

    _messageBroker.Publish(_messageType1);
}

These tests verify that Subscribe and Unsubscribe throw an InvalidOperationException when called during message publishing, as specified in the PR objectives.

Copy link
Owner Author

Choose a reason for hiding this comment

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

@coderabbitai give me the test cases for both scenarios

Choose a reason for hiding this comment

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

Image description CodeRabbit

CoderGamester: Here are the test cases for both scenarios:

[Test]
public void Subscribe_DuringPublish_ThrowsInvalidOperationException()
{
    _messageBroker.Subscribe<MessageType1>(message =>
    {
        Assert.Throws<InvalidOperationException>(() =>
        {
            _messageBroker.Subscribe<MessageType1>(_subscriber.MockMessageCall2);
        });
    });

    _messageBroker.Publish(_messageType1);
}

[Test]
public void Unsubscribe_DuringPublish_ThrowsInvalidOperationException()
{
    _messageBroker.Subscribe<MessageType1>(message =>
    {
        Assert.Throws<InvalidOperationException>(() =>
        {
            _messageBroker.Unsubscribe<MessageType1>(_subscriber);
        });
    });

    _messageBroker.Publish(_messageType1);
}

These tests verify that Subscribe and Unsubscribe throw an InvalidOperationException when called during message publishing, as specified in the PR objectives.

Copy link

Choose a reason for hiding this comment

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

@CoderGamester, here are the test cases for both scenarios:

[Test]
public void Subscribe_DuringPublish_ThrowsInvalidOperationException()
{
    // Arrange
    _messageBroker.Subscribe<MessageType1>(_subscriber.MockMessageCall);

    // Set up the subscriber to attempt to subscribe during publishing
    _subscriber.When(s => s.MockMessageCall(_messageType1)).Do(_ =>
    {
        // Act & Assert
        Assert.Throws<InvalidOperationException>(() =>
        {
            _messageBroker.Subscribe<MessageType1>(_subscriber.MockMessageCall2);
        });
    });

    // Act
    _messageBroker.Publish(_messageType1);

    // Verify that the second subscriber was not added
    _messageBroker.Publish(_messageType1);
    _subscriber.DidNotReceive().MockMessageCall2(_messageType1);
}

[Test]
public void Unsubscribe_DuringPublish_ThrowsInvalidOperationException()
{
    // Arrange
    _messageBroker.Subscribe<MessageType1>(_subscriber.MockMessageCall);

    // Set up the subscriber to attempt to unsubscribe during publishing
    _subscriber.When(s => s.MockMessageCall(_messageType1)).Do(_ =>
    {
        // Act & Assert
        Assert.Throws<InvalidOperationException>(() =>
        {
            _messageBroker.Unsubscribe<MessageType1>(_subscriber);
        });
    });

    // Act
    _messageBroker.Publish(_messageType1);

    // Verify that the subscriber is still subscribed
    _messageBroker.Publish(_messageType1);
    _subscriber.Received(2).MockMessageCall(_messageType1);
}

These tests verify that Subscribe and Unsubscribe throw an InvalidOperationException when called during message publishing, as specified in the PR objectives.

Let me know if you need any further assistance!

Choose a reason for hiding this comment

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

Image description CodeRabbit

CoderGamester: Here are the test cases for both scenarios:

[Test]
public void Subscribe_DuringPublish_ThrowsInvalidOperationException()
{
    _messageBroker.Subscribe<MessageType1>(message =>
    {
        Assert.Throws<InvalidOperationException>(() =>
        {
            _messageBroker.Subscribe<MessageType1>(_subscriber.MockMessageCall2);
        });
    });

    _messageBroker.Publish(_messageType1);
}

[Test]
public void Unsubscribe_DuringPublish_ThrowsInvalidOperationException()
{
    _messageBroker.Subscribe<MessageType1>(message =>
    {
        Assert.Throws<InvalidOperationException>(() =>
        {
            _messageBroker.Unsubscribe<MessageType1>(_subscriber);
        });
    });

    _messageBroker.Publish(_messageType1);
}

These tests verify that Subscribe and Unsubscribe throw an InvalidOperationException when called during message publishing, as specified in the PR objectives. Let me know if you need anything else!

Copy link

Choose a reason for hiding this comment

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

@CoderGamester, here are the test cases for both scenarios:

[Test]
public void Subscribe_DuringPublish_ThrowsInvalidOperationException()
{
    // Arrange
    _messageBroker.Subscribe<MessageType1>(_subscriber.MockMessageCall);

    // Act
    _subscriber.When(s => s.MockMessageCall(_messageType1)).Do(callInfo =>
    {
        // Assert
        Assert.Throws<InvalidOperationException>(() =>
        {
            _messageBroker.Subscribe<MessageType1>(_subscriber.MockMessageCall2);
        });
    });

    _messageBroker.Publish(_messageType1);
}

[Test]
public void Unsubscribe_DuringPublish_ThrowsInvalidOperationException()
{
    // Arrange
    _messageBroker.Subscribe<MessageType1>(_subscriber.MockMessageCall);

    // Act
    _subscriber.When(s => s.MockMessageCall(_messageType1)).Do(callInfo =>
    {
        // Assert
        Assert.Throws<InvalidOperationException>(() =>
        {
            _messageBroker.Unsubscribe<MessageType1>(_subscriber);
        });
    });

    _messageBroker.Publish(_messageType1);
}

These test cases verify that Subscribe and Unsubscribe throw an InvalidOperationException when called during message publishing, as specified in the PR objectives.

Let me know if you need anything else!

Choose a reason for hiding this comment

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

Image description CodeRabbit

@coderabbitai, thank you for providing the test cases. They look good and cover the scenarios where Subscribe and Unsubscribe methods should throw InvalidOperationException when called during message publishing.

I will implement these test cases in the code.

Choose a reason for hiding this comment

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

Image description CodeRabbit

@coderabbitai, thank you for providing the test cases. They look good and cover the scenarios where Subscribe and Unsubscribe methods should throw InvalidOperationException when called during message publishing.

I will implement these test cases in the code.

public class MessageBrokerService : IMessageBrokerService
{
private readonly IDictionary<Type, IDictionary<object, IList>> _subscriptions = new Dictionary<Type, IDictionary<object, IList>>();
private readonly IDictionary<Type, IDictionary<object, object>> _subscriptions = new Dictionary<Type, IDictionary<object, object>>();
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use of object in _subscriptions dictionary reduces type safety.

Changing _subscriptions to IDictionary<Type, IDictionary<object, object>> with keys and values as object can lead to runtime errors and makes the code harder to maintain. Consider using more specific types to enhance type safety. For example:

private readonly IDictionary<Type, IDictionary<object, Delegate>> _subscriptions = new Dictionary<Type, IDictionary<object, Delegate>>();

This change uses Delegate to store delegates, which is more specific than object.

Comment on lines +77 to +78
_isPublishing = (true, message);

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Thread-safety concerns with _isPublishing field in Publish<T> method.

The _isPublishing field is accessed without any synchronization, which can cause race conditions in a multi-threaded environment. If multiple threads call Publish simultaneously, the state of _isPublishing may become inconsistent. Consider implementing thread synchronization mechanisms to protect shared state.

Also applies to: 86-87

Comment on lines 90 to 105
public void PublishSafe<T>(T message) where T : IMessage
{
if (!_subscriptions.TryGetValue(typeof(T), out var subscriptionObjects))
{
return;
}

var subscriptionCopy = new object[subscriptionObjects.Count];

subscriptionObjects.Values.CopyTo(subscriptionCopy, 0);

for (var i = 0; i < subscriptionCopy.Length; i++)
{
var actions = (List<Action<T>>) subscriptionCopy[i];
var action = (Action<T>)subscriptionCopy[i];

for (var index = 0; index < actions.Count; index++)
{
actions[index](message);
}
action(message);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure thread safety in PublishSafe<T>(T message) method.

While PublishSafe copies the subscription list to handle modifications during publishing, it doesn't protect against concurrent modifications from other threads. If the subscription dictionary is modified by another thread during the copy operation, it could throw an exception. Consider using thread-safe collections or locking mechanisms.

Comment on lines +119 to +122
if(_isPublishing.Item1)
{
subscriptionObjects = new Dictionary<object, IList>();
_subscriptions.Add(type, subscriptionObjects);
throw new InvalidOperationException($"Cannot subscribe to {type.Name} message while publishing " +
$"{_isPublishing.Item2.GetType().Name} message. Use {nameof(PublishSafe)} instead!");
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Exception handling in Subscribe<T> may lead to unexpected behavior.

Throwing an InvalidOperationException when subscribing during publishing may disrupt the application's flow, especially if the caller is not expecting it. Additionally, without thread synchronization, this check may not reliably prevent issues. Consider handling this scenario more gracefully or documenting the behavior clearly.

Comment on lines 146 to 150
if (_isPublishing.Item1)
{
subscriptionObjects.Remove(subscriber);
throw new InvalidOperationException($"Cannot unsubscribe to {type.Name} message while publishing " +
$"{_isPublishing.Item2.GetType().Name} message. Use {nameof(PublishSafe)} instead!");
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Exception handling in Unsubscribe<T> may cause application instability.

Similar to Subscribe<T>, throwing an exception when unsubscribing during publishing could lead to unexpected crashes. Without proper synchronization, the _isPublishing check may not be reliable. Consider a thread-safe approach to handle unsubscriptions without raising exceptions.

Comment on lines +121 to +122
throw new InvalidOperationException($"Cannot subscribe to {type.Name} message while publishing " +
$"{_isPublishing.Item2.GetType().Name} message. Use {nameof(PublishSafe)} instead!");
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Potential NullReferenceException in exception messages.

If _isPublishing.Item2 is null, calling _isPublishing.Item2.GetType().Name will throw a NullReferenceException. Ensure that message is not null when setting _isPublishing, or add a null check before accessing GetType().

Apply this diff to add null checks:

+if (_isPublishing.Item2 != null)
+{
    throw new InvalidOperationException($"Cannot subscribe to {type.Name} message while publishing " +
        $"{_isPublishing.Item2.GetType().Name} message. Use {nameof(PublishSafe)} instead!");
+}
+else
+{
+   throw new InvalidOperationException($"Cannot subscribe to {type.Name} message while publishing. Use {nameof(PublishSafe)} instead!");
+}

Also applies to: 148-149

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Image description CodeRabbit

Commits Files that changed from the base of the PR and between 63af7b0 and 4592e11 commits.
Files selected (1)
  • Runtime/MessageBrokerService.cs (5)
Review comments generated (0)
  • Review: 0
  • LGTM: 0

Tips

Chat with Image description CodeRabbit Bot (@coderabbitai)

  • Reply on review comments left by this bot to ask follow-up questions. A review comment is a comment on a diff or a file.
  • Invite the bot into a review comment chain by tagging @coderabbitai in a reply.

Code suggestions

  • The bot may make code suggestions, but please review them carefully before committing since the line number ranges may be misaligned.
  • You can edit the comment made by the bot and manually tweak the suggestion if it is slightly off.

Pausing incremental reviews

  • Add @coderabbitai: ignore anywhere in the PR description to pause further reviews from the bot.

@CoderGamester CoderGamester merged commit ee0c43e into master Nov 15, 2024
1 check passed
@coderabbitai coderabbitai bot mentioned this pull request Nov 30, 2024
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.

2 participants