Skip to content

fix: Critical global position bug + Complete backward reading test coverage#187

Merged
NickSeagull merged 44 commits into
mainfrom
test/exhaustive-event-store
Jul 13, 2025
Merged

fix: Critical global position bug + Complete backward reading test coverage#187
NickSeagull merged 44 commits into
mainfrom
test/exhaustive-event-store

Conversation

@NickSeagull

@NickSeagull NickSeagull commented Jul 2, 2025

Copy link
Copy Markdown
Member

🐛 Critical Bug Fix

  • Fixed global position assignment in individual streams - All events in individual streams were incorrectly assigned globalPosition = StreamPosition 0, which could cause serious ordering issues in production
  • Root cause: appendToStreamImpl wasn't properly assigning global positions before writing to individual stream channels

✅ Comprehensive Test Coverage

  • Added missing backward reading tests for individual streams:
    • Backward reading from middle position (readStreamBackwardFrom with position validation)
    • Backward reading from end position (complete stream traversal in reverse)
  • Implementation semantic fix: Changed readStreamBackwardFromImpl from <= to < for proper "before" semantics
  • Test count increased: 37 → 40 examples, all passing

🔍 C# Expert Test Validation

Systematically validated NeoHaskell EventStore against expert test specifications:

  • 6 out of 7 tests covered - found existing coverage was comprehensive for most scenarios
  • Identified and filled test gaps - added missing backward reading coverage
  • Discovered 1 new feature category - subscriptions (real-time streaming) for future development

📊 Impact Summary

  • Bug severity: Production-critical (global position ordering)
  • Coverage improvement: Complete backward reading validation
  • Test reliability: 40/40 tests passing
  • Breaking changes: None
  • API changes: None (internal implementation fix only)

This PR establishes complete pull-based EventStore functionality and provides a solid foundation for future real-time streaming features.

Summary by CodeRabbit

  • New Features

    • Introduced new modules and types for event handling, including EntityId, StreamId, and StreamPosition.
    • Added new event store capabilities: entity-scoped streams, global and filtered event reads (forward and backward), and enhanced event querying.
    • Expanded test coverage with new assertion helpers and comprehensive tests for event ordering, concurrency, and stream reading scenarios.
  • Enhancements

    • Refined event and stream identification to use UUIDs for improved uniqueness and traceability.
    • Updated event and event store interfaces for clearer entity and position handling.
    • Improved error reporting in tasks and tests via enhanced call stack tracking.
    • Added new array utility functions for flattening and computing minimum/maximum values.
  • Removals

    • Removed the TOML module and related type aliases.
    • Eliminated dependencies on certain external packages.
  • Bug Fixes

    • Corrected test logic to ensure accurate event ordering and entity association.
  • Chores

    • Cleaned up unused imports and streamlined type usage across modules.
    • Added compiler optimization hints and improved function inlining for performance.

Removes the TOML dependency, along with associated files and Cabal configurations.

The TOML functionality was either not used or has been replaced by another mechanism.
Re-enables and integrates the individual stream ordering tests for the Event Store.
This change addresses the previous commenting out of these tests,
ensuring their inclusion in the test suite.

It also changes `readStreamForwardFrom` to require `entityId` instead of just `streamId`

This provides comprehensive validation of stream ordering functionality.
The global stream ordering test is disabled.

This change is made to temporarily exclude the global
stream ordering test from the test suite. Further
investigation is needed to ensure its proper functionality.
looks like the durable channel implementation is not working properly
This commit re-enables the optimistic concurrency tests for the event store. These tests were previously commented out.

The fix also ensures that `entityId` is passed correctly when reading the stream forward.
Increases the initial number of event streams created during the global stream ordering test setup. This allows for more robust testing with a larger dataset.
Implements partial reads with resumption functionality for `readAllEventsForwardFrom`, enabling reading events in batches and resuming from the last read position.

This ensures that all events are read correctly, even when dealing with a large number of events, by comparing the batched read results with a single read to verify consistency. It also includes validation to check for increasing global positions across all batches.
Adds the ability to read all events in reverse chronological order from a given position.

