mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-06 23:55:12 +00:00
refactor(discord): share partial channel test fixtures
This commit is contained in:
@@ -275,7 +275,7 @@ export async function processDiscordMessage(
|
||||
const forumParentSlug =
|
||||
isForumParent && threadParentName ? normalizeDiscordSlug(threadParentName) : "";
|
||||
const threadChannelId = threadChannel?.id;
|
||||
const threadInheritParent = discordConfig?.thread?.inheritParent ?? false;
|
||||
const threadParentInheritanceEnabled = discordConfig?.thread?.inheritParent ?? false;
|
||||
const isForumStarter =
|
||||
Boolean(threadChannelId && isForumParent && forumParentSlug) && message.id === threadChannelId;
|
||||
const forumContextLine = isForumStarter ? `[Forum parent: #${forumParentSlug}]` : null;
|
||||
@@ -411,7 +411,7 @@ export async function processDiscordMessage(
|
||||
peer: { kind: "channel", id: threadParentId },
|
||||
});
|
||||
}
|
||||
if (!threadInheritParent) {
|
||||
if (!threadParentInheritanceEnabled) {
|
||||
parentSessionKey = undefined;
|
||||
}
|
||||
}
|
||||
@@ -438,7 +438,7 @@ export async function processDiscordMessage(
|
||||
agentId: route.agentId,
|
||||
channel: route.channel,
|
||||
cfg,
|
||||
threadInheritParent,
|
||||
threadParentInheritanceEnabled,
|
||||
});
|
||||
const deliverTarget = replyPlan.deliverTarget;
|
||||
const replyTarget = replyPlan.replyTarget;
|
||||
|
||||
@@ -284,20 +284,20 @@ describe("resolveDiscordAutoThreadContext", () => {
|
||||
name: "no created thread",
|
||||
createdThreadId: undefined,
|
||||
expectedNull: true,
|
||||
inheritParent: undefined,
|
||||
parentInheritanceEnabled: undefined,
|
||||
},
|
||||
{
|
||||
name: "created thread without parent inheritance",
|
||||
createdThreadId: "thread",
|
||||
expectedNull: false,
|
||||
inheritParent: false,
|
||||
parentInheritanceEnabled: false,
|
||||
expectedParentSessionKey: undefined,
|
||||
},
|
||||
{
|
||||
name: "created thread with parent inheritance",
|
||||
createdThreadId: "thread",
|
||||
expectedNull: false,
|
||||
inheritParent: true,
|
||||
parentInheritanceEnabled: true,
|
||||
expectedParentSessionKey: buildAgentSessionKey({
|
||||
agentId: "agent",
|
||||
channel: "discord",
|
||||
@@ -312,7 +312,7 @@ describe("resolveDiscordAutoThreadContext", () => {
|
||||
channel: "discord",
|
||||
messageChannelId: "parent",
|
||||
createdThreadId: testCase.createdThreadId,
|
||||
inheritParent: testCase.inheritParent,
|
||||
parentInheritanceEnabled: testCase.parentInheritanceEnabled,
|
||||
});
|
||||
|
||||
if (testCase.expectedNull) {
|
||||
@@ -472,7 +472,7 @@ describe("resolveDiscordAutoThreadReplyPlan", () => {
|
||||
client?: Client;
|
||||
channelConfig?: DiscordChannelConfigResolved;
|
||||
threadChannel?: { id: string } | null;
|
||||
threadInheritParent?: boolean;
|
||||
threadParentInheritanceEnabled?: boolean;
|
||||
}) {
|
||||
return {
|
||||
client:
|
||||
@@ -492,7 +492,7 @@ describe("resolveDiscordAutoThreadReplyPlan", () => {
|
||||
replyToMode: "all" as const,
|
||||
agentId: "agent",
|
||||
channel: "discord" as const,
|
||||
threadInheritParent: overrides?.threadInheritParent,
|
||||
threadParentInheritanceEnabled: overrides?.threadParentInheritanceEnabled,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -513,7 +513,7 @@ describe("resolveDiscordAutoThreadReplyPlan", () => {
|
||||
{
|
||||
name: "created thread with parent inheritance",
|
||||
params: {
|
||||
threadInheritParent: true,
|
||||
threadParentInheritanceEnabled: true,
|
||||
},
|
||||
expectedDeliverTarget: "channel:thread",
|
||||
expectedReplyReference: undefined,
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { DiscordAccountConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
import * as pluginCommandsModule from "openclaw/plugin-sdk/plugin-runtime";
|
||||
import * as dispatcherModule from "openclaw/plugin-sdk/reply-dispatch-runtime";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { defineThrowingDiscordChannelGetter } from "../test-support/partial-channel.js";
|
||||
import { __testing as nativeCommandTesting, createDiscordNativeCommand } from "./native-command.js";
|
||||
import {
|
||||
createMockCommandInteraction,
|
||||
@@ -157,15 +158,7 @@ describe("Discord native slash commands with commands.allowFrom", () => {
|
||||
it("tolerates partial guild channels whose name getter throws", async () => {
|
||||
const { dispatchSpy, interaction } = await runGuildSlashCommand({
|
||||
mutateInteraction: (currentInteraction) => {
|
||||
Object.defineProperty(currentInteraction.channel, "name", {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get() {
|
||||
throw new Error(
|
||||
"Cannot access rawData on partial Channel. Use fetch() to populate data.",
|
||||
);
|
||||
},
|
||||
});
|
||||
defineThrowingDiscordChannelGetter(currentInteraction.channel, "name");
|
||||
},
|
||||
});
|
||||
expect(interaction.defer).toHaveBeenCalledTimes(1);
|
||||
@@ -176,15 +169,7 @@ describe("Discord native slash commands with commands.allowFrom", () => {
|
||||
it("tolerates partial guild channels whose topic getter throws", async () => {
|
||||
const { dispatchSpy, interaction } = await runGuildSlashCommand({
|
||||
mutateInteraction: (currentInteraction) => {
|
||||
Object.defineProperty(currentInteraction.channel, "topic", {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get() {
|
||||
throw new Error(
|
||||
"Cannot access rawData on partial Channel. Use fetch() to populate data.",
|
||||
);
|
||||
},
|
||||
});
|
||||
defineThrowingDiscordChannelGetter(currentInteraction.channel, "topic");
|
||||
},
|
||||
});
|
||||
expect(interaction.defer).toHaveBeenCalledTimes(1);
|
||||
@@ -199,15 +184,7 @@ describe("Discord native slash commands with commands.allowFrom", () => {
|
||||
type: ChannelType.PublicThread,
|
||||
id: currentInteraction.channel.id,
|
||||
} as MockCommandInteraction["channel"];
|
||||
Object.defineProperty(currentInteraction.channel, "parentId", {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get() {
|
||||
throw new Error(
|
||||
"Cannot access rawData on partial Channel. Use fetch() to populate data.",
|
||||
);
|
||||
},
|
||||
});
|
||||
defineThrowingDiscordChannelGetter(currentInteraction.channel, "parentId");
|
||||
},
|
||||
});
|
||||
expect(interaction.defer).toHaveBeenCalledTimes(1);
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
import * as globalsModule from "openclaw/plugin-sdk/runtime-env";
|
||||
import * as commandTextModule from "openclaw/plugin-sdk/text-runtime";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { defineThrowingDiscordChannelGetter } from "../test-support/partial-channel.js";
|
||||
import { resolveDiscordChannelContext } from "./agent-components-helpers.js";
|
||||
import * as modelPickerPreferencesModule from "./model-picker-preferences.js";
|
||||
import * as modelPickerModule from "./model-picker.js";
|
||||
@@ -105,20 +106,6 @@ function createInteraction(params?: { userId?: string; values?: string[] }): Moc
|
||||
};
|
||||
}
|
||||
|
||||
function makePartialChannelThrow<T extends object>(
|
||||
target: T,
|
||||
key: keyof T & string,
|
||||
message = "Cannot access rawData on partial Channel. Use fetch() to populate data.",
|
||||
) {
|
||||
Object.defineProperty(target, key, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get() {
|
||||
throw new Error(message);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function createDefaultModelPickerData(): ModelsProviderData {
|
||||
return createModelsProviderData({
|
||||
openai: ["gpt-4.1", "gpt-4o"],
|
||||
@@ -357,7 +344,7 @@ describe("Discord model picker interactions", () => {
|
||||
|
||||
const dispatchSpy = createDispatchSpy();
|
||||
const submitInteraction = createInteraction({ userId: "owner" });
|
||||
makePartialChannelThrow(submitInteraction.channel, "name");
|
||||
defineThrowingDiscordChannelGetter(submitInteraction.channel, "name");
|
||||
|
||||
const button = createModelPickerFallbackButton(context, dispatchSpy);
|
||||
await button.run(
|
||||
@@ -395,7 +382,10 @@ describe("Discord model picker interactions", () => {
|
||||
parent?: { id?: string; name?: string };
|
||||
};
|
||||
submitInteraction.channel = threadChannel as MockInteraction["channel"];
|
||||
makePartialChannelThrow(threadChannel.parent as { id?: string; name?: string }, "name");
|
||||
defineThrowingDiscordChannelGetter(
|
||||
threadChannel.parent as { id?: string; name?: string },
|
||||
"name",
|
||||
);
|
||||
|
||||
const button = createModelPickerFallbackButton(context, dispatchSpy);
|
||||
await button.run(
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
createTestRegistry,
|
||||
setActivePluginRegistry,
|
||||
} from "../../../../test/helpers/plugins/plugin-registry.js";
|
||||
import { defineThrowingDiscordChannelGetter } from "../test-support/partial-channel.js";
|
||||
import { resolveDiscordNativeInteractionRouteState } from "./native-command-route.js";
|
||||
import {
|
||||
createMockCommandInteraction as createInteraction,
|
||||
@@ -636,13 +637,7 @@ describe("Discord native plugin command dispatch", () => {
|
||||
guildId: "345678901234567890",
|
||||
guildName: "Test Guild",
|
||||
});
|
||||
Object.defineProperty(interaction.channel, "parentId", {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get() {
|
||||
throw new Error("Cannot access rawData on partial Channel. Use fetch() to populate data.");
|
||||
},
|
||||
});
|
||||
defineThrowingDiscordChannelGetter(interaction.channel, "parentId");
|
||||
(interaction.client as { fetchChannel: ReturnType<typeof vi.fn> }).fetchChannel = vi.fn(
|
||||
async (channelId: string) => {
|
||||
if (channelId === "partial-thread-123") {
|
||||
|
||||
@@ -388,7 +388,7 @@ export function resolveDiscordAutoThreadContext(params: {
|
||||
channel: string;
|
||||
messageChannelId: string;
|
||||
createdThreadId?: string | null;
|
||||
inheritParent?: boolean;
|
||||
parentInheritanceEnabled?: boolean;
|
||||
}): DiscordAutoThreadContext | null {
|
||||
const createdThreadId = normalizeOptionalStringifiedId(params.createdThreadId) ?? "";
|
||||
if (!createdThreadId) {
|
||||
@@ -405,7 +405,7 @@ export function resolveDiscordAutoThreadContext(params: {
|
||||
peer: { kind: "channel", id: createdThreadId },
|
||||
});
|
||||
const parentSessionKey =
|
||||
params.inheritParent === true
|
||||
params.parentInheritanceEnabled === true
|
||||
? buildAgentSessionKey({
|
||||
agentId: params.agentId,
|
||||
channel: params.channel,
|
||||
@@ -451,7 +451,7 @@ export async function resolveDiscordAutoThreadReplyPlan(
|
||||
agentId: string;
|
||||
channel: string;
|
||||
cfg?: OpenClawConfig;
|
||||
threadInheritParent?: boolean;
|
||||
threadParentInheritanceEnabled?: boolean;
|
||||
},
|
||||
): Promise<DiscordAutoThreadReplyPlan> {
|
||||
const messageChannelId = resolveTrimmedDiscordMessageChannelId(params);
|
||||
@@ -487,7 +487,7 @@ export async function resolveDiscordAutoThreadReplyPlan(
|
||||
channel: params.channel,
|
||||
messageChannelId,
|
||||
createdThreadId,
|
||||
inheritParent: params.threadInheritParent,
|
||||
parentInheritanceEnabled: params.threadParentInheritanceEnabled,
|
||||
})
|
||||
: null;
|
||||
return { ...deliveryPlan, createdThreadId, autoThreadContext };
|
||||
|
||||
26
extensions/discord/src/test-support/partial-channel.ts
Normal file
26
extensions/discord/src/test-support/partial-channel.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
export const DISCORD_PARTIAL_CHANNEL_RAW_DATA_ERROR =
|
||||
"Cannot access rawData on partial Channel. Use fetch() to populate data.";
|
||||
|
||||
export function defineThrowingDiscordChannelGetter(
|
||||
channel: object,
|
||||
key: string,
|
||||
message = DISCORD_PARTIAL_CHANNEL_RAW_DATA_ERROR,
|
||||
) {
|
||||
Object.defineProperty(channel, key, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get() {
|
||||
throw new Error(message);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function createPartialDiscordChannelWithThrowingGetters<T extends object>(
|
||||
channel: T,
|
||||
keys: readonly string[],
|
||||
): T {
|
||||
for (const key of keys) {
|
||||
defineThrowingDiscordChannelGetter(channel, key);
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { CommandInteraction, CommandWithSubcommands } from "@buape/carbon";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { createPartialDiscordChannelWithThrowingGetters } from "../test-support/partial-channel.js";
|
||||
import { createDiscordVoiceCommand } from "./command.js";
|
||||
import type { DiscordVoiceManager } from "./manager.js";
|
||||
|
||||
@@ -103,19 +104,10 @@ describe("createDiscordVoiceCommand", () => {
|
||||
status: statusSpy,
|
||||
} as unknown as DiscordVoiceManager;
|
||||
const { status } = createVoiceCommandHarness(manager);
|
||||
const partialChannel = { id: "123456789012345678" };
|
||||
Object.defineProperties(partialChannel, {
|
||||
name: {
|
||||
get() {
|
||||
throw new Error("Cannot access rawData on partial Channel");
|
||||
},
|
||||
},
|
||||
parentId: {
|
||||
get() {
|
||||
throw new Error("Cannot access rawData on partial Channel");
|
||||
},
|
||||
},
|
||||
});
|
||||
const partialChannel = createPartialDiscordChannelWithThrowingGetters(
|
||||
{ id: "123456789012345678" },
|
||||
["name", "parentId"],
|
||||
);
|
||||
const { interaction, reply } = createInteraction({
|
||||
channel: partialChannel as CommandInteraction["channel"],
|
||||
client: { fetchChannel: vi.fn(async () => null) } as unknown as CommandInteraction["client"],
|
||||
|
||||
Reference in New Issue
Block a user