Replaces legacy adapter ids (codex_local / claude_local / gemini_local / claude_compatible) across ~42 test files with pi-mono equivalents: - codex_local → openai-codex - claude_local → claude-pro - gemini_local → gemini-cli - claude_compatible → anthropic Hand-rewrites where fixture semantics changed: - connector-matching: TASK_TYPE_PREFERENCE ordering rebased (claude-pro first for code/content/management; google first for research) - connector-model-policy: getModels(provider) from pi-ai instead of the deleted adapter.module.models array - session-management + edge-case-session: adapter exemption removed; rotation now uniform under pi-mono - ipc-core-admin-handlers: CSV compat model replaced with pi-ai anthropic - connectors.spec: lookup by openai-codex id (catalog now starts with claude-pro) - onboarding-model-pool: listProviderModels(provider) helper; label text changed to "ChatGPT Plus/Pro (Codex)" - database integration: added vault insecure-fallback env + electron mock - command-paths: uses new @main/connector-helpers normalizeCommandValue - pi-mono-migrations: added `as string` casts for 3 negative-existence assertions verifying legacy ids were removed Drops: - pnpm-workspace.yaml packages: [] (was "packages/*"; directory empty) - obsolete CONNECTOR_STRENGTHS import; .module.models probing Verification: - pnpm typecheck → 0 errors - pnpm lint → 0 errors / 0 warnings - pnpm build:verify → 0 errors / 0 warnings - pnpm test:unit → 508/508 pass - pnpm test:integration → 96/96 pass - All 6 adapter hard gates pass (NOT FOUND / 0 matches / empty) - pi-ai / pi-agent-core / pi-coding-agent all resolved @0.67.6
147 lines
5.0 KiB
TypeScript
147 lines
5.0 KiB
TypeScript
import { mkdtemp, rm } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
import { AppDatabase } from "../../src/main/database";
|
|
import { CredentialStore } from "../../src/main/credentials/credential-store";
|
|
import { CredentialResolver, CredentialError } from "../../src/main/credentials/credential-resolver";
|
|
|
|
beforeAll(() => {
|
|
process.env.AGENTCOMPANY_ALLOW_INSECURE_VAULT = "true";
|
|
});
|
|
|
|
describe("CredentialResolver", () => {
|
|
let tempDir: string;
|
|
let db: AppDatabase;
|
|
let store: CredentialStore;
|
|
let resolver: CredentialResolver;
|
|
|
|
beforeEach(async () => {
|
|
tempDir = await mkdtemp(join(tmpdir(), "agentcompany-credential-resolver-"));
|
|
db = new AppDatabase(tempDir);
|
|
db.init();
|
|
store = new CredentialStore({ db });
|
|
resolver = new CredentialResolver(store);
|
|
});
|
|
|
|
afterEach(async () => {
|
|
db.close();
|
|
await rm(tempDir, { recursive: true, force: true });
|
|
});
|
|
|
|
it("returns the literal apiKey for an api_key connector", async () => {
|
|
store.save("anthropic", {
|
|
kind: "api_key",
|
|
value: { source: "literal", value: "sk-ant-test-1234567890abcd" },
|
|
});
|
|
const resolved = await resolver.resolve("anthropic");
|
|
expect(resolved.apiKey).toBe("sk-ant-test-1234567890abcd");
|
|
expect(resolved.extraEnv).toBeUndefined();
|
|
});
|
|
|
|
it("resolves an api_key connector backed by an env variable", async () => {
|
|
process.env.MY_TEST_OPENAI_KEY = "sk-from-env-1234";
|
|
store.save("openai", {
|
|
kind: "api_key",
|
|
value: { source: "env", value: "MY_TEST_OPENAI_KEY" },
|
|
});
|
|
try {
|
|
const resolved = await resolver.resolve("openai");
|
|
expect(resolved.apiKey).toBe("sk-from-env-1234");
|
|
} finally {
|
|
delete process.env.MY_TEST_OPENAI_KEY;
|
|
}
|
|
});
|
|
|
|
it("throws CREDENTIAL_MISSING when the env-backed key is unset", async () => {
|
|
delete process.env.UNSET_TEST_KEY;
|
|
store.save("xai", { kind: "api_key", value: { source: "env", value: "UNSET_TEST_KEY" } });
|
|
await expect(resolver.resolve("xai")).rejects.toBeInstanceOf(CredentialError);
|
|
});
|
|
|
|
it("assembles AWS extraEnv for IAM auth mode", async () => {
|
|
store.save("amazon-bedrock", {
|
|
kind: "aws",
|
|
value: {
|
|
authMode: "iam",
|
|
region: "us-west-2",
|
|
accessKeyId: "AKIA-TEST-FAKE-1234",
|
|
secretAccessKey: "secret-fake-secret",
|
|
sessionToken: "session-fake-token",
|
|
},
|
|
});
|
|
const resolved = await resolver.resolve("amazon-bedrock");
|
|
expect(resolved.apiKey).toBe("");
|
|
expect(resolved.extraEnv).toEqual({
|
|
AWS_REGION: "us-west-2",
|
|
AWS_ACCESS_KEY_ID: "AKIA-TEST-FAKE-1234",
|
|
AWS_SECRET_ACCESS_KEY: "secret-fake-secret",
|
|
AWS_SESSION_TOKEN: "session-fake-token",
|
|
});
|
|
});
|
|
|
|
it("assembles AWS extraEnv for profile auth mode plus advanced flags", async () => {
|
|
store.save("amazon-bedrock", {
|
|
kind: "aws",
|
|
value: {
|
|
authMode: "profile",
|
|
region: "eu-west-1",
|
|
awsProfile: "production",
|
|
forceCache: true,
|
|
endpointUrl: "https://bedrock-runtime.example.com",
|
|
skipAuth: true,
|
|
forceHttp1: true,
|
|
},
|
|
});
|
|
const resolved = await resolver.resolve("amazon-bedrock");
|
|
expect(resolved.extraEnv).toEqual({
|
|
AWS_REGION: "eu-west-1",
|
|
AWS_PROFILE: "production",
|
|
AWS_BEDROCK_FORCE_CACHE: "1",
|
|
AWS_ENDPOINT_URL_BEDROCK_RUNTIME: "https://bedrock-runtime.example.com",
|
|
AWS_BEDROCK_SKIP_AUTH: "1",
|
|
AWS_BEDROCK_FORCE_HTTP1: "1",
|
|
});
|
|
});
|
|
|
|
it("assembles Azure extraEnv with deploymentMap", async () => {
|
|
store.save("azure-openai", {
|
|
kind: "azure",
|
|
value: {
|
|
apiKey: "azure-secret-key",
|
|
baseUrl: "https://my-resource.openai.azure.com/",
|
|
resourceName: "my-resource",
|
|
apiVersion: "2024-08-01-preview",
|
|
deploymentMap: { "gpt-4o": "gpt4-deploy", "gpt-4o-mini": "gpt4mini-deploy" },
|
|
},
|
|
});
|
|
const resolved = await resolver.resolve("azure-openai");
|
|
expect(resolved.apiKey).toBe("azure-secret-key");
|
|
expect(resolved.extraEnv).toMatchObject({
|
|
AZURE_OPENAI_API_KEY: "azure-secret-key",
|
|
AZURE_OPENAI_BASE_URL: "https://my-resource.openai.azure.com/",
|
|
AZURE_OPENAI_RESOURCE_NAME: "my-resource",
|
|
AZURE_OPENAI_API_VERSION: "2024-08-01-preview",
|
|
});
|
|
expect(resolved.extraEnv?.AZURE_OPENAI_DEPLOYMENT_NAME_MAP).toContain("gpt-4o=gpt4-deploy");
|
|
});
|
|
|
|
it("assembles Vertex extraEnv from project + location + credentials path", async () => {
|
|
store.save("google-vertex", {
|
|
kind: "gcp_adc",
|
|
value: {
|
|
project: "my-gcp-project",
|
|
location: "us-central1",
|
|
credentialsPath: "/etc/gcp/service-account.json",
|
|
},
|
|
});
|
|
const resolved = await resolver.resolve("google-vertex");
|
|
expect(resolved.apiKey).toBe("");
|
|
expect(resolved.extraEnv).toEqual({
|
|
GOOGLE_CLOUD_PROJECT: "my-gcp-project",
|
|
GOOGLE_CLOUD_LOCATION: "us-central1",
|
|
GOOGLE_APPLICATION_CREDENTIALS: "/etc/gcp/service-account.json",
|
|
});
|
|
});
|
|
});
|