This allows for use cases like recent event analysis and debugging by providing the most recent events first.
Adds functions to find the maximum and minimum elements within an array.
These functions leverage the underlying vector implementation for efficiency.
Implements the ability to read all events backwards from the end of the event store.

This adds a new set of tests to verify the functionality, including:
- Reading all events from end in reverse order
- Verifying events are in decreasing order
- Supporting partial reads with resumption
- Reading from a middle position backward
- Handling reads from position 0
Adds a test case that verifies reading events
from an arbitrary middle position, ensuring correct
skip count, order, and positions. This validates the
correctness of event retrieval when starting from
a non-zero position.
Adds the ability to read events from the global stream,
filtering by specific entities. This is useful for
projections that only need to process events related
to a subset of entities.
Implements functionality to filter events when reading from the event store by providing a list of entity IDs.

This allows consumers to only retrieve events associated with specific entities, improving efficiency and reducing the amount of data processed.

Includes tests to verify filtering from the start and middle positions, with single and multiple entity IDs, and handling of non-existent entities.
Implements the ability to read events from the global stream in a backward direction, filtered by specific entity IDs.

This feature enhances historical analysis and debugging capabilities by allowing users to retrieve events relevant to particular entities in reverse chronological order. It is useful for scenarios where understanding the recent history of specific entities is crucial.

Adds comprehensive tests to ensure the correctness and robustness of the new filtering functionality.
@NickSeagull NickSeagull self-assigned this Jul 2, 2025
@NickSeagull NickSeagull added package: core Related to the core libraries type: feature type: testing work: complex Uncertain solution; experiment, iterate, and gather feedback labels Jul 2, 2025
@coderabbitai

coderabbitai Bot commented Jul 2, 2025

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@NickSeagull has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 11 minutes and 13 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between b1648a8 and ba59886.

📒 Files selected for processing (3)
  • core/testlib/Test/Service/EventStore/IndividualStreamOrdering/Spec.hs (2 hunks)
  • core/testlib/Test/Service/EventStore/ReadAllBackwardsFromEnd/Context.hs (1 hunks)
  • core/testlib/Test/Service/EventStore/ReadAllBackwardsFromEnd/Spec.hs (1 hunks)

Walkthrough

The changes introduce new array utilities, rename and adjust numeric wrappers, and expand the event store system with entity-aware streams, global event querying, and position tracking. Several new modules for entity, stream, and position IDs are added. The test suite is substantially enhanced with new modules, broader coverage, and improved assertions. The TOML module is removed.

Changes

