Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/nperez0111/bookhive. Track your books, share your shelves, see what others are reading bookhive.buzz
atproto bluesky books bookshelf goodreads management-system
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149import { describe, it, expect, mock, beforeEach } from "bun:test";import type { AppContext } from "../context";import type { FollowsSync } from "./getFollows";
const { shouldSyncFollows, getUserFollows } = await import("./getFollows");
// Mock KV storageconst mockKv = { get: mock(), set: mock(),};
// Mock databaseconst mockDb = { selectFrom: mock(() => ({ select: mock(() => ({ where: mock(() => ({ where: mock(() => ({ orderBy: mock(() => ({ limit: mock(() => ({ execute: mock(), })), })), })), })), })), })),};
const mockCtx: AppContext = { kv: mockKv as any, db: mockDb as any, addWideEventContext: mock(),} as any;
describe("getFollows utilities", () => { beforeEach(() => { mockKv.get.mockClear(); mockKv.set.mockClear(); mockDb.selectFrom.mockClear(); });
describe("shouldSyncFollows", () => { it("should return true when no previous sync data exists", async () => { mockKv.get.mockResolvedValueOnce(null);
const result = await shouldSyncFollows(mockCtx, "did:plc:test123");
expect(result).toBe(true); expect(mockKv.get).toHaveBeenCalledWith("follows_sync:did:plc:test123"); });
it("should return true when no lastIncrementalSync exists", async () => { const syncData: FollowsSync = { userDid: "did:plc:test123", lastFullSync: "2025-08-01T10:00:00.000Z", lastIncrementalSync: null, cursor: null, }; mockKv.get.mockResolvedValueOnce(syncData);
const result = await shouldSyncFollows(mockCtx, "did:plc:test123");
expect(result).toBe(true); });
it("should return false when last sync was recent (< 6 hours)", async () => { const recentTime = new Date(Date.now() - 3 * 60 * 60 * 1000).toISOString(); const syncData: FollowsSync = { userDid: "did:plc:test123", lastFullSync: "2025-08-01T10:00:00.000Z", lastIncrementalSync: recentTime, cursor: null, }; mockKv.get.mockResolvedValueOnce(syncData);
const result = await shouldSyncFollows(mockCtx, "did:plc:test123");
expect(result).toBe(false); });
it("should return true when last sync was old (> 6 hours)", async () => { const oldTime = new Date(Date.now() - 8 * 60 * 60 * 1000).toISOString(); const syncData: FollowsSync = { userDid: "did:plc:test123", lastFullSync: "2025-08-01T10:00:00.000Z", lastIncrementalSync: oldTime, cursor: null, }; mockKv.get.mockResolvedValueOnce(syncData);
const result = await shouldSyncFollows(mockCtx, "did:plc:test123");
expect(result).toBe(true); });
it("should return true on KV error (fail safe)", async () => { mockKv.get.mockRejectedValueOnce(new Error("KV error"));
const result = await shouldSyncFollows(mockCtx, "did:plc:test123");
expect(result).toBe(true); }); });
describe("getUserFollows", () => { it("should query database with correct parameters and return follow DIDs", async () => { const mockFollows = [ { followsDid: "did:plc:follow1" }, { followsDid: "did:plc:follow2" }, { followsDid: "did:plc:follow3" }, ];
const mockExecute = mock().mockResolvedValueOnce(mockFollows); const mockLimit = mock(() => ({ execute: mockExecute })); const mockOrderBy = mock(() => ({ limit: mockLimit })); const mockWhere2 = mock(() => ({ orderBy: mockOrderBy })); const mockWhere1 = mock(() => ({ where: mockWhere2 })); const mockSelect = mock(() => ({ where: mockWhere1 })); mockDb.selectFrom.mockReturnValueOnce({ select: mockSelect });
const result = await getUserFollows(mockCtx, "did:plc:test123", 100);
expect(mockDb.selectFrom).toHaveBeenCalledWith("user_follows"); expect(mockSelect).toHaveBeenCalledWith("followsDid"); expect(mockWhere1).toHaveBeenCalledWith("userDid", "=", "did:plc:test123"); expect(mockWhere2).toHaveBeenCalledWith("isActive", "=", 1); expect(mockOrderBy).toHaveBeenCalledWith("syncedAt", "desc"); expect(mockLimit).toHaveBeenCalledWith(100);
expect(result).toEqual(["did:plc:follow1", "did:plc:follow2", "did:plc:follow3"]); });
it("should return empty array when no follows found", async () => { const mockExecute = mock().mockResolvedValueOnce([]); const mockLimit = mock(() => ({ execute: mockExecute })); const mockOrderBy = mock(() => ({ limit: mockLimit })); const mockWhere2 = mock(() => ({ orderBy: mockOrderBy })); const mockWhere1 = mock(() => ({ where: mockWhere2 })); const mockSelect = mock(() => ({ where: mockWhere1 })); mockDb.selectFrom.mockReturnValueOnce({ select: mockSelect });
const result = await getUserFollows(mockCtx, "did:plc:test123");
expect(result).toEqual([]); }); });});