import { CommandId, EnvironmentId, ORCHESTRATION_WS_METHODS, ProjectId, ThreadId, type ClientOrchestrationCommand,} from "@t3tools/contracts";import { describe, expect, it } from "@effect/vitest";import * as Crypto from "effect/Crypto";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as SubscriptionRef from "effect/SubscriptionRef"; import { AVAILABLE_CONNECTION_STATE, PrimaryConnectionTarget, type PreparedConnection,} from "../connection/model.ts";import * as EnvironmentSupervisor from "../connection/supervisor.ts";import * as RpcSession from "../rpc/session.ts";import type { WsRpcProtocolClient } from "../rpc/protocol.ts";import { archiveThread, createProject, reorderActiveThread, settleThread, stopThreadSession, unsettleThread,} from "./commands.ts"; const TEST_CRYPTO_LAYER = Layer.succeed( Crypto.Crypto, Crypto.make({ randomBytes: (size) => new Uint8Array(size), digest: (_algorithm, data) => Effect.succeed(data), }),); const TARGET = new PrimaryConnectionTarget({ environmentId: EnvironmentId.make("environment-1"), label: "Test environment", httpBaseUrl: "https://environment.example.test", wsBaseUrl: "wss://environment.example.test",}); const makeSupervisor = Effect.fn("TestEnvironmentCommands.makeSupervisor")(function* ( dispatched: ClientOrchestrationCommand[],) { const client = { [ORCHESTRATION_WS_METHODS.dispatchCommand]: (command: ClientOrchestrationCommand) => Effect.sync(() => { dispatched.push(command); return { sequence: dispatched.length }; }), } as unknown as WsRpcProtocolClient; const session: RpcSession.RpcSession = { client, initialConfig: Effect.never, subscribeServerConfig: (input) => client.subscribeServerConfig(input), ready: Effect.void, probe: Effect.void, closed: Effect.never, }; return EnvironmentSupervisor.EnvironmentSupervisor.of({ target: TARGET, state: yield* SubscriptionRef.make(AVAILABLE_CONNECTION_STATE), session: yield* SubscriptionRef.make(Option.some(session)), prepared: yield* SubscriptionRef.make(Option.none<PreparedConnection>()), connect: Effect.void, disconnect: Effect.void, retryNow: Effect.void, } satisfies EnvironmentSupervisor.EnvironmentSupervisor["Service"]);}); describe("environment commands", () => { it.effect("adds generated command metadata", () => Effect.gen(function* () { const dispatched: ClientOrchestrationCommand[] = []; const supervisor = yield* makeSupervisor(dispatched); const result = yield* createProject({ projectId: ProjectId.make("project-1"), title: "Project", workspaceRoot: "/workspace/project", createdAt: "2026-06-06T00:00:00.000Z", }).pipe(Effect.provideService(EnvironmentSupervisor.EnvironmentSupervisor, supervisor)); expect(result).toEqual({ sequence: 1 }); expect(dispatched).toEqual([ { type: "project.create", commandId: "00000000-0000-4000-8000-000000000000", projectId: "project-1", title: "Project", workspaceRoot: "/workspace/project", createdAt: "2026-06-06T00:00:00.000Z", }, ]); }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), ); it.effect("preserves caller metadata for idempotent queued commands", () => Effect.gen(function* () { const dispatched: ClientOrchestrationCommand[] = []; const supervisor = yield* makeSupervisor(dispatched); yield* stopThreadSession({ commandId: CommandId.make("queued-command"), threadId: ThreadId.make("thread-1"), createdAt: "2026-06-06T00:01:00.000Z", }).pipe(Effect.provideService(EnvironmentSupervisor.EnvironmentSupervisor, supervisor)); expect(dispatched).toEqual([ { type: "thread.session.stop", commandId: "queued-command", threadId: "thread-1", createdAt: "2026-06-06T00:01:00.000Z", }, ]); }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), ); it.effect("does not add timestamps to commands without createdAt", () => Effect.gen(function* () { const dispatched: ClientOrchestrationCommand[] = []; const supervisor = yield* makeSupervisor(dispatched); yield* archiveThread({ commandId: CommandId.make("archive-command"), threadId: ThreadId.make("thread-1"), }).pipe(Effect.provideService(EnvironmentSupervisor.EnvironmentSupervisor, supervisor)); expect(dispatched).toEqual([ { type: "thread.archive", commandId: "archive-command", threadId: "thread-1", }, ]); }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), ); it.effect("dispatches settle and unsettle commands without timestamps", () => Effect.gen(function* () { const dispatched: ClientOrchestrationCommand[] = []; const supervisor = yield* makeSupervisor(dispatched); yield* settleThread({ commandId: CommandId.make("settle-command"), threadId: ThreadId.make("thread-1"), }).pipe(Effect.provideService(EnvironmentSupervisor.EnvironmentSupervisor, supervisor)); yield* unsettleThread({ commandId: CommandId.make("unsettle-command"), threadId: ThreadId.make("thread-1"), reason: "user", }).pipe(Effect.provideService(EnvironmentSupervisor.EnvironmentSupervisor, supervisor)); expect(dispatched).toEqual([ { type: "thread.settle", commandId: "settle-command", threadId: "thread-1", }, { type: "thread.unsettle", commandId: "unsettle-command", threadId: "thread-1", reason: "user", }, ]); }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), ); it.effect("sends an active order key without changing activity timestamps", () => Effect.gen(function* () { const dispatched: ClientOrchestrationCommand[] = []; const supervisor = yield* makeSupervisor(dispatched); yield* reorderActiveThread({ commandId: CommandId.make("reorder-command"), threadId: ThreadId.make("thread-1"), orderKey: "mf", }).pipe(Effect.provideService(EnvironmentSupervisor.EnvironmentSupervisor, supervisor)); expect(dispatched).toEqual([ { type: "thread.active.reorder", commandId: "reorder-command", threadId: "thread-1", orderKey: "mf", }, ]); }).pipe(Effect.provide(TEST_CRYPTO_LAYER)), );});
import {
CommandId,
EnvironmentId,
ORCHESTRATION_WS_METHODS,
ProjectId,
ThreadId,
type ClientOrchestrationCommand,
} from "@t3tools/contracts";
import { describe, expect, it } from "@effect/vitest";
import * as Crypto from "effect/Crypto";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as Option from "effect/Option";
import * as SubscriptionRef from "effect/SubscriptionRef";
import {
AVAILABLE_CONNECTION_STATE,
PrimaryConnectionTarget,
type PreparedConnection,
} from "../connection/model.ts";
import * as EnvironmentSupervisor from "../connection/supervisor.ts";
import * as RpcSession from "../rpc/session.ts";
import type { WsRpcProtocolClient } from "../rpc/protocol.ts";
import {
archiveThread,
createProject,
reorderActiveThread,
settleThread,
stopThreadSession,
unsettleThread,
} from "./commands.ts";
const TEST_CRYPTO_LAYER = Layer.succeed(
Crypto.Crypto,
Crypto.make({
randomBytes: (size) => new Uint8Array(size),
digest: (_algorithm, data) => Effect.succeed(data),
}),
);
const TARGET = new PrimaryConnectionTarget({
environmentId: EnvironmentId.make("environment-1"),
label: "Test environment",
httpBaseUrl: "https://environment.example.test",
wsBaseUrl: "wss://environment.example.test",
});
const makeSupervisor = Effect.fn("TestEnvironmentCommands.makeSupervisor")(function* (
dispatched: ClientOrchestrationCommand[],
) {
const client = {
[ORCHESTRATION_WS_METHODS.dispatchCommand]: (command: ClientOrchestrationCommand) =>
Effect.sync(() => {
dispatched.push(command);
return { sequence: dispatched.length };
}),
} as unknown as WsRpcProtocolClient;
const session: RpcSession.RpcSession = {
client,
initialConfig: Effect.never,
subscribeServerConfig: (input) => client.subscribeServerConfig(input),
ready: Effect.void,
probe: Effect.void,
closed: Effect.never,
};
return EnvironmentSupervisor.EnvironmentSupervisor.of({
target: TARGET,
state: yield* SubscriptionRef.make(AVAILABLE_CONNECTION_STATE),
session: yield* SubscriptionRef.make(Option.some(session)),
prepared: yield* SubscriptionRef.make(Option.none<PreparedConnection>()),
connect: Effect.void,
disconnect: Effect.void,
retryNow: Effect.void,
} satisfies EnvironmentSupervisor.EnvironmentSupervisor["Service"]);
});
describe("environment commands", () => {
it.effect("adds generated command metadata", () =>
Effect.gen(function* () {
const dispatched: ClientOrchestrationCommand[] = [];
const supervisor = yield* makeSupervisor(dispatched);
const result = yield* createProject({
projectId: ProjectId.make("project-1"),
title: "Project",
workspaceRoot: "/workspace/project",
createdAt: "2026-06-06T00:00:00.000Z",
}).pipe(Effect.provideService(EnvironmentSupervisor.EnvironmentSupervisor, supervisor));
expect(result).toEqual({ sequence: 1 });
expect(dispatched).toEqual([
{
type: "project.create",
commandId: "00000000-0000-4000-8000-000000000000",
projectId: "project-1",
title: "Project",
workspaceRoot: "/workspace/project",
createdAt: "2026-06-06T00:00:00.000Z",
},
]);
}).pipe(Effect.provide(TEST_CRYPTO_LAYER)),
);
it.effect("preserves caller metadata for idempotent queued commands", () =>
Effect.gen(function* () {
const dispatched: ClientOrchestrationCommand[] = [];
const supervisor = yield* makeSupervisor(dispatched);
yield* stopThreadSession({
commandId: CommandId.make("queued-command"),
threadId: ThreadId.make("thread-1"),
createdAt: "2026-06-06T00:01:00.000Z",
}).pipe(Effect.provideService(EnvironmentSupervisor.EnvironmentSupervisor, supervisor));
expect(dispatched).toEqual([
{
type: "thread.session.stop",
commandId: "queued-command",
threadId: "thread-1",
createdAt: "2026-06-06T00:01:00.000Z",
},
]);
}).pipe(Effect.provide(TEST_CRYPTO_LAYER)),
);
it.effect("does not add timestamps to commands without createdAt", () =>
Effect.gen(function* () {
const dispatched: ClientOrchestrationCommand[] = [];
const supervisor = yield* makeSupervisor(dispatched);
yield* archiveThread({
commandId: CommandId.make("archive-command"),
threadId: ThreadId.make("thread-1"),
}).pipe(Effect.provideService(EnvironmentSupervisor.EnvironmentSupervisor, supervisor));
expect(dispatched).toEqual([
{
type: "thread.archive",
commandId: "archive-command",
threadId: "thread-1",
},
]);
}).pipe(Effect.provide(TEST_CRYPTO_LAYER)),
);
it.effect("dispatches settle and unsettle commands without timestamps", () =>
Effect.gen(function* () {
const dispatched: ClientOrchestrationCommand[] = [];
const supervisor = yield* makeSupervisor(dispatched);
yield* settleThread({
commandId: CommandId.make("settle-command"),
threadId: ThreadId.make("thread-1"),
}).pipe(Effect.provideService(EnvironmentSupervisor.EnvironmentSupervisor, supervisor));
yield* unsettleThread({
commandId: CommandId.make("unsettle-command"),
threadId: ThreadId.make("thread-1"),
reason: "user",
}).pipe(Effect.provideService(EnvironmentSupervisor.EnvironmentSupervisor, supervisor));
expect(dispatched).toEqual([
{
type: "thread.settle",
commandId: "settle-command",
threadId: "thread-1",
},
{
type: "thread.unsettle",
commandId: "unsettle-command",
threadId: "thread-1",
reason: "user",
},
]);
}).pipe(Effect.provide(TEST_CRYPTO_LAYER)),
);
it.effect("sends an active order key without changing activity timestamps", () =>
Effect.gen(function* () {
const dispatched: ClientOrchestrationCommand[] = [];
const supervisor = yield* makeSupervisor(dispatched);
yield* reorderActiveThread({
commandId: CommandId.make("reorder-command"),
threadId: ThreadId.make("thread-1"),
orderKey: "mf",
}).pipe(Effect.provideService(EnvironmentSupervisor.EnvironmentSupervisor, supervisor));
expect(dispatched).toEqual([
{
type: "thread.active.reorder",
commandId: "reorder-command",
threadId: "thread-1",
orderKey: "mf",
},
]);
}).pipe(Effect.provide(TEST_CRYPTO_LAYER)),
);
});