test(desktop): own exact recipient restoration and publication evidence

Keep one full storage-to-transport witness and both local restoration checks. Select the destination fixture by pointer so this prefix does not depend on the later native Enter repair.

Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Signed-off-by: Logan Johnson <loganj@squareup.com>
This commit is contained in:
Logan Johnson
2026-09-04 07:11:24 -04:00
parent d0c3498409
commit bb8cca21e1
3 changed files with 114 additions and 3 deletions

View File

@@ -549,7 +549,7 @@ test("relay-only shared agents emit an outbound mention tag when selected", asyn
await expect
.poll(() => readOutgoingMentionPubkeys(page, content))
.toContain(TEST_IDENTITIES.alice.pubkey);
.toEqual([TEST_IDENTITIES.alice.pubkey]);
});
test("typing an exact agent name and Space commits its chip and mention tag", async ({
@@ -580,7 +580,7 @@ test("typing an exact agent name and Space commits its chip and mention tag", as
await page.getByTestId("send-message").click();
await expect
.poll(() => readOutgoingMentionPubkeys(page, content))
.toContain(TEST_IDENTITIES.alice.pubkey);
.toEqual([TEST_IDENTITIES.alice.pubkey]);
});
test("Shift+Space leaves an exact agent name plain and emits no mention tag", async ({

View File

@@ -137,6 +137,76 @@ async function readOutgoingMentionPubkeys(page: Page, content: string) {
}, content);
}
// Exercise the persistence/identity boundary after the duplicate-chip check.
async function assertAuthoredDuplicateIdentity(page: Page) {
await expect
.poll(() =>
page.evaluate((channelId) => {
for (const key of Object.keys(localStorage)) {
if (!key.startsWith("buzz-drafts.v2:")) continue;
const draft = JSON.parse(localStorage.getItem(key) ?? "{}")[
channelId
];
if (draft) return draft.mentionRefs;
}
return null;
}, CHANNEL_ID),
)
.toEqual([{ displayName: "Morgarita", pubkey: AGENT_A, isAgent: true }]);
expect(
await page.evaluate(() =>
(window.__BUZZ_E2E_SIGNED_EVENTS__ ?? []).filter(
(event) => event.kind === 9,
),
),
).toEqual([]);
await openGeneral(page);
const composer = channelComposer(page);
const input = composer.getByTestId("message-input");
await expect(input).toHaveText("@Morgarita authored duplicate");
await expect(input.locator(".agent-mention-highlight")).toHaveCount(1);
await expect(
composer.getByTestId(`composer-address-lock-${AGENT_B}`),
).toHaveCount(0);
await composer.getByTestId("send-message").click();
const expected = [{ content: "@Morgarita authored duplicate", p: [AGENT_A] }];
await expect
.poll(() =>
page.evaluate(() =>
(window.__BUZZ_E2E_SIGNED_EVENTS__ ?? [])
.filter((event) => event.kind === 9)
.map((event) => ({
content: event.content,
p: event.tags.filter((tag) => tag[0] === "p").map((tag) => tag[1]),
})),
),
)
.toEqual(expected);
await expect
.poll(() =>
page.evaluate(() => {
const events: { content: string; p: string[] }[] = [];
for (const entry of window.__BUZZ_E2E_COMMAND_LOG__ ?? []) {
if (entry.command !== "plugin:websocket|send") continue;
const data = (entry.payload as { message?: { data?: string } })
?.message?.data;
if (!data) continue;
const frame = JSON.parse(data);
if (frame[0] !== "EVENT" || frame[1]?.kind !== 9) continue;
events.push({
content: frame[1].content,
p: frame[1].tags
.filter((tag: string[]) => tag[0] === "p")
.map((tag: string[]) => tag[1]),
});
}
return events;
}),
)
.toEqual(expected);
}
async function emitMockMessage(
page: Page,
content: string,
@@ -1318,6 +1388,7 @@ test("an authored duplicate leading mention survives draft restoration", async (
}, CHANNEL_ID),
)
.toBe("@Morgarita authored duplicate");
await assertAuthoredDuplicateIdentity(page);
});
test("typed deletion preserves an identical authored mention in drafts", async ({
@@ -1469,6 +1540,12 @@ test("re-enabling an automatic mention preserves an authored duplicate after dra
}, CHANNEL_ID),
)
.toBe("@Morgarita authored duplicate");
await openGeneral(page);
await expect(input).toHaveText("@Morgarita authored duplicate");
await expect(input.locator(".agent-mention-highlight")).toHaveCount(1);
await expect(
composer.getByTestId(`composer-address-lock-${AGENT_B}`),
).toHaveCount(0);
});
test("a restored multi-word automatic mention remains a chip with the caret after its space", async ({

View File

@@ -53,6 +53,15 @@ test("message with agent mention lands in compose-time channel despite mid-send
}) => {
const MESSAGE_TEXT = `send-binding-repro-${Date.now()}`;
// This case exercises a persistent address; ordinary Enter completion is
// one-time by default. Opt in before the bridge mounts the composer.
await page.addInitScript(() => {
window.localStorage.setItem(
"buzz.messages.keepMentionedAgentsPinned",
"true",
);
});
// Install bridge with:
// - a managed agent that is NOT in general (forces add_channel_members path)
// - a 500ms delay on add_channel_members to open the race window
@@ -81,7 +90,7 @@ test("message with agent mention lands in compose-time channel despite mid-send
await expect(botRow).toBeVisible();
await expect(botRow.getByText("not in channel")).toBeVisible();
// Select BotA from the autocomplete
await input.press("Enter");
await botRow.click();
await page.keyboard.type(` ${MESSAGE_TEXT}`);
// Verify the inline mention and persistent address are present before submitting.
@@ -127,6 +136,31 @@ test("message with agent mention lands in compose-time channel despite mid-send
MESSAGE_TEXT,
);
// The signed event must retain both the compose-time channel and exact key.
await expect
.poll(() =>
page.evaluate(
(text) =>
(window.__BUZZ_E2E_SIGNED_EVENTS__ ?? [])
.filter((event) => event.content.includes(text))
.map((event) => ({
channels: event.tags
.filter((tag) => tag[0] === "h")
.map((tag) => tag[1]),
recipients: event.tags
.filter((tag) => tag[0] === "p")
.map((tag) => tag[1]),
})),
MESSAGE_TEXT,
),
)
.toEqual([
{
channels: ["9a1657ac-f7aa-5db0-b632-d8bbeb6dfb50"],
recipients: [OUT_OF_CHANNEL_BOT_PUBKEY],
},
]);
// --- Assert message did NOT land in agents (switched-to channel) ---
await page.getByTestId("channel-agents").click();
await expect(page.getByTestId("chat-title")).toHaveText("agents");