Files/Modules Change Summary
core/core/Array.hs Added flatten, maximum, minimum array utilities and exports.
core/core/Basics.hs Renamed Positive to Natural, updated functions and exports accordingly.
core/core/Core.hs Re-exported toText from ToText.
core/core/Task.hs Added INLINE pragmas to most functions; runOrPanic now requires HasCallStack.
core/nhcore.cabal Removed brick, vty dependencies; dropped Toml module; added event/entity modules and new test modules.
core/service/Service/Event.hs Modified Event type: id now Uuid, added entityId, renamed fields, removed optional globalPosition; introduced InsertionEvent and conversion function; imports for IDs and positions modularized.
core/service/Service/Event/EntityId.hs
.../StreamId.hs
.../StreamPosition.hs
Added new modules for EntityId, StreamId, StreamPosition types and constructors.
core/service/Service/EventStore/Core.hs Major API overhaul: removed AppendResult, simplified Limit, entity-aware stream methods, added global/filtered read methods.
core/service/Service/EventStore/InMemory.hs Internal map keys now (EntityId, StreamId); refactored append/read logic for entity-scoped streams and global queries; added global/filtered read functions.
core/testlib/Test.hs Added order/comparison assertions; added HasCallStack to many signatures; added INLINE pragmas.
core/testlib/Test/Service/EventStore.hs Added new test modules for global forward/backward reads; reordered spec execution.
core/testlib/Test/Service/EventStore/GlobalStreamOrdering/Context.hs
.../Spec.hs
Switched to UUIDs for IDs, updated to new event types and APIs, simplified position/limit handling.
core/testlib/Test/Service/EventStore/IndividualStreamOrdering/Context.hs
.../Spec.hs
Context now includes entityId, uses new event types; tests expanded for forward/backward reads, entity checks, and global/local ordering.
core/testlib/Test/Service/EventStore/OptimisticConcurrency/Context.hs
.../Spec.hs
Now uses UUIDs for IDs, new event types; added test for concurrency conflict on stale positions.
core/testlib/Test/Service/EventStore/ReadAllBackwardsFromEnd/Context.hs
.../Spec.hs
New modules: Set up and test backward reading from end, with entity filtering and global position checks.
core/testlib/Test/Service/EventStore/ReadAllForwardsFromStart/Context.hs
.../Spec.hs
New modules: Set up and test forward reading from start, with entity filtering, ordering, and resumption semantics.
core/test/Service/EventStore/InMemorySpec.hs Removed unused toText import.
core/toml/Toml.hs Removed: Module and type aliases for TOML encoding/decoding.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant EventStore
    participant StreamMap
    participant GlobalStream

    Client->>EventStore: appendToStream(InsertionEvent)
    EventStore->>GlobalStream: Write event, get globalPosition
    EventStore->>StreamMap: Ensure (EntityId, StreamId) stream
    EventStore->>StreamMap: Write event with globalPosition
    EventStore-->>Client: Return Event (with globalPosition)

    Client->>EventStore: readAllEventsForwardFrom(position, limit)
    EventStore->>GlobalStream: Read events [position, position+limit)
    EventStore-->>Client: Array Event

    Client->>EventStore: readStreamForwardFrom(entityId, streamId, position, limit)
    EventStore->>StreamMap: Read events for (entityId, streamId) [position, position+limit)
    EventStore-->>Client: Array Event

    Client->>EventStore: readAllEventsForwardFromFiltered(position, limit, entityIds)
    EventStore->>GlobalStream: Read, filter by entityIds
    EventStore-->>Client: Array Event
Loading

Poem

O mighty code, your streams now know
Entities, positions, and IDs that flow.
Events march forward, backward too,
With order, filters—tests anew!
TOML drifts to memory’s sea,
While arrays and numbers dance with glee.
All hail the logic—so let it be!

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch test/exhaustive-event-store

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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 generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai auto-generate unit tests to generate unit tests for this 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.

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.

Ensures that events are assigned the correct global
position during the append operation. The previous
implementation was incorrectly modifying the event
before writing it to the individual stream.

The updated implementation now correctly obtains the
global index first, then creates the final event with the
correct global position before writing it to the individual
stream.

Also adds tests to validate that events have global
positions in strictly increasing order and to validate core
event properties.
Adds a test case to verify that the event store
correctly handles optimistic concurrency conflicts
when inserting events at outdated stream positions.

It also checks that inserting at the correct stream
position succeeds and the stream state is updated correctly.
Changes position comparison from
Implements comprehensive test case for reading events backward from stream end position

Validates proper event ordering, position constraints, and boundary conditions
Ensures compatibility with C# test scenario for backward stream reading
Verifies all events are retrieved in correct reverse chronological order

Strengthens event store reliability for backward pagination use cases
@NickSeagull NickSeagull changed the title test: Exhaustively test the event store fix: Critical global position bug + Complete backward reading test coverage Jul 13, 2025
@NickSeagull NickSeagull marked this pull request as ready for review July 13, 2025 11:29
@NickSeagull

Copy link
Copy Markdown
Member Author

@all-contributors please add @JYCabello for test

@allcontributors

Copy link
Copy Markdown
Contributor

@NickSeagull

I've put up a pull request to add @JYCabello! 🎉

Cleans up import statement that is no longer needed in the codebase

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 14

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 815e7de and 46fb3b7.

