Discord: stabilize directory lookup tests

This commit is contained in:
huntharo 2026-03-14 22:01:44 -04:00 committed by Vincent Koc
parent 12bff4ee3f
commit d0731c35b2
2 changed files with 52 additions and 56 deletions

View File

@ -1,74 +1,72 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { DirectoryConfigParams } from "../../../src/channels/plugins/directory-config.js";
const mocks = vi.hoisted(() => ({
fetchDiscord: vi.fn(),
normalizeDiscordToken: vi.fn((token: string) => token.trim()),
resolveDiscordAccount: vi.fn(),
}));
vi.mock("./accounts.js", () => ({
resolveDiscordAccount: mocks.resolveDiscordAccount,
}));
vi.mock("./api.js", () => ({
fetchDiscord: mocks.fetchDiscord,
}));
vi.mock("./token.js", () => ({
normalizeDiscordToken: mocks.normalizeDiscordToken,
}));
import type { OpenClawConfig } from "../../../src/config/config.js";
import { listDiscordDirectoryGroupsLive, listDiscordDirectoryPeersLive } from "./directory-live.js";
function makeParams(overrides: Partial<DirectoryConfigParams> = {}): DirectoryConfigParams {
return {
cfg: {} as DirectoryConfigParams["cfg"],
cfg: {
channels: {
discord: {
token: "test-token",
},
},
} as OpenClawConfig,
accountId: "default",
...overrides,
};
}
function jsonResponse(value: unknown): Response {
return new Response(JSON.stringify(value), {
status: 200,
headers: { "content-type": "application/json" },
});
}
describe("discord directory live lookups", () => {
beforeEach(() => {
vi.clearAllMocks();
mocks.resolveDiscordAccount.mockReturnValue({ token: "test-token" });
mocks.normalizeDiscordToken.mockImplementation((token: string) => token.trim());
vi.restoreAllMocks();
});
it("returns empty group directory when token is missing", async () => {
mocks.normalizeDiscordToken.mockReturnValue("");
const rows = await listDiscordDirectoryGroupsLive(makeParams({ query: "general" }));
const rows = await listDiscordDirectoryGroupsLive({
...makeParams(),
cfg: { channels: { discord: { token: "" } } } as OpenClawConfig,
query: "general",
});
expect(rows).toEqual([]);
expect(mocks.fetchDiscord).not.toHaveBeenCalled();
});
it("returns empty peer directory without query and skips guild listing", async () => {
const fetchSpy = vi.spyOn(globalThis, "fetch");
const rows = await listDiscordDirectoryPeersLive(makeParams({ query: " " }));
expect(rows).toEqual([]);
expect(mocks.fetchDiscord).not.toHaveBeenCalled();
expect(fetchSpy).not.toHaveBeenCalled();
});
it("filters group channels by query and respects limit", async () => {
mocks.fetchDiscord.mockImplementation(async (path: string) => {
if (path === "/users/@me/guilds") {
return [
vi.spyOn(globalThis, "fetch").mockImplementation(async (input) => {
const url = String(input);
if (url.endsWith("/users/@me/guilds")) {
return jsonResponse([
{ id: "g1", name: "Guild 1" },
{ id: "g2", name: "Guild 2" },
];
]);
}
if (path === "/guilds/g1/channels") {
return [
if (url.endsWith("/guilds/g1/channels")) {
return jsonResponse([
{ id: "c1", name: "general" },
{ id: "c2", name: "random" },
];
]);
}
if (path === "/guilds/g2/channels") {
return [{ id: "c3", name: "announcements" }];
if (url.endsWith("/guilds/g2/channels")) {
return jsonResponse([{ id: "c3", name: "announcements" }]);
}
return [];
return jsonResponse([]);
});
const rows = await listDiscordDirectoryGroupsLive(makeParams({ query: "an", limit: 2 }));
@ -80,21 +78,22 @@ describe("discord directory live lookups", () => {
});
it("returns ranked peer results and caps member search by limit", async () => {
mocks.fetchDiscord.mockImplementation(async (path: string) => {
if (path === "/users/@me/guilds") {
return [{ id: "g1", name: "Guild 1" }];
vi.spyOn(globalThis, "fetch").mockImplementation(async (input) => {
const url = String(input);
if (url.endsWith("/users/@me/guilds")) {
return jsonResponse([{ id: "g1", name: "Guild 1" }]);
}
if (path.startsWith("/guilds/g1/members/search?")) {
const params = new URLSearchParams(path.split("?")[1] ?? "");
if (url.includes("/guilds/g1/members/search?")) {
const params = new URL(url).searchParams;
expect(params.get("query")).toBe("alice");
expect(params.get("limit")).toBe("2");
return [
return jsonResponse([
{ user: { id: "u1", username: "alice", bot: false }, nick: "Ali" },
{ user: { id: "u2", username: "alice-bot", bot: true }, nick: null },
{ user: { id: "u3", username: "ignored", bot: false }, nick: null },
];
]);
}
return [];
return jsonResponse([]);
});
const rows = await listDiscordDirectoryPeersLive(makeParams({ query: "alice", limit: 2 }));

View File

@ -1,13 +1,9 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../../../src/config/config.js";
import { listDiscordDirectoryPeersLive } from "./directory-live.js";
import * as directoryLive from "./directory-live.js";
import { normalizeDiscordMessagingTarget } from "./normalize.js";
import { parseDiscordTarget, resolveDiscordChannelId, resolveDiscordTarget } from "./targets.js";
vi.mock("./directory-live.js", () => ({
listDiscordDirectoryPeersLive: vi.fn(),
}));
describe("parseDiscordTarget", () => {
it("parses user mention and prefixes", () => {
const cases = [
@ -73,14 +69,15 @@ describe("resolveDiscordChannelId", () => {
describe("resolveDiscordTarget", () => {
const cfg = { channels: { discord: {} } } as OpenClawConfig;
const listPeers = vi.mocked(listDiscordDirectoryPeersLive);
beforeEach(() => {
listPeers.mockClear();
vi.restoreAllMocks();
});
it("returns a resolved user for usernames", async () => {
listPeers.mockResolvedValueOnce([{ kind: "user", id: "user:999", name: "Jane" } as const]);
vi.spyOn(directoryLive, "listDiscordDirectoryPeersLive").mockResolvedValueOnce([
{ kind: "user", id: "user:999", name: "Jane" } as const,
]);
await expect(
resolveDiscordTarget("jane", { cfg, accountId: "default" }),
@ -88,14 +85,14 @@ describe("resolveDiscordTarget", () => {
});
it("falls back to parsing when lookup misses", async () => {
listPeers.mockResolvedValueOnce([]);
vi.spyOn(directoryLive, "listDiscordDirectoryPeersLive").mockResolvedValueOnce([]);
await expect(
resolveDiscordTarget("general", { cfg, accountId: "default" }),
).resolves.toMatchObject({ kind: "channel", id: "general" });
});
it("does not call directory lookup for explicit user ids", async () => {
listPeers.mockResolvedValueOnce([]);
const listPeers = vi.spyOn(directoryLive, "listDiscordDirectoryPeersLive");
await expect(
resolveDiscordTarget("user:123", { cfg, accountId: "default" }),
).resolves.toMatchObject({ kind: "user", id: "123" });