220 lines
8.5 KiB
TypeScript
220 lines
8.5 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
apiKeyValueSchema,
|
|
awsCredentialSchema,
|
|
azureCredentialSchema,
|
|
credentialPayloadSchema,
|
|
gcpAdcCredentialSchema,
|
|
isAwsBedrockEndpointUrl,
|
|
isLocalConnectorBaseUrl,
|
|
localCredentialSchema,
|
|
removeCredentialInputSchema,
|
|
saveCredentialInputSchema,
|
|
testCredentialInputSchema,
|
|
} from "../../src/shared/contracts/schemas-credentials";
|
|
import {
|
|
oauthCancelInputSchema,
|
|
oauthManualCodeResponseSchema,
|
|
oauthPromptResponseSchema,
|
|
oauthStartInputSchema,
|
|
} from "../../src/shared/contracts/schemas-oauth";
|
|
|
|
/** Verifies the IPC payload schemas reject malformed input and accept valid
|
|
* shapes. These are the only contract between renderer ↔ main for the new
|
|
* credential / OAuth channels — incorrect validation would let bad data
|
|
* reach pi-mono primitives. */
|
|
|
|
describe("credentials Zod schemas", () => {
|
|
it("apiKeyValueSchema rejects empty value", () => {
|
|
const result = apiKeyValueSchema.safeParse({ source: "literal", value: "" });
|
|
expect(result.success).toBe(false);
|
|
});
|
|
|
|
it("apiKeyValueSchema accepts both source kinds", () => {
|
|
expect(apiKeyValueSchema.safeParse({ source: "literal", value: "sk-x" }).success).toBe(true);
|
|
expect(apiKeyValueSchema.safeParse({ source: "env", value: "MY_KEY" }).success).toBe(true);
|
|
});
|
|
|
|
it("apiKeyValueSchema rejects unknown source", () => {
|
|
const result = apiKeyValueSchema.safeParse({ source: "shell", value: "$(echo x)" });
|
|
expect(result.success).toBe(false);
|
|
});
|
|
|
|
it("awsCredentialSchema requires authMode", () => {
|
|
expect(awsCredentialSchema.safeParse({ region: "us-east-1" }).success).toBe(false);
|
|
expect(awsCredentialSchema.safeParse({ authMode: "profile", region: "us-east-1", awsProfile: "default" }).success).toBe(true);
|
|
});
|
|
|
|
it("awsCredentialSchema accepts all three auth modes", () => {
|
|
for (const authMode of ["profile", "iam", "bearer"] as const) {
|
|
const result = awsCredentialSchema.safeParse({ authMode, region: "us-east-1" });
|
|
expect(result.success).toBe(true);
|
|
}
|
|
});
|
|
|
|
it("awsCredentialSchema accepts AWS Bedrock and private HTTPS endpointUrl values", () => {
|
|
for (const endpointUrl of [
|
|
"https://bedrock-runtime.us-east-1.amazonaws.com",
|
|
"https://bedrock-runtime.us-east-1.amazonaws.com/",
|
|
"https://bedrock-runtime-fips.us-gov-west-1.amazonaws.com",
|
|
"https://bedrock-runtime.cn-north-1.amazonaws.com.cn",
|
|
"https://vpce-123abc.bedrock-runtime.us-east-1.vpce.amazonaws.com",
|
|
"https://vpce-123abc-us-east-1a.bedrock-runtime.us-east-1.vpce.amazonaws.com",
|
|
"https://10.0.0.20",
|
|
"https://192.168.1.20",
|
|
"https://[fd12::1]",
|
|
]) {
|
|
const result = awsCredentialSchema.safeParse({ authMode: "profile", region: "us-east-1", awsProfile: "default", endpointUrl });
|
|
expect(result.success).toBe(true);
|
|
expect(isAwsBedrockEndpointUrl(endpointUrl)).toBe(true);
|
|
}
|
|
});
|
|
|
|
it("awsCredentialSchema rejects unsafe endpointUrl values", () => {
|
|
for (const endpointUrl of [
|
|
"http://bedrock-runtime.us-east-1.amazonaws.com",
|
|
"https://api.openai.com/v1",
|
|
"https://bedrock-runtime.example.com",
|
|
"https://user:pass@bedrock-runtime.us-east-1.amazonaws.com",
|
|
"https://bedrock-runtime.us-east-1.amazonaws.com/model/test",
|
|
"https://bedrock-runtime.us-east-1.amazonaws.com?proxy=1",
|
|
"ftp://bedrock-runtime.us-east-1.amazonaws.com",
|
|
"https://bedrock-runtime.us-east-1.amazonaws.com?",
|
|
"https://bedrock-runtime.us-east-1.amazonaws.com#",
|
|
"https://bedrock-runtime.us-east-1.amazonaws.com/%2e%2e",
|
|
"https://bedrock-runtime.us-east-1.amazonaws.com/a/..",
|
|
"https://bedrock-runtime.us-east-1.vpce.amazonaws.com",
|
|
"https://foo.bar.bedrock-runtime.us-east-1.vpce.amazonaws.com",
|
|
]) {
|
|
const result = awsCredentialSchema.safeParse({ authMode: "profile", region: "us-east-1", awsProfile: "default", endpointUrl });
|
|
expect(result.success).toBe(false);
|
|
expect(isAwsBedrockEndpointUrl(endpointUrl)).toBe(false);
|
|
}
|
|
});
|
|
|
|
it("azureCredentialSchema requires apiKey", () => {
|
|
expect(azureCredentialSchema.safeParse({}).success).toBe(false);
|
|
expect(azureCredentialSchema.safeParse({ apiKey: "azure-secret" }).success).toBe(true);
|
|
});
|
|
|
|
it("gcpAdcCredentialSchema requires project + location", () => {
|
|
expect(gcpAdcCredentialSchema.safeParse({ project: "" }).success).toBe(false);
|
|
expect(gcpAdcCredentialSchema.safeParse({ project: "p", location: "us-central1" }).success).toBe(true);
|
|
});
|
|
|
|
it("localCredentialSchema accepts loopback and private-network baseUrl values", () => {
|
|
for (const baseUrl of [
|
|
"http://localhost:11434/v1",
|
|
"http://127.0.0.1:11434/v1",
|
|
"http://10.0.0.20:11434/v1",
|
|
"https://172.16.0.10:11434/v1",
|
|
"http://172.31.255.255:11434/v1",
|
|
"http://192.168.1.20:11434/v1",
|
|
"http://169.254.1.20:11434/v1",
|
|
"http://[::1]:11434/v1",
|
|
"http://[fc00::1]:11434/v1",
|
|
"http://[fd12::1]:11434/v1",
|
|
"http://[fe80::1]:11434/v1",
|
|
]) {
|
|
expect(localCredentialSchema.safeParse({ baseUrl }).success).toBe(true);
|
|
expect(isLocalConnectorBaseUrl(baseUrl)).toBe(true);
|
|
}
|
|
});
|
|
|
|
it("localCredentialSchema rejects remote OpenAI-compatible endpoints", () => {
|
|
for (const baseUrl of [
|
|
"https://api.openai.com/v1",
|
|
"https://example.com/v1",
|
|
"http://172.32.0.1:11434/v1",
|
|
"http://100.64.0.1:11434/v1",
|
|
"ftp://localhost:11434/v1",
|
|
"http://user:pass@127.0.0.1:11434/v1",
|
|
]) {
|
|
expect(localCredentialSchema.safeParse({ baseUrl }).success).toBe(false);
|
|
expect(isLocalConnectorBaseUrl(baseUrl)).toBe(false);
|
|
}
|
|
});
|
|
|
|
it("credentialPayloadSchema discriminates on kind", () => {
|
|
expect(
|
|
credentialPayloadSchema.safeParse({
|
|
kind: "api_key",
|
|
value: { source: "literal", value: "sk-x" },
|
|
}).success,
|
|
).toBe(true);
|
|
expect(
|
|
credentialPayloadSchema.safeParse({
|
|
kind: "azure",
|
|
value: { source: "literal", value: "wrong-shape" },
|
|
}).success,
|
|
).toBe(false);
|
|
});
|
|
|
|
it("saveCredentialInputSchema requires connectorId + payload", () => {
|
|
expect(
|
|
saveCredentialInputSchema.safeParse({
|
|
connectorId: "anthropic",
|
|
payload: { kind: "api_key", value: { source: "literal", value: "sk-x" } },
|
|
}).success,
|
|
).toBe(true);
|
|
expect(saveCredentialInputSchema.safeParse({ connectorId: "" }).success).toBe(false);
|
|
expect(saveCredentialInputSchema.safeParse({ payload: {} }).success).toBe(false);
|
|
});
|
|
|
|
it("removeCredentialInputSchema rejects empty connectorId", () => {
|
|
expect(removeCredentialInputSchema.safeParse({ connectorId: "openai" }).success).toBe(true);
|
|
expect(removeCredentialInputSchema.safeParse({ connectorId: "" }).success).toBe(false);
|
|
expect(removeCredentialInputSchema.safeParse({}).success).toBe(false);
|
|
});
|
|
|
|
it("testCredentialInputSchema rejects empty connectorId", () => {
|
|
expect(testCredentialInputSchema.safeParse({ connectorId: "openai" }).success).toBe(true);
|
|
expect(testCredentialInputSchema.safeParse({ connectorId: "" }).success).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("oauth Zod schemas", () => {
|
|
it("oauthStartInputSchema rejects empty connectorId", () => {
|
|
expect(oauthStartInputSchema.safeParse({ connectorId: "claude-pro" }).success).toBe(true);
|
|
expect(oauthStartInputSchema.safeParse({ connectorId: "" }).success).toBe(false);
|
|
});
|
|
|
|
it("oauthCancelInputSchema rejects missing connectorId", () => {
|
|
expect(oauthCancelInputSchema.safeParse({ connectorId: "claude-pro" }).success).toBe(true);
|
|
expect(oauthCancelInputSchema.safeParse({}).success).toBe(false);
|
|
});
|
|
|
|
it("oauthPromptResponseSchema requires connectorId, requestId and a (possibly empty) value", () => {
|
|
expect(
|
|
oauthPromptResponseSchema.safeParse({
|
|
connectorId: "github-copilot",
|
|
requestId: "req-1",
|
|
value: "github.com",
|
|
}).success,
|
|
).toBe(true);
|
|
expect(
|
|
oauthPromptResponseSchema.safeParse({ connectorId: "github-copilot", requestId: "" }).success,
|
|
).toBe(false);
|
|
expect(
|
|
oauthPromptResponseSchema.safeParse({ connectorId: "github-copilot", value: "x" }).success,
|
|
).toBe(false);
|
|
});
|
|
|
|
it("oauthManualCodeResponseSchema requires non-empty code", () => {
|
|
expect(
|
|
oauthManualCodeResponseSchema.safeParse({
|
|
connectorId: "anthropic",
|
|
requestId: "req-1",
|
|
code: "abcd",
|
|
}).success,
|
|
).toBe(true);
|
|
expect(
|
|
oauthManualCodeResponseSchema.safeParse({
|
|
connectorId: "anthropic",
|
|
requestId: "req-1",
|
|
code: "",
|
|
}).success,
|
|
).toBe(false);
|
|
});
|
|
});
|