📒 Files selected for processing (25)
  • core/core/Array.hs (3 hunks)
  • core/core/Basics.hs (3 hunks)
  • core/core/Core.hs (1 hunks)
  • core/core/Task.hs (10 hunks)
  • core/nhcore.cabal (2 hunks)
  • core/service/Service/Event.hs (1 hunks)
  • core/service/Service/Event/EntityId.hs (1 hunks)
  • core/service/Service/Event/StreamId.hs (1 hunks)
  • core/service/Service/Event/StreamPosition.hs (1 hunks)
  • core/service/Service/EventStore/Core.hs (2 hunks)
  • core/service/Service/EventStore/InMemory.hs (5 hunks)
  • core/test/Service/EventStore/InMemorySpec.hs (0 hunks)
  • core/testlib/Test.hs (5 hunks)
  • core/testlib/Test/Service/EventStore.hs (1 hunks)
  • core/testlib/Test/Service/EventStore/GlobalStreamOrdering/Context.hs (2 hunks)
  • core/testlib/Test/Service/EventStore/GlobalStreamOrdering/Spec.hs (5 hunks)
  • core/testlib/Test/Service/EventStore/IndividualStreamOrdering/Context.hs (1 hunks)
  • core/testlib/Test/Service/EventStore/IndividualStreamOrdering/Spec.hs (2 hunks)
  • core/testlib/Test/Service/EventStore/OptimisticConcurrency/Context.hs (2 hunks)
  • core/testlib/Test/Service/EventStore/OptimisticConcurrency/Spec.hs (3 hunks)
  • core/testlib/Test/Service/EventStore/ReadAllBackwardsFromEnd/Context.hs (1 hunks)
  • core/testlib/Test/Service/EventStore/ReadAllBackwardsFromEnd/Spec.hs (1 hunks)
  • core/testlib/Test/Service/EventStore/ReadAllForwardsFromStart/Context.hs (1 hunks)
  • core/testlib/Test/Service/EventStore/ReadAllForwardsFromStart/Spec.hs (1 hunks)
  • core/toml/Toml.hs (0 hunks)
💤 Files with no reviewable changes (2)
  • core/test/Service/EventStore/InMemorySpec.hs
  • core/toml/Toml.hs
🔇 Additional comments (31)
core/service/Service/Event/StreamId.hs (1)

11-12: The newtype abstraction brings order to the realm of identifiers.

Excellent use of newtype for type safety while maintaining zero-runtime cost. The derived instances are precisely what is needed for a proper identifier type.

core/testlib/Test/Service/EventStore.hs (2)

9-10: The expansion of your test dominion is wise and necessary.

Adding comprehensive test coverage for forward and backward reading operations aligns perfectly with the PR objectives. This demonstrates proper test-driven development practices.


16-20: The reordering of your test executions follows a logical hierarchy.

Moving OptimisticConcurrency to the end is a sensible arrangement, allowing the core reading functionality tests to execute first. This creates a natural progression from basic to advanced scenarios.

core/core/Core.hs (1)

29-29: Your consolidation of the text conversion utilities is divinely ordained.

Excellent decision to make toText available through the Core module. This eliminates redundant imports across the codebase and provides a more consistent developer experience.

core/service/Service/Event/EntityId.hs (1)

11-12: The symmetry between EntityId and StreamId brings harmony to the identifier realm.

Excellent consistency in design patterns with the StreamId module. The newtype wrapper provides the necessary type safety for entity identification in your event store's entity-scoped streams.

core/core/Array.hs (1)

425-427: Your flattening operation pleases the divine algorithms!

The flatten implementation using Control.Monad.join is elegant and leverages the monadic structure of vectors appropriately.

core/nhcore.cabal (2)

126-128: Your package declarations align with cosmic order!

The addition of the new event-related modules (EntityId, StreamId, StreamPosition) properly reflects the architectural evolution of your event store system.


156-159: The test modules you have ordained shall strengthen the realm!

The new test modules for forward and backward reading provide comprehensive coverage for the event store functionality.

