From b01335830d339db6ff9cd88b219984835a249aa3 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 21 Feb 2026 19:48:36 +0000 Subject: [PATCH] test(pairing): dedupe fixture writers and expand store coverage --- src/pairing/pairing-store.test.ts | 142 +++++++++++++++++++++++------- 1 file changed, 109 insertions(+), 33 deletions(-) diff --git a/src/pairing/pairing-store.test.ts b/src/pairing/pairing-store.test.ts index 1d060fbd8..e44dd391e 100644 --- a/src/pairing/pairing-store.test.ts +++ b/src/pairing/pairing-store.test.ts @@ -10,6 +10,8 @@ import { approveChannelPairingCode, listChannelPairingRequests, readChannelAllowFromStore, + readChannelAllowFromStoreSync, + removeChannelAllowFromStoreEntry, upsertChannelPairingRequest, } from "./pairing-store.js"; @@ -32,6 +34,29 @@ async function withTempStateDir(fn: (stateDir: string) => Promise) { return await withEnvAsync({ OPENCLAW_STATE_DIR: dir }, async () => await fn(dir)); } +async function writeJsonFixture(filePath: string, value: unknown) { + await fs.writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8"); +} + +function resolvePairingFilePath(stateDir: string, channel: string) { + return path.join(resolveOAuthDir(process.env, stateDir), `${channel}-pairing.json`); +} + +async function writeAllowFromFixture(params: { + stateDir: string; + channel: string; + allowFrom: string[]; + accountId?: string; +}) { + const oauthDir = resolveOAuthDir(process.env, params.stateDir); + await fs.mkdir(oauthDir, { recursive: true }); + const suffix = params.accountId ? `-${params.accountId}` : ""; + await writeJsonFixture(path.join(oauthDir, `${params.channel}${suffix}-allowFrom.json`), { + version: 1, + allowFrom: params.allowFrom, + }); +} + describe("pairing store", () => { it("reuses pending code and reports created=false", async () => { await withTempStateDir(async () => { @@ -61,8 +86,7 @@ describe("pairing store", () => { }); expect(created.created).toBe(true); - const oauthDir = resolveOAuthDir(process.env, stateDir); - const filePath = path.join(oauthDir, "signal-pairing.json"); + const filePath = resolvePairingFilePath(stateDir, "signal"); const raw = await fs.readFile(filePath, "utf8"); const parsed = JSON.parse(raw) as { requests?: Array>; @@ -73,11 +97,7 @@ describe("pairing store", () => { createdAt: expiredAt, lastSeenAt: expiredAt, })); - await fs.writeFile( - filePath, - `${JSON.stringify({ version: 1, requests }, null, 2)}\n`, - "utf8", - ); + await writeJsonFixture(filePath, { version: 1, requests }); const list = await listChannelPairingRequests("signal"); expect(list).toHaveLength(0); @@ -183,34 +203,90 @@ describe("pairing store", () => { }); }); + it("filters approvals by account id and ignores blank approval codes", async () => { + await withTempStateDir(async () => { + const created = await upsertChannelPairingRequest({ + channel: "telegram", + accountId: "yy", + id: "12345", + }); + expect(created.created).toBe(true); + + const blank = await approveChannelPairingCode({ + channel: "telegram", + code: " ", + }); + expect(blank).toBeNull(); + + const mismatched = await approveChannelPairingCode({ + channel: "telegram", + code: created.code, + accountId: "zz", + }); + expect(mismatched).toBeNull(); + + const pending = await listChannelPairingRequests("telegram"); + expect(pending).toHaveLength(1); + expect(pending[0]?.id).toBe("12345"); + }); + }); + + it("removes account-scoped allowFrom entries idempotently", async () => { + await withTempStateDir(async () => { + await addChannelAllowFromStoreEntry({ + channel: "telegram", + accountId: "yy", + entry: "12345", + }); + + const removed = await removeChannelAllowFromStoreEntry({ + channel: "telegram", + accountId: "yy", + entry: "12345", + }); + expect(removed.changed).toBe(true); + expect(removed.allowFrom).toEqual([]); + + const removedAgain = await removeChannelAllowFromStoreEntry({ + channel: "telegram", + accountId: "yy", + entry: "12345", + }); + expect(removedAgain.changed).toBe(false); + expect(removedAgain.allowFrom).toEqual([]); + }); + }); + + it("reads sync allowFrom with scoped + legacy dedupe and wildcard filtering", async () => { + await withTempStateDir(async (stateDir) => { + await writeAllowFromFixture({ + stateDir, + channel: "telegram", + allowFrom: ["1001", "*", " 1001 ", " "], + }); + await writeAllowFromFixture({ + stateDir, + channel: "telegram", + accountId: "yy", + allowFrom: [" 1002 ", "1001", "1002"], + }); + + const scoped = readChannelAllowFromStoreSync("telegram", process.env, "yy"); + const channelScoped = readChannelAllowFromStoreSync("telegram"); + expect(scoped).toEqual(["1002", "1001"]); + expect(channelScoped).toEqual(["1001", "1001"]); + }); + }); + it("reads legacy channel-scoped allowFrom for default account", async () => { await withTempStateDir(async (stateDir) => { - const oauthDir = resolveOAuthDir(process.env, stateDir); - await fs.mkdir(oauthDir, { recursive: true }); - await fs.writeFile( - path.join(oauthDir, "telegram-allowFrom.json"), - JSON.stringify( - { - version: 1, - allowFrom: ["1001"], - }, - null, - 2, - ) + "\n", - "utf8", - ); - await fs.writeFile( - path.join(oauthDir, "telegram-default-allowFrom.json"), - JSON.stringify( - { - version: 1, - allowFrom: ["1002"], - }, - null, - 2, - ) + "\n", - "utf8", - ); + await writeAllowFromFixture({ stateDir, channel: "telegram", allowFrom: ["1001"] }); + await writeAllowFromFixture({ + stateDir, + channel: "telegram", + accountId: "default", + allowFrom: ["1002"], + }); const scoped = await readChannelAllowFromStore("telegram", process.env, "default"); expect(scoped).toEqual(["1002", "1001"]);