mirror of https://github.com/openclaw/openclaw.git
fix: stabilize line and feishu ci shards
This commit is contained in:
parent
aeb9ad52fa
commit
3de91d9e01
|
|
@ -89,6 +89,9 @@ describe("broadcast dispatch", () => {
|
|||
routing: {
|
||||
resolveAgentRoute: (params: unknown) => mockResolveAgentRoute(params),
|
||||
},
|
||||
session: {
|
||||
resolveStorePath: vi.fn(() => "/tmp/feishu-session-store.json"),
|
||||
},
|
||||
reply: {
|
||||
resolveEnvelopeFormatOptions: resolveEnvelopeFormatOptionsMock,
|
||||
formatAgentEnvelope: vi.fn((params: { body: string }) => params.body),
|
||||
|
|
|
|||
|
|
@ -5,34 +5,146 @@ import type { LineAccountConfig } from "./types.js";
|
|||
|
||||
// Avoid pulling in globals/pairing/media dependencies; this suite only asserts
|
||||
// allowlist/groupPolicy gating and message-context wiring.
|
||||
vi.mock("openclaw/plugin-sdk/runtime-env", async () => {
|
||||
const actual = await vi.importActual<typeof import("openclaw/plugin-sdk/runtime-env")>(
|
||||
"openclaw/plugin-sdk/runtime-env",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
danger: (text: string) => text,
|
||||
logVerbose: () => {},
|
||||
shouldLogVerbose: () => false,
|
||||
};
|
||||
});
|
||||
vi.mock("openclaw/plugin-sdk/channel-inbound", () => ({
|
||||
buildMentionRegexes: () => [],
|
||||
matchesMentionPatterns: () => false,
|
||||
resolveMentionGatingWithBypass: ({
|
||||
isGroup,
|
||||
requireMention,
|
||||
canDetectMention,
|
||||
wasMentioned,
|
||||
hasAnyMention,
|
||||
allowTextCommands,
|
||||
hasControlCommand,
|
||||
commandAuthorized,
|
||||
}: {
|
||||
isGroup: boolean;
|
||||
requireMention: boolean;
|
||||
canDetectMention: boolean;
|
||||
wasMentioned: boolean;
|
||||
hasAnyMention: boolean;
|
||||
allowTextCommands: boolean;
|
||||
hasControlCommand: boolean;
|
||||
commandAuthorized: boolean;
|
||||
}) => ({
|
||||
shouldSkip:
|
||||
isGroup &&
|
||||
requireMention &&
|
||||
canDetectMention &&
|
||||
!wasMentioned &&
|
||||
!(allowTextCommands && hasControlCommand && commandAuthorized && !hasAnyMention),
|
||||
}),
|
||||
}));
|
||||
vi.mock("openclaw/plugin-sdk/channel-pairing", () => ({
|
||||
createChannelPairingChallengeIssuer:
|
||||
({ upsertPairingRequest }: { upsertPairingRequest: (args: unknown) => Promise<unknown> }) =>
|
||||
async ({ senderId, onCreated }: { senderId: string; onCreated?: () => void }) => {
|
||||
await upsertPairingRequest({ id: senderId, meta: {} });
|
||||
onCreated?.();
|
||||
},
|
||||
}));
|
||||
vi.mock("openclaw/plugin-sdk/command-auth", () => ({
|
||||
hasControlCommand: (text: string) => text.trim().startsWith("!"),
|
||||
resolveControlCommandGate: ({
|
||||
hasControlCommand,
|
||||
authorizers,
|
||||
}: {
|
||||
hasControlCommand: boolean;
|
||||
authorizers: Array<{ configured: boolean; allowed: boolean }>;
|
||||
}) => ({
|
||||
commandAuthorized:
|
||||
hasControlCommand && authorizers.some((entry) => entry.allowed || entry.configured === false),
|
||||
}),
|
||||
}));
|
||||
vi.mock("openclaw/plugin-sdk/config-runtime", () => ({
|
||||
resolveAllowlistProviderRuntimeGroupPolicy: ({
|
||||
groupPolicy,
|
||||
defaultGroupPolicy,
|
||||
}: {
|
||||
groupPolicy?: string;
|
||||
defaultGroupPolicy: string;
|
||||
}) => ({
|
||||
groupPolicy: groupPolicy ?? defaultGroupPolicy,
|
||||
providerMissingFallbackApplied: false,
|
||||
}),
|
||||
resolveDefaultGroupPolicy: (cfg: { channels?: { line?: { groupPolicy?: string } } }) =>
|
||||
cfg.channels?.line?.groupPolicy ?? "open",
|
||||
warnMissingProviderGroupPolicyFallbackOnce: () => {},
|
||||
}));
|
||||
vi.mock("openclaw/plugin-sdk/runtime-env", () => ({
|
||||
danger: (text: string) => text,
|
||||
logVerbose: () => {},
|
||||
}));
|
||||
vi.mock("openclaw/plugin-sdk/group-access", () => ({
|
||||
evaluateMatchedGroupAccessForPolicy: ({
|
||||
groupPolicy,
|
||||
hasMatchInput,
|
||||
allowlistConfigured,
|
||||
allowlistMatched,
|
||||
}: {
|
||||
groupPolicy: string;
|
||||
hasMatchInput: boolean;
|
||||
allowlistConfigured: boolean;
|
||||
allowlistMatched: boolean;
|
||||
}) => {
|
||||
if (groupPolicy === "disabled") {
|
||||
return { allowed: false, reason: "disabled" };
|
||||
}
|
||||
if (groupPolicy !== "allowlist") {
|
||||
return { allowed: true, reason: null };
|
||||
}
|
||||
if (!hasMatchInput) {
|
||||
return { allowed: false, reason: "missing_match_input" };
|
||||
}
|
||||
if (!allowlistConfigured) {
|
||||
return { allowed: false, reason: "empty_allowlist" };
|
||||
}
|
||||
if (!allowlistMatched) {
|
||||
return { allowed: false, reason: "not_allowlisted" };
|
||||
}
|
||||
return { allowed: true, reason: null };
|
||||
},
|
||||
}));
|
||||
vi.mock("openclaw/plugin-sdk/reply-history", () => ({
|
||||
DEFAULT_GROUP_HISTORY_LIMIT: 20,
|
||||
clearHistoryEntriesIfEnabled: ({
|
||||
historyMap,
|
||||
historyKey,
|
||||
}: {
|
||||
historyMap: Map<string, HistoryEntry[]>;
|
||||
historyKey: string;
|
||||
}) => {
|
||||
historyMap.delete(historyKey);
|
||||
},
|
||||
recordPendingHistoryEntryIfEnabled: ({
|
||||
historyMap,
|
||||
historyKey,
|
||||
limit,
|
||||
entry,
|
||||
}: {
|
||||
historyMap: Map<string, HistoryEntry[]>;
|
||||
historyKey: string;
|
||||
limit: number;
|
||||
entry: HistoryEntry;
|
||||
}) => {
|
||||
const existing = historyMap.get(historyKey) ?? [];
|
||||
historyMap.set(historyKey, [...existing, entry].slice(-limit));
|
||||
},
|
||||
}));
|
||||
vi.mock("openclaw/plugin-sdk/routing", () => ({
|
||||
resolveAgentRoute: () => ({ agentId: "default" }),
|
||||
}));
|
||||
|
||||
const { readAllowFromStoreMock, upsertPairingRequestMock } = vi.hoisted(() => ({
|
||||
readAllowFromStoreMock: vi.fn(async () => [] as string[]),
|
||||
upsertPairingRequestMock: vi.fn(async () => ({ code: "CODE", created: true })),
|
||||
}));
|
||||
|
||||
vi.mock("openclaw/plugin-sdk/conversation-runtime", async () => {
|
||||
const actual = await vi.importActual<typeof import("openclaw/plugin-sdk/conversation-runtime")>(
|
||||
"openclaw/plugin-sdk/conversation-runtime",
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
resolvePairingIdLabel: () => "lineUserId",
|
||||
readChannelAllowFromStore: readAllowFromStoreMock,
|
||||
upsertChannelPairingRequest: upsertPairingRequestMock,
|
||||
};
|
||||
});
|
||||
vi.mock("openclaw/plugin-sdk/conversation-runtime", () => ({
|
||||
resolvePairingIdLabel: () => "lineUserId",
|
||||
readChannelAllowFromStore: readAllowFromStoreMock,
|
||||
upsertChannelPairingRequest: upsertPairingRequestMock,
|
||||
}));
|
||||
|
||||
vi.mock("./download.js", () => ({
|
||||
downloadLineMedia: async () => {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ import {
|
|||
} from "./channel-api.js";
|
||||
import { getLineRuntime } from "./runtime.js";
|
||||
|
||||
const loadLineChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime.js"));
|
||||
const loadLineProbeRuntime = createLazyRuntimeModule(() => import("./probe.runtime.js"));
|
||||
const loadLineMonitorRuntime = createLazyRuntimeModule(() => import("./monitor.runtime.js"));
|
||||
|
||||
export const lineGatewayAdapter: NonNullable<ChannelPlugin<ResolvedLineAccount>["gateway"]> = {
|
||||
startAccount: async (ctx) => {
|
||||
|
|
@ -30,7 +31,7 @@ export const lineGatewayAdapter: NonNullable<ChannelPlugin<ResolvedLineAccount>[
|
|||
|
||||
let lineBotLabel = "";
|
||||
try {
|
||||
const probe = await (await loadLineChannelRuntime()).probeLineBot(token, 2500);
|
||||
const probe = await (await loadLineProbeRuntime()).probeLineBot(token, 2500);
|
||||
const displayName = probe.ok ? probe.bot?.displayName?.trim() : null;
|
||||
if (displayName) {
|
||||
lineBotLabel = ` (${displayName})`;
|
||||
|
|
@ -45,7 +46,7 @@ export const lineGatewayAdapter: NonNullable<ChannelPlugin<ResolvedLineAccount>[
|
|||
|
||||
const monitorLineProvider =
|
||||
getLineRuntime().channel.line?.monitorLineProvider ??
|
||||
(await loadLineChannelRuntime()).monitorLineProvider;
|
||||
(await loadLineMonitorRuntime()).monitorLineProvider;
|
||||
|
||||
return await monitorLineProvider({
|
||||
channelAccessToken: token,
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
export { monitorLineProvider } from "./monitor.js";
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { probeLineBot } from "./probe.js";
|
||||
|
|
@ -28,7 +28,6 @@ vi.mock("@line/bot-sdk", () => ({
|
|||
}));
|
||||
|
||||
const lineConfigure = createPluginSetupWizardConfigure(linePlugin);
|
||||
let probeLineBot: typeof import("./probe.js").probeLineBot;
|
||||
const LINE_SRC_PREFIX = `../../${bundledPluginRoot("line")}/src/`;
|
||||
|
||||
function normalizeModuleSpecifier(specifier: string): string | null {
|
||||
|
|
@ -296,10 +295,6 @@ describe("line setup wizard", () => {
|
|||
});
|
||||
|
||||
describe("probeLineBot", () => {
|
||||
beforeAll(async () => {
|
||||
({ probeLineBot } = await import("./probe.js"));
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
getBotInfoMock.mockReset();
|
||||
MessagingApiClientMock.mockReset();
|
||||
|
|
@ -315,6 +310,7 @@ describe("probeLineBot", () => {
|
|||
});
|
||||
|
||||
it("returns timeout when bot info stalls", async () => {
|
||||
const { probeLineBot } = await import("./probe.js");
|
||||
vi.useFakeTimers();
|
||||
getBotInfoMock.mockImplementation(() => new Promise(() => {}));
|
||||
|
||||
|
|
@ -327,6 +323,7 @@ describe("probeLineBot", () => {
|
|||
});
|
||||
|
||||
it("returns bot info when available", async () => {
|
||||
const { probeLineBot } = await import("./probe.js");
|
||||
getBotInfoMock.mockResolvedValue({
|
||||
displayName: "OpenClaw",
|
||||
userId: "U123",
|
||||
|
|
@ -343,6 +340,7 @@ describe("probeLineBot", () => {
|
|||
|
||||
describe("linePlugin status.probeAccount", () => {
|
||||
it("falls back to the direct probe helper when runtime is not initialized", async () => {
|
||||
const { probeLineBot } = await import("./probe.js");
|
||||
MessagingApiClientMock.mockReset();
|
||||
MessagingApiClientMock.mockImplementation(function () {
|
||||
return { getBotInfo: getBotInfoMock };
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
import { hasLineCredentials } from "./account-helpers.js";
|
||||
import { DEFAULT_ACCOUNT_ID, type ChannelPlugin, type ResolvedLineAccount } from "./channel-api.js";
|
||||
|
||||
const loadLineChannelRuntime = createLazyRuntimeModule(() => import("./channel.runtime.js"));
|
||||
const loadLineProbeRuntime = createLazyRuntimeModule(() => import("./probe.runtime.js"));
|
||||
|
||||
const collectLineStatusIssues = createDependentCredentialStatusIssueCollector({
|
||||
channel: "line",
|
||||
|
|
@ -23,7 +23,7 @@ export const lineStatusAdapter: NonNullable<ChannelPlugin<ResolvedLineAccount>["
|
|||
collectStatusIssues: collectLineStatusIssues,
|
||||
buildChannelSummary: ({ snapshot }) => buildTokenChannelStatusSummary(snapshot),
|
||||
probeAccount: async ({ account, timeoutMs }) =>
|
||||
await (await loadLineChannelRuntime()).probeLineBot(account.channelAccessToken, timeoutMs),
|
||||
await (await loadLineProbeRuntime()).probeLineBot(account.channelAccessToken, timeoutMs),
|
||||
resolveAccountSnapshot: ({ account }) => ({
|
||||
accountId: account.accountId,
|
||||
name: account.name,
|
||||
|
|
|
|||
Loading…
Reference in New Issue