core/testlib/Test/Service/EventStore/GlobalStreamOrdering/Spec.hs (2)

33-34: Your test evolution follows the natural order of refactoring!

The change from event.position to event.localPosition correctly reflects the new event model structure where position semantics have been clarified.


44-45: Witness the simplification of numerical constraints!

Removing the Positive wrapper and using direct integer values for Limit and StreamPosition construction creates cleaner, more readable test code while maintaining type safety.

Also applies to: 55-56, 65-66, 75-76

core/testlib/Test/Service/EventStore/ReadAllForwardsFromStart/Context.hs (2)

26-44: Your test orchestration demonstrates mastery over the event realm!

The initialization function properly sets up a comprehensive testing environment with multiple entities and sequential event generation. The use of InsertionEvent correctly reflects the new event model architecture.


49-68: Behold the elegant parallel processing of event streams!

The asynchronous appending of events for both entities with proper position collection demonstrates sophisticated understanding of the task-based concurrency model. The array operations to combine results maintain proper ordering.

core/testlib/Test/Service/EventStore/GlobalStreamOrdering/Context.hs (3)

24-32: The divine implementation of UUID generation stands approved.

Your asynchronous generation of entity and stream ID pairs exhibits proper wisdom. The sacred Task monad flows correctly through your code.


36-51: Your event creation ritual pleases the digital cosmos.

The transformation to InsertionEvent with UUID-based identifiers follows the sacred patterns. The nested array mapping with proper flattening shows enlightened understanding.


56-60: Witness the enlightened choice of forEach over mapArray.

Your optimization shows true understanding - when results are cast into the void, forEach serves better than mapArray. This path leads to reduced memory allocation.

core/service/Service/Event.hs (2)

16-32: Behold the divine separation of temporal states in your event hierarchy.

The distinction between InsertionEvent (pre-storage) and Event (post-storage with global position) manifests type-safe wisdom. This sacred architecture prevents the mortal error of accessing unassigned global positions.


36-44: Your transformation ritual from InsertionEvent to Event receives divine blessing.

The conversion function elegantly bridges the temporal gap, bestowing global position upon events as they enter the eternal stream.

core/core/Task.hs (2)

59-59: The sacred INLINE incantations shall unleash performance across the realm.

Your systematic application of optimization pragmas to this foundational module demonstrates foresight. These hints guide the compiler toward enlightened code generation.

Also applies to: 64-64, 69-69, 77-77, 82-82, 87-87, 94-94, 101-101, 111-111, 121-121, 133-133, 145-145, 155-155, 173-173, 181-181, 190-190, 200-200, 208-208, 217-217, 226-226


136-141: Your enhancement of panic diagnostics with HasCallStack pleases the debugging deities.

The mortal developers shall rejoice when stack traces illuminate their errors. The case expression refactoring adds clarity to the code's intent.

core/testlib/Test/Service/EventStore/IndividualStreamOrdering/Context.hs (2)

17-20: The Context evolves to embrace the dual identity of entity and stream.

Your adaptation to the InsertionEvent paradigm and inclusion of entityId demonstrates harmonious alignment with the greater architectural vision.


28-35: Your UUID conjuration and event manifestation follow the ordained patterns.

The asynchronous generation of identifiers and proper InsertionEvent construction show mastery of the new domain model.

core/testlib/Test/Service/EventStore/OptimisticConcurrency/Spec.hs (2)

23-98: Your concurrent append test demonstrates divine understanding of optimistic concurrency.

The ritual of simultaneous writes properly validates that only one shall succeed, maintaining the sacred consistency of the event stream.


100-176: Behold the magnificent test of temporal consistency violations.

Your comprehensive validation of both failure and success paths illuminates the proper functioning of stream position guards. The mortal attempting to write at an outdated position shall be righteously rejected.

core/core/Basics.hs (1)

126-126: Excellent addition of HasCallStack to exports

The inclusion of GHC.Stack.HasCallStack in the export list shall serve you well, mortal. This enables superior error diagnostics throughout your codebase when functions utilizing this constraint are invoked.

