1import type { EnvironmentId, ThreadId } from "@t3tools/contracts";
2import { describe, expect, it } from "@effect/vitest";
3import * as Effect from "effect/Effect";
4import * as Layer from "effect/Layer";
5
6import * as ThreadHistoryController from "./threadHistoryController.ts";
7
8const ENV = "env-history" as EnvironmentId;
9const THREAD = "thread-history" as ThreadId;
10
11function handler(
12 tag: string,
13): ThreadHistoryController.ThreadHistoryHandler & { readonly tag: string; readonly calls: number } {
14 const state = { tag, calls: 0 };
15 return {
16 get tag() {
17 return state.tag;
18 },
19 get calls() {
20 return state.calls;
21 },
22 loadEarlier: () => {
23 state.calls += 1;
24 return Effect.succeed({
25 _tag: "loaded",
26 } satisfies ThreadHistoryController.ThreadHistoryLoadEarlierResult);
27 },
28 };
29}
30
31describe("ThreadHistoryController", () => {
32 it.effect("does not let an older finalizer delete a newer registration", () =>
33 Effect.gen(function* () {
34 const controller = yield* ThreadHistoryController.ThreadHistoryController;
35 const older = handler("older");
36 const newer = handler("newer");
37
38 const olderRegistration = yield* controller.register(ENV, THREAD, older);
39 const newerRegistration = yield* controller.register(ENV, THREAD, newer);
40
41 // Older fiber finalizes after the newer handler is already registered.
42 yield* controller.unregister(olderRegistration);
43
44 const result = yield* controller.loadEarlier(ENV, THREAD);
45 expect(result).toEqual({ _tag: "loaded" });
46 expect(newer.calls).toBe(1);
47 expect(older.calls).toBe(0);
48
49 yield* controller.unregister(newerRegistration);
50 expect(yield* controller.loadEarlier(ENV, THREAD)).toEqual({ _tag: "noop" });
51 }).pipe(Effect.provide(ThreadHistoryController.layer)),
52 );
53
54 it.effect("unregister removes only its own matching registration", () =>
55 Effect.gen(function* () {
56 const controller = yield* ThreadHistoryController.ThreadHistoryController;
57 const first = handler("first");
58 const registration = yield* controller.register(ENV, THREAD, first);
59 yield* controller.unregister(registration);
60 expect(yield* controller.loadEarlier(ENV, THREAD)).toEqual({ _tag: "noop" });
61 }).pipe(Effect.provide(Layer.fresh(ThreadHistoryController.layer))),
62 );
63});