// @effect-diagnostics nodeBuiltinImport:off - Builds a Chromium-shaped cookie// table with the same native bindings the source reads.import * as NodePath from "node:path";import * as NodeServices from "@effect/platform-node/NodeServices";import { assert, describe, it } from "@effect/vitest";import { HostProcessEnvironment, HostProcessHostname, HostProcessPlatform,} from "@t3tools/shared/hostProcess";import * as Effect from "effect/Effect";import * as FileSystem from "effect/FileSystem";import * as Path from "effect/Path";import * as PlatformError from "effect/PlatformError";import * as Scope from "effect/Scope";import * as Stream from "effect/Stream";import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";import * as NodeSqlite from "node:sqlite"; import type { BrowserImportPathContext } from "./Sources.ts";import { BROWSER_IMPORT_SOURCES, chromiumProcessIsAlive, chromiumSingletonLockIsHeld, cookieDatabaseCandidatePaths, firefoxSymlinkLockIsHeld, resolveCookieDatabase, isSourceInstalled, isSourceRunning, isWindowsLockHeldError, posixLockIsHeld, listSourceProfiles, sourcePathContext, windowsChromiumCookiesAreHeld,} from "./Sources.ts";import { symlinksSupported } from "@t3tools/shared/testing/symlinks"; const helium = BROWSER_IMPORT_SOURCES.find((source) => source.id === "helium")!; describe("Linux Chromium secret applications", () => { it("pins the libsecret application attribute for each supported fork", () => { assert.deepEqual( Object.fromEntries( BROWSER_IMPORT_SOURCES.filter((source) => source.platforms.includes("linux")).map( (source) => [source.id, source.linuxSecretApplication], ), ), { chrome: "chrome", edge: "msedge", brave: "brave", vivaldi: "vivaldi", opera: "opera", helium: "chromium", firefox: undefined, }, ); });}); const platformError = (reasonTag: string): PlatformError.PlatformError => ({ _tag: "PlatformError", reason: { _tag: reasonTag } }) as never; describe("Windows browser lock errors", () => { it("treats sharing and lock violations reported as Busy as held", () => { assert.isTrue(isWindowsLockHeldError(platformError("Busy"))); }); it("does not treat access denied as proof of an active lock", () => { assert.isFalse(isWindowsLockHeldError(platformError("PermissionDenied"))); }); it("does not treat a missing lock file as held", () => { assert.isFalse(isWindowsLockHeldError(platformError("NotFound"))); });}); /** A scratch home with the source's user-data directory already created. */const withSourceHome = Effect.fnUntraced(function* () { const fileSystem = yield* FileSystem.FileSystem; const home = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3code-sources-" }); const context = yield* sourcePathContext.pipe( Effect.provideService(HostProcessEnvironment, { HOME: home }), Effect.provideService(HostProcessPlatform, "darwin"), ); yield* fileSystem.makeDirectory(userDataDirectory(context), { recursive: true }); return context;}); /** Every case here runs on darwin, where Helium always resolves a directory. */const userDataDirectory = (context: BrowserImportPathContext) => { const root = helium.userDataDirectory(context); if (root === undefined) throw new Error("Helium has no macOS user-data directory"); return root;}; const run = <A, E>( effect: Effect.Effect< A, E, FileSystem.FileSystem | Path.Path | Scope.Scope | ChildProcessSpawner.ChildProcessSpawner >,) => effect.pipe(Effect.provide(NodeServices.layer), Effect.scoped); /** Writes a Chromium-shaped cookie table with `count` rows. */const writeCookieDatabase = (file: string, count: number) => Effect.sync(() => { const database = new NodeSqlite.DatabaseSync(file); database.exec("create table cookies (host_key text, name text)"); const insert = database.prepare("insert into cookies (host_key, name) values (?, ?)"); for (let index = 0; index < count; index += 1) insert.run("example.test", `c${index}`); database.close(); }); const writeFirefoxCookieDatabase = ( file: string, defaultContainerCount: number, containerCount: number,) => Effect.sync(() => { const database = new NodeSqlite.DatabaseSync(file); database.exec("create table moz_cookies (originAttributes text not null)"); const insert = database.prepare("insert into moz_cookies (originAttributes) values (?)"); for (let index = 0; index < defaultContainerCount; index += 1) insert.run(""); for (let index = 0; index < containerCount; index += 1) insert.run("^userContextId=2"); database.close(); }); describe("Helium on Linux", () => { it.effect.skipIf(!symlinksSupported)("discovers its profiles and checks the user-data lock", () => run( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const home = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3code-helium-linux-" }); const context = yield* sourcePathContext.pipe( Effect.provideService(HostProcessEnvironment, { HOME: home }), Effect.provideService(HostProcessPlatform, "linux"), ); const root = `${home}/.config/net.imput.helium`; yield* fileSystem.makeDirectory(`${root}/Default`, { recursive: true }); yield* writeCookieDatabase(`${root}/Default/Cookies`, 3); yield* fileSystem.writeFileString( `${root}/Local State`, '{"profile":{"info_cache":{"Default":{"name":"Personal"}}}}', ); assert.include(helium.platforms, "linux"); assert.isTrue(yield* isSourceInstalled(helium, context)); assert.deepEqual(yield* listSourceProfiles(helium, context), [ { directory: "Default", name: "Personal", cookieCount: 3 }, ]); assert.isFalse(yield* isSourceRunning(helium, context)); yield* fileSystem.symlink("foreign-host-4242", `${root}/SingletonLock`); assert.isTrue(yield* isSourceRunning(helium, context)); }), ), );}); describe("Helium on Windows", () => { it.effect("uses Helium's local app-data profile while other Chromium forks stay disabled", () => run( Effect.gen(function* () { const context = yield* sourcePathContext.pipe( Effect.provideService(HostProcessEnvironment, { USERPROFILE: "C:\\Users\\browser-user", LOCALAPPDATA: "C:\\Users\\browser-user\\AppData\\Local", }), Effect.provideService(HostProcessPlatform, "win32"), ); assert.include(helium.platforms, "win32"); assert.equal( helium.userDataDirectory(context), context.path.join( "C:\\Users\\browser-user\\AppData\\Local", "imput", "Helium", "User Data", ), ); for (const source of BROWSER_IMPORT_SOURCES) { if (source.engine === "chromium" && source.id !== "helium") { assert.notInclude(source.platforms, "win32"); } } }), ), );}); describe("isSourceRunning", () => { it.effect("uses the held cookie database as Chromium's Windows running signal", () => run( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const home = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3code-helium-windows-lock-", }); const context = yield* sourcePathContext.pipe( Effect.provideService(HostProcessEnvironment, { HOME: home, LOCALAPPDATA: home, }), Effect.provideService(HostProcessPlatform, "win32"), ); const profile = context.path.join(helium.userDataDirectory(context)!, "Default"); const database = context.path.join(profile, "Network", "Cookies"); yield* fileSystem.makeDirectory(context.path.join(profile, "Network"), { recursive: true }); yield* writeCookieDatabase(database, 1); const probed: string[] = []; assert.isTrue( yield* windowsChromiumCookiesAreHeld(helium, context, (path) => Effect.sync(() => { probed.push(path); return true; }), ), ); assert.deepEqual(probed, [database]); }), ), ); it.effect.skipIf(!symlinksSupported)( "reads Chromium's dangling SingletonLock symlink as a running browser", () => run( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const context = yield* withSourceHome(); assert.isFalse(yield* isSourceRunning(helium, context)); // Chromium points the lock at `<host>-<pid>`, a target that never // exists on disk. A check that follows the link reports a running // browser as closed, letting an import read a live, mid-write database. yield* fileSystem.symlink( "host-that-does-not-exist-1234", `${userDataDirectory(context)}/SingletonLock`, ); assert.isTrue(yield* isSourceRunning(helium, context)); }), ), ); it.effect.skipIf(!symlinksSupported)( "uses the provided hostname to classify Chromium locks", () => run( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const paths = yield* withSourceHome(); yield* fileSystem.symlink( "lock-owner-99999999", `${helium.userDataDirectory(paths)}/SingletonLock`, ); assert.isTrue( yield* isSourceRunning(helium, paths).pipe( Effect.provideService(HostProcessHostname, "another-host"), ), ); assert.isFalse( yield* isSourceRunning(helium, paths).pipe( Effect.provideService(HostProcessHostname, "lock-owner"), ), ); }), ), );}); describe("chromiumSingletonLockIsHeld", () => { it.effect("ignores a positively dead PID on the current host", () => Effect.gen(function* () { const checked: number[] = []; const held = yield* chromiumSingletonLockIsHeld("current-host-4321", "current-host", (pid) => Effect.sync(() => { checked.push(pid); return false; }), ); assert.isFalse(held); assert.deepEqual(checked, [4321]); }), ); it.effect("keeps a live PID on the current host", () => chromiumSingletonLockIsHeld("current-host-4321", "current-host", () => Effect.succeed(true), ).pipe(Effect.tap((held) => Effect.sync(() => assert.isTrue(held)))), ); it.effect("keeps foreign-host and malformed targets without probing a PID", () => Effect.gen(function* () { let probes = 0; const probe = (_pid: number) => Effect.sync(() => { probes += 1; return false; }); assert.isTrue(yield* chromiumSingletonLockIsHeld("another-host-4321", "current-host", probe)); assert.isTrue( yield* chromiumSingletonLockIsHeld("current-host-no-pid", "current-host", probe), ); assert.isTrue(yield* chromiumSingletonLockIsHeld("current-host-0", "current-host", probe)); assert.strictEqual(probes, 0); }), );}); describe("chromiumProcessIsAlive", () => { it.effect("returns false only when signal 0 reports a missing process", () => Effect.gen(function* () { const missing = Object.assign(new Error("missing"), { code: "ESRCH" }); const denied = Object.assign(new Error("denied"), { code: "EPERM" }); assert.isFalse( yield* chromiumProcessIsAlive(4321, () => { throw missing; }), ); assert.isTrue( yield* chromiumProcessIsAlive(4321, () => { throw denied; }), ); assert.isTrue( yield* chromiumProcessIsAlive(4321, () => { throw undefined; }), ); assert.isTrue( yield* chromiumProcessIsAlive(4321, () => { throw "unknown failure"; }), ); assert.isTrue( yield* chromiumProcessIsAlive(4321, () => { throw null; }), ); assert.isTrue(yield* chromiumProcessIsAlive(4321, () => true)); }), );}); describe("isSourceInstalled", () => { it.effect("ignores a user-data directory that holds no cookie database", () => run( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const context = yield* withSourceHome(); const root = userDataDirectory(context); // Installers for native messaging hosts create an empty user-data // directory for every Chromium fork they know about, so treating the // directory as evidence lists browsers the user does not have. yield* fileSystem.makeDirectory(`${root}/NativeMessagingHosts`, { recursive: true }); assert.isFalse(yield* isSourceInstalled(helium, context)); yield* fileSystem.makeDirectory(`${root}/Default`, { recursive: true }); yield* fileSystem.writeFileString(`${root}/Default/Cookies`, "db"); assert.isTrue(yield* isSourceInstalled(helium, context)); // A real install whose cookies live outside `Default` still counts: // reporting it as absent hides the source from the menu entirely. yield* fileSystem.remove(`${root}/Default`, { recursive: true }); yield* fileSystem.makeDirectory(`${root}/Profile 1`, { recursive: true }); yield* fileSystem.writeFileString(`${root}/Profile 1/Cookies`, "db"); assert.isTrue(yield* isSourceInstalled(helium, context)); yield* fileSystem.remove(root, { recursive: true }); assert.isFalse(yield* isSourceInstalled(helium, context)); }), ), ); it.effect("detects a Chromium 127+ install with cookies under Network/", () => run( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const context = yield* withSourceHome(); const root = userDataDirectory(context); yield* fileSystem.makeDirectory(`${root}/Default/Network`, { recursive: true }); yield* fileSystem.writeFileString(`${root}/Default/Network/Cookies`, "db"); assert.isTrue(yield* isSourceInstalled(helium, context)); }), ), ); it.effect.skipIf(!symlinksSupported)( "follows cookie database symlinks when detecting profiles", () => run( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const context = yield* withSourceHome(); const root = userDataDirectory(context); yield* fileSystem.makeDirectory(`${root}/Default`, { recursive: true }); yield* fileSystem.symlink("missing-cookies", `${root}/Default/Cookies`); assert.deepEqual(yield* listSourceProfiles(helium, context), []); assert.isFalse(yield* isSourceInstalled(helium, context)); yield* fileSystem.writeFileString(`${root}/Default/missing-cookies`, "db"); assert.deepEqual(yield* listSourceProfiles(helium, context), [ { directory: "Default", name: "Default" }, ]); assert.isTrue(yield* isSourceInstalled(helium, context)); }), ), );}); describe("listSourceProfiles", () => { it.effect("ignores a profile whose Cookies entry is not a file", () => run( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const paths = yield* withSourceHome(); const root = helium.userDataDirectory(paths); // A directory named `Cookies` would list as importable and then fail // the SQLite open, so only a regular file counts as a database. yield* fileSystem.makeDirectory(`${root}/Broken/Cookies`, { recursive: true }); yield* fileSystem.makeDirectory(`${root}/Real`, { recursive: true }); yield* fileSystem.writeFileString(`${root}/Real/Cookies`, "db"); assert.deepEqual(yield* listSourceProfiles(helium, paths), [ { directory: "Real", name: "Real" }, ]); assert.isTrue(yield* isSourceInstalled(helium, paths)); }), ), ); it.effect("discovers profiles by their cookie database when Local State is absent", () => run( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const context = yield* withSourceHome(); const root = userDataDirectory(context); // Assuming `Default` would report a browser whose cookies live in // `Profile 1` as having nothing to import, and it is then hidden. yield* fileSystem.makeDirectory(`${root}/Profile 1`, { recursive: true }); yield* fileSystem.writeFileString(`${root}/Profile 1/Cookies`, "db"); yield* fileSystem.makeDirectory(`${root}/NativeMessagingHosts`, { recursive: true }); assert.deepEqual(yield* listSourceProfiles(helium, context), [ { directory: "Profile 1", name: "Profile 1" }, ]); }), ), ); it.effect("reads the profile names the browser shows", () => run( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const context = yield* withSourceHome(); yield* fileSystem.writeFileString( `${userDataDirectory(context)}/Local State`, `{"profile":{"info_cache":{"Default":{"name":"You"},"Profile 2":{"name":" "}}}}`, ); assert.deepEqual(yield* listSourceProfiles(helium, context), [ { directory: "Default", name: "You" }, // Blank display name falls back to the directory rather than // rendering an empty row. { directory: "Profile 2", name: "Profile 2" }, ]); }), ), ); it.effect("scans for profiles when Local State is malformed", () => run( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const context = yield* withSourceHome(); const root = userDataDirectory(context); yield* fileSystem.writeFileString(`${root}/Local State`, "{not-json"); yield* fileSystem.makeDirectory(`${root}/Default`, { recursive: true }); yield* fileSystem.writeFileString(`${root}/Default/Cookies`, "db"); assert.deepEqual(yield* listSourceProfiles(helium, context), [ { directory: "Default", name: "Default" }, ]); }), ), ); it.effect("reports nothing when no directory holds a cookie database", () => run( Effect.gen(function* () { const context = yield* withSourceHome(); assert.deepEqual(yield* listSourceProfiles(helium, context), []); }),Lines 1–500 of 1198. Load more
// @effect-diagnostics nodeBuiltinImport:off - Builds a Chromium-shaped cookie // table with the same native bindings the source reads. import * as NodePath from "node:path"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { assert, describe, it } from "@effect/vitest"; import { HostProcessEnvironment, HostProcessHostname, HostProcessPlatform, } from "@t3tools/shared/hostProcess"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Path from "effect/Path"; import * as PlatformError from "effect/PlatformError"; import * as Scope from "effect/Scope"; import * as Stream from "effect/Stream"; import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; import * as NodeSqlite from "node:sqlite"; import type { BrowserImportPathContext } from "./Sources.ts"; import { BROWSER_IMPORT_SOURCES, chromiumProcessIsAlive, chromiumSingletonLockIsHeld, cookieDatabaseCandidatePaths, firefoxSymlinkLockIsHeld, resolveCookieDatabase, isSourceInstalled, isSourceRunning, isWindowsLockHeldError, posixLockIsHeld, listSourceProfiles, sourcePathContext, windowsChromiumCookiesAreHeld, } from "./Sources.ts"; import { symlinksSupported } from "@t3tools/shared/testing/symlinks"; const helium = BROWSER_IMPORT_SOURCES.find((source) => source.id === "helium")!; describe("Linux Chromium secret applications", () => { it("pins the libsecret application attribute for each supported fork", () => { assert.deepEqual( Object.fromEntries( BROWSER_IMPORT_SOURCES.filter((source) => source.platforms.includes("linux")).map( (source) => [source.id, source.linuxSecretApplication], ), ), { chrome: "chrome", edge: "msedge", brave: "brave", vivaldi: "vivaldi", opera: "opera", helium: "chromium", firefox: undefined, }, ); }); }); const platformError = (reasonTag: string): PlatformError.PlatformError => ({ _tag: "PlatformError", reason: { _tag: reasonTag } }) as never; describe("Windows browser lock errors", () => { it("treats sharing and lock violations reported as Busy as held", () => { assert.isTrue(isWindowsLockHeldError(platformError("Busy"))); }); it("does not treat access denied as proof of an active lock", () => { assert.isFalse(isWindowsLockHeldError(platformError("PermissionDenied"))); }); it("does not treat a missing lock file as held", () => { assert.isFalse(isWindowsLockHeldError(platformError("NotFound"))); }); }); /** A scratch home with the source's user-data directory already created. */ const withSourceHome = Effect.fnUntraced(function* () { const fileSystem = yield* FileSystem.FileSystem; const home = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3code-sources-" }); const context = yield* sourcePathContext.pipe( Effect.provideService(HostProcessEnvironment, { HOME: home }), Effect.provideService(HostProcessPlatform, "darwin"), ); yield* fileSystem.makeDirectory(userDataDirectory(context), { recursive: true }); return context; }); /** Every case here runs on darwin, where Helium always resolves a directory. */ const userDataDirectory = (context: BrowserImportPathContext) => { const root = helium.userDataDirectory(context); if (root === undefined) throw new Error("Helium has no macOS user-data directory"); return root; }; const run = <A, E>( effect: Effect.Effect< A, E, FileSystem.FileSystem | Path.Path | Scope.Scope | ChildProcessSpawner.ChildProcessSpawner >, ) => effect.pipe(Effect.provide(NodeServices.layer), Effect.scoped); /** Writes a Chromium-shaped cookie table with `count` rows. */ const writeCookieDatabase = (file: string, count: number) => Effect.sync(() => { const database = new NodeSqlite.DatabaseSync(file); database.exec("create table cookies (host_key text, name text)"); const insert = database.prepare("insert into cookies (host_key, name) values (?, ?)"); for (let index = 0; index < count; index += 1) insert.run("example.test", `c${index}`); database.close(); }); const writeFirefoxCookieDatabase = ( file: string, defaultContainerCount: number, containerCount: number, ) => Effect.sync(() => { const database = new NodeSqlite.DatabaseSync(file); database.exec("create table moz_cookies (originAttributes text not null)"); const insert = database.prepare("insert into moz_cookies (originAttributes) values (?)"); for (let index = 0; index < defaultContainerCount; index += 1) insert.run(""); for (let index = 0; index < containerCount; index += 1) insert.run("^userContextId=2"); database.close(); }); describe("Helium on Linux", () => { it.effect.skipIf(!symlinksSupported)("discovers its profiles and checks the user-data lock", () => run( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const home = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3code-helium-linux-" }); const context = yield* sourcePathContext.pipe( Effect.provideService(HostProcessEnvironment, { HOME: home }), Effect.provideService(HostProcessPlatform, "linux"), ); const root = `${home}/.config/net.imput.helium`; yield* fileSystem.makeDirectory(`${root}/Default`, { recursive: true }); yield* writeCookieDatabase(`${root}/Default/Cookies`, 3); yield* fileSystem.writeFileString( `${root}/Local State`, '{"profile":{"info_cache":{"Default":{"name":"Personal"}}}}', ); assert.include(helium.platforms, "linux"); assert.isTrue(yield* isSourceInstalled(helium, context)); assert.deepEqual(yield* listSourceProfiles(helium, context), [ { directory: "Default", name: "Personal", cookieCount: 3 }, ]); assert.isFalse(yield* isSourceRunning(helium, context)); yield* fileSystem.symlink("foreign-host-4242", `${root}/SingletonLock`); assert.isTrue(yield* isSourceRunning(helium, context)); }), ), ); }); describe("Helium on Windows", () => { it.effect("uses Helium's local app-data profile while other Chromium forks stay disabled", () => run( Effect.gen(function* () { const context = yield* sourcePathContext.pipe( Effect.provideService(HostProcessEnvironment, { USERPROFILE: "C:\\Users\\browser-user", LOCALAPPDATA: "C:\\Users\\browser-user\\AppData\\Local", }), Effect.provideService(HostProcessPlatform, "win32"), ); assert.include(helium.platforms, "win32"); assert.equal( helium.userDataDirectory(context), context.path.join( "C:\\Users\\browser-user\\AppData\\Local", "imput", "Helium", "User Data", ), ); for (const source of BROWSER_IMPORT_SOURCES) { if (source.engine === "chromium" && source.id !== "helium") { assert.notInclude(source.platforms, "win32"); } } }), ), ); }); describe("isSourceRunning", () => { it.effect("uses the held cookie database as Chromium's Windows running signal", () => run( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const home = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3code-helium-windows-lock-", }); const context = yield* sourcePathContext.pipe( Effect.provideService(HostProcessEnvironment, { HOME: home, LOCALAPPDATA: home, }), Effect.provideService(HostProcessPlatform, "win32"), ); const profile = context.path.join(helium.userDataDirectory(context)!, "Default"); const database = context.path.join(profile, "Network", "Cookies"); yield* fileSystem.makeDirectory(context.path.join(profile, "Network"), { recursive: true }); yield* writeCookieDatabase(database, 1); const probed: string[] = []; assert.isTrue( yield* windowsChromiumCookiesAreHeld(helium, context, (path) => Effect.sync(() => { probed.push(path); return true; }), ), ); assert.deepEqual(probed, [database]); }), ), ); it.effect.skipIf(!symlinksSupported)( "reads Chromium's dangling SingletonLock symlink as a running browser", () => run( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const context = yield* withSourceHome(); assert.isFalse(yield* isSourceRunning(helium, context)); // Chromium points the lock at `<host>-<pid>`, a target that never // exists on disk. A check that follows the link reports a running // browser as closed, letting an import read a live, mid-write database. yield* fileSystem.symlink( "host-that-does-not-exist-1234", `${userDataDirectory(context)}/SingletonLock`, ); assert.isTrue(yield* isSourceRunning(helium, context)); }), ), ); it.effect.skipIf(!symlinksSupported)( "uses the provided hostname to classify Chromium locks", () => run( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const paths = yield* withSourceHome(); yield* fileSystem.symlink( "lock-owner-99999999", `${helium.userDataDirectory(paths)}/SingletonLock`, ); assert.isTrue( yield* isSourceRunning(helium, paths).pipe( Effect.provideService(HostProcessHostname, "another-host"), ), ); assert.isFalse( yield* isSourceRunning(helium, paths).pipe( Effect.provideService(HostProcessHostname, "lock-owner"), ), ); }), ), ); }); describe("chromiumSingletonLockIsHeld", () => { it.effect("ignores a positively dead PID on the current host", () => Effect.gen(function* () { const checked: number[] = []; const held = yield* chromiumSingletonLockIsHeld("current-host-4321", "current-host", (pid) => Effect.sync(() => { checked.push(pid); return false; }), ); assert.isFalse(held); assert.deepEqual(checked, [4321]); }), ); it.effect("keeps a live PID on the current host", () => chromiumSingletonLockIsHeld("current-host-4321", "current-host", () => Effect.succeed(true), ).pipe(Effect.tap((held) => Effect.sync(() => assert.isTrue(held)))), ); it.effect("keeps foreign-host and malformed targets without probing a PID", () => Effect.gen(function* () { let probes = 0; const probe = (_pid: number) => Effect.sync(() => { probes += 1; return false; }); assert.isTrue(yield* chromiumSingletonLockIsHeld("another-host-4321", "current-host", probe)); assert.isTrue( yield* chromiumSingletonLockIsHeld("current-host-no-pid", "current-host", probe), ); assert.isTrue(yield* chromiumSingletonLockIsHeld("current-host-0", "current-host", probe)); assert.strictEqual(probes, 0); }), ); }); describe("chromiumProcessIsAlive", () => { it.effect("returns false only when signal 0 reports a missing process", () => Effect.gen(function* () { const missing = Object.assign(new Error("missing"), { code: "ESRCH" }); const denied = Object.assign(new Error("denied"), { code: "EPERM" }); assert.isFalse( yield* chromiumProcessIsAlive(4321, () => { throw missing; }), ); assert.isTrue( yield* chromiumProcessIsAlive(4321, () => { throw denied; }), ); assert.isTrue( yield* chromiumProcessIsAlive(4321, () => { throw undefined; }), ); assert.isTrue( yield* chromiumProcessIsAlive(4321, () => { throw "unknown failure"; }), ); assert.isTrue( yield* chromiumProcessIsAlive(4321, () => { throw null; }), ); assert.isTrue(yield* chromiumProcessIsAlive(4321, () => true)); }), ); }); describe("isSourceInstalled", () => { it.effect("ignores a user-data directory that holds no cookie database", () => run( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const context = yield* withSourceHome(); const root = userDataDirectory(context); // Installers for native messaging hosts create an empty user-data // directory for every Chromium fork they know about, so treating the // directory as evidence lists browsers the user does not have. yield* fileSystem.makeDirectory(`${root}/NativeMessagingHosts`, { recursive: true }); assert.isFalse(yield* isSourceInstalled(helium, context)); yield* fileSystem.makeDirectory(`${root}/Default`, { recursive: true }); yield* fileSystem.writeFileString(`${root}/Default/Cookies`, "db"); assert.isTrue(yield* isSourceInstalled(helium, context)); // A real install whose cookies live outside `Default` still counts: // reporting it as absent hides the source from the menu entirely. yield* fileSystem.remove(`${root}/Default`, { recursive: true }); yield* fileSystem.makeDirectory(`${root}/Profile 1`, { recursive: true }); yield* fileSystem.writeFileString(`${root}/Profile 1/Cookies`, "db"); assert.isTrue(yield* isSourceInstalled(helium, context)); yield* fileSystem.remove(root, { recursive: true }); assert.isFalse(yield* isSourceInstalled(helium, context)); }), ), ); it.effect("detects a Chromium 127+ install with cookies under Network/", () => run( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const context = yield* withSourceHome(); const root = userDataDirectory(context); yield* fileSystem.makeDirectory(`${root}/Default/Network`, { recursive: true }); yield* fileSystem.writeFileString(`${root}/Default/Network/Cookies`, "db"); assert.isTrue(yield* isSourceInstalled(helium, context)); }), ), ); it.effect.skipIf(!symlinksSupported)( "follows cookie database symlinks when detecting profiles", () => run( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const context = yield* withSourceHome(); const root = userDataDirectory(context); yield* fileSystem.makeDirectory(`${root}/Default`, { recursive: true }); yield* fileSystem.symlink("missing-cookies", `${root}/Default/Cookies`); assert.deepEqual(yield* listSourceProfiles(helium, context), []); assert.isFalse(yield* isSourceInstalled(helium, context)); yield* fileSystem.writeFileString(`${root}/Default/missing-cookies`, "db"); assert.deepEqual(yield* listSourceProfiles(helium, context), [ { directory: "Default", name: "Default" }, ]); assert.isTrue(yield* isSourceInstalled(helium, context)); }), ), ); }); describe("listSourceProfiles", () => { it.effect("ignores a profile whose Cookies entry is not a file", () => run( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const paths = yield* withSourceHome(); const root = helium.userDataDirectory(paths); // A directory named `Cookies` would list as importable and then fail // the SQLite open, so only a regular file counts as a database. yield* fileSystem.makeDirectory(`${root}/Broken/Cookies`, { recursive: true }); yield* fileSystem.makeDirectory(`${root}/Real`, { recursive: true }); yield* fileSystem.writeFileString(`${root}/Real/Cookies`, "db"); assert.deepEqual(yield* listSourceProfiles(helium, paths), [ { directory: "Real", name: "Real" }, ]); assert.isTrue(yield* isSourceInstalled(helium, paths)); }), ), ); it.effect("discovers profiles by their cookie database when Local State is absent", () => run( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const context = yield* withSourceHome(); const root = userDataDirectory(context); // Assuming `Default` would report a browser whose cookies live in // `Profile 1` as having nothing to import, and it is then hidden. yield* fileSystem.makeDirectory(`${root}/Profile 1`, { recursive: true }); yield* fileSystem.writeFileString(`${root}/Profile 1/Cookies`, "db"); yield* fileSystem.makeDirectory(`${root}/NativeMessagingHosts`, { recursive: true }); assert.deepEqual(yield* listSourceProfiles(helium, context), [ { directory: "Profile 1", name: "Profile 1" }, ]); }), ), ); it.effect("reads the profile names the browser shows", () => run( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const context = yield* withSourceHome(); yield* fileSystem.writeFileString( `${userDataDirectory(context)}/Local State`, `{"profile":{"info_cache":{"Default":{"name":"You"},"Profile 2":{"name":" "}}}}`, ); assert.deepEqual(yield* listSourceProfiles(helium, context), [ { directory: "Default", name: "You" }, // Blank display name falls back to the directory rather than // rendering an empty row. { directory: "Profile 2", name: "Profile 2" }, ]); }), ), ); it.effect("scans for profiles when Local State is malformed", () => run( Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const context = yield* withSourceHome(); const root = userDataDirectory(context); yield* fileSystem.writeFileString(`${root}/Local State`, "{not-json"); yield* fileSystem.makeDirectory(`${root}/Default`, { recursive: true }); yield* fileSystem.writeFileString(`${root}/Default/Cookies`, "db"); assert.deepEqual(yield* listSourceProfiles(helium, context), [ { directory: "Default", name: "Default" }, ]); }), ), ); it.effect("reports nothing when no directory holds a cookie database", () => run( Effect.gen(function* () { const context = yield* withSourceHome(); assert.deepEqual(yield* listSourceProfiles(helium, context), []); }),