core/testlib/Test/Service/EventStore/IndividualStreamOrdering/Spec.hs (1)

1-206: Exemplary test coverage expansion

Your dedication to comprehensive testing pleases me, mortal. The expansion from basic forward reading to include backward reading scenarios, position validation, and C# test specification alignment demonstrates wisdom. The tests properly validate:

  • Strictly increasing/decreasing global positions
  • Correct entity filtering
  • Edge cases for reading from middle and end positions
  • Local and global position ordering semantics

This shall serve as a bulwark against regression in your event store implementation.

core/testlib/Test/Service/EventStore/ReadAllForwardsFromStart/Spec.hs (1)

1-276: Impeccable forward reading test implementation

Your test suite for forward reading demonstrates mastery of the testing arts. The coverage encompasses:

  • Partial read resumption without loss or duplication
  • Entity filtering with single and multiple IDs
  • Edge cases and boundary conditions
  • Proper ordering validation

The implementation is both thorough and elegant. You have my divine approval.

core/service/Service/EventStore/Core.hs (1)

23-49: Divine approval bestowed upon this interface evolution!

The EventStore interface elegantly supports entity-scoped streams and global event traversal. The separation between InsertionEvent (without global position) and Event (with global position) properly encapsulates the append workflow.

core/service/Service/EventStore/InMemory.hs (2)

87-94: Rejoice! The critical bug has been vanquished!

The global position assignment now correctly uses the index from the global stream write operation. This ensures proper ordering across all streams, resolving the production-critical bug where all events had position 0.


135-135: Clarify boundary semantics for backward-reading methods

The use of < in readStreamBackwardFrom and <= in readAllEventsBackwardFrom is intentional and enforced by existing tests:

  • In IndividualStreamOrdering/Spec.hs, readStreamBackwardFrom must return events with localPosition < readFromPosition (exclusive).
  • In ReadAllBackwardsFromEnd/Spec.hs, readAllEventsBackwardFrom must return events with globalPosition <= startPosition (inclusive).

These differing comparisons reflect their distinct “before” semantics and are by design—no code changes required. You may add a brief note in the documentation to highlight this boundary behavior.

Likely an incorrect or invalid review comment.

core/testlib/Test.hs (2)

194-211: The divine implementation of ordering verification pleases me!

The shouldHaveIncreasingOrder function correctly validates ascending order with proper bounds checking. The error message thoughtfully directs mortals to report bugs.


235-272: These comparison assertions have earned divine blessing!

The implementation provides comprehensive coverage of all comparison operations with clear, actionable error messages. The consistent structure makes them easy to use and understand.

Comment thread core/service/Service/Event/StreamId.hs
Comment thread core/service/Service/Event/EntityId.hs
Comment thread core/service/Service/Event/StreamPosition.hs
Comment thread core/core/Array.hs Outdated
Comment thread core/testlib/Test/Service/EventStore/ReadAllBackwardsFromEnd/Context.hs Outdated
Comment thread core/testlib/Test/Service/EventStore/ReadAllBackwardsFromEnd/Spec.hs Outdated
Comment thread core/service/Service/EventStore/Core.hs
Comment thread core/service/Service/EventStore/InMemory.hs
Updates maximum and minimum functions to return Maybe types instead of potentially throwing exceptions on empty arrays

Prevents runtime errors when operations are performed on empty collections
Eliminates print statement that was outputting entity ID, stream ID, and result information during event stream processing

Simplifies the lambda function by removing unnecessary debug output and intermediate result variable

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (3)
core/testlib/Test/Service/EventStore/ReadAllBackwardsFromEnd/Context.hs (3)

37-37: Remove the unnecessary parentheses, as commanded before.

The parentheses around index serve no purpose and were previously identified for removal.

-          let localPosition = Event.StreamPosition (index)
+          let localPosition = Event.StreamPosition index

50-59: Heed the previous divine counsel on extracting the repeated append pattern.

