mirror of https://github.com/openclaw/openclaw.git
test: fix CI type regressions
This commit is contained in:
parent
d17490ff54
commit
60d308cff0
|
|
@ -161,6 +161,12 @@ export type ScheduledTaskInfo = {
|
|||
lastRunResult?: string;
|
||||
};
|
||||
|
||||
function hasListenerPid<T extends { pid?: number | null }>(
|
||||
listener: T,
|
||||
): listener is T & { pid: number } {
|
||||
return typeof listener.pid === "number";
|
||||
}
|
||||
|
||||
export function parseSchtasksQuery(output: string): ScheduledTaskInfo {
|
||||
const entries = parseKeyValueOutput(output, ":");
|
||||
const info: ScheduledTaskInfo = {};
|
||||
|
|
@ -388,7 +394,7 @@ async function resolveScheduledTaskGatewayListenerPids(port: number): Promise<nu
|
|||
new Set(
|
||||
diagnostics.listeners
|
||||
.map((listener) => listener.pid)
|
||||
.filter((pid): pid is number => Number.isFinite(pid) && pid > 0),
|
||||
.filter((pid): pid is number => typeof pid === "number" && Number.isFinite(pid) && pid > 0),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -472,7 +478,7 @@ async function terminateBusyPortListeners(port: number): Promise<number[]> {
|
|||
new Set(
|
||||
diagnostics.listeners
|
||||
.map((listener) => listener.pid)
|
||||
.filter((pid): pid is number => Number.isFinite(pid) && pid > 0),
|
||||
.filter((pid): pid is number => typeof pid === "number" && Number.isFinite(pid) && pid > 0),
|
||||
),
|
||||
);
|
||||
for (const pid of pids) {
|
||||
|
|
@ -496,7 +502,7 @@ async function resolveFallbackRuntime(env: GatewayServiceEnv): Promise<GatewaySe
|
|||
detail: `Startup-folder login item installed; could not inspect port ${port}.`,
|
||||
};
|
||||
}
|
||||
const listener = diagnostics.listeners.find((item) => typeof item.pid === "number");
|
||||
const listener = diagnostics.listeners.find(hasListenerPid);
|
||||
return {
|
||||
status: diagnostics.status === "busy" ? "running" : "stopped",
|
||||
...(listener?.pid ? { pid: listener.pid } : {}),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { DedupeEntry } from "../server-shared.js";
|
||||
import {
|
||||
__testing,
|
||||
readTerminalSnapshotFromGatewayDedupe,
|
||||
|
|
@ -8,7 +9,7 @@ import {
|
|||
|
||||
describe("agent wait dedupe helper", () => {
|
||||
function setRunEntry(params: {
|
||||
dedupe: Map<unknown, unknown>;
|
||||
dedupe: Map<string, DedupeEntry>;
|
||||
kind: "agent" | "chat";
|
||||
runId: string;
|
||||
ts?: number;
|
||||
|
|
|
|||
|
|
@ -251,7 +251,7 @@ describe("resolveGatewayRuntimeConfig", () => {
|
|||
});
|
||||
|
||||
describe("HTTP security headers", () => {
|
||||
it.each([
|
||||
const cases = [
|
||||
{
|
||||
name: "resolves strict transport security headers from config",
|
||||
strictTransportSecurity: " max-age=31536000; includeSubDomains ",
|
||||
|
|
@ -267,7 +267,13 @@ describe("resolveGatewayRuntimeConfig", () => {
|
|||
strictTransportSecurity: " ",
|
||||
expected: undefined,
|
||||
},
|
||||
])("$name", async ({ strictTransportSecurity, expected }) => {
|
||||
] satisfies ReadonlyArray<{
|
||||
name: string;
|
||||
strictTransportSecurity: string | false;
|
||||
expected: string | undefined;
|
||||
}>;
|
||||
|
||||
it.each(cases)("$name", async ({ strictTransportSecurity, expected }) => {
|
||||
const result = await resolveGatewayRuntimeConfig({
|
||||
cfg: {
|
||||
gateway: {
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ type TalkConfigPayload = {
|
|||
ui?: { seamColor?: string };
|
||||
};
|
||||
};
|
||||
type TalkConfig = NonNullable<NonNullable<TalkConfigPayload["config"]>["talk"]>;
|
||||
const TALK_CONFIG_DEVICE_PATH = path.join(
|
||||
os.tmpdir(),
|
||||
`openclaw-talk-config-device-${process.pid}.json`,
|
||||
|
|
@ -95,7 +96,7 @@ async function fetchTalkConfig(
|
|||
}
|
||||
|
||||
function expectElevenLabsTalkConfig(
|
||||
talk: TalkConfigPayload["config"] extends { talk?: infer T } ? T : never,
|
||||
talk: TalkConfig | undefined,
|
||||
expected: {
|
||||
voiceId?: string;
|
||||
apiKey?: string | SecretRef;
|
||||
|
|
|
|||
|
|
@ -91,13 +91,13 @@ describe("agent-events sequencing", () => {
|
|||
isControlUiVisible: true,
|
||||
});
|
||||
registerAgentRunContext("run-ctx", {
|
||||
verboseLevel: "high",
|
||||
verboseLevel: "full",
|
||||
isHeartbeat: true,
|
||||
});
|
||||
|
||||
expect(getAgentRunContext("run-ctx")).toEqual({
|
||||
sessionKey: "session-main",
|
||||
verboseLevel: "high",
|
||||
verboseLevel: "full",
|
||||
isHeartbeat: true,
|
||||
isControlUiVisible: true,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -32,7 +32,10 @@ describe("error helpers", () => {
|
|||
child.cause = root;
|
||||
|
||||
expect(
|
||||
collectErrorGraphCandidates(root, (current) => [current.cause, ...(current.errors ?? [])]),
|
||||
collectErrorGraphCandidates(root, (current) => [
|
||||
current.cause,
|
||||
...((current as { errors?: unknown[] }).errors ?? []),
|
||||
]),
|
||||
).toEqual([root, child, leaf]);
|
||||
expect(collectErrorGraphCandidates(null)).toEqual([]);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ describe("format-datetime", () => {
|
|||
formatToParts: () => {
|
||||
throw new Error("boom");
|
||||
},
|
||||
} as Intl.DateTimeFormat;
|
||||
} as unknown as Intl.DateTimeFormat;
|
||||
}
|
||||
|
||||
vi.spyOn(Intl, "DateTimeFormat").mockImplementation(
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import {
|
|||
resolveTimedInstallModeOptions,
|
||||
} from "./install-mode-options.js";
|
||||
|
||||
type LoggerKey = "default" | "explicit";
|
||||
|
||||
describe("install mode option helpers", () => {
|
||||
it.each([
|
||||
{
|
||||
|
|
@ -21,11 +23,15 @@ describe("install mode option helpers", () => {
|
|||
params: { mode: "update" as const, dryRun: false },
|
||||
expected: { loggerKey: "default", mode: "update", dryRun: false },
|
||||
},
|
||||
])("$name", ({ params, expected }) => {
|
||||
] satisfies Array<{
|
||||
name: string;
|
||||
params: { loggerKey?: LoggerKey; mode?: "install" | "update"; dryRun?: boolean };
|
||||
expected: { loggerKey: LoggerKey; mode: "install" | "update"; dryRun: boolean };
|
||||
}>)("$name", ({ params, expected }) => {
|
||||
const loggers = {
|
||||
default: { warn: (_message: string) => {} },
|
||||
explicit: { warn: (_message: string) => {} },
|
||||
};
|
||||
} satisfies Record<LoggerKey, { warn: (_message: string) => void }>;
|
||||
|
||||
expect(
|
||||
resolveInstallModeOptions(
|
||||
|
|
|
|||
|
|
@ -462,7 +462,14 @@ describe("resolveSessionDeliveryTarget", () => {
|
|||
expectedChannel: "none",
|
||||
expectedReason: "dm-blocked",
|
||||
},
|
||||
])("$name", ({ name, entry, directPolicy, expectedChannel, expectedTo, expectedReason }) => {
|
||||
] satisfies Array<{
|
||||
name: string;
|
||||
entry: NonNullable<Parameters<typeof resolveHeartbeatDeliveryTarget>[0]["entry"]>;
|
||||
directPolicy?: "allow" | "block";
|
||||
expectedChannel: string;
|
||||
expectedTo?: string;
|
||||
expectedReason?: string;
|
||||
}>)("$name", ({ name, entry, directPolicy, expectedChannel, expectedTo, expectedReason }) => {
|
||||
expectHeartbeatTarget({
|
||||
name,
|
||||
entry,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import os from "node:os";
|
|||
import path from "node:path";
|
||||
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
||||
import { NON_ENV_SECRETREF_MARKER } from "../agents/model-auth-markers.js";
|
||||
import { resolveProviderAuths } from "./provider-usage.auth.js";
|
||||
import { resolveProviderAuths, type ProviderAuth } from "./provider-usage.auth.js";
|
||||
|
||||
describe("resolveProviderAuths key normalization", () => {
|
||||
let suiteRoot = "";
|
||||
|
|
@ -214,7 +214,12 @@ describe("resolveProviderAuths key normalization", () => {
|
|||
},
|
||||
expected: [{ provider: "minimax", token: "code-plan-key" }],
|
||||
},
|
||||
])("$name", async ({ providers, env, expected }) => {
|
||||
] satisfies Array<{
|
||||
name: string;
|
||||
providers: readonly Parameters<typeof resolveProviderAuths>[0]["providers"][number][];
|
||||
env: Record<string, string | undefined>;
|
||||
expected: ProviderAuth[];
|
||||
}>)("$name", async ({ providers, env, expected }) => {
|
||||
await expectResolvedAuthsFromSuiteHome({ providers: [...providers], env, expected });
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -32,10 +32,11 @@ describe("provider usage fetch shared helpers", () => {
|
|||
it("forwards request init and clears the timeout on success", async () => {
|
||||
vi.useFakeTimers();
|
||||
const clearTimeoutSpy = vi.spyOn(globalThis, "clearTimeout");
|
||||
const fetchFn = vi.fn(
|
||||
async (_url: string, init?: RequestInit) =>
|
||||
const fetchFnMock = vi.fn(
|
||||
async (_input: URL | RequestInfo, init?: RequestInit) =>
|
||||
new Response(JSON.stringify({ aborted: init?.signal?.aborted ?? false }), { status: 200 }),
|
||||
);
|
||||
const fetchFn = fetchFnMock as typeof fetch;
|
||||
|
||||
const response = await fetchJson(
|
||||
"https://example.com/usage",
|
||||
|
|
@ -47,7 +48,7 @@ describe("provider usage fetch shared helpers", () => {
|
|||
fetchFn,
|
||||
);
|
||||
|
||||
expect(fetchFn).toHaveBeenCalledWith(
|
||||
expect(fetchFnMock).toHaveBeenCalledWith(
|
||||
"https://example.com/usage",
|
||||
expect.objectContaining({
|
||||
method: "POST",
|
||||
|
|
@ -62,14 +63,15 @@ describe("provider usage fetch shared helpers", () => {
|
|||
it("aborts timed out requests and clears the timer on rejection", async () => {
|
||||
vi.useFakeTimers();
|
||||
const clearTimeoutSpy = vi.spyOn(globalThis, "clearTimeout");
|
||||
const fetchFn = vi.fn(
|
||||
(_url: string, init?: RequestInit) =>
|
||||
const fetchFnMock = vi.fn(
|
||||
(_input: URL | RequestInfo, init?: RequestInit) =>
|
||||
new Promise<Response>((_, reject) => {
|
||||
init?.signal?.addEventListener("abort", () => reject(new Error("aborted by timeout")), {
|
||||
once: true,
|
||||
});
|
||||
}),
|
||||
);
|
||||
const fetchFn = fetchFnMock as typeof fetch;
|
||||
|
||||
const request = fetchJson("https://example.com/usage", {}, 50, fetchFn);
|
||||
const rejection = expect(request).rejects.toThrow("aborted by timeout");
|
||||
|
|
|
|||
|
|
@ -5,6 +5,11 @@ import path from "node:path";
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { openVerifiedFileSync } from "./safe-open-sync.js";
|
||||
|
||||
type SafeOpenSyncFs = NonNullable<Parameters<typeof openVerifiedFileSync>[0]["ioFs"]>;
|
||||
type SafeOpenSyncLstatSync = SafeOpenSyncFs["lstatSync"];
|
||||
type SafeOpenSyncRealpathSync = SafeOpenSyncFs["realpathSync"];
|
||||
type SafeOpenSyncFstatSync = SafeOpenSyncFs["fstatSync"];
|
||||
|
||||
async function withTempDir<T>(prefix: string, run: (dir: string) => Promise<T>): Promise<T> {
|
||||
const dir = await fsp.mkdtemp(path.join(os.tmpdir(), prefix));
|
||||
try {
|
||||
|
|
@ -33,6 +38,20 @@ function mockStat(params: {
|
|||
} as unknown as fs.Stats;
|
||||
}
|
||||
|
||||
function mockRealpathSync(result: string): SafeOpenSyncRealpathSync {
|
||||
const resolvePath = ((_: fs.PathLike) => result) as SafeOpenSyncRealpathSync;
|
||||
resolvePath.native = ((_: fs.PathLike) => result) as typeof resolvePath.native;
|
||||
return resolvePath;
|
||||
}
|
||||
|
||||
function mockLstatSync(read: (filePath: fs.PathLike) => fs.Stats): SafeOpenSyncLstatSync {
|
||||
return ((filePath: fs.PathLike) => read(filePath)) as unknown as SafeOpenSyncLstatSync;
|
||||
}
|
||||
|
||||
function mockFstatSync(stat: fs.Stats): SafeOpenSyncFstatSync {
|
||||
return ((_: number) => stat) as unknown as SafeOpenSyncFstatSync;
|
||||
}
|
||||
|
||||
describe("openVerifiedFileSync", () => {
|
||||
it("returns a path error for missing files", async () => {
|
||||
await withTempDir("openclaw-safe-open-", async (root) => {
|
||||
|
|
@ -115,15 +134,16 @@ describe("openVerifiedFileSync", () => {
|
|||
closed.push(fd);
|
||||
};
|
||||
const closed: number[] = [];
|
||||
const ioFs = {
|
||||
const ioFs: SafeOpenSyncFs = {
|
||||
constants: fs.constants,
|
||||
lstatSync: (filePath: string) =>
|
||||
filePath === "/real/file.txt"
|
||||
lstatSync: mockLstatSync((filePath) =>
|
||||
String(filePath) === "/real/file.txt"
|
||||
? mockStat({ isFile: true, size: 1, dev: 1, ino: 1 })
|
||||
: mockStat({ isFile: false }),
|
||||
realpathSync: () => "/real/file.txt",
|
||||
),
|
||||
realpathSync: mockRealpathSync("/real/file.txt"),
|
||||
openSync: () => 42,
|
||||
fstatSync: () => mockStat({ isFile: true, size: 1, dev: 2, ino: 1 }),
|
||||
fstatSync: mockFstatSync(mockStat({ isFile: true, size: 1, dev: 2, ino: 1 })),
|
||||
closeSync,
|
||||
};
|
||||
|
||||
|
|
@ -139,16 +159,16 @@ describe("openVerifiedFileSync", () => {
|
|||
});
|
||||
|
||||
it("reports non-path filesystem failures as io errors", () => {
|
||||
const ioFs = {
|
||||
const ioFs: SafeOpenSyncFs = {
|
||||
constants: fs.constants,
|
||||
lstatSync: () => {
|
||||
const err = new Error("permission denied") as NodeJS.ErrnoException;
|
||||
err.code = "EACCES";
|
||||
throw err;
|
||||
},
|
||||
realpathSync: () => "/real/file.txt",
|
||||
realpathSync: mockRealpathSync("/real/file.txt"),
|
||||
openSync: () => 42,
|
||||
fstatSync: () => mockStat({ isFile: true }),
|
||||
fstatSync: mockFstatSync(mockStat({ isFile: true })),
|
||||
closeSync: () => {},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import {
|
|||
normalizeUpdateChannel,
|
||||
resolveEffectiveUpdateChannel,
|
||||
resolveUpdateChannelDisplay,
|
||||
type UpdateChannel,
|
||||
type UpdateChannelSource,
|
||||
} from "./update-channels.js";
|
||||
|
||||
describe("update-channels tag detection", () => {
|
||||
|
|
@ -32,9 +34,12 @@ describe("normalizeUpdateChannel", () => {
|
|||
{ value: " nightly ", expected: null },
|
||||
{ value: null, expected: null },
|
||||
{ value: undefined, expected: null },
|
||||
])("normalizes %j", ({ value, expected }) => {
|
||||
expect(normalizeUpdateChannel(value)).toBe(expected);
|
||||
});
|
||||
] satisfies Array<{ value: string | null | undefined; expected: UpdateChannel | null }>)(
|
||||
"normalizes %j",
|
||||
({ value, expected }) => {
|
||||
expect(normalizeUpdateChannel(value)).toBe(expected);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe("channelToNpmTag", () => {
|
||||
|
|
@ -42,9 +47,12 @@ describe("channelToNpmTag", () => {
|
|||
{ channel: "stable", expected: "latest" },
|
||||
{ channel: "beta", expected: "beta" },
|
||||
{ channel: "dev", expected: "dev" },
|
||||
])("maps $channel to $expected", ({ channel, expected }) => {
|
||||
expect(channelToNpmTag(channel)).toBe(expected);
|
||||
});
|
||||
] satisfies Array<{ channel: UpdateChannel; expected: string }>)(
|
||||
"maps $channel to $expected",
|
||||
({ channel, expected }) => {
|
||||
expect(channelToNpmTag(channel)).toBe(expected);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe("resolveEffectiveUpdateChannel", () => {
|
||||
|
|
@ -100,7 +108,11 @@ describe("resolveEffectiveUpdateChannel", () => {
|
|||
params: { installKind: "unknown" as const },
|
||||
expected: { channel: "stable", source: "default" },
|
||||
},
|
||||
])("$name", ({ params, expected }) => {
|
||||
] satisfies Array<{
|
||||
name: string;
|
||||
params: Parameters<typeof resolveEffectiveUpdateChannel>[0];
|
||||
expected: { channel: UpdateChannel; source: UpdateChannelSource };
|
||||
}>)("$name", ({ params, expected }) => {
|
||||
expect(resolveEffectiveUpdateChannel(params)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
|
@ -145,7 +157,11 @@ describe("formatUpdateChannelLabel", () => {
|
|||
params: { channel: "stable", source: "default" as const },
|
||||
expected: "stable (default)",
|
||||
},
|
||||
])("$name", ({ params, expected }) => {
|
||||
] satisfies Array<{
|
||||
name: string;
|
||||
params: Parameters<typeof formatUpdateChannelLabel>[0];
|
||||
expected: string;
|
||||
}>)("$name", ({ params, expected }) => {
|
||||
expect(formatUpdateChannelLabel(params)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,19 +7,20 @@ import {
|
|||
normalizeWideAreaDomain,
|
||||
renderWideAreaGatewayZoneText,
|
||||
resolveWideAreaDiscoveryDomain,
|
||||
type WideAreaGatewayZoneOpts,
|
||||
writeWideAreaGatewayZone,
|
||||
} from "./widearea-dns.js";
|
||||
|
||||
const baseZoneOpts = {
|
||||
const baseZoneOpts: WideAreaGatewayZoneOpts = {
|
||||
domain: "openclaw.internal.",
|
||||
gatewayPort: 18789,
|
||||
displayName: "Mac Studio (OpenClaw)",
|
||||
tailnetIPv4: "100.123.224.76",
|
||||
hostLabel: "studio-london",
|
||||
instanceLabel: "studio-london",
|
||||
} as const;
|
||||
};
|
||||
|
||||
function makeZoneOpts(overrides: Partial<typeof baseZoneOpts> = {}) {
|
||||
function makeZoneOpts(overrides: Partial<WideAreaGatewayZoneOpts> = {}): WideAreaGatewayZoneOpts {
|
||||
return { ...baseZoneOpts, ...overrides };
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -678,7 +678,7 @@ describe("handleLineWebhookEvents", () => {
|
|||
it("skips group messages by default when requireMention is not configured", async () => {
|
||||
const processMessage = vi.fn();
|
||||
const event = createTestMessageEvent({
|
||||
message: { id: "m-default-skip", type: "text", text: "hi there" },
|
||||
message: { id: "m-default-skip", type: "text", text: "hi there", quoteToken: "q-default" },
|
||||
source: { type: "group", groupId: "group-default", userId: "user-default" },
|
||||
webhookEventId: "evt-default-skip",
|
||||
});
|
||||
|
|
@ -702,7 +702,7 @@ describe("handleLineWebhookEvents", () => {
|
|||
import("../auto-reply/reply/history.js").HistoryEntry[]
|
||||
>();
|
||||
const event = createTestMessageEvent({
|
||||
message: { id: "m-hist-1", type: "text", text: "hello history" },
|
||||
message: { id: "m-hist-1", type: "text", text: "hello history", quoteToken: "q-hist-1" },
|
||||
timestamp: 1700000000000,
|
||||
source: { type: "group", groupId: "group-hist-1", userId: "user-hist" },
|
||||
webhookEventId: "evt-hist-1",
|
||||
|
|
@ -730,7 +730,7 @@ describe("handleLineWebhookEvents", () => {
|
|||
it("skips group messages without mention when requireMention is set", async () => {
|
||||
const processMessage = vi.fn();
|
||||
const event = createTestMessageEvent({
|
||||
message: { id: "m-mention-1", type: "text", text: "hi there" },
|
||||
message: { id: "m-mention-1", type: "text", text: "hi there", quoteToken: "q-mention-1" },
|
||||
source: { type: "group", groupId: "group-mention", userId: "user-mention" },
|
||||
webhookEventId: "evt-mention-1",
|
||||
});
|
||||
|
|
@ -808,7 +808,7 @@ describe("handleLineWebhookEvents", () => {
|
|||
it("does not apply requireMention gating to DM messages", async () => {
|
||||
const processMessage = vi.fn();
|
||||
const event = createTestMessageEvent({
|
||||
message: { id: "m-mention-dm", type: "text", text: "hi" },
|
||||
message: { id: "m-mention-dm", type: "text", text: "hi", quoteToken: "q-mention-dm" },
|
||||
source: { type: "user", userId: "user-dm" },
|
||||
webhookEventId: "evt-mention-dm",
|
||||
});
|
||||
|
|
@ -830,7 +830,12 @@ describe("handleLineWebhookEvents", () => {
|
|||
const processMessage = vi.fn();
|
||||
// Image message -- LINE only carries mention metadata on text messages.
|
||||
const event = createTestMessageEvent({
|
||||
message: { id: "m-mention-img", type: "image", contentProvider: { type: "line" } },
|
||||
message: {
|
||||
id: "m-mention-img",
|
||||
type: "image",
|
||||
contentProvider: { type: "line" },
|
||||
quoteToken: "q-mention-img",
|
||||
},
|
||||
source: { type: "group", groupId: "group-1", userId: "user-img" },
|
||||
webhookEventId: "evt-mention-img",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1488,7 +1488,7 @@ describe("loadOpenClawPlugins", () => {
|
|||
load: { paths: [plugin.file] },
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
};
|
||||
|
||||
loadOpenClawPlugins(options);
|
||||
loadOpenClawPlugins(options);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import type { TelegramNetworkConfig } from "../config/types.telegram.js";
|
||||
import {
|
||||
resetTelegramNetworkConfigStateForTests,
|
||||
resolveTelegramAutoSelectFamilyDecision,
|
||||
|
|
@ -157,7 +158,9 @@ describe("resolveTelegramDnsResultOrderDecision", () => {
|
|||
},
|
||||
{
|
||||
name: "normalizes trimmed config values",
|
||||
network: { dnsResultOrder: " Verbatim " },
|
||||
network: { dnsResultOrder: " Verbatim " } as TelegramNetworkConfig & {
|
||||
dnsResultOrder: string;
|
||||
},
|
||||
nodeMajor: 20,
|
||||
expected: { value: "verbatim", source: "config" },
|
||||
},
|
||||
|
|
@ -171,11 +174,17 @@ describe("resolveTelegramDnsResultOrderDecision", () => {
|
|||
{
|
||||
name: "ignores invalid env and config values before applying Node 22 default",
|
||||
env: { OPENCLAW_TELEGRAM_DNS_RESULT_ORDER: "bogus" },
|
||||
network: { dnsResultOrder: "invalid" },
|
||||
network: { dnsResultOrder: "invalid" } as TelegramNetworkConfig & { dnsResultOrder: string },
|
||||
nodeMajor: 22,
|
||||
expected: { value: "ipv4first", source: "default-node22" },
|
||||
},
|
||||
])("$name", ({ env, network, nodeMajor, expected }) => {
|
||||
] satisfies Array<{
|
||||
name: string;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
network?: TelegramNetworkConfig | (TelegramNetworkConfig & { dnsResultOrder: string });
|
||||
nodeMajor: number;
|
||||
expected: ReturnType<typeof resolveTelegramDnsResultOrderDecision>;
|
||||
}>)("$name", ({ env, network, nodeMajor, expected }) => {
|
||||
const decision = resolveTelegramDnsResultOrderDecision({
|
||||
env,
|
||||
network,
|
||||
|
|
|
|||
|
|
@ -28,14 +28,7 @@ export function expectSingleNpmInstallIgnoreScriptsCall(params: {
|
|||
throw new Error("expected npm install call");
|
||||
}
|
||||
const [argv, opts] = first;
|
||||
expect(argv).toEqual([
|
||||
"npm",
|
||||
"install",
|
||||
"--omit=dev",
|
||||
"--omit=peer",
|
||||
"--silent",
|
||||
"--ignore-scripts",
|
||||
]);
|
||||
expect(argv).toEqual(["npm", "install", "--omit=dev", "--silent", "--ignore-scripts"]);
|
||||
expect(opts?.cwd).toBeTruthy();
|
||||
const cwd = String(opts?.cwd);
|
||||
const expectedTargetDir = params.expectedTargetDir;
|
||||
|
|
|
|||
Loading…
Reference in New Issue