import assert from "node:assert/strict"; import test from "node:test"; import { runPublicAccountIdRefresh, } from "../lib/account-id-refresh-service.ts"; function fakeDb(accounts) { const updates = []; return { db: { prepare: (sql) => { if (sql.startsWith("SELECT")) { return { all: async () => ({ results: accounts.map((account) => ({ id: account.id, platform: account.platform, platform_uid: account.platform_uid, profile_url: account.profile_url, })), success: true, meta: { changes: 0, last_row_id: 0 }, }), }; } const match = sql.match(/WHERE id = \?$/); assert.ok(match, `unexpected sql: ${sql}`); assert.match(sql, /UPDATE accounts SET public_account_id = \?/); assert.doesNotMatch(sql, /followers|tags|nickname|bio|gender|ip_location/); return { bind: (value, id) => ({ run: async () => { updates.push({ value, id }); return { success: true, meta: { changes: 1, last_row_id: 0 } }; }, }), }; }, }, updates, }; } test("updates only public_account_id for scrapeable accounts", async () => { const accounts = [ { id: "a1", platform: "小红书", platform_uid: "uid-1", profile_url: "https://www.xiaohongshu.com/user/profile/a1" }, { id: "a2", platform: "抖音", platform_uid: "uid-2", profile_url: "https://www.douyin.com/user/a2" }, ]; const { db, updates } = fakeDb(accounts); const status = await runPublicAccountIdRefresh(db, async (url) => { if (url.endsWith("a1")) return { redId: "xhs-id-1" }; if (url.endsWith("a2")) return { redId: " dy-id-2 " }; return { redId: null }; }); assert.equal(status.status, "done"); assert.equal(status.total, 2); assert.equal(status.processed, 2); assert.equal(status.updated, 2); assert.equal(status.failed, 0); assert.deepEqual(updates, [ { value: "xhs-id-1", id: "a1" }, { value: "dy-id-2", id: "a2" }, ]); }); test("counts failures and skips empty redId without writing", async () => { const accounts = [ { id: "a1", platform: "小红书", platform_uid: "uid-1", profile_url: "https://www.xiaohongshu.com/user/profile/a1" }, { id: "a2", platform: "小红书", platform_uid: "uid-2", profile_url: "https://www.xiaohongshu.com/user/profile/a2" }, { id: "a3", platform: "小红书", platform_uid: "uid-3", profile_url: "https://www.xiaohongshu.com/user/profile/a3" }, ]; const { db, updates } = fakeDb(accounts); const status = await runPublicAccountIdRefresh(db, async (url) => { if (url.endsWith("a1")) throw new Error("mcp down"); if (url.endsWith("a2")) return { redId: " " }; return { redId: "ok-3" }; }); assert.equal(status.total, 3); assert.equal(status.updated, 1); assert.equal(status.failed, 1); assert.equal(status.skipped, 1); assert.deepEqual(updates, [{ value: "ok-3", id: "a3" }]); }); test("ignores demo accounts and accounts without profile_url at query level", async () => { const accounts = [ { id: "demo", platform: "小红书", platform_uid: "xhs-8af3", profile_url: "https://www.xiaohongshu.com/user/profile/demo" }, { id: "real", platform: "小红书", platform_uid: "uid-9", profile_url: "https://www.xiaohongshu.com/user/profile/real" }, ]; const { db, updates } = fakeDb(accounts); const status = await runPublicAccountIdRefresh(db, async () => ({ redId: "id-9" })); assert.equal(status.total, 1); assert.equal(status.updated, 1); assert.deepEqual(updates, [{ value: "id-9", id: "real" }]); });