Makespace / members-app

Simplifying operations for Cambridge MakeSpace
MIT License
1 stars 1 forks source link

Flaky failure: a resource › has independant events #27

Closed chromy closed 6 days ago

chromy commented 1 week ago

bun jest
Determining test suites to run...
.....................x...................................................................  ● event-store end-to-end › setup event store › a resource › has independant events

    expect(received).toHaveLength(expected)

    Expected length: 3
    Received length: 2
    Received array:  [{"actor": {"tag": "system"}, "email": "Kristofer95@hotmail.com", "memberNumber": 1800049914806272, "recordedAt": 2024-06-21T11:16:13.109Z, "type": "MemberNumberLinkedToEmail"}, {"actor": {"tag": "system"}, "email": "Else2@gmail.com", "memberNumber": 7320636919644160, "recordedAt": 2024-06-21T11:16:13.043Z, "type": "MemberNumberLinkedToEmail"}]

      162 |
      163 |       it('has independant events', async () => {
    > 164 |         expect(await getTestEvents()).toHaveLength(3);
          |                                       ^
      165 |         expect(resourceEvents.events).toStrictEqual([event]);
      166 |       });
      167 |     });

      at Object.<anonymous> (tests/init-dependencies/event-store/end-to-end.test.ts:164:39)

Tests: 88 passed, 1 failed
Time: 3.899s
error: "jest" exited with code 1
make: *** [test] Error 1```
chromy commented 1 week ago

Running the test lots of times in a row seems to repro. Here I extracted all the code into a single test I could loop because jest setup takes a long time: https://github.com/Makespace/members-app/compare/chromy/2024-06-21-flake. It's about 2% flaky for me (~20/1000).

erkannt commented 6 days ago

Issue seems to be use of faker.string.alpha() for the resource IDs. It generates single character alpha and causes sporadic collisions.

    describe.each(Array(200).fill(null))('a resource', () => {
      const arbitraryResourceOfSameType = () => ({
        type: resource.type,
        id: faker.string.alpha(),
      });
      const otherResourceOfSameTypeA = arbitraryResourceOfSameType();
      const otherResourceOfSameTypeB = arbitraryResourceOfSameType();
      beforeEach(async () => {
        await commitEvent(dbClient, testLogger)(
          otherResourceOfSameTypeA,
          faker.number.int()
        )(arbitraryMemberNumberLinkedToEmailEvent())();
        await commitEvent(dbClient, testLogger)(
          otherResourceOfSameTypeB,
          faker.number.int()
        )(arbitraryMemberNumberLinkedToEmailEvent())();
        await commitEvent(dbClient, testLogger)(resource, initialVersion)(
          event
        )();
        resourceEvents = await pipe(
          resource,
          getResourceEvents(dbClient),
          T.map(getRightOrFail)
        )();
      });

      it('has independant versions', () => {
        expect(resourceEvents.version).toStrictEqual(initialVersion);
      });

      it('has independant events', async () => {
        const events = await getTestEvents();
        if (events.length !== 3) {
          console.log('>>>> otherA', otherResourceOfSameTypeA);
          console.log('>>>> otherA', otherResourceOfSameTypeB);
          console.log('>>>> resource', resource);
        }
        expect(events).toHaveLength(3);
        expect(resourceEvents.events).toStrictEqual([event]);
      });
    });
  });