import { describe, expect, it } from "@effect/vitest"; import { EnvironmentId } from "@t3tools/contracts"; import * as Cause from "effect/Cause"; import * as Effect from "effect/Effect"; import * as Exit from "effect/Exit"; import * as Fiber from "effect/Fiber"; import * as Latch from "effect/Latch"; import * as Layer from "effect/Layer"; import * as Stream from "effect/Stream"; import { AsyncResult, Atom, AtomRegistry } from "effect/unstable/reactivity"; import { environmentRpcKey, createAtomCommandScheduler, createRuntimeCommand, executeAtomCommand, executeAtomQuery, isAtomCommandInterrupted, mapAtomCommandResult, runAtomCommand, settleAsyncResult, settlePromise, squashAtomCommandFailure, } from "./runtime.ts"; describe("settleAsyncResult", () => { it("preserves successful values and typed failures", async () => { const success = await settleAsyncResult(() => Promise.resolve(Exit.succeed("done"))); expect(AsyncResult.isSuccess(success)).toBe(true); if (AsyncResult.isSuccess(success)) { expect(success.value).toBe("done"); } const expectedFailure = new Error("request failed"); const failure = await settleAsyncResult(() => Promise.resolve(Exit.fail(expectedFailure))); expect(AsyncResult.isFailure(failure)).toBe(true); if (AsyncResult.isFailure(failure)) { expect(Cause.hasDies(failure.cause)).toBe(false); expect(Cause.squash(failure.cause)).toBe(expectedFailure); } }); it("encodes thrown and rejected promises as defects", async () => { const thrownDefect = new Error("thrown defect"); const thrown = await settleAsyncResult(() => { throw thrownDefect; }); expect(AsyncResult.isFailure(thrown)).toBe(true); if (AsyncResult.isFailure(thrown)) { expect(Cause.hasDies(thrown.cause)).toBe(true); expect(Cause.squash(thrown.cause)).toBe(thrownDefect); } const rejectedDefect = new Error("rejected defect"); const rejected = await settleAsyncResult(() => Promise.reject(rejectedDefect)); expect(AsyncResult.isFailure(rejected)).toBe(true); if (AsyncResult.isFailure(rejected)) { expect(Cause.hasDies(rejected.cause)).toBe(true); expect(Cause.squash(rejected.cause)).toBe(rejectedDefect); } }); }); describe("atom command result helpers", () => { it("maps successful command values", () => { const result = mapAtomCommandResult(AsyncResult.success(2), (value) => value * 3); expect(result._tag).toBe("Success"); if (result._tag === "Success") { expect(result.value).toBe(6); } }); it("preserves failures while mapping", () => { const result = mapAtomCommandResult( AsyncResult.failure(Cause.fail("nope")), (value) => value * 3, ); expect(result._tag).toBe("Failure"); if (result._tag === "Failure") { expect(Cause.squash(result.cause)).toBe("nope"); } }); it("distinguishes interruption from other failures", () => { const interrupted = AsyncResult.failure(Cause.interrupt(1)); const failed = AsyncResult.failure(Cause.fail("nope")); expect(isAtomCommandInterrupted(interrupted)).toBe(true); expect(isAtomCommandInterrupted(failed)).toBe(false); expect(squashAtomCommandFailure(failed)).toBe("nope"); }); it("settles raw promise boundaries as successes or defects", async () => { const success = await settlePromise(() => Promise.resolve("done")); expect(success._tag).toBe("Success"); const defect = new Error("raw promise rejected"); const failure = await settlePromise(() => Promise.reject(defect)); expect(failure._tag).toBe("Failure"); if (failure._tag === "Failure") { expect(Cause.hasDies(failure.cause)).toBe(true); expect(Cause.squash(failure.cause)).toBe(defect); } }); it("reports expected failures and defects through separate policies", async () => { const warnings: string[] = []; const errors: string[] = []; const reporter = { warn: (message: string) => { warnings.push(message); }, error: (message: string) => { errors.push(message); }, }; await executeAtomCommand(() => Promise.resolve(Exit.fail("nope")), { label: "save" }, reporter); await executeAtomCommand( () => Promise.resolve(Exit.fail("ignored")), { label: "quiet save", reportFailure: false }, reporter, ); await executeAtomCommand( () => Promise.reject(new Error("defect")), { label: "quiet save", reportFailure: false }, reporter, ); await executeAtomCommand( () => Promise.resolve(Exit.interrupt(1)), { label: "interrupted" }, reporter, ); expect(warnings).toEqual(["[atom-command] save failed"]); expect(errors).toEqual(["[atom-command] quiet save defected"]); }); }); describe("environmentRpcKey", () => { it("isolates subscription state by environment and cwd", () => { const environmentId = EnvironmentId.make("environment-1"); const originalTarget = { environmentId, input: { cwd: "/repo/original" }, }; const nextTarget = { environmentId, input: { cwd: "/repo/next" }, }; expect(environmentRpcKey(originalTarget)).not.toBe(environmentRpcKey(nextTarget)); expect(environmentRpcKey(originalTarget)).toBe(environmentRpcKey({ ...originalTarget })); expect( environmentRpcKey({ environmentId: EnvironmentId.make("environment-2"), input: originalTarget.input, }), ).not.toBe(environmentRpcKey(originalTarget)); }); }); describe("Atom.fn mutation semantics", () => { it.effect("interrupts the previous invocation when the same mutation atom is written again", () => Effect.gen(function* () { const firstLatch = Latch.makeUnsafe(); const secondLatch = Latch.makeUnsafe(); const interrupted: string[] = []; const mutation = Atom.fn((id: "first" | "second") => (id === "first" ? firstLatch : secondLatch).await.pipe( Effect.as(id), Effect.onInterrupt(() => Effect.sync(() => { interrupted.push(id); }), ), ), ); const registry = AtomRegistry.make(); const unmount = registry.mount(mutation); registry.set(mutation, "first"); registry.set(mutation, "second"); yield* Effect.yieldNow; expect(interrupted).toEqual(["first"]); secondLatch.openUnsafe(); expect( yield* AtomRegistry.getResult(registry, mutation, { suspendOnWaiting: true, }), ).toBe("second"); unmount(); registry.dispose(); }), ); it.effect("keeps stream mutations waiting until the final emitted value", () => Effect.gen(function* () { const completionLatch = Latch.makeUnsafe(); const mutation = Atom.fn(() => Stream.make("progress").pipe( Stream.concat(Stream.fromEffect(completionLatch.await.pipe(Effect.as("done")))), ), ); const registry = AtomRegistry.make(); const unmount = registry.mount(mutation); registry.set(mutation, undefined); const progress = registry.get(mutation); expect(AsyncResult.isSuccess(progress)).toBe(true); if (AsyncResult.isSuccess(progress)) { expect(progress.value).toBe("progress"); expect(progress.waiting).toBe(true); } completionLatch.openUnsafe(); expect( yield* AtomRegistry.getResult(registry, mutation, { suspendOnWaiting: true, }), ).toBe("done"); unmount(); registry.dispose(); }), ); it.effect( "allows concurrent effects to finish but does not correlate results to individual writes", () => Effect.gen(function* () { const firstLatch = Latch.makeUnsafe(); const secondLatch = Latch.makeUnsafe(); const mutation = Atom.fn( (id: "first" | "second") => (id === "first" ? firstLatch : secondLatch).await.pipe(Effect.as(id)), { concurrent: true }, ); const registry = AtomRegistry.make(); const unmount = registry.mount(mutation); registry.set(mutation, "first"); const firstResult = yield* AtomRegistry.getResult(registry, mutation, { suspendOnWaiting: true, }).pipe(Effect.forkChild({ startImmediately: true })); registry.set(mutation, "second"); const secondResult = yield* AtomRegistry.getResult(registry, mutation, { suspendOnWaiting: true, }).pipe(Effect.forkChild({ startImmediately: true })); secondLatch.openUnsafe(); yield* Effect.yieldNow; const stillWaiting = registry.get(mutation); expect(stillWaiting.waiting).toBe(true); firstLatch.openUnsafe(); expect(yield* Fiber.join(firstResult)).toBe("first"); expect(yield* Fiber.join(secondResult)).toBe("first"); unmount(); registry.dispose(); }), ); }); describe("executeAtomQuery", () => { it("keeps concurrent query results correlated to their atoms", async () => { const firstLatch = Latch.makeUnsafe(); const secondLatch = Latch.makeUnsafe(); const firstAtom = Atom.make(firstLatch.await.pipe(Effect.as("first"))); const secondAtom = Atom.make(secondLatch.await.pipe(Effect.as("second"))); const registry = AtomRegistry.make(); const firstResult = executeAtomQuery(registry, firstAtom); const secondResult = executeAtomQuery(registry, secondAtom); secondLatch.openUnsafe(); firstLatch.openUnsafe(); const [first, second] = await Promise.all([firstResult, secondResult]); expect(first._tag).toBe("Success"); expect(second._tag).toBe("Success"); if (first._tag === "Success" && second._tag === "Success") { expect(first.value).toBe("first"); expect(second.value).toBe("second"); } registry.dispose(); }); }); describe("runtime command runner", () => { it("encodes custom command rejections as defects", async () => { const defect = new Error("custom command rejected"); const registry = AtomRegistry.make(); const result = await runAtomCommand( registry, { label: "test.rejected-command", run: () => Promise.reject(defect), }, undefined, { reportDefect: false }, ); expect(result._tag).toBe("Failure"); if (result._tag === "Failure") { expect(Cause.hasDies(result.cause)).toBe(true); expect(Cause.squash(result.cause)).toBe(defect); } registry.dispose(); }); it("settles generated command scheduler defects from direct callers", async () => { const defect = new Error("invalid command key"); const runtime = Atom.runtime(Layer.empty); const command = createRuntimeCommand(runtime, { label: "test.invalid-key", concurrency: { mode: "serial", key: () => { throw defect; }, }, execute: () => Effect.void, }); const registry = AtomRegistry.make(); const result = await command.run(registry, undefined); expect(result._tag).toBe("Failure"); if (result._tag === "Failure") { expect(Cause.hasDies(result.cause)).toBe(true); expect(Cause.squash(result.cause)).toBe(defect); } registry.dispose(); }); it("correlates parallel invocation results", async () => { const firstLatch = Latch.makeUnsafe(); const secondLatch = Latch.makeUnsafe(); const runtime = Atom.runtime(Layer.empty); const command = createRuntimeCommand(runtime, { label: "test.parallel", execute: (id: "first" | "second") => (id === "first" ? firstLatch : secondLatch).await.pipe(Effect.as(id)), }); const registry = AtomRegistry.make(); const first = command.run(registry, "first"); const second = command.run(registry, "second"); secondLatch.openUnsafe(); firstLatch.openUnsafe(); expect(await first).toMatchObject({ _tag: "Success", value: "first", waiting: false }); expect(await second).toMatchObject({ _tag: "Success", value: "second", waiting: false }); registry.dispose(); }); it("serializes commands that share a scheduler and lane", async () => { const firstLatch = Latch.makeUnsafe(); const events: string[] = []; const runtime = Atom.runtime(Layer.empty); const scheduler = createAtomCommandScheduler(); const concurrency = { mode: "serial" as const, key: () => "shared" }; const firstCommand = createRuntimeCommand(runtime, { label: "test.first", scheduler, concurrency, execute: () => Effect.sync(() => events.push("first:start")).pipe( Effect.andThen(firstLatch.await), Effect.tap(() => Effect.sync(() => events.push("first:end"))), ), }); const secondCommand = createRuntimeCommand(runtime, { label: "test.second", scheduler, concurrency, execute: () => Effect.sync(() => events.push("second:start")), }); const registry = AtomRegistry.make(); const first = firstCommand.run(registry, undefined); const second = secondCommand.run(registry, undefined); await Promise.resolve(); expect(events).toEqual(["first:start"]); firstLatch.openUnsafe(); await Promise.all([first, second]); expect(events).toEqual(["first:start", "first:end", "second:start"]); registry.dispose(); }); it("deduplicates single-flight commands by key", async () => { const latch = Latch.makeUnsafe(); let executions = 0; const runtime = Atom.runtime(Layer.empty); const command = createRuntimeCommand(runtime, { label: "test.single-flight", concurrency: { mode: "singleFlight", key: (key: string) => key }, execute: () => Effect.sync(() => executions++).pipe(Effect.andThen(latch.await), Effect.as("done")), }); const registry = AtomRegistry.make(); const first = command.run(registry, "same"); const second = command.run(registry, "same"); latch.openUnsafe(); expect(await first).toMatchObject({ _tag: "Success", value: "done", waiting: false }); expect(await second).toMatchObject({ _tag: "Success", value: "done", waiting: false }); expect(executions).toBe(1); registry.dispose(); }); it("coalesces pending latest-value commands without interrupting the active call", async () => { const firstLatch = Latch.makeUnsafe(); const executed: number[] = []; const runtime = Atom.runtime(Layer.empty); const command = createRuntimeCommand(runtime, { label: "test.latest", concurrency: { mode: "latest", key: () => "shared" }, execute: (value: number) => Effect.sync(() => executed.push(value)).pipe( Effect.andThen(value === 1 ? firstLatch.await : Effect.void), Effect.as(value), ), }); const registry = AtomRegistry.make(); const first = command.run(registry, 1); await Promise.resolve(); const second = command.run(registry, 2); const third = command.run(registry, 3); firstLatch.openUnsafe(); expect(await first).toMatchObject({ _tag: "Success", value: 1, waiting: false }); expect(await second).toMatchObject({ _tag: "Success", value: 3, waiting: false }); expect(await third).toMatchObject({ _tag: "Success", value: 3, waiting: false }); expect(executed).toEqual([1, 3]); registry.dispose(); }); });