import { describe, it, expect } from "@effect/vitest";import { Effect } from "effect";import { ProviderItemId, ProviderKey, createExecutor } from "@executor-js/sdk";import { makeTestConfig } from "@executor-js/sdk/testing";import { keychainPlugin } from "./index";import { makeKeychainProvider } from "./provider";import { setPassword, deletePassword } from "./keyring";// removed: v1 tests routed through `executor.secrets.set/get/remove` with// `ScopeId`/`SecretId`/`SetSecretInput`/`RemoveSecretInput` and a scope-derived// keychain service name. v2 deletes the secrets table and scope partitioning —// a connection IS the credential and the provider sees only an opaque// ProviderItemId. The keychain-backed round-trip is now exercised directly// against the CredentialProvider contract below; creating a connection through// the executor requires a contributing integration plugin, which is out of// scope for keychain's own unit tests.const KEYCHAIN = ProviderKey.make("keychain");// Detect whether the real system keychain is reachable (writes a sentinel and// removes it). Mirrors the plugin's own registration probe.const probeReachable = (serviceName: string): Effect.Effect<boolean> => Effect.gen(function* () { const account = `__executor_keychain_test_probe__:${process.pid}:${Date.now()}`; return yield* setPassword(serviceName, account, "probe").pipe( Effect.andThen(deletePassword(serviceName, account).pipe(Effect.catch(() => Effect.void))), Effect.as(true), Effect.catch(() => Effect.succeed(false)), ); });describe("keychain plugin", () => { it.effect("exposes keychain metadata and registers a provider when reachable", () => Effect.gen(function* () { const executor = yield* createExecutor( makeTestConfig({ plugins: [keychainPlugin()] as const, }), ); expect(executor.keychain.displayName).toBeTypeOf("string"); expect(executor.keychain.isSupported).toBeTypeOf("boolean"); const providers = yield* executor.providers.list(); expect(providers.filter((provider) => provider === KEYCHAIN).length).toBeLessThanOrEqual(1); }), ); // The tests below exercise the real system keychain. // They no-op when the platform package loads but no keychain service is reachable. it.effect.skipIf(!!process.env.CI)( "stores, checks, resolves, and deletes a value via system keychain", () => Effect.gen(function* () { const serviceName = "executor-test"; if (!(yield* probeReachable(serviceName))) { return; } const executor = yield* createExecutor( makeTestConfig({ plugins: [keychainPlugin({ serviceName })] as const, }), ); const provider = makeKeychainProvider(serviceName); const id = ProviderItemId.make(`test-keychain-${Date.now()}`); yield* Effect.gen(function* () { // Write through the provider (the v2 writable contract). yield* provider.set!(id, "keychain-test-value"); // Plugin extension can check existence by opaque id. const exists = yield* executor.keychain.has(id); expect(exists).toBe(true); // Provider resolves the value back. const resolved = yield* provider.get(id); expect(resolved).toBe("keychain-test-value"); }).pipe(Effect.ensuring(provider.delete!(id).pipe(Effect.orElseSucceed(() => undefined)))); }), ); it.effect.skipIf(!!process.env.CI)("has returns false for missing value", () => Effect.gen(function* () { const executor = yield* createExecutor( makeTestConfig({ plugins: [keychainPlugin({ serviceName: "executor-test" })] as const, }), ); if (!(yield* probeReachable("executor-test"))) { return; } const exists = yield* executor.keychain.has("nonexistent-secret"); expect(exists).toBe(false); }), );});
import { describe, it, expect } from "@effect/vitest";
import { Effect } from "effect";
import { ProviderItemId, ProviderKey, createExecutor } from "@executor-js/sdk";
import { makeTestConfig } from "@executor-js/sdk/testing";
import { keychainPlugin } from "./index";
import { makeKeychainProvider } from "./provider";
import { setPassword, deletePassword } from "./keyring";
// removed: v1 tests routed through `executor.secrets.set/get/remove` with
// `ScopeId`/`SecretId`/`SetSecretInput`/`RemoveSecretInput` and a scope-derived
// keychain service name. v2 deletes the secrets table and scope partitioning —
// a connection IS the credential and the provider sees only an opaque
// ProviderItemId. The keychain-backed round-trip is now exercised directly
// against the CredentialProvider contract below; creating a connection through
// the executor requires a contributing integration plugin, which is out of
// scope for keychain's own unit tests.
const KEYCHAIN = ProviderKey.make("keychain");
// Detect whether the real system keychain is reachable (writes a sentinel and
// removes it). Mirrors the plugin's own registration probe.
const probeReachable = (serviceName: string): Effect.Effect<boolean> =>
Effect.gen(function* () {
const account = `__executor_keychain_test_probe__:${process.pid}:${Date.now()}`;
return yield* setPassword(serviceName, account, "probe").pipe(
Effect.andThen(deletePassword(serviceName, account).pipe(Effect.catch(() => Effect.void))),
Effect.as(true),
Effect.catch(() => Effect.succeed(false)),
);
});
describe("keychain plugin", () => {
it.effect("exposes keychain metadata and registers a provider when reachable", () =>
Effect.gen(function* () {
const executor = yield* createExecutor(
makeTestConfig({
plugins: [keychainPlugin()] as const,
}),
);
expect(executor.keychain.displayName).toBeTypeOf("string");
expect(executor.keychain.isSupported).toBeTypeOf("boolean");
const providers = yield* executor.providers.list();
expect(providers.filter((provider) => provider === KEYCHAIN).length).toBeLessThanOrEqual(1);
}),
);
// The tests below exercise the real system keychain.
// They no-op when the platform package loads but no keychain service is reachable.
it.effect.skipIf(!!process.env.CI)(
"stores, checks, resolves, and deletes a value via system keychain",
() =>
Effect.gen(function* () {
const serviceName = "executor-test";
if (!(yield* probeReachable(serviceName))) {
return;
}
const executor = yield* createExecutor(
makeTestConfig({
plugins: [keychainPlugin({ serviceName })] as const,
}),
);
const provider = makeKeychainProvider(serviceName);
const id = ProviderItemId.make(`test-keychain-${Date.now()}`);
yield* Effect.gen(function* () {
// Write through the provider (the v2 writable contract).
yield* provider.set!(id, "keychain-test-value");
// Plugin extension can check existence by opaque id.
const exists = yield* executor.keychain.has(id);
expect(exists).toBe(true);
// Provider resolves the value back.
const resolved = yield* provider.get(id);
expect(resolved).toBe("keychain-test-value");
}).pipe(Effect.ensuring(provider.delete!(id).pipe(Effect.orElseSucceed(() => undefined))));
}),
);
it.effect.skipIf(!!process.env.CI)("has returns false for missing value", () =>
Effect.gen(function* () {
const executor = yield* createExecutor(
makeTestConfig({
plugins: [keychainPlugin({ serviceName: "executor-test" })] as const,
}),
);
if (!(yield* probeReachable("executor-test"))) {
return;
}
const exists = yield* executor.keychain.has("nonexistent-secret");
expect(exists).toBe(false);
}),
);
});