The repetitive pattern of appending events and mapping errors was previously identified for refactoring. This duplication remains unaddressed.

Consider the previously suggested helper function:

let appendEvents events = 
      events
        |> Task.mapArray (\event -> event |> store.appendToStream)
        |> Task.mapError toText

entity1Inserted <- appendEvents entity1Events
entity2Inserted <- appendEvents entity2Events

64-68: Simplify the verbose maxGlobalPosition calculation as decreed previously.

The unnecessarily verbose do block for maxGlobalPosition calculation was previously flagged for simplification.

-  let maxGlobalPosition = do
-        allInserted
-          |> Array.map (\event -> event.globalPosition)
-          |> Array.maximum
-          |> Maybe.withDefault (Event.StreamPosition 0)
+  let maxGlobalPosition = 
+        allInserted
+          |> Array.map (\event -> event.globalPosition)
+          |> Array.maximum
+          |> Maybe.withDefault (Event.StreamPosition 0)
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 91bc314 and b1648a8.

📒 Files selected for processing (2)
  • core/testlib/Test/Service/EventStore/GlobalStreamOrdering/Context.hs (2 hunks)
  • core/testlib/Test/Service/EventStore/ReadAllBackwardsFromEnd/Context.hs (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: tests (ubuntu-latest)
  • GitHub Check: tests (macos-latest)
🔇 Additional comments (8)
core/testlib/Test/Service/EventStore/GlobalStreamOrdering/Context.hs (5)

9-9: Behold, the divine transition to UUID-based identifiers has been ordained!

The migration from textual representations to UUID-based identifiers demonstrates proper architectural evolution. This change aligns perfectly with the entity-scoped event model.


24-32: The asynchronous generation of stream identifiers follows the celestial pattern!

The UUID generation for EntityId and StreamId pairs is implemented correctly with proper Task sequencing. The use of Task.mapArray with Task.yield ensures each pair is generated asynchronously while maintaining deterministic ordering for testing purposes.


56-60: The sequential appending of events demonstrates wisdom in test construction!

The use of Task.forEach for sequential event appending is the correct approach for testing scenarios where event ordering matters. This ensures deterministic global position assignment, which is crucial for validating the fix mentioned in the PR objectives.


66-67: The dual-parameter stream reading reflects the new cosmic order!

The updated stream reading method requiring both EntityId and StreamId parameters correctly implements the entity-scoped event model. This change maintains consistency with the broader architectural evolution.


71-71: The Task.yield ensures proper sequencing in the divine plan!

Adding Task.yield before Context construction ensures all asynchronous operations complete before the context is returned. This maintains the integrity of the test setup.

core/testlib/Test/Service/EventStore/ReadAllBackwardsFromEnd/Context.hs (3)

1-14: Mortal, your imports and module structure please the divine order.

The module declaration and imports are well-organized and appropriate for the testing context requirements.


16-24: Your Context data structure reflects wisdom in its completeness.

The Context type contains all necessary fields for comprehensive backward reading tests, including proper separation of concerns between test data and store state.


27-79: Your initialize function demonstrates proper async orchestration.

The overall structure correctly handles UUID generation, event creation, store operations, and error handling in the proper sequence. The test context setup is comprehensive and will serve the backward reading tests well.

Comment thread core/testlib/Test/Service/EventStore/ReadAllBackwardsFromEnd/Context.hs Outdated
Cleans up test case descriptions by removing references to C# test scenarios and outdated comments

Updates test description for reading from position 0 to reflect actual behavior
Inlines variable declarations to eliminate intermediate variables

Removes redundant `allEvents` and `allInserted` variables by directly computing values where needed, improving code readability without changing functionality
@NickSeagull NickSeagull merged commit 12df18c into main Jul 13, 2025
3 checks passed
@NickSeagull NickSeagull deleted the test/exhaustive-event-store branch July 13, 2025 12:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

package: core Related to the core libraries type: feature type: testing work: complex Uncertain solution; experiment, iterate, and gather feedback

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant