Compare commits

...

3 Commits

Author SHA1 Message Date
Tak Hoffman
0f1953c170
chore(ci): retrigger windows matrix 2026-03-16 22:01:42 -05:00
Tak Hoffman
4f9ab1b438
fix(tests): unblock current typecheck drift 2026-03-16 20:58:20 -05:00
Tak Hoffman
25a1f49c9c
fix(tests): tighten windows ci follow-ups 2026-03-16 20:49:26 -05:00
8 changed files with 75 additions and 56 deletions

View File

@ -13,6 +13,7 @@ beforeEach(() => {
code: 1, code: 1,
signal: null, signal: null,
killed: false, killed: false,
termination: "exit",
}); });
}); });

View File

@ -37,7 +37,7 @@ beforeEach(() => {
warn: warnMock, warn: warnMock,
child: () => logger, child: () => logger,
}; };
return logger as ReturnType<typeof subsystemModule.createSubsystemLogger>; return logger as unknown as ReturnType<typeof subsystemModule.createSubsystemLogger>;
}); });
}); });

View File

@ -8,9 +8,9 @@ import {
getChatChannelMeta, getChatChannelMeta,
normalizeAccountId, normalizeAccountId,
TelegramConfigSchema, TelegramConfigSchema,
type ChannelPlugin,
type OpenClawConfig, type OpenClawConfig,
} from "openclaw/plugin-sdk/telegram"; } from "openclaw/plugin-sdk/telegram";
import type { ChannelPlugin } from "../../../src/channels/plugins/types.js";
import { inspectTelegramAccount } from "./account-inspect.js"; import { inspectTelegramAccount } from "./account-inspect.js";
import { import {
listTelegramAccountIds, listTelegramAccountIds,

View File

@ -28,10 +28,10 @@ import {
resolveTelegramGroupToolPolicy, resolveTelegramGroupToolPolicy,
TelegramConfigSchema, TelegramConfigSchema,
type ChannelMessageActionAdapter, type ChannelMessageActionAdapter,
type ChannelPlugin,
type OpenClawConfig, type OpenClawConfig,
} from "openclaw/plugin-sdk/telegram"; } from "openclaw/plugin-sdk/telegram";
import { parseTelegramTopicConversation } from "../../../src/acp/conversation-id.js"; import { parseTelegramTopicConversation } from "../../../src/acp/conversation-id.js";
import type { ChannelPlugin } from "../../../src/channels/plugins/types.js";
import { resolveExecApprovalCommandDisplay } from "../../../src/infra/exec-approval-command-display.js"; import { resolveExecApprovalCommandDisplay } from "../../../src/infra/exec-approval-command-display.js";
import { buildExecApprovalPendingReplyPayload } from "../../../src/infra/exec-approval-reply.js"; import { buildExecApprovalPendingReplyPayload } from "../../../src/infra/exec-approval-reply.js";
import { import {

View File

@ -1,7 +1,7 @@
import path from "node:path"; import path from "node:path";
import { import {
DEFAULT_ACCOUNT_ID, DEFAULT_ACCOUNT_ID,
type DmPolicy, type ChannelSetupDmPolicy,
formatCliCommand, formatCliCommand,
formatDocsLink, formatDocsLink,
normalizeAccountId, normalizeAccountId,
@ -18,10 +18,14 @@ import { loginWeb } from "./login.js";
import { whatsappSetupAdapter } from "./setup-core.js"; import { whatsappSetupAdapter } from "./setup-core.js";
const channel = "whatsapp" as const; const channel = "whatsapp" as const;
type WhatsAppConfig = NonNullable<NonNullable<OpenClawConfig["channels"]>["whatsapp"]>;
type WhatsAppDmPolicy = ChannelSetupDmPolicy["getCurrent"] extends (...args: never[]) => infer T
? T
: never;
function mergeWhatsAppConfig( function mergeWhatsAppConfig(
cfg: OpenClawConfig, cfg: OpenClawConfig,
patch: Partial<NonNullable<OpenClawConfig["channels"]>["whatsapp"]>, patch: Partial<WhatsAppConfig>,
options?: { unsetOnUndefined?: string[] }, options?: { unsetOnUndefined?: string[] },
): OpenClawConfig { ): OpenClawConfig {
const base = { ...(cfg.channels?.whatsapp ?? {}) } as Record<string, unknown>; const base = { ...(cfg.channels?.whatsapp ?? {}) } as Record<string, unknown>;
@ -43,7 +47,7 @@ function mergeWhatsAppConfig(
}; };
} }
function setWhatsAppDmPolicy(cfg: OpenClawConfig, dmPolicy: DmPolicy): OpenClawConfig { function setWhatsAppDmPolicy(cfg: OpenClawConfig, dmPolicy: WhatsAppDmPolicy): OpenClawConfig {
return mergeWhatsAppConfig(cfg, { dmPolicy }); return mergeWhatsAppConfig(cfg, { dmPolicy });
} }
@ -202,7 +206,7 @@ async function promptWhatsAppDmAccess(params: {
{ value: "open", label: "Open (public inbound DMs)" }, { value: "open", label: "Open (public inbound DMs)" },
{ value: "disabled", label: "Disabled (ignore WhatsApp DMs)" }, { value: "disabled", label: "Disabled (ignore WhatsApp DMs)" },
], ],
})) as DmPolicy; })) as WhatsAppDmPolicy;
let next = setWhatsAppSelfChatMode(params.cfg, false); let next = setWhatsAppSelfChatMode(params.cfg, false);
next = setWhatsAppDmPolicy(next, policy); next = setWhatsAppDmPolicy(next, policy);

View File

@ -8,6 +8,7 @@ import { resolveProviderAuths, type ProviderAuth } from "./provider-usage.auth.j
describe("resolveProviderAuths key normalization", () => { describe("resolveProviderAuths key normalization", () => {
let suiteRoot = ""; let suiteRoot = "";
let suiteCase = 0; let suiteCase = 0;
const previousFastEnv = process.env.OPENCLAW_TEST_FAST;
const EMPTY_PROVIDER_ENV = { const EMPTY_PROVIDER_ENV = {
ZAI_API_KEY: undefined, ZAI_API_KEY: undefined,
Z_AI_API_KEY: undefined, Z_AI_API_KEY: undefined,
@ -17,11 +18,17 @@ describe("resolveProviderAuths key normalization", () => {
} satisfies Record<string, string | undefined>; } satisfies Record<string, string | undefined>;
beforeAll(async () => { beforeAll(async () => {
process.env.OPENCLAW_TEST_FAST = "1";
suiteRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-provider-auth-suite-")); suiteRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-provider-auth-suite-"));
}); });
afterAll(async () => { afterAll(async () => {
await fs.rm(suiteRoot, { recursive: true, force: true }); await fs.rm(suiteRoot, { recursive: true, force: true });
if (previousFastEnv === undefined) {
delete process.env.OPENCLAW_TEST_FAST;
} else {
process.env.OPENCLAW_TEST_FAST = previousFastEnv;
}
suiteRoot = ""; suiteRoot = "";
suiteCase = 0; suiteCase = 0;
}); });

View File

@ -1,27 +1,31 @@
import { beforeEach, describe, expect, it, vi } from "vitest"; import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
import type { ProviderPlugin } from "../types.js"; import {
import { providerContractRegistry } from "./registry.js";
function uniqueProviders(): ProviderPlugin[] {
return [
...new Map(
providerContractRegistry.map((entry) => [entry.provider.id, entry.provider]),
).values(),
];
}
const resolvePluginProvidersMock = vi.fn();
vi.mock("../providers.js", () => ({
resolvePluginProviders: (...args: unknown[]) => resolvePluginProvidersMock(...args),
}));
const {
buildProviderPluginMethodChoice, buildProviderPluginMethodChoice,
resolveProviderModelPickerEntries, resolveProviderModelPickerEntries,
resolveProviderPluginChoice, resolveProviderPluginChoice,
resolveProviderWizardOptions, resolveProviderWizardOptions,
} = await import("../provider-wizard.js"); } from "../provider-wizard.js";
import { resolvePluginProviders } from "../providers.js";
import type { ProviderPlugin } from "../types.js";
import { providerContractRegistry } from "./registry.js";
const fastModeEnv = vi.hoisted(() => {
const previous = process.env.OPENCLAW_TEST_FAST;
process.env.OPENCLAW_TEST_FAST = "1";
return { previous };
});
function createBundledProviderConfig() {
return {
plugins: {
enabled: true,
allow: [...new Set(providerContractRegistry.map((entry) => entry.pluginId))],
slots: {
memory: "none",
},
},
};
}
function resolveExpectedWizardChoiceValues(providers: ProviderPlugin[]) { function resolveExpectedWizardChoiceValues(providers: ProviderPlugin[]) {
const values: string[] = []; const values: string[] = [];
@ -80,27 +84,35 @@ function resolveExpectedModelPickerValues(providers: ProviderPlugin[]) {
} }
describe("provider wizard contract", () => { describe("provider wizard contract", () => {
beforeEach(() => { let providers: ProviderPlugin[] = [];
const providers = uniqueProviders(); let options: ReturnType<typeof resolveProviderWizardOptions> = [];
resolvePluginProvidersMock.mockReset(); let modelPickerEntries: ReturnType<typeof resolveProviderModelPickerEntries> = [];
resolvePluginProvidersMock.mockReturnValue(providers);
beforeAll(() => {
const config = createBundledProviderConfig();
providers = resolvePluginProviders({
config,
env: process.env,
});
options = resolveProviderWizardOptions({
config,
env: process.env,
});
modelPickerEntries = resolveProviderModelPickerEntries({
config,
env: process.env,
});
});
afterAll(() => {
if (fastModeEnv.previous === undefined) {
delete process.env.OPENCLAW_TEST_FAST;
return;
}
process.env.OPENCLAW_TEST_FAST = fastModeEnv.previous;
}); });
it("exposes every registered provider setup choice through the shared wizard layer", () => { it("exposes every registered provider setup choice through the shared wizard layer", () => {
const providers = uniqueProviders();
const options = resolveProviderWizardOptions({
config: {
plugins: {
enabled: true,
allow: [...new Set(providerContractRegistry.map((entry) => entry.pluginId))],
slots: {
memory: "none",
},
},
},
env: process.env,
});
expect( expect(
options.map((option) => option.value).toSorted((left, right) => left.localeCompare(right)), options.map((option) => option.value).toSorted((left, right) => left.localeCompare(right)),
).toEqual(resolveExpectedWizardChoiceValues(providers)); ).toEqual(resolveExpectedWizardChoiceValues(providers));
@ -110,9 +122,7 @@ describe("provider wizard contract", () => {
}); });
it("round-trips every shared wizard choice back to its provider and auth method", () => { it("round-trips every shared wizard choice back to its provider and auth method", () => {
const providers = uniqueProviders(); for (const option of options) {
for (const option of resolveProviderWizardOptions({ config: {}, env: process.env })) {
const resolved = resolveProviderPluginChoice({ const resolved = resolveProviderPluginChoice({
providers, providers,
choice: option.value, choice: option.value,
@ -124,13 +134,12 @@ describe("provider wizard contract", () => {
}); });
it("exposes every registered model-picker entry through the shared wizard layer", () => { it("exposes every registered model-picker entry through the shared wizard layer", () => {
const providers = uniqueProviders();
const entries = resolveProviderModelPickerEntries({ config: {}, env: process.env });
expect( expect(
entries.map((entry) => entry.value).toSorted((left, right) => left.localeCompare(right)), modelPickerEntries
.map((entry) => entry.value)
.toSorted((left, right) => left.localeCompare(right)),
).toEqual(resolveExpectedModelPickerValues(providers)); ).toEqual(resolveExpectedModelPickerValues(providers));
for (const entry of entries) { for (const entry of modelPickerEntries) {
const resolved = resolveProviderPluginChoice({ const resolved = resolveProviderPluginChoice({
providers, providers,
choice: entry.value, choice: entry.value,

View File

@ -153,9 +153,7 @@ describe("stageBundledPluginRuntime", () => {
description: string; description: string;
acceptsArgs: boolean; acceptsArgs: boolean;
}>; }>;
matchPluginCommand: ( matchPluginCommand: (commandBody: string) => {
commandBody: string,
) => {
command: { handler: ({ args }: { args?: string }) => Promise<{ text: string }> }; command: { handler: ({ args }: { args?: string }) => Promise<{ text: string }> };
args?: string; args?: string;
} | null; } | null;