import { afterEach, beforeEach, describe, expect, it, vi } from "@effect/vitest"; import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync, } from "node:fs"; import { tmpdir } from "node:os"; import { dirname, join } from "node:path"; import { Effect, Predicate, Result } from "effect"; import { ProviderKey } from "@executor-js/sdk"; import { makeTestWorkspaceHarness } from "@executor-js/sdk/testing"; import { fileSecretsPlugin } from "./index"; const FILE_PROVIDER = ProviderKey.make("file"); const inspectPlugin = (plugin: ReturnType) => Effect.scoped( Effect.gen(function* () { const workspace = yield* makeTestWorkspaceHarness({ plugins: [plugin] as const }); const items = yield* workspace.executor.providers.items(FILE_PROVIDER); return { filePath: workspace.executor.fileSecrets.filePath, itemIds: items.map((item) => String(item.id)), }; }), ); const writeAuthFile = (filePath: string, contents: string, mode = 0o600): void => { mkdirSync(dirname(filePath), { recursive: true }); writeFileSync(filePath, contents, { mode }); }; describe("file secrets auth location", () => { let workDir: string; let dataDir: string; let otherDataDir: string; let xdgDataHome: string; let overrideDir: string; let legacyFilePath: string; beforeEach(() => { workDir = mkdtempSync(join(tmpdir(), "executor-file-secrets-location-")); dataDir = join(workDir, "data"); otherDataDir = join(workDir, "other-data"); xdgDataHome = join(workDir, "xdg"); overrideDir = join(workDir, "override"); legacyFilePath = join(xdgDataHome, "executor", "auth.json"); vi.stubEnv("XDG_DATA_HOME", xdgDataHome); }); afterEach(() => { vi.unstubAllEnvs(); rmSync(workDir, { recursive: true, force: true }); }); it.effect("uses auth.json directly under EXECUTOR_DATA_DIR resolved at construction", () => Effect.gen(function* () { vi.stubEnv("EXECUTOR_DATA_DIR", dataDir); const plugin = fileSecretsPlugin(); vi.stubEnv("EXECUTOR_DATA_DIR", otherDataDir); const inspected = yield* inspectPlugin(plugin); expect(inspected.filePath).toBe(join(dataDir, "auth.json")); }), ); it.effect("keeps the XDG location when EXECUTOR_DATA_DIR is unset", () => Effect.gen(function* () { vi.stubEnv("EXECUTOR_DATA_DIR", ""); const inspected = yield* inspectPlugin(fileSecretsPlugin()); expect(inspected.filePath).toBe(legacyFilePath); }), ); it.effect("gives an explicit directory precedence without migration", () => Effect.gen(function* () { vi.stubEnv("EXECUTOR_DATA_DIR", dataDir); writeAuthFile(legacyFilePath, '{"legacy":"legacy-secret"}'); const inspected = yield* inspectPlugin(fileSecretsPlugin({ directory: overrideDir })); expect(inspected.filePath).toBe(join(overrideDir, "auth.json")); expect(inspected.itemIds).toEqual([]); expect(existsSync(join(dataDir, "auth.json"))).toBe(false); expect(readFileSync(legacyFilePath, "utf8")).toBe('{"legacy":"legacy-secret"}'); }), ); it.effect("copies a valid legacy XDG file once and preserves 0600 permissions", () => Effect.gen(function* () { vi.stubEnv("EXECUTOR_DATA_DIR", dataDir); const legacyContents = '{"legacy-token":"legacy-secret"}'; writeAuthFile(legacyFilePath, legacyContents, 0o644); const inspected = yield* inspectPlugin(fileSecretsPlugin()); const migratedFilePath = join(dataDir, "auth.json"); expect(inspected.filePath).toBe(migratedFilePath); expect(inspected.itemIds).toEqual(["legacy-token"]); expect(readFileSync(migratedFilePath, "utf8")).toContain('"legacy-token": "legacy-secret"'); expect(statSync(migratedFilePath).mode & 0o777).toBe(0o600); expect(readFileSync(legacyFilePath, "utf8")).toBe(legacyContents); }), ); it.effect("uses an existing data-dir file without merging the legacy file", () => Effect.gen(function* () { vi.stubEnv("EXECUTOR_DATA_DIR", dataDir); const activeFilePath = join(dataDir, "auth.json"); const activeContents = '{"active-token":"active-secret"}'; const legacyContents = '{"legacy-token":"legacy-secret"}'; writeAuthFile(activeFilePath, activeContents); writeAuthFile(legacyFilePath, legacyContents); const inspected = yield* inspectPlugin(fileSecretsPlugin()); expect(inspected.itemIds).toEqual(["active-token"]); expect(readFileSync(activeFilePath, "utf8")).toBe(activeContents); expect(readFileSync(legacyFilePath, "utf8")).toBe(legacyContents); }), ); it.effect("leaves the new store empty when the legacy file is corrupt", () => Effect.scoped( Effect.gen(function* () { vi.stubEnv("EXECUTOR_DATA_DIR", dataDir); writeAuthFile(legacyFilePath, "not-json"); const workspace = yield* makeTestWorkspaceHarness({ plugins: [fileSecretsPlugin()] as const, }); const initial = yield* workspace.executor.providers.items(FILE_PROVIDER); expect(initial).toEqual([]); expect(existsSync(join(dataDir, "auth.json"))).toBe(false); expect(readFileSync(legacyFilePath, "utf8")).toBe("not-json"); writeAuthFile(legacyFilePath, '{"repaired-token":"repaired-secret"}'); const afterRepair = yield* workspace.executor.providers.items(FILE_PROVIDER); expect(afterRepair).toEqual([]); expect(existsSync(join(dataDir, "auth.json"))).toBe(false); }), ), ); it.effect("retries migration after a legacy read I/O failure", () => Effect.scoped( Effect.gen(function* () { vi.stubEnv("EXECUTOR_DATA_DIR", dataDir); mkdirSync(legacyFilePath, { recursive: true }); const workspace = yield* makeTestWorkspaceHarness({ plugins: [fileSecretsPlugin()] as const, }); const failed = yield* Effect.result(workspace.executor.providers.items(FILE_PROVIDER)); expect(Result.isFailure(failed)).toBe(true); if (!Result.isFailure(failed)) return; expect(Predicate.isTagged("StorageError")(failed.failure)).toBe(true); rmSync(legacyFilePath, { recursive: true, force: true }); writeAuthFile(legacyFilePath, '{"recovered-token":"recovered-secret"}'); const recovered = yield* workspace.executor.providers.items(FILE_PROVIDER); expect(recovered.map((item) => String(item.id))).toEqual(["recovered-token"]); expect(readFileSync(join(dataDir, "auth.json"), "utf8")).toContain( '"recovered-token": "recovered-secret"', ); }), ), ); it.effect("shares one migration across concurrent first provider operations", () => Effect.scoped( Effect.gen(function* () { vi.stubEnv("EXECUTOR_DATA_DIR", dataDir); const legacyContents = '{"legacy-token":"legacy-secret"}'; writeAuthFile(legacyFilePath, legacyContents); const workspace = yield* makeTestWorkspaceHarness({ plugins: [fileSecretsPlugin()] as const, }); const results = yield* Effect.all( [ workspace.executor.providers.items(FILE_PROVIDER), workspace.executor.providers.items(FILE_PROVIDER), ], { concurrency: "unbounded" }, ); expect(results.map((items) => items.map((item) => String(item.id)))).toEqual([ ["legacy-token"], ["legacy-token"], ]); expect(readdirSync(dataDir)).toEqual(["auth.json"]); expect(readFileSync(legacyFilePath, "utf8")).toBe(legacyContents); }), ), ); });