Skip to content
Source
Sign inSign up

FxAdapterV2.testkit.ts

103 lines3.9 KB

1import * as NodeCrypto from "@effect/platform-node/NodeCrypto";
2import * as NodeServices from "@effect/platform-node/NodeServices";
3import { FxSettings } from "@t3tools/provider-fx/settings";
4import {
5 FX_ACP_CANCEL_BEHAVIOR,
6 FX_DRIVER_KIND,
7 makeFxAdapterV2,
8} from "@t3tools/provider-fx/testing";
9import { ProviderInstanceId } from "@t3tools/contracts";
10import * as Effect from "effect/Effect";
11import * as FileSystem from "effect/FileSystem";
12import * as Layer from "effect/Layer";
13import * as Path from "effect/Path";
14import * as Schema from "effect/Schema";
15import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner";
16import { resolveSelfInvocation } from "@t3tools/shared/nodeRuntime";
17​
18import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
19import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost";
20import type { ProviderReplayGate } from "@t3tools/provider-testing/replayGate";
21import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts";
22import type { OrchestratorV2ProviderReplayHarness } from "../testkit/ProviderReplayHarness.ts";
23import {
24 type AcpReplayTranscript,
25 AcpReplayTranscriptDecodeError,
26 decodeAcpReplayTranscript,
27 makeAcpReplayCompletenessAssertion,
28 makeAcpReplayRuntime,
29} from "./AcpAdapterV2.testkit.ts";
30​
31const DEFAULT_FX_SETTINGS = Schema.decodeSync(FxSettings)({});
32const FX_INSTANCE_ID = ProviderInstanceId.make("fx");
33​
34function layerFxProviderAdapterRegistryReplay(
35 transcript: AcpReplayTranscript,
36 options: { readonly replayGate?: ProviderReplayGate } = {},
37) {
38 const layerHost = TestProviderHost.layer().pipe(
39 Layer.provide(Layer.mergeAll(NodeServices.layer, NodeCrypto.layer)),
40 );
41​
42 return ProviderAdapterRegistry.layerFromAdaptersEffect(
43 Effect.gen(function* () {
44 const fileSystem = yield* FileSystem.FileSystem;
45 const path = yield* Path.Path;
46 const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner;
47 const replayGate = options.replayGate;
48 const replayDir = yield* fileSystem
49 .makeTempDirectoryScoped({
50 prefix: `t3-orchestration-v2-fx-replay-${transcript.scenario}-`,
51 })
52 .pipe(Effect.orDie);
53 const statusPath = path.join(replayDir, "status.json");
54 const scriptPath = yield* path
55 .fromFileUrl(new URL("../../../scripts/acp-replay-agent.ts", import.meta.url))
56 .pipe(Effect.orDie);
57 const adapter = yield* makeFxAdapterV2({
58 instanceId: FX_INSTANCE_ID,
59 settings: DEFAULT_FX_SETTINGS,
60 environment: {},
61 selfInvocation: yield* resolveSelfInvocation(),
62 makeRuntime: (runtimeInput) =>
63 makeAcpReplayRuntime({
64 transcript,
65 statusPath,
66 scriptPath,
67 childProcessSpawner,
68 fileSystem,
69 cancelBehavior: FX_ACP_CANCEL_BEHAVIOR,
70 ...(replayGate === undefined ? {} : { replayGate }),
71 })(runtimeInput),
72 assertComplete: makeAcpReplayCompletenessAssertion(fileSystem, statusPath, transcript),
73 ...(replayGate === undefined
74 ? {}
75 : {
76 testHooks: {
77 onDeferredFinalizeScheduled: (debounce) =>
78 Effect.sync(() => replayGate.recordFinishArmed(debounce)),
79 },
80 }),
81 });
82 return [adapter];
83 }),
84 ).pipe(
85 Layer.provide(
86 Layer.mergeAll(layerHost, NodeServices.layer, NodeCrypto.layer, IdAllocator.layer),
87 ),
88 Layer.merge(
89 Layer.effectDiscard(
90 Effect.addFinalizer(() => Effect.sync(() => options.replayGate?.releaseAll())),
91 ),
92 ),
93 );
95​
96export const FxOrchestratorReplayHarness: OrchestratorV2ProviderReplayHarness<
97 AcpReplayTranscript,
98 AcpReplayTranscriptDecodeError
99> = {
100 driver: FX_DRIVER_KIND,
101 decodeTranscript: (transcript) => decodeAcpReplayTranscript(transcript, FX_DRIVER_KIND),
102 makeProviderAdapterRegistryLayer: layerFxProviderAdapterRegistryReplay,
103};