114 lines
3.7 KiB
TypeScript
114 lines
3.7 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import type { DiscordDmCommandAccess } from "./dm-command-auth.js";
|
|
import { handleDiscordDmCommandDecision } from "./dm-command-decision.js";
|
|
|
|
function buildDmAccess(overrides: Partial<DiscordDmCommandAccess>): DiscordDmCommandAccess {
|
|
return {
|
|
decision: "allow",
|
|
reason: "ok",
|
|
commandAuthorized: true,
|
|
allowMatch: { allowed: true, matchKey: "123", matchSource: "id" },
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
const TEST_ACCOUNT_ID = "default";
|
|
const TEST_SENDER = { id: "123", tag: "alice#0001", name: "alice" };
|
|
|
|
function createDmDecisionHarness(params?: { pairingCreated?: boolean }) {
|
|
const onPairingCreated = vi.fn(async () => {});
|
|
const onUnauthorized = vi.fn(async () => {});
|
|
const upsertPairingRequest = vi.fn(async () => ({
|
|
code: "PAIR-1",
|
|
created: params?.pairingCreated ?? true,
|
|
}));
|
|
return { onPairingCreated, onUnauthorized, upsertPairingRequest };
|
|
}
|
|
|
|
async function runPairingDecision(params?: { pairingCreated?: boolean }) {
|
|
const harness = createDmDecisionHarness({ pairingCreated: params?.pairingCreated });
|
|
const allowed = await handleDiscordDmCommandDecision({
|
|
dmAccess: buildDmAccess({
|
|
decision: "pairing",
|
|
commandAuthorized: false,
|
|
allowMatch: { allowed: false },
|
|
}),
|
|
accountId: TEST_ACCOUNT_ID,
|
|
sender: TEST_SENDER,
|
|
onPairingCreated: harness.onPairingCreated,
|
|
onUnauthorized: harness.onUnauthorized,
|
|
upsertPairingRequest: harness.upsertPairingRequest,
|
|
});
|
|
return { allowed, ...harness };
|
|
}
|
|
|
|
describe("handleDiscordDmCommandDecision", () => {
|
|
it("returns true for allowed DM access", async () => {
|
|
const { onPairingCreated, onUnauthorized, upsertPairingRequest } = createDmDecisionHarness();
|
|
|
|
const allowed = await handleDiscordDmCommandDecision({
|
|
dmAccess: buildDmAccess({ decision: "allow" }),
|
|
accountId: TEST_ACCOUNT_ID,
|
|
sender: TEST_SENDER,
|
|
onPairingCreated,
|
|
onUnauthorized,
|
|
upsertPairingRequest,
|
|
});
|
|
|
|
expect(allowed).toBe(true);
|
|
expect(upsertPairingRequest).not.toHaveBeenCalled();
|
|
expect(onPairingCreated).not.toHaveBeenCalled();
|
|
expect(onUnauthorized).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("creates pairing reply for new pairing requests", async () => {
|
|
const { allowed, onPairingCreated, onUnauthorized, upsertPairingRequest } =
|
|
await runPairingDecision();
|
|
|
|
expect(allowed).toBe(false);
|
|
expect(upsertPairingRequest).toHaveBeenCalledWith({
|
|
channel: "discord",
|
|
id: "123",
|
|
accountId: TEST_ACCOUNT_ID,
|
|
meta: {
|
|
tag: TEST_SENDER.tag,
|
|
name: TEST_SENDER.name,
|
|
},
|
|
});
|
|
expect(onPairingCreated).toHaveBeenCalledWith("PAIR-1");
|
|
expect(onUnauthorized).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("skips pairing reply when pairing request already exists", async () => {
|
|
const { allowed, onPairingCreated, onUnauthorized } = await runPairingDecision({
|
|
pairingCreated: false,
|
|
});
|
|
|
|
expect(allowed).toBe(false);
|
|
expect(onPairingCreated).not.toHaveBeenCalled();
|
|
expect(onUnauthorized).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("runs unauthorized handler for blocked DM access", async () => {
|
|
const { onPairingCreated, onUnauthorized, upsertPairingRequest } = createDmDecisionHarness();
|
|
|
|
const allowed = await handleDiscordDmCommandDecision({
|
|
dmAccess: buildDmAccess({
|
|
decision: "block",
|
|
commandAuthorized: false,
|
|
allowMatch: { allowed: false },
|
|
}),
|
|
accountId: TEST_ACCOUNT_ID,
|
|
sender: TEST_SENDER,
|
|
onPairingCreated,
|
|
onUnauthorized,
|
|
upsertPairingRequest,
|
|
});
|
|
|
|
expect(allowed).toBe(false);
|
|
expect(onUnauthorized).toHaveBeenCalledTimes(1);
|
|
expect(upsertPairingRequest).not.toHaveBeenCalled();
|
|
expect(onPairingCreated).not.toHaveBeenCalled();
|
|
});
|
|
});
|