import * as NodeServices from "@effect/platform-node/NodeServices";import { assert, describe, it } from "@effect/vitest";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Logger from "effect/Logger";import * as PlatformError from "effect/PlatformError";import * as Schema from "effect/Schema";import * as Sink from "effect/Sink";import * as Stream from "effect/Stream";import * as ChildProcess from "effect/unstable/process/ChildProcess";import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner"; import * as DesktopEnvironment from "../app/DesktopEnvironment.ts";import * as DesktopShellEnvironment from "./DesktopShellEnvironment.ts"; const textEncoder = new TextEncoder(); const isDesktopShellEnvironmentCommandError = Schema.is( DesktopShellEnvironment.DesktopShellEnvironmentCommandError,); function envOutput(values: Readonly<Record<string, string>>): string { return Object.entries(values) .flatMap(([name, value]) => [ `__T3CODE_ENV_${name}_START__`, value, `__T3CODE_ENV_${name}_END__`, ]) .join("\n");} function makeProcess(output: string): ChildProcessSpawner.ChildProcessHandle { const stdout = output.length === 0 ? Stream.empty : Stream.make(textEncoder.encode(output)); return ChildProcessSpawner.makeHandle({ pid: ChildProcessSpawner.ProcessId(123), stdout, stderr: Stream.empty, all: stdout, exitCode: Effect.succeed(ChildProcessSpawner.ExitCode(0)), isRunning: Effect.succeed(false), kill: () => Effect.void, stdin: Sink.drain, getInputFd: () => Sink.drain, getOutputFd: () => Stream.empty, unref: Effect.succeed(Effect.void), });} function withProcessEnv<A, E, R>( env: NodeJS.ProcessEnv, effect: Effect.Effect<A, E, R>,): Effect.Effect<A, E, R> { return Effect.acquireUseRelease( Effect.sync(() => { const previous = process.env; process.env = env; return previous; }), () => effect, (previous) => Effect.sync(() => { process.env = previous; }), );} function runShellEnvironment(input: { readonly env: NodeJS.ProcessEnv; readonly platform: NodeJS.Platform; readonly handler: (command: ChildProcess.Command) => string; readonly failure?: PlatformError.PlatformError;}) { const environmentLayer = Layer.succeed( DesktopEnvironment.DesktopEnvironment, DesktopEnvironment.DesktopEnvironment.of({ platform: input.platform, } as DesktopEnvironment.DesktopEnvironment["Service"]), ); const spawnerLayer = Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make((command) => input.failure === undefined ? Effect.succeed(makeProcess(input.handler(command))) : Effect.fail(input.failure), ), ); const program = Effect.gen(function* () { const shellEnvironment = yield* DesktopShellEnvironment.DesktopShellEnvironment; yield* shellEnvironment.installIntoProcess; }).pipe( Effect.provide( DesktopShellEnvironment.layer.pipe( Layer.provide(Layer.mergeAll(environmentLayer, NodeServices.layer, spawnerLayer)), ), ), ); return withProcessEnv(input.env, program);} describe("DesktopShellEnvironment", () => { it.effect("hydrates PATH and missing SSH_AUTH_SOCK from the login shell on macOS", () => Effect.gen(function* () { const env: NodeJS.ProcessEnv = { SHELL: "/bin/zsh", PATH: "/Users/test/.local/bin:/usr/bin", }; const commands: ChildProcess.Command[] = []; yield* runShellEnvironment({ env, platform: "darwin", handler: (command) => { commands.push(command); return envOutput({ PATH: "/opt/homebrew/bin:/usr/bin", SSH_AUTH_SOCK: "/tmp/secretive.sock", HOMEBREW_PREFIX: "/opt/homebrew", }); }, }); assert.equal(commands.length, 1); assert.equal(commands[0]?._tag === "StandardCommand" ? commands[0].command : "", "/bin/zsh"); assert.equal(env.PATH, "/opt/homebrew/bin:/usr/bin:/Users/test/.local/bin"); assert.equal(env.SSH_AUTH_SOCK, "/tmp/secretive.sock"); assert.equal(env.HOMEBREW_PREFIX, "/opt/homebrew"); }), ); it.effect("preserves inherited POSIX values when present", () => Effect.gen(function* () { const env: NodeJS.ProcessEnv = { SHELL: "/bin/zsh", PATH: "/usr/bin", SSH_AUTH_SOCK: "/tmp/inherited.sock", }; yield* runShellEnvironment({ env, platform: "darwin", handler: () => envOutput({ PATH: "/opt/homebrew/bin:/usr/bin", SSH_AUTH_SOCK: "/tmp/login-shell.sock", }), }); assert.equal(env.PATH, "/opt/homebrew/bin:/usr/bin"); assert.equal(env.SSH_AUTH_SOCK, "/tmp/inherited.sock"); }), ); it.effect("hydrates the locale from the login shell on macOS", () => Effect.gen(function* () { const env: NodeJS.ProcessEnv = { SHELL: "/bin/zsh", PATH: "/usr/bin", }; yield* runShellEnvironment({ env, platform: "darwin", handler: () => envOutput({ PATH: "/opt/homebrew/bin:/usr/bin", LANG: "de_DE.UTF-8", }), }); assert.equal(env.LANG, "de_DE.UTF-8"); }), ); it.effect("preserves an inherited locale over the login shell on macOS", () => Effect.gen(function* () { const env: NodeJS.ProcessEnv = { SHELL: "/bin/zsh", PATH: "/usr/bin", LANG: "en_US.UTF-8", }; yield* runShellEnvironment({ env, platform: "darwin", handler: () => envOutput({ PATH: "/opt/homebrew/bin:/usr/bin", LANG: "de_DE.UTF-8", }), }); assert.equal(env.LANG, "en_US.UTF-8"); }), ); it.effect("does not mix login-shell locale categories into an inherited locale", () => Effect.gen(function* () { const env: NodeJS.ProcessEnv = { SHELL: "/bin/zsh", PATH: "/usr/bin", LANG: "en_US.UTF-8", }; yield* runShellEnvironment({ env, platform: "darwin", handler: () => envOutput({ PATH: "/opt/homebrew/bin:/usr/bin", LC_ALL: "de_DE.UTF-8", }), }); assert.equal(env.LANG, "en_US.UTF-8"); assert.equal(env.LC_ALL, undefined); }), ); it.effect("falls back to a UTF-8 LC_CTYPE when no locale is available on macOS", () => Effect.gen(function* () { const env: NodeJS.ProcessEnv = { SHELL: "/bin/zsh", PATH: "/usr/bin", }; yield* runShellEnvironment({ env, platform: "darwin", handler: () => envOutput({ PATH: "/opt/homebrew/bin:/usr/bin" }), }); assert.equal(env.LANG, undefined); assert.equal(env.LC_ALL, undefined); assert.equal(env.LC_CTYPE, "en_US.UTF-8"); }), ); it.effect("does not apply the locale fallback on linux", () => Effect.gen(function* () { const env: NodeJS.ProcessEnv = { SHELL: "/bin/zsh", PATH: "/usr/bin", }; yield* runShellEnvironment({ env, platform: "linux", handler: () => envOutput({ PATH: "/home/linuxbrew/.linuxbrew/bin:/usr/bin" }), }); assert.equal(env.LANG, undefined); }), ); it.effect("hydrates PATH and missing SSH_AUTH_SOCK from the login shell on linux", () => Effect.gen(function* () { const env: NodeJS.ProcessEnv = { SHELL: "/bin/zsh", PATH: "/usr/bin", }; yield* runShellEnvironment({ env, platform: "linux", handler: () => envOutput({ PATH: "/home/linuxbrew/.linuxbrew/bin:/usr/bin", SSH_AUTH_SOCK: "/tmp/secretive.sock", }), }); assert.equal(env.PATH, "/home/linuxbrew/.linuxbrew/bin:/usr/bin"); assert.equal(env.SSH_AUTH_SOCK, "/tmp/secretive.sock"); }), ); it.effect("falls back to launchctl PATH on macOS when shell probing does not return one", () => Effect.gen(function* () { const env: NodeJS.ProcessEnv = { SHELL: "/opt/homebrew/bin/nu", PATH: "/usr/bin", }; const commands: string[] = []; yield* runShellEnvironment({ env, platform: "darwin", handler: (command) => { if (command._tag !== "StandardCommand") return ""; commands.push(command.command); return command.command === "/bin/launchctl" ? "/opt/homebrew/bin:/usr/bin" : ""; }, }); assert.deepEqual(commands, ["/opt/homebrew/bin/nu", "/bin/zsh", "/bin/launchctl"]); assert.equal(env.PATH, "/opt/homebrew/bin:/usr/bin"); }), ); it.effect("loads PowerShell profile environment on Windows", () => Effect.gen(function* () { const env: NodeJS.ProcessEnv = { PATH: "C:\\Windows\\System32", APPDATA: "C:\\Users\\testuser\\AppData\\Roaming", LOCALAPPDATA: "C:\\Users\\testuser\\AppData\\Local", USERPROFILE: "C:\\Users\\testuser", }; yield* runShellEnvironment({ env, platform: "win32", handler: (command) => { if (command._tag !== "StandardCommand") return ""; const loadProfile = !command.args.includes("-NoProfile"); return loadProfile ? envOutput({ PATH: "C:\\Profile\\Node;C:\\Windows\\System32", FNM_DIR: "C:\\Users\\testuser\\AppData\\Roaming\\fnm", FNM_MULTISHELL_PATH: "C:\\Users\\testuser\\AppData\\Local\\fnm_multishells\\123", }) : envOutput({ PATH: 'C:\\Custom\\Bin;C:";C:\\Windows\\System32' }); }, }); assert.equal( env.PATH, [ "C:\\Profile\\Node", "C:\\Windows\\System32", "C:\\Users\\testuser\\AppData\\Roaming\\npm", "C:\\Users\\testuser\\AppData\\Local\\Programs\\nodejs", "C:\\Users\\testuser\\AppData\\Local\\Volta\\bin", "C:\\Users\\testuser\\AppData\\Local\\pnpm", "C:\\Users\\testuser\\.local\\bin", "C:\\Users\\testuser\\.bun\\bin", "C:\\Users\\testuser\\scoop\\shims", "C:\\Custom\\Bin", "C:", ].join(";"), ); assert.equal(env.FNM_DIR, "C:\\Users\\testuser\\AppData\\Roaming\\fnm"); assert.equal( env.FNM_MULTISHELL_PATH, "C:\\Users\\testuser\\AppData\\Local\\fnm_multishells\\123", ); }), ); it.effect("prefers login-shell desktop session hints over inherited values on linux", () => Effect.gen(function* () { const env: NodeJS.ProcessEnv = { SHELL: "/bin/zsh", PATH: "/usr/bin", XDG_CURRENT_DESKTOP: "wrong-launcher", XDG_SESSION_DESKTOP: "wrong-launcher", }; yield* runShellEnvironment({ env, platform: "linux", handler: () => envOutput({ PATH: "/home/linuxbrew/.linuxbrew/bin:/usr/bin", XDG_CURRENT_DESKTOP: "KDE", XDG_SESSION_DESKTOP: "KDE", XDG_SESSION_TYPE: "wayland", }), }); assert.equal(env.XDG_CURRENT_DESKTOP, "KDE"); assert.equal(env.XDG_SESSION_DESKTOP, "KDE"); assert.equal(env.XDG_SESSION_TYPE, "wayland"); }), ); it.effect("overrides stale dbus session addresses from the login shell", () => Effect.gen(function* () { const env: NodeJS.ProcessEnv = { SHELL: "/bin/zsh", PATH: "/usr/bin", DBUS_SESSION_BUS_ADDRESS: "unix:path=/tmp/stale-bus", }; yield* runShellEnvironment({ env, platform: "linux", handler: () => envOutput({ PATH: "/usr/bin", DBUS_SESSION_BUS_ADDRESS: "unix:path=/run/user/1000/bus", }), }); assert.equal(env.DBUS_SESSION_BUS_ADDRESS, "unix:path=/run/user/1000/bus"); }), ); it("resolves dbus runtime dir candidates with existence checks", () => { const busPath = DesktopShellEnvironment.resolveDefaultLinuxDbusSessionBusAddress({ env: { XDG_RUNTIME_DIR: "/tmp/stale-runtime" }, uid: 1000, exists: (path) => path === "/run/user/1000/bus", }); assert.equal(busPath, "unix:path=/run/user/1000/bus"); }); it.effect("logs command failures with safe probe context and the exact cause", () => { const env: NodeJS.ProcessEnv = { SHELL: "/bin/bash", PATH: "/usr/bin", }; const cause = PlatformError.systemError({ _tag: "PermissionDenied", module: "ChildProcess", method: "spawn", pathOrDescriptor: "/bin/bash", }); const messages: Array<unknown> = []; const logger = Logger.make(({ message }) => { messages.push(message); }); return runShellEnvironment({ env, platform: "linux", handler: () => "", failure: cause, }).pipe( Effect.andThen( Effect.sync(() => { const errors = messages .flatMap((message) => (Array.isArray(message) ? message : [message])) .filter(isDesktopShellEnvironmentCommandError); assert.lengthOf(errors, 1); assert.equal(errors[0]?.probe, "login-shell"); assert.equal(errors[0]?.executable, "bash"); assert.equal(errors[0]?.argumentCount, 2); assert.notProperty(errors[0] ?? {}, "args"); assert.equal(errors[0]?.cause, cause); assert.notInclude(errors[0]?.message ?? "", cause.message); }), ), Effect.provide(Logger.layer([logger], { mergeWithExisting: false })), ); });});
import * as NodeServices from "@effect/platform-node/NodeServices";
import { assert, describe, it } from "@effect/vitest";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as Logger from "effect/Logger";
import * as PlatformError from "effect/PlatformError";
import * as Schema from "effect/Schema";
import * as Sink from "effect/Sink";
import * as Stream from "effect/Stream";
import * as ChildProcess from "effect/unstable/process/ChildProcess";
import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner";
import * as DesktopEnvironment from "../app/DesktopEnvironment.ts";
import * as DesktopShellEnvironment from "./DesktopShellEnvironment.ts";
const textEncoder = new TextEncoder();
const isDesktopShellEnvironmentCommandError = Schema.is(
DesktopShellEnvironment.DesktopShellEnvironmentCommandError,
);
function envOutput(values: Readonly<Record<string, string>>): string {
return Object.entries(values)
.flatMap(([name, value]) => [
`__T3CODE_ENV_${name}_START__`,
value,
`__T3CODE_ENV_${name}_END__`,
])
.join("\n");
}
function makeProcess(output: string): ChildProcessSpawner.ChildProcessHandle {
const stdout = output.length === 0 ? Stream.empty : Stream.make(textEncoder.encode(output));
return ChildProcessSpawner.makeHandle({
pid: ChildProcessSpawner.ProcessId(123),
stdout,
stderr: Stream.empty,
all: stdout,
exitCode: Effect.succeed(ChildProcessSpawner.ExitCode(0)),
isRunning: Effect.succeed(false),
kill: () => Effect.void,
stdin: Sink.drain,
getInputFd: () => Sink.drain,
getOutputFd: () => Stream.empty,
unref: Effect.succeed(Effect.void),
});
}
function withProcessEnv<A, E, R>(
env: NodeJS.ProcessEnv,
effect: Effect.Effect<A, E, R>,
): Effect.Effect<A, E, R> {
return Effect.acquireUseRelease(
Effect.sync(() => {
const previous = process.env;
process.env = env;
return previous;
}),
() => effect,
(previous) =>
Effect.sync(() => {
process.env = previous;
}),
);
}
function runShellEnvironment(input: {
readonly env: NodeJS.ProcessEnv;
readonly platform: NodeJS.Platform;
readonly handler: (command: ChildProcess.Command) => string;
readonly failure?: PlatformError.PlatformError;
}) {
const environmentLayer = Layer.succeed(
DesktopEnvironment.DesktopEnvironment,
DesktopEnvironment.DesktopEnvironment.of({
platform: input.platform,
} as DesktopEnvironment.DesktopEnvironment["Service"]),
);
const spawnerLayer = Layer.succeed(
ChildProcessSpawner.ChildProcessSpawner,
ChildProcessSpawner.make((command) =>
input.failure === undefined
? Effect.succeed(makeProcess(input.handler(command)))
: Effect.fail(input.failure),
),
);
const program = Effect.gen(function* () {
const shellEnvironment = yield* DesktopShellEnvironment.DesktopShellEnvironment;
yield* shellEnvironment.installIntoProcess;
}).pipe(
Effect.provide(
DesktopShellEnvironment.layer.pipe(
Layer.provide(Layer.mergeAll(environmentLayer, NodeServices.layer, spawnerLayer)),
),
),
);
return withProcessEnv(input.env, program);
}
describe("DesktopShellEnvironment", () => {
it.effect("hydrates PATH and missing SSH_AUTH_SOCK from the login shell on macOS", () =>
Effect.gen(function* () {
const env: NodeJS.ProcessEnv = {
SHELL: "/bin/zsh",
PATH: "/Users/test/.local/bin:/usr/bin",
};
const commands: ChildProcess.Command[] = [];
yield* runShellEnvironment({
env,
platform: "darwin",
handler: (command) => {
commands.push(command);
return envOutput({
PATH: "/opt/homebrew/bin:/usr/bin",
SSH_AUTH_SOCK: "/tmp/secretive.sock",
HOMEBREW_PREFIX: "/opt/homebrew",
});
},
});
assert.equal(commands.length, 1);
assert.equal(commands[0]?._tag === "StandardCommand" ? commands[0].command : "", "/bin/zsh");
assert.equal(env.PATH, "/opt/homebrew/bin:/usr/bin:/Users/test/.local/bin");
assert.equal(env.SSH_AUTH_SOCK, "/tmp/secretive.sock");
assert.equal(env.HOMEBREW_PREFIX, "/opt/homebrew");
}),
);
it.effect("preserves inherited POSIX values when present", () =>
Effect.gen(function* () {
const env: NodeJS.ProcessEnv = {
SHELL: "/bin/zsh",
PATH: "/usr/bin",
SSH_AUTH_SOCK: "/tmp/inherited.sock",
};
yield* runShellEnvironment({
env,
platform: "darwin",
handler: () =>
envOutput({
PATH: "/opt/homebrew/bin:/usr/bin",
SSH_AUTH_SOCK: "/tmp/login-shell.sock",
}),
});
assert.equal(env.PATH, "/opt/homebrew/bin:/usr/bin");
assert.equal(env.SSH_AUTH_SOCK, "/tmp/inherited.sock");
}),
);
it.effect("hydrates the locale from the login shell on macOS", () =>
Effect.gen(function* () {
const env: NodeJS.ProcessEnv = {
SHELL: "/bin/zsh",
PATH: "/usr/bin",
};
yield* runShellEnvironment({
env,
platform: "darwin",
handler: () =>
envOutput({
PATH: "/opt/homebrew/bin:/usr/bin",
LANG: "de_DE.UTF-8",
}),
});
assert.equal(env.LANG, "de_DE.UTF-8");
}),
);
it.effect("preserves an inherited locale over the login shell on macOS", () =>
Effect.gen(function* () {
const env: NodeJS.ProcessEnv = {
SHELL: "/bin/zsh",
PATH: "/usr/bin",
LANG: "en_US.UTF-8",
};
yield* runShellEnvironment({
env,
platform: "darwin",
handler: () =>
envOutput({
PATH: "/opt/homebrew/bin:/usr/bin",
LANG: "de_DE.UTF-8",
}),
});
assert.equal(env.LANG, "en_US.UTF-8");
}),
);
it.effect("does not mix login-shell locale categories into an inherited locale", () =>
Effect.gen(function* () {
const env: NodeJS.ProcessEnv = {
SHELL: "/bin/zsh",
PATH: "/usr/bin",
LANG: "en_US.UTF-8",
};
yield* runShellEnvironment({
env,
platform: "darwin",
handler: () =>
envOutput({
PATH: "/opt/homebrew/bin:/usr/bin",
LC_ALL: "de_DE.UTF-8",
}),
});
assert.equal(env.LANG, "en_US.UTF-8");
assert.equal(env.LC_ALL, undefined);
}),
);
it.effect("falls back to a UTF-8 LC_CTYPE when no locale is available on macOS", () =>
Effect.gen(function* () {
const env: NodeJS.ProcessEnv = {
SHELL: "/bin/zsh",
PATH: "/usr/bin",
};
yield* runShellEnvironment({
env,
platform: "darwin",
handler: () => envOutput({ PATH: "/opt/homebrew/bin:/usr/bin" }),
});
assert.equal(env.LANG, undefined);
assert.equal(env.LC_ALL, undefined);
assert.equal(env.LC_CTYPE, "en_US.UTF-8");
}),
);
it.effect("does not apply the locale fallback on linux", () =>
Effect.gen(function* () {
const env: NodeJS.ProcessEnv = {
SHELL: "/bin/zsh",
PATH: "/usr/bin",
};
yield* runShellEnvironment({
env,
platform: "linux",
handler: () => envOutput({ PATH: "/home/linuxbrew/.linuxbrew/bin:/usr/bin" }),
});
assert.equal(env.LANG, undefined);
}),
);
it.effect("hydrates PATH and missing SSH_AUTH_SOCK from the login shell on linux", () =>
Effect.gen(function* () {
const env: NodeJS.ProcessEnv = {
SHELL: "/bin/zsh",
PATH: "/usr/bin",
};
yield* runShellEnvironment({
env,
platform: "linux",
handler: () =>
envOutput({
PATH: "/home/linuxbrew/.linuxbrew/bin:/usr/bin",
SSH_AUTH_SOCK: "/tmp/secretive.sock",
}),
});
assert.equal(env.PATH, "/home/linuxbrew/.linuxbrew/bin:/usr/bin");
assert.equal(env.SSH_AUTH_SOCK, "/tmp/secretive.sock");
}),
);
it.effect("falls back to launchctl PATH on macOS when shell probing does not return one", () =>
Effect.gen(function* () {
const env: NodeJS.ProcessEnv = {
SHELL: "/opt/homebrew/bin/nu",
PATH: "/usr/bin",
};
const commands: string[] = [];
yield* runShellEnvironment({
env,
platform: "darwin",
handler: (command) => {
if (command._tag !== "StandardCommand") return "";
commands.push(command.command);
return command.command === "/bin/launchctl" ? "/opt/homebrew/bin:/usr/bin" : "";
},
});
assert.deepEqual(commands, ["/opt/homebrew/bin/nu", "/bin/zsh", "/bin/launchctl"]);
assert.equal(env.PATH, "/opt/homebrew/bin:/usr/bin");
}),
);
it.effect("loads PowerShell profile environment on Windows", () =>
Effect.gen(function* () {
const env: NodeJS.ProcessEnv = {
PATH: "C:\\Windows\\System32",
APPDATA: "C:\\Users\\testuser\\AppData\\Roaming",
LOCALAPPDATA: "C:\\Users\\testuser\\AppData\\Local",
USERPROFILE: "C:\\Users\\testuser",
};
yield* runShellEnvironment({
env,
platform: "win32",
handler: (command) => {
if (command._tag !== "StandardCommand") return "";
const loadProfile = !command.args.includes("-NoProfile");
return loadProfile
? envOutput({
PATH: "C:\\Profile\\Node;C:\\Windows\\System32",
FNM_DIR: "C:\\Users\\testuser\\AppData\\Roaming\\fnm",
FNM_MULTISHELL_PATH: "C:\\Users\\testuser\\AppData\\Local\\fnm_multishells\\123",
})
: envOutput({ PATH: 'C:\\Custom\\Bin;C:";C:\\Windows\\System32' });
},
});
assert.equal(
env.PATH,
[
"C:\\Profile\\Node",
"C:\\Windows\\System32",
"C:\\Users\\testuser\\AppData\\Roaming\\npm",
"C:\\Users\\testuser\\AppData\\Local\\Programs\\nodejs",
"C:\\Users\\testuser\\AppData\\Local\\Volta\\bin",
"C:\\Users\\testuser\\AppData\\Local\\pnpm",
"C:\\Users\\testuser\\.local\\bin",
"C:\\Users\\testuser\\.bun\\bin",
"C:\\Users\\testuser\\scoop\\shims",
"C:\\Custom\\Bin",
"C:",
].join(";"),
);
assert.equal(env.FNM_DIR, "C:\\Users\\testuser\\AppData\\Roaming\\fnm");
assert.equal(
env.FNM_MULTISHELL_PATH,
"C:\\Users\\testuser\\AppData\\Local\\fnm_multishells\\123",
);
}),
);
it.effect("prefers login-shell desktop session hints over inherited values on linux", () =>
Effect.gen(function* () {
const env: NodeJS.ProcessEnv = {
SHELL: "/bin/zsh",
PATH: "/usr/bin",
XDG_CURRENT_DESKTOP: "wrong-launcher",
XDG_SESSION_DESKTOP: "wrong-launcher",
};
yield* runShellEnvironment({
env,
platform: "linux",
handler: () =>
envOutput({
PATH: "/home/linuxbrew/.linuxbrew/bin:/usr/bin",
XDG_CURRENT_DESKTOP: "KDE",
XDG_SESSION_DESKTOP: "KDE",
XDG_SESSION_TYPE: "wayland",
}),
});
assert.equal(env.XDG_CURRENT_DESKTOP, "KDE");
assert.equal(env.XDG_SESSION_DESKTOP, "KDE");
assert.equal(env.XDG_SESSION_TYPE, "wayland");
}),
);
it.effect("overrides stale dbus session addresses from the login shell", () =>
Effect.gen(function* () {
const env: NodeJS.ProcessEnv = {
SHELL: "/bin/zsh",
PATH: "/usr/bin",
DBUS_SESSION_BUS_ADDRESS: "unix:path=/tmp/stale-bus",
};
yield* runShellEnvironment({
env,
platform: "linux",
handler: () =>
envOutput({
PATH: "/usr/bin",
DBUS_SESSION_BUS_ADDRESS: "unix:path=/run/user/1000/bus",
}),
});
assert.equal(env.DBUS_SESSION_BUS_ADDRESS, "unix:path=/run/user/1000/bus");
}),
);
it("resolves dbus runtime dir candidates with existence checks", () => {
const busPath = DesktopShellEnvironment.resolveDefaultLinuxDbusSessionBusAddress({
env: { XDG_RUNTIME_DIR: "/tmp/stale-runtime" },
uid: 1000,
exists: (path) => path === "/run/user/1000/bus",
});
assert.equal(busPath, "unix:path=/run/user/1000/bus");
});
it.effect("logs command failures with safe probe context and the exact cause", () => {
const env: NodeJS.ProcessEnv = {
SHELL: "/bin/bash",
PATH: "/usr/bin",
};
const cause = PlatformError.systemError({
_tag: "PermissionDenied",
module: "ChildProcess",
method: "spawn",
pathOrDescriptor: "/bin/bash",
});
const messages: Array<unknown> = [];
const logger = Logger.make(({ message }) => {
messages.push(message);
});
return runShellEnvironment({
env,
platform: "linux",
handler: () => "",
failure: cause,
}).pipe(
Effect.andThen(
Effect.sync(() => {
const errors = messages
.flatMap((message) => (Array.isArray(message) ? message : [message]))
.filter(isDesktopShellEnvironmentCommandError);
assert.lengthOf(errors, 1);
assert.equal(errors[0]?.probe, "login-shell");
assert.equal(errors[0]?.executable, "bash");
assert.equal(errors[0]?.argumentCount, 2);
assert.notProperty(errors[0] ?? {}, "args");
assert.equal(errors[0]?.cause, cause);
assert.notInclude(errors[0]?.message ?? "", cause.message);
}),
),
Effect.provide(Logger.layer([logger], { mergeWithExisting: false })),
);
});
});