Skip to content

Conversation

@jwilger
Copy link
Owner

@jwilger jwilger commented Dec 28, 2025

Summary

Establishes the contract testing pattern for backend implementations: define behavior once, all backends must comply.

This PR demonstrates the unified backend_contract_tests! macro (from PR #233) by adding CheckpointStore contract tests that automatically verify both InMemory and Postgres implementations.

Key Insight

The migration file is an implementation detail. The real deliverable is:

  1. CheckpointStore trait defines the contract
  2. Contract tests specify expected behavior
  3. Both backends must satisfy the same tests
  4. Adding a new contract test immediately fails any non-compliant backend

Changes

New Abstractions

  • CheckpointStore trait in eventcore-types (load/save checkpoint positions)
  • InMemoryCheckpointStore in eventcore-memory
  • PostgresCheckpointStore in eventcore-postgres (with migration)
  • NoCheckpointStore default for runners without checkpointing

Contract Tests Added

  • checkpoint_save_and_load - basic round-trip
  • checkpoint_update_overwrites - updates replace previous value
  • checkpoint_load_missing_returns_none - missing returns None
  • checkpoint_independent_subscriptions - multiple subscriptions don't interfere

Refactoring

  • ProjectionRunner now generic over C: CheckpointStore
  • Old InMemoryCheckpointStore removed from eventcore (was duplicate)

Migration

Creates eventcore_subscription_versions table per ADR-026:

subscription_name TEXT PRIMARY KEY
last_position UUID NOT NULL
updated_at TIMESTAMPTZ NOT NULL (indexed for monitoring)

Closes eventcore-6ta

…mplementations

Implements CheckpointStore abstraction per ADR-026 for projector checkpoint tracking.

Changes:
- Add CheckpointStore trait in eventcore-types with load/save methods
- Add InMemoryCheckpointStore in eventcore-memory implementing trait
- Add PostgresCheckpointStore in eventcore-postgres with migration
- Add contract tests for checkpoint operations (save, load, update, independence)
- Refactor ProjectionRunner to be generic over CheckpointStore
- Add NoCheckpointStore as default for runners without checkpointing

Migration adds eventcore_subscription_versions table:
- subscription_name (TEXT PRIMARY KEY)
- last_position (UUID)
- updated_at (TIMESTAMPTZ with index for monitoring)

Contract tests verify:
- Basic save and load round-trip
- Update overwrites existing checkpoint
- Missing checkpoint returns None
- Multiple subscriptions are independent

Closes eventcore-6ta
@jwilger jwilger force-pushed the eventcore-6ta-checkpoint-store branch from 86b0195 to 625e195 Compare December 28, 2025 21:30
@jwilger
Copy link
Owner Author

jwilger commented Dec 28, 2025

Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

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

Clean CheckpointStore implementation. Trait design is solid, both InMemory and Postgres implementations are correct, contract tests are comprehensive.

@jwilger jwilger changed the title chore(beads): claim eventcore-6ta for CheckpointStore implementation feat(testing): contract-first CheckpointStore with unified backend verification Dec 28, 2025
@jwilger jwilger merged commit f6b33c6 into main Dec 28, 2025
20 checks passed
@jwilger jwilger deleted the eventcore-6ta-checkpoint-store branch December 28, 2025 21:40
@jwilger jwilger mentioned this pull request Dec 28, 2025
jwilger added a commit that referenced this pull request Dec 29, 2025
## 🤖 New release

* `eventcore-macros`: 0.3.0 -> 0.4.0
* `eventcore-types`: 0.3.0 -> 0.4.0 (✓ API compatible changes)
* `eventcore-postgres`: 0.3.0 -> 0.4.0 (✓ API compatible changes)
* `eventcore`: 0.3.0 -> 0.4.0 (⚠ API breaking changes)
* `eventcore-memory`: 0.3.0 -> 0.4.0 (✓ API compatible changes)
* `eventcore-testing`: 0.3.0 -> 0.4.0 (⚠ API breaking changes)

### ⚠ `eventcore` breaking changes

```text
--- failure method_parameter_count_changed: pub method parameter count changed ---

Description:
A publicly-visible method now takes a different number of parameters.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#fn-change-arity
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.45.0/src/lints/method_parameter_count_changed.ron

Failed in:
  eventcore::ProjectionRunner::new now takes 2 parameters instead of 3, in /tmp/.tmpemizEG/eventcore/eventcore/src/projection.rs:196

--- failure method_requires_different_generic_type_params: method now requires a different number of generic type parameters ---

Description:
A method now requires a different number of generic type parameters than it used to. Uses of this method that supplied the previous number of generic types will be broken.
        ref: https://doc.rust-lang.org/reference/items/generics.html
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.45.0/src/lints/method_requires_different_generic_type_params.ron

Failed in:
  eventcore::ProjectionRunner::with_checkpoint_store takes 1 generic types instead of 0, in /tmp/.tmpemizEG/eventcore/eventcore/src/projection.rs:222

--- failure struct_missing: pub struct removed or renamed ---

Description:
A publicly-visible struct cannot be imported by its prior path. A `pub use` may have been removed, or the struct itself may have been renamed or removed entirely.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.45.0/src/lints/struct_missing.ron

Failed in:
  struct eventcore::LocalCoordinator, previously in file /tmp/.tmpdrFSjc/eventcore/src/projection.rs:221
  struct eventcore::CoordinatorGuard, previously in file /tmp/.tmpdrFSjc/eventcore/src/projection.rs:178
  struct eventcore::InMemoryCheckpointStore, previously in file /tmp/.tmpdrFSjc/eventcore/src/projection.rs:126

--- failure trait_requires_more_generic_type_params: trait now requires more generic type parameters ---

Description:
A trait now requires more generic type parameters than it used to. Uses of this trait that supplied the previously-required number of generic types will be broken. To fix this, consider supplying default values for newly-added generic types.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#trait-new-parameter-no-default
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.45.0/src/lints/trait_requires_more_generic_type_params.ron

Failed in:
  trait ProjectionRunner (3 -> 4 required generic types) in /tmp/.tmpemizEG/eventcore/eventcore/src/projection.rs:133

--- failure type_requires_more_generic_type_params: type now requires more generic type parameters ---

Description:
A type now requires more generic type parameters than it used to. Uses of this type that supplied the previously-required number of generic types will be broken. To fix this, consider supplying default values for newly-added generic types.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#trait-new-parameter-no-default
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.45.0/src/lints/type_requires_more_generic_type_params.ron

Failed in:
  Struct ProjectionRunner (3 -> 4 required generic types) in /tmp/.tmpemizEG/eventcore/eventcore/src/projection.rs:133
```

### ⚠ `eventcore-testing` breaking changes

```text
--- failure declarative_macro_missing: macro_rules declaration removed or renamed ---

Description:
A `macro_rules!` declarative macro cannot be invoked by its prior name. The macro may have been renamed or removed entirely.
        ref: https://doc.rust-lang.org/reference/macros-by-example.html#path-based-scope
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.45.0/src/lints/declarative_macro_missing.ron

Failed in:
  macro event_store_suite, previously in file /tmp/.tmpdrFSjc/eventcore-testing/src/contract.rs:508
  macro event_store_suite, previously in file /tmp/.tmpdrFSjc/eventcore-testing/src/contract.rs:508
  macro event_store_contract_tests, previously in file /tmp/.tmpdrFSjc/eventcore-testing/src/contract.rs:407
  macro event_store_contract_tests, previously in file /tmp/.tmpdrFSjc/eventcore-testing/src/contract.rs:407
  macro event_reader_contract_tests, previously in file /tmp/.tmpdrFSjc/eventcore-testing/src/contract.rs:456
  macro event_reader_contract_tests, previously in file /tmp/.tmpdrFSjc/eventcore-testing/src/contract.rs:456
```

<details><summary><i><b>Changelog</b></i></summary><p>

## `eventcore-macros`

<blockquote>

##
[0.3.0](eventcore-macros-v0.2.0...eventcore-macros-v0.3.0)
- 2025-12-27

### Refactoring

- *(release)* switch to workspace version inheritance for full lockstep
versioning ([#221](#221))
</blockquote>

## `eventcore-types`

<blockquote>

##
[0.4.0](eventcore-types-v0.3.0...eventcore-types-v0.4.0)
- 2025-12-29

### Features

- *(eventcore-postgres)* add database triggers to enforce event log
immutability ([#229](#229))
- *(testing)* contract-first CheckpointStore with unified backend
verification ([#234](#234))
</blockquote>

## `eventcore-postgres`

<blockquote>

##
[0.4.0](eventcore-postgres-v0.3.0...eventcore-postgres-v0.4.0)
- 2025-12-29

### Features

- *(eventcore-postgres)* add database triggers to enforce event log
immutability ([#229](#229))
- *(testing)* contract-first CheckpointStore with unified backend
verification ([#234](#234))

### Refactoring

- *(eventcore-postgres)* replace testcontainers with docker-compose
([#224](#224))
- *(testing)* unify contract test macros into backend_contract_tests!
([#233](#233))
</blockquote>

## `eventcore`

<blockquote>

##
[0.4.0](eventcore-v0.3.0...eventcore-v0.4.0)
- 2025-12-29

### Features

- *(testing)* contract-first CheckpointStore with unified backend
verification ([#234](#234))

### Refactoring

- remove vestigial LocalCoordinator and CoordinatorGuard
([#255](#255))
</blockquote>

## `eventcore-memory`

<blockquote>

##
[0.4.0](eventcore-memory-v0.3.0...eventcore-memory-v0.4.0)
- 2025-12-29

### Features

- *(testing)* contract-first CheckpointStore with unified backend
verification ([#234](#234))
</blockquote>

## `eventcore-testing`

<blockquote>

##
[0.4.0](eventcore-testing-v0.3.0...eventcore-testing-v0.4.0)
- 2025-12-29

### Features

- *(testing)* contract-first CheckpointStore with unified backend
verification ([#234](#234))

### Refactoring

- *(testing)* unify contract test macros into backend_contract_tests!
([#233](#233))
</blockquote>


</p></details>

---
This PR was generated with
[release-plz](https://github.com/release-plz/release-plz/).
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