mirror of https://github.com/openclaw/openclaw.git
refactor: share auth overview and fetch test helpers
This commit is contained in:
parent
985be2a864
commit
94e748086c
|
|
@ -1,7 +1,28 @@
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { NON_ENV_SECRETREF_MARKER } from "../../agents/model-auth-markers.js";
|
import { NON_ENV_SECRETREF_MARKER } from "../../agents/model-auth-markers.js";
|
||||||
|
import { withEnv } from "../../test-utils/env.js";
|
||||||
import { resolveProviderAuthOverview } from "./list.auth-overview.js";
|
import { resolveProviderAuthOverview } from "./list.auth-overview.js";
|
||||||
|
|
||||||
|
function resolveOpenAiOverview(apiKey: string) {
|
||||||
|
return resolveProviderAuthOverview({
|
||||||
|
provider: "openai",
|
||||||
|
cfg: {
|
||||||
|
models: {
|
||||||
|
providers: {
|
||||||
|
openai: {
|
||||||
|
baseUrl: "https://api.openai.com/v1",
|
||||||
|
api: "openai-completions",
|
||||||
|
apiKey,
|
||||||
|
models: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as never,
|
||||||
|
store: { version: 1, profiles: {} } as never,
|
||||||
|
modelsPath: "/tmp/models.json",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
describe("resolveProviderAuthOverview", () => {
|
describe("resolveProviderAuthOverview", () => {
|
||||||
it("does not throw when token profile only has tokenRef", () => {
|
it("does not throw when token profile only has tokenRef", () => {
|
||||||
const overview = resolveProviderAuthOverview({
|
const overview = resolveProviderAuthOverview({
|
||||||
|
|
@ -24,23 +45,9 @@ describe("resolveProviderAuthOverview", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders marker-backed models.json auth as marker detail", () => {
|
it("renders marker-backed models.json auth as marker detail", () => {
|
||||||
const overview = resolveProviderAuthOverview({
|
const overview = withEnv({ OPENAI_API_KEY: undefined }, () =>
|
||||||
provider: "openai",
|
resolveOpenAiOverview(NON_ENV_SECRETREF_MARKER),
|
||||||
cfg: {
|
);
|
||||||
models: {
|
|
||||||
providers: {
|
|
||||||
openai: {
|
|
||||||
baseUrl: "https://api.openai.com/v1",
|
|
||||||
api: "openai-completions",
|
|
||||||
apiKey: NON_ENV_SECRETREF_MARKER,
|
|
||||||
models: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as never,
|
|
||||||
store: { version: 1, profiles: {} } as never,
|
|
||||||
modelsPath: "/tmp/models.json",
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(overview.effective.kind).toBe("missing");
|
expect(overview.effective.kind).toBe("missing");
|
||||||
expect(overview.effective.detail).toBe("missing");
|
expect(overview.effective.detail).toBe("missing");
|
||||||
|
|
@ -48,23 +55,9 @@ describe("resolveProviderAuthOverview", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("keeps env-var-shaped models.json values masked to avoid accidental plaintext exposure", () => {
|
it("keeps env-var-shaped models.json values masked to avoid accidental plaintext exposure", () => {
|
||||||
const overview = resolveProviderAuthOverview({
|
const overview = withEnv({ OPENAI_API_KEY: undefined }, () =>
|
||||||
provider: "openai",
|
resolveOpenAiOverview("OPENAI_API_KEY"),
|
||||||
cfg: {
|
);
|
||||||
models: {
|
|
||||||
providers: {
|
|
||||||
openai: {
|
|
||||||
baseUrl: "https://api.openai.com/v1",
|
|
||||||
api: "openai-completions",
|
|
||||||
apiKey: "OPENAI_API_KEY", // pragma: allowlist secret
|
|
||||||
models: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as never,
|
|
||||||
store: { version: 1, profiles: {} } as never,
|
|
||||||
modelsPath: "/tmp/models.json",
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(overview.effective.kind).toBe("missing");
|
expect(overview.effective.kind).toBe("missing");
|
||||||
expect(overview.effective.detail).toBe("missing");
|
expect(overview.effective.detail).toBe("missing");
|
||||||
|
|
@ -76,23 +69,7 @@ describe("resolveProviderAuthOverview", () => {
|
||||||
const prior = process.env.OPENAI_API_KEY;
|
const prior = process.env.OPENAI_API_KEY;
|
||||||
process.env.OPENAI_API_KEY = "sk-openai-from-env"; // pragma: allowlist secret
|
process.env.OPENAI_API_KEY = "sk-openai-from-env"; // pragma: allowlist secret
|
||||||
try {
|
try {
|
||||||
const overview = resolveProviderAuthOverview({
|
const overview = resolveOpenAiOverview("OPENAI_API_KEY");
|
||||||
provider: "openai",
|
|
||||||
cfg: {
|
|
||||||
models: {
|
|
||||||
providers: {
|
|
||||||
openai: {
|
|
||||||
baseUrl: "https://api.openai.com/v1",
|
|
||||||
api: "openai-completions",
|
|
||||||
apiKey: "OPENAI_API_KEY", // pragma: allowlist secret
|
|
||||||
models: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as never,
|
|
||||||
store: { version: 1, profiles: {} } as never,
|
|
||||||
modelsPath: "/tmp/models.json",
|
|
||||||
});
|
|
||||||
expect(overview.effective.kind).toBe("env");
|
expect(overview.effective.kind).toBe("env");
|
||||||
expect(overview.effective.detail).not.toContain("OPENAI_API_KEY");
|
expect(overview.effective.detail).not.toContain("OPENAI_API_KEY");
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,22 @@ import { botCtorSpy } from "./bot.create-telegram-bot.test-harness.js";
|
||||||
import { createTelegramBot } from "./bot.js";
|
import { createTelegramBot } from "./bot.js";
|
||||||
import { getTelegramNetworkErrorOrigin } from "./network-errors.js";
|
import { getTelegramNetworkErrorOrigin } from "./network-errors.js";
|
||||||
|
|
||||||
|
function createWrappedTelegramClientFetch(proxyFetch: typeof fetch) {
|
||||||
|
const shutdown = new AbortController();
|
||||||
|
botCtorSpy.mockClear();
|
||||||
|
createTelegramBot({
|
||||||
|
token: "tok",
|
||||||
|
fetchAbortSignal: shutdown.signal,
|
||||||
|
proxyFetch,
|
||||||
|
});
|
||||||
|
const clientFetch = (botCtorSpy.mock.calls.at(-1)?.[1] as { client?: { fetch?: unknown } })
|
||||||
|
?.client?.fetch as (input: RequestInfo | URL, init?: RequestInit) => Promise<unknown>;
|
||||||
|
expect(clientFetch).toBeTypeOf("function");
|
||||||
|
return { clientFetch, shutdown };
|
||||||
|
}
|
||||||
|
|
||||||
describe("createTelegramBot fetch abort", () => {
|
describe("createTelegramBot fetch abort", () => {
|
||||||
it("aborts wrapped client fetch when fetchAbortSignal aborts", async () => {
|
it("aborts wrapped client fetch when fetchAbortSignal aborts", async () => {
|
||||||
const shutdown = new AbortController();
|
|
||||||
const fetchSpy = vi.fn(
|
const fetchSpy = vi.fn(
|
||||||
(_input: RequestInfo | URL, init?: RequestInit) =>
|
(_input: RequestInfo | URL, init?: RequestInit) =>
|
||||||
new Promise<AbortSignal>((resolve) => {
|
new Promise<AbortSignal>((resolve) => {
|
||||||
|
|
@ -13,15 +26,9 @@ describe("createTelegramBot fetch abort", () => {
|
||||||
signal.addEventListener("abort", () => resolve(signal), { once: true });
|
signal.addEventListener("abort", () => resolve(signal), { once: true });
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
botCtorSpy.mockClear();
|
const { clientFetch, shutdown } = createWrappedTelegramClientFetch(
|
||||||
createTelegramBot({
|
fetchSpy as unknown as typeof fetch,
|
||||||
token: "tok",
|
);
|
||||||
fetchAbortSignal: shutdown.signal,
|
|
||||||
proxyFetch: fetchSpy as unknown as typeof fetch,
|
|
||||||
});
|
|
||||||
const clientFetch = (botCtorSpy.mock.calls.at(-1)?.[1] as { client?: { fetch?: unknown } })
|
|
||||||
?.client?.fetch as (input: RequestInfo | URL, init?: RequestInit) => Promise<unknown>;
|
|
||||||
expect(clientFetch).toBeTypeOf("function");
|
|
||||||
|
|
||||||
const observedSignalPromise = clientFetch("https://example.test");
|
const observedSignalPromise = clientFetch("https://example.test");
|
||||||
shutdown.abort(new Error("shutdown"));
|
shutdown.abort(new Error("shutdown"));
|
||||||
|
|
@ -32,7 +39,6 @@ describe("createTelegramBot fetch abort", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("tags wrapped Telegram fetch failures with the Bot API method", async () => {
|
it("tags wrapped Telegram fetch failures with the Bot API method", async () => {
|
||||||
const shutdown = new AbortController();
|
|
||||||
const fetchError = Object.assign(new TypeError("fetch failed"), {
|
const fetchError = Object.assign(new TypeError("fetch failed"), {
|
||||||
cause: Object.assign(new Error("connect timeout"), {
|
cause: Object.assign(new Error("connect timeout"), {
|
||||||
code: "UND_ERR_CONNECT_TIMEOUT",
|
code: "UND_ERR_CONNECT_TIMEOUT",
|
||||||
|
|
@ -41,15 +47,7 @@ describe("createTelegramBot fetch abort", () => {
|
||||||
const fetchSpy = vi.fn(async () => {
|
const fetchSpy = vi.fn(async () => {
|
||||||
throw fetchError;
|
throw fetchError;
|
||||||
});
|
});
|
||||||
botCtorSpy.mockClear();
|
const { clientFetch } = createWrappedTelegramClientFetch(fetchSpy as unknown as typeof fetch);
|
||||||
createTelegramBot({
|
|
||||||
token: "tok",
|
|
||||||
fetchAbortSignal: shutdown.signal,
|
|
||||||
proxyFetch: fetchSpy as unknown as typeof fetch,
|
|
||||||
});
|
|
||||||
const clientFetch = (botCtorSpy.mock.calls.at(-1)?.[1] as { client?: { fetch?: unknown } })
|
|
||||||
?.client?.fetch as (input: RequestInfo | URL, init?: RequestInit) => Promise<unknown>;
|
|
||||||
expect(clientFetch).toBeTypeOf("function");
|
|
||||||
|
|
||||||
await expect(clientFetch("https://api.telegram.org/bot123456:ABC/getUpdates")).rejects.toBe(
|
await expect(clientFetch("https://api.telegram.org/bot123456:ABC/getUpdates")).rejects.toBe(
|
||||||
fetchError,
|
fetchError,
|
||||||
|
|
@ -61,7 +59,6 @@ describe("createTelegramBot fetch abort", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("preserves the original fetch error when tagging cannot attach metadata", async () => {
|
it("preserves the original fetch error when tagging cannot attach metadata", async () => {
|
||||||
const shutdown = new AbortController();
|
|
||||||
const frozenError = Object.freeze(
|
const frozenError = Object.freeze(
|
||||||
Object.assign(new TypeError("fetch failed"), {
|
Object.assign(new TypeError("fetch failed"), {
|
||||||
cause: Object.assign(new Error("connect timeout"), {
|
cause: Object.assign(new Error("connect timeout"), {
|
||||||
|
|
@ -72,15 +69,7 @@ describe("createTelegramBot fetch abort", () => {
|
||||||
const fetchSpy = vi.fn(async () => {
|
const fetchSpy = vi.fn(async () => {
|
||||||
throw frozenError;
|
throw frozenError;
|
||||||
});
|
});
|
||||||
botCtorSpy.mockClear();
|
const { clientFetch } = createWrappedTelegramClientFetch(fetchSpy as unknown as typeof fetch);
|
||||||
createTelegramBot({
|
|
||||||
token: "tok",
|
|
||||||
fetchAbortSignal: shutdown.signal,
|
|
||||||
proxyFetch: fetchSpy as unknown as typeof fetch,
|
|
||||||
});
|
|
||||||
const clientFetch = (botCtorSpy.mock.calls.at(-1)?.[1] as { client?: { fetch?: unknown } })
|
|
||||||
?.client?.fetch as (input: RequestInfo | URL, init?: RequestInit) => Promise<unknown>;
|
|
||||||
expect(clientFetch).toBeTypeOf("function");
|
|
||||||
|
|
||||||
await expect(clientFetch("https://api.telegram.org/bot123456:ABC/getUpdates")).rejects.toBe(
|
await expect(clientFetch("https://api.telegram.org/bot123456:ABC/getUpdates")).rejects.toBe(
|
||||||
frozenError,
|
frozenError,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue