import { assert, describe, it } from "@effect/vitest";import * as Duration from "effect/Duration";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Option from "effect/Option"; import * as NetService from "@t3tools/shared/Net"; import * as DesktopBackendConfiguration from "../backend/DesktopBackendConfiguration.ts";import * as DesktopBackendPool from "../backend/DesktopBackendPool.ts";import type { DesktopBackendSnapshot, DesktopBackendStartConfig,} from "../backend/DesktopBackendManager.ts";import * as DesktopServerExposure from "../backend/DesktopServerExposure.ts";import * as DesktopAppSettings from "../settings/DesktopAppSettings.ts";import * as DesktopWslEnvironment from "./DesktopWslEnvironment.ts";import * as DesktopWslBackend from "./DesktopWslBackend.ts"; function makeStubInstance(input: { readonly id: DesktopBackendPool.BackendInstanceId; readonly label: string; readonly snapshot: DesktopBackendSnapshot; readonly start?: Effect.Effect<void>;}): DesktopBackendPool.DesktopBackendInstance { return { id: input.id, label: Effect.succeed(input.label), start: input.start ?? Effect.void, stop: () => Effect.void, currentConfig: Effect.succeed(Option.none<DesktopBackendStartConfig>()), snapshot: Effect.succeed(input.snapshot), waitForReady: (_timeout: Duration.Duration) => Effect.succeed(false), };} const idleSnapshot: DesktopBackendSnapshot = { desiredRunning: false, ready: false, activePid: Option.none(), restartAttempt: 5, restartScheduled: false,}; const primarySnapshot: DesktopBackendSnapshot = { desiredRunning: true, ready: true, activePid: Option.some(123), restartAttempt: 0, restartScheduled: false,}; const serverExposureLayer = Layer.succeed(DesktopServerExposure.DesktopServerExposure, { getState: Effect.die("unexpected getState"), backendConfig: Effect.succeed({ port: 3773, bindHost: "127.0.0.1", httpBaseUrl: new URL("http://127.0.0.1:3773"), tailscaleServeEnabled: false, tailscaleServePort: 443, }), configureFromSettings: () => Effect.die("unexpected configureFromSettings"), setMode: () => Effect.die("unexpected setMode"), setTailscaleServeEnabled: () => Effect.die("unexpected setTailscaleServeEnabled"), getAdvertisedEndpoints: Effect.succeed([]),} satisfies DesktopServerExposure.DesktopServerExposure["Service"]); const backendConfigurationLayer = Layer.succeed( DesktopBackendConfiguration.DesktopBackendConfiguration, { resolvePrimary: Effect.die("unexpected resolvePrimary"), resolvePrimaryLabel: Effect.succeed("Windows"), resolveWsl: () => Effect.die("unexpected resolveWsl"), } satisfies DesktopBackendConfiguration.DesktopBackendConfiguration["Service"],); const netLayer = Layer.succeed(NetService.NetService, { canListenOnHost: () => Effect.succeed(true), isPortAvailableOnLoopback: () => Effect.succeed(true), hasListenerOnHost: () => Effect.succeed(false), reserveLoopbackPort: () => Effect.succeed(41773), findAvailablePort: (preferred) => Effect.succeed(preferred),} satisfies NetService.NetService["Service"]); describe("DesktopWslBackend", () => { it.effect("clears the stored preflight error when a registered WSL backend becomes ready", () => { let registeredSpec: DesktopBackendPool.BackendInstanceSpec | undefined; const primary = makeStubInstance({ id: DesktopBackendPool.PRIMARY_INSTANCE_ID, label: "Windows", snapshot: primarySnapshot, }); const wsl = makeStubInstance({ id: DesktopBackendPool.BackendInstanceId("wsl:Ubuntu"), label: "WSL (Ubuntu)", snapshot: primarySnapshot, }); const poolLayer = Layer.succeed(DesktopBackendPool.DesktopBackendPool, { get: (id) => Effect.succeed( id === DesktopBackendPool.PRIMARY_INSTANCE_ID ? Option.some(primary) : Option.none<DesktopBackendPool.DesktopBackendInstance>(), ), list: Effect.succeed([primary]), primary: Effect.succeed(primary), register: (spec) => Effect.sync(() => { registeredSpec = spec; return wsl; }), unregister: () => Effect.die("unexpected unregister"), } satisfies DesktopBackendPool.DesktopBackendPool["Service"]); return Effect.gen(function* () { const backend = yield* DesktopWslBackend.DesktopWslBackend; yield* backend.reconcile; const spec = registeredSpec; assert.isDefined(spec); if (spec === undefined) { throw new Error("Expected WSL backend registration"); } const recordFailure = spec.onPreflightFailed; const clearFailure = spec.onReady; assert.isDefined(recordFailure); assert.isDefined(clearFailure); if (recordFailure === undefined || clearFailure === undefined) { throw new Error("Expected WSL backend callbacks"); } assert.isFalse(yield* recordFailure({ reason: "Node.js not found", fatal: true })); assert.deepEqual(yield* backend.lastPreflightError, Option.some("Node.js not found")); yield* clearFailure(new URL("http://127.0.0.1:41773")); assert.deepEqual(yield* backend.lastPreflightError, Option.none()); }).pipe( Effect.provide( DesktopWslBackend.layer.pipe( Layer.provideMerge(poolLayer), Layer.provideMerge(backendConfigurationLayer), Layer.provideMerge(serverExposureLayer), Layer.provideMerge(netLayer), Layer.provideMerge( DesktopAppSettings.layerTest({ ...DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS, wslBackendEnabled: true, wslDistro: "Ubuntu", wslOnly: false, }), ), Layer.provideMerge(DesktopWslEnvironment.layerTest({ isAvailable: true })), ), ), ); }); it.effect("retries an unchanged WSL instance when it is idle after failed preflight", () => { let startCount = 0; const primary = makeStubInstance({ id: DesktopBackendPool.PRIMARY_INSTANCE_ID, label: "Windows", snapshot: primarySnapshot, }); const wsl = makeStubInstance({ id: DesktopBackendPool.BackendInstanceId("wsl:Ubuntu"), label: "WSL (Ubuntu)", snapshot: idleSnapshot, start: Effect.sync(() => { startCount += 1; }), }); return Effect.gen(function* () { const backend = yield* DesktopWslBackend.DesktopWslBackend; yield* backend.reconcile; assert.equal(startCount, 1); }).pipe( Effect.provide( DesktopWslBackend.layer.pipe( Layer.provideMerge(DesktopBackendPool.layerTest([primary, wsl])), Layer.provideMerge(backendConfigurationLayer), Layer.provideMerge(serverExposureLayer), Layer.provideMerge(netLayer), Layer.provideMerge( DesktopAppSettings.layerTest({ ...DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS, wslBackendEnabled: true, wslDistro: "Ubuntu", wslOnly: false, }), ), Layer.provideMerge(DesktopWslEnvironment.layerTest({ isAvailable: true })), ), ), ); });});
import { assert, describe, it } from "@effect/vitest";
import * as Duration from "effect/Duration";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as Option from "effect/Option";
import * as NetService from "@t3tools/shared/Net";
import * as DesktopBackendConfiguration from "../backend/DesktopBackendConfiguration.ts";
import * as DesktopBackendPool from "../backend/DesktopBackendPool.ts";
import type {
DesktopBackendSnapshot,
DesktopBackendStartConfig,
} from "../backend/DesktopBackendManager.ts";
import * as DesktopServerExposure from "../backend/DesktopServerExposure.ts";
import * as DesktopAppSettings from "../settings/DesktopAppSettings.ts";
import * as DesktopWslEnvironment from "./DesktopWslEnvironment.ts";
import * as DesktopWslBackend from "./DesktopWslBackend.ts";
function makeStubInstance(input: {
readonly id: DesktopBackendPool.BackendInstanceId;
readonly label: string;
readonly snapshot: DesktopBackendSnapshot;
readonly start?: Effect.Effect<void>;
}): DesktopBackendPool.DesktopBackendInstance {
return {
id: input.id,
label: Effect.succeed(input.label),
start: input.start ?? Effect.void,
stop: () => Effect.void,
currentConfig: Effect.succeed(Option.none<DesktopBackendStartConfig>()),
snapshot: Effect.succeed(input.snapshot),
waitForReady: (_timeout: Duration.Duration) => Effect.succeed(false),
};
}
const idleSnapshot: DesktopBackendSnapshot = {
desiredRunning: false,
ready: false,
activePid: Option.none(),
restartAttempt: 5,
restartScheduled: false,
};
const primarySnapshot: DesktopBackendSnapshot = {
desiredRunning: true,
ready: true,
activePid: Option.some(123),
restartAttempt: 0,
restartScheduled: false,
};
const serverExposureLayer = Layer.succeed(DesktopServerExposure.DesktopServerExposure, {
getState: Effect.die("unexpected getState"),
backendConfig: Effect.succeed({
port: 3773,
bindHost: "127.0.0.1",
httpBaseUrl: new URL("http://127.0.0.1:3773"),
tailscaleServeEnabled: false,
tailscaleServePort: 443,
}),
configureFromSettings: () => Effect.die("unexpected configureFromSettings"),
setMode: () => Effect.die("unexpected setMode"),
setTailscaleServeEnabled: () => Effect.die("unexpected setTailscaleServeEnabled"),
getAdvertisedEndpoints: Effect.succeed([]),
} satisfies DesktopServerExposure.DesktopServerExposure["Service"]);
const backendConfigurationLayer = Layer.succeed(
DesktopBackendConfiguration.DesktopBackendConfiguration,
{
resolvePrimary: Effect.die("unexpected resolvePrimary"),
resolvePrimaryLabel: Effect.succeed("Windows"),
resolveWsl: () => Effect.die("unexpected resolveWsl"),
} satisfies DesktopBackendConfiguration.DesktopBackendConfiguration["Service"],
);
const netLayer = Layer.succeed(NetService.NetService, {
canListenOnHost: () => Effect.succeed(true),
isPortAvailableOnLoopback: () => Effect.succeed(true),
hasListenerOnHost: () => Effect.succeed(false),
reserveLoopbackPort: () => Effect.succeed(41773),
findAvailablePort: (preferred) => Effect.succeed(preferred),
} satisfies NetService.NetService["Service"]);
describe("DesktopWslBackend", () => {
it.effect("clears the stored preflight error when a registered WSL backend becomes ready", () => {
let registeredSpec: DesktopBackendPool.BackendInstanceSpec | undefined;
const primary = makeStubInstance({
id: DesktopBackendPool.PRIMARY_INSTANCE_ID,
label: "Windows",
snapshot: primarySnapshot,
});
const wsl = makeStubInstance({
id: DesktopBackendPool.BackendInstanceId("wsl:Ubuntu"),
label: "WSL (Ubuntu)",
snapshot: primarySnapshot,
});
const poolLayer = Layer.succeed(DesktopBackendPool.DesktopBackendPool, {
get: (id) =>
Effect.succeed(
id === DesktopBackendPool.PRIMARY_INSTANCE_ID
? Option.some(primary)
: Option.none<DesktopBackendPool.DesktopBackendInstance>(),
),
list: Effect.succeed([primary]),
primary: Effect.succeed(primary),
register: (spec) =>
Effect.sync(() => {
registeredSpec = spec;
return wsl;
}),
unregister: () => Effect.die("unexpected unregister"),
} satisfies DesktopBackendPool.DesktopBackendPool["Service"]);
return Effect.gen(function* () {
const backend = yield* DesktopWslBackend.DesktopWslBackend;
yield* backend.reconcile;
const spec = registeredSpec;
assert.isDefined(spec);
if (spec === undefined) {
throw new Error("Expected WSL backend registration");
}
const recordFailure = spec.onPreflightFailed;
const clearFailure = spec.onReady;
assert.isDefined(recordFailure);
assert.isDefined(clearFailure);
if (recordFailure === undefined || clearFailure === undefined) {
throw new Error("Expected WSL backend callbacks");
}
assert.isFalse(yield* recordFailure({ reason: "Node.js not found", fatal: true }));
assert.deepEqual(yield* backend.lastPreflightError, Option.some("Node.js not found"));
yield* clearFailure(new URL("http://127.0.0.1:41773"));
assert.deepEqual(yield* backend.lastPreflightError, Option.none());
}).pipe(
Effect.provide(
DesktopWslBackend.layer.pipe(
Layer.provideMerge(poolLayer),
Layer.provideMerge(backendConfigurationLayer),
Layer.provideMerge(serverExposureLayer),
Layer.provideMerge(netLayer),
Layer.provideMerge(
DesktopAppSettings.layerTest({
...DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS,
wslBackendEnabled: true,
wslDistro: "Ubuntu",
wslOnly: false,
}),
),
Layer.provideMerge(DesktopWslEnvironment.layerTest({ isAvailable: true })),
),
),
);
});
it.effect("retries an unchanged WSL instance when it is idle after failed preflight", () => {
let startCount = 0;
const primary = makeStubInstance({
id: DesktopBackendPool.PRIMARY_INSTANCE_ID,
label: "Windows",
snapshot: primarySnapshot,
});
const wsl = makeStubInstance({
id: DesktopBackendPool.BackendInstanceId("wsl:Ubuntu"),
label: "WSL (Ubuntu)",
snapshot: idleSnapshot,
start: Effect.sync(() => {
startCount += 1;
}),
});
return Effect.gen(function* () {
const backend = yield* DesktopWslBackend.DesktopWslBackend;
yield* backend.reconcile;
assert.equal(startCount, 1);
}).pipe(
Effect.provide(
DesktopWslBackend.layer.pipe(
Layer.provideMerge(DesktopBackendPool.layerTest([primary, wsl])),
Layer.provideMerge(backendConfigurationLayer),
Layer.provideMerge(serverExposureLayer),
Layer.provideMerge(netLayer),
Layer.provideMerge(
DesktopAppSettings.layerTest({
...DesktopAppSettings.DEFAULT_DESKTOP_SETTINGS,
wslBackendEnabled: true,
wslDistro: "Ubuntu",
wslOnly: false,
}),
),
Layer.provideMerge(DesktopWslEnvironment.layerTest({ isAvailable: true })),
),
),
);
});
});