Closed thiagomini closed 5 months ago
I was thinking that we could locate such tests into either the main src
folder or other place. If we had either base class for tests, or generator that would take the event store implementation and some specific settings (e.g. EventStoreDB adds first event at 0
position, while others on 1
) then this could be quite easy way to deal with permutations.
Potentially we could have the curried function that would return the describe
and run it for multiple store implementations.
Of course, we should also eventually document the requirements and test cases in some markdown (and eventually docs).
Hey @oskardudycz , before implementing anything, I'm trying to understand the base requirements for our "toy" domain (shopping carts). I realized that we have two layers in our tests:
ShoppingCart
, ProductItem
entities and the rules around the ProductItemAdded
and DiscountApplied
events. These are just the "essential complexities" necessary to test saving and aggregating back an event stream.Most of the duplication lies in the domain layer, which should be the same for any implementation. We might have some changes in the infra layer, and we need some abstraction over it. I've created these gherkin-like descriptions for these two layers:
Feature: ShoppingCart
Scenario: Add a product to the shopping cart
Given I have a shopping cart
When I add a product to the shopping cart
Then the shopping cart should contain 1 product
Scenario: Add a product to the shopping cart changes the total price
Given I have an empty shopping cart
And there is a product item with id "Book-1" and price 3
When I add a 10 units to the shopping cart
Then the total price of the shopping cart should be 30
Scenario: Apply a discount to the shopping cart
Given there is a product item with id "Book-1" and price 3
And I have a shopping cart with 10 units of product "Book-1"
When I apply a 10% discount to the shopping cart
Then the total price of the shopping cart should be 27
Feature: Time-Travel
Scenario: Travels to the first version of an event stream
Given an event store with the following events:
| EventID | EventType | EventData |
| 1 | ProductItemAdded | { "productId": "Book-1", "quantity": 10, "price": 3 } |
| 2 | ProductItemAdded | { "productId": "Book-1", "quantity": 10, "price": 3 } |
| 3 | DiscountApplied | { "percent": 10 } |
When I travel to the **first** version of the event stream
Then the ShoppingCart should have the following state:
{
"totalAmount": 30,
"productItems": [
{ "productId": "Book-1", "quantity": 10, "price": 3 }
],
}
Scenario: Travels to the second version of an event stream
Given an event store with the following events:
| EventID | EventType | EventData |
| 1 | ProductItemAdded | { "productId": "Book-1", "quantity": 10, "price": 3 } |
| 2 | ProductItemAdded | { "productId": "Book-1", "quantity": 10, "price": 3 } |
| 3 | DiscountApplied | { "percent": 10 } |
When I travel to the **second** version of the event stream
Then the ShoppingCart should have the following state:
{
"totalAmount": 60,
"productItems": [
{ "productId": "Book-1", "quantity": 10, "price": 3 },
{ "productId": "Book-1", "quantity": 10, "price": 3 },
],
}
Scenario: Travels to the third version of an event stream
Given an event store with the following events:
| EventID | EventType | EventData |
| 1 | ProductItemAdded | { "productId": "Book-1", "quantity": 10, "price": 3 } |
| 2 | ProductItemAdded | { "productId": "Book-1", "quantity": 10, "price": 3 } |
| 3 | DiscountApplied | { "percent": 10 } |
When I travel to the **third** version of the event stream
Then the ShoppingCart should have the following state:
{
"totalAmount": 54,
"productItems": [
{ "productId": "Book-1", "quantity": 10, "price": 3 },
{ "productId": "Book-1", "quantity": 10, "price": 3 },
],
}
I'd like your review of both cases, Oskar, and to confirm if it makes sense. If we agree, the following steps would be:
evolve
functions// Given
const shoppingCart = await dsl.shoppingCart.addProductItems(...productItems);
await dsl.shoppingCart.applyDiscount(shoppingCart, 10);
// When
await eventStore.aggregateStream(shoppingCartId, {
evolve,
getInitialState,
read: { to: 1n },
});
// Then
dsl.shoppingCart.shouldEqual(expectedState)
the dsl
object would be an abstraction where we could implement how it works with different drivers for different ES implementations. I took inspiration from Dave Farley's video on DSLs: https://youtu.be/JDD5EEJgpHU?si=gP5CHBFlWMPVKAhz&t=379
Description
As we noted in this PR, some test cases are duplicated among our packages. Therefore, when we want to fix them or change something, we have to repeat the same code in multiple files, which is unnecessary and prone to human error. We need to create some sort of matrix testing or a testing utility that can be reused across packages to execute the same logical test.
A few things to discuss:
emmet
package and eventStoreTest in theemmet-esdb
one. Should we keep them separated?Acceptance Criteria
Additional Information
To inspire this, we can use ESLint's Testing Helper. They've created a tool to facilitate creating tests that are usually very similar.