






























@@ -118,6 +118,41 @@ describe("discord component registry", () => {
118118expect(resolveDiscordComponentEntry({ id: "btn_1" })).toBeNull();
119119});
120120121+it("consumes sibling entries from the same non-reusable component message", () => {
122+const result = buildDiscordComponentMessage({
123+spec: {
124+text: "Confirm action",
125+blocks: [
126+{
127+type: "actions",
128+buttons: [
129+{ label: "Confirm", callbackData: "confirm" },
130+{ label: "Cancel", callbackData: "cancel" },
131+],
132+},
133+],
134+},
135+});
136+const confirm = result.entries.find((entry) => entry.label === "Confirm");
137+const cancel = result.entries.find((entry) => entry.label === "Cancel");
138+expect(confirm?.consumptionGroupId).toBeTruthy();
139+expect(cancel?.consumptionGroupId).toBe(confirm?.consumptionGroupId);
140+expect(confirm?.consumptionGroupEntryIds).toEqual(
141+expect.arrayContaining([confirm?.id, cancel?.id]),
142+);
143+144+registerDiscordComponentEntries({
145+entries: result.entries,
146+modals: [],
147+messageId: "msg_1",
148+ttlMs: 1000,
149+});
150+151+const consumed = resolveDiscordComponentEntry({ id: confirm?.id ?? "" });
152+expect(consumed?.label).toBe("Confirm");
153+expect(resolveDiscordComponentEntry({ id: cancel?.id ?? "", consume: false })).toBeNull();
154+});
155+121156it("shares registry state across duplicate module instances", async () => {
122157const first = (await import(
123158`${componentsRegistryModuleUrl}?t=first-${Date.now()}`
@@ -208,6 +243,49 @@ describe("discord component registry", () => {
208243expect(openKeyedStore).toHaveBeenCalledTimes(4);
209244});
210245246+it("deletes sibling persistent component entries when a group entry is consumed", async () => {
247+const componentDelete = vi.fn().mockResolvedValue(true);
248+const componentStore = {
249+register: vi.fn(),
250+lookup: vi.fn(),
251+consume: vi.fn().mockResolvedValue({
252+version: 1,
253+entry: {
254+id: "btn_confirm",
255+kind: "button",
256+label: "Confirm",
257+consumptionGroupId: "grp_1",
258+consumptionGroupEntryIds: ["btn_confirm", "btn_cancel"],
259+},
260+}),
261+delete: componentDelete,
262+};
263+const modalStore = {
264+register: vi.fn(),
265+lookup: vi.fn(),
266+consume: vi.fn(),
267+delete: vi.fn(),
268+};
269+const openKeyedStore = vi.fn((opts: { namespace: string }) =>
270+opts.namespace === "discord.components" ? componentStore : modalStore,
271+);
272+const { setDiscordRuntime } = await import("./runtime.js");
273+setDiscordRuntime({
274+state: { openKeyedStore },
275+logging: { getChildLogger: () => ({ warn: vi.fn() }) },
276+} as never);
277+278+clearDiscordComponentEntries();
279+await expect(
280+resolveDiscordComponentEntryWithPersistence({ id: "btn_confirm" }),
281+).resolves.toMatchObject({
282+id: "btn_confirm",
283+});
284+285+await vi.waitFor(() => expect(componentDelete).toHaveBeenCalledWith("btn_cancel"));
286+expect(componentDelete).toHaveBeenCalledWith("btn_confirm");
287+});
288+211289it("falls back to the in-memory registry when persistent state cannot open", async () => {
212290const warn = vi.fn();
213291const { setDiscordRuntime } = await import("./runtime.js");
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。