Spaces:
Paused
Paused
File size: 4,358 Bytes
fb4d8fe | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | import crypto from "node:crypto";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { describe, expect, it, vi } from "vitest";
import { resolveOAuthDir } from "../config/paths.js";
import { listChannelPairingRequests, upsertChannelPairingRequest } from "./pairing-store.js";
async function withTempStateDir<T>(fn: (stateDir: string) => Promise<T>) {
const previous = process.env.OPENCLAW_STATE_DIR;
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-pairing-"));
process.env.OPENCLAW_STATE_DIR = dir;
try {
return await fn(dir);
} finally {
if (previous === undefined) {
delete process.env.OPENCLAW_STATE_DIR;
} else {
process.env.OPENCLAW_STATE_DIR = previous;
}
await fs.rm(dir, { recursive: true, force: true });
}
}
describe("pairing store", () => {
it("reuses pending code and reports created=false", async () => {
await withTempStateDir(async () => {
const first = await upsertChannelPairingRequest({
channel: "discord",
id: "u1",
});
const second = await upsertChannelPairingRequest({
channel: "discord",
id: "u1",
});
expect(first.created).toBe(true);
expect(second.created).toBe(false);
expect(second.code).toBe(first.code);
const list = await listChannelPairingRequests("discord");
expect(list).toHaveLength(1);
expect(list[0]?.code).toBe(first.code);
});
});
it("expires pending requests after TTL", async () => {
await withTempStateDir(async (stateDir) => {
const created = await upsertChannelPairingRequest({
channel: "signal",
id: "+15550001111",
});
expect(created.created).toBe(true);
const oauthDir = resolveOAuthDir(process.env, stateDir);
const filePath = path.join(oauthDir, "signal-pairing.json");
const raw = await fs.readFile(filePath, "utf8");
const parsed = JSON.parse(raw) as {
requests?: Array<Record<string, unknown>>;
};
const expiredAt = new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString();
const requests = (parsed.requests ?? []).map((entry) => ({
...entry,
createdAt: expiredAt,
lastSeenAt: expiredAt,
}));
await fs.writeFile(
filePath,
`${JSON.stringify({ version: 1, requests }, null, 2)}\n`,
"utf8",
);
const list = await listChannelPairingRequests("signal");
expect(list).toHaveLength(0);
const next = await upsertChannelPairingRequest({
channel: "signal",
id: "+15550001111",
});
expect(next.created).toBe(true);
});
});
it("regenerates when a generated code collides", async () => {
await withTempStateDir(async () => {
const spy = vi.spyOn(crypto, "randomInt");
try {
spy.mockReturnValue(0);
const first = await upsertChannelPairingRequest({
channel: "telegram",
id: "123",
});
expect(first.code).toBe("AAAAAAAA");
const sequence = Array(8).fill(0).concat(Array(8).fill(1));
let idx = 0;
spy.mockImplementation(() => sequence[idx++] ?? 1);
const second = await upsertChannelPairingRequest({
channel: "telegram",
id: "456",
});
expect(second.code).toBe("BBBBBBBB");
} finally {
spy.mockRestore();
}
});
});
it("caps pending requests at the default limit", async () => {
await withTempStateDir(async () => {
const ids = ["+15550000001", "+15550000002", "+15550000003"];
for (const id of ids) {
const created = await upsertChannelPairingRequest({
channel: "whatsapp",
id,
});
expect(created.created).toBe(true);
}
const blocked = await upsertChannelPairingRequest({
channel: "whatsapp",
id: "+15550000004",
});
expect(blocked.created).toBe(false);
const list = await listChannelPairingRequests("whatsapp");
const listIds = list.map((entry) => entry.id);
expect(listIds).toHaveLength(3);
expect(listIds).toContain("+15550000001");
expect(listIds).toContain("+15550000002");
expect(listIds).toContain("+15550000003");
expect(listIds).not.toContain("+15550000004");
});
});
});
|