WIP! A BB-style forum, on the ATmosphere! We're still working... we'll be back soon when we have something to show off!
node typescript hono htmx atproto
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 127 lines 4.5 kB view raw
1import { describe, it, expect, beforeEach, vi } from "vitest"; 2import { BanEnforcer } from "../ban-enforcer.js"; 3import { createMockLogger } from "./mock-logger.js"; 4import type { Database } from "@atbb/db"; 5 6const createMockDb = () => { 7 const mockSelect = vi.fn(); 8 const mockUpdate = vi.fn(); 9 10 return { 11 select: mockSelect, 12 update: mockUpdate, 13 } as unknown as Database; 14}; 15 16describe("BanEnforcer", () => { 17 let mockDb: Database; 18 let enforcer: BanEnforcer; 19 let mockLogger: ReturnType<typeof createMockLogger>; 20 21 beforeEach(() => { 22 vi.clearAllMocks(); 23 mockDb = createMockDb(); 24 mockLogger = createMockLogger(); 25 enforcer = new BanEnforcer(mockDb, mockLogger); 26 }); 27 28 describe("isBanned", () => { 29 it("returns true when an active ban exists (no expiry)", async () => { 30 (mockDb.select as ReturnType<typeof vi.fn>).mockReturnValue({ 31 from: vi.fn().mockReturnValue({ 32 where: vi.fn().mockReturnValue({ 33 limit: vi.fn().mockResolvedValue([{ id: 1n }]), 34 }), 35 }), 36 }); 37 38 expect(await enforcer.isBanned("did:plc:banned123")).toBe(true); 39 }); 40 41 it("returns false when no ban exists", async () => { 42 (mockDb.select as ReturnType<typeof vi.fn>).mockReturnValue({ 43 from: vi.fn().mockReturnValue({ 44 where: vi.fn().mockReturnValue({ 45 limit: vi.fn().mockResolvedValue([]), 46 }), 47 }), 48 }); 49 50 expect(await enforcer.isBanned("did:plc:user123")).toBe(false); 51 }); 52 53 it("returns true (fail closed) when DB throws", async () => { 54 (mockDb.select as ReturnType<typeof vi.fn>).mockReturnValue({ 55 from: vi.fn().mockReturnValue({ 56 where: vi.fn().mockReturnValue({ 57 limit: vi.fn().mockRejectedValue(new Error("DB connection lost")), 58 }), 59 }), 60 }); 61 62 expect(await enforcer.isBanned("did:plc:user123")).toBe(true); 63 }); 64 65 it("re-throws programming errors (TypeError) without fail-closed", async () => { 66 (mockDb.select as ReturnType<typeof vi.fn>).mockReturnValue({ 67 from: vi.fn().mockReturnValue({ 68 where: vi.fn().mockReturnValue({ 69 limit: vi.fn().mockRejectedValue(new TypeError("Cannot read property")), 70 }), 71 }), 72 }); 73 74 await expect(enforcer.isBanned("did:plc:user123")).rejects.toThrow(TypeError); 75 }); 76 }); 77 78 describe("applyBan", () => { 79 it("sets bannedByMod=true on all posts for the subject DID", async () => { 80 const mockWhere = vi.fn().mockResolvedValue(undefined); 81 const mockSet = vi.fn().mockReturnValue({ where: mockWhere }); 82 (mockDb.update as ReturnType<typeof vi.fn>).mockReturnValue({ set: mockSet }); 83 84 await enforcer.applyBan("did:plc:banned123"); 85 86 expect(mockSet).toHaveBeenCalledWith({ bannedByMod: true }); 87 expect(mockWhere).toHaveBeenCalled(); 88 }); 89 90 it("re-throws when DB throws during applyBan", async () => { 91 const dbError = new Error("DB connection lost"); 92 (mockDb.update as ReturnType<typeof vi.fn>).mockReturnValue({ 93 set: vi.fn().mockReturnValue({ 94 where: vi.fn().mockRejectedValue(dbError), 95 }), 96 }); 97 98 await expect(enforcer.applyBan("did:plc:banned123")).rejects.toThrow("DB connection lost"); 99 }); 100 }); 101 102 describe("liftBan", () => { 103 it("sets bannedByMod=false on all posts for the subject DID without touching deleted", async () => { 104 const mockWhere = vi.fn().mockResolvedValue(undefined); 105 const mockSet = vi.fn().mockReturnValue({ where: mockWhere }); 106 (mockDb.update as ReturnType<typeof vi.fn>).mockReturnValue({ set: mockSet }); 107 108 await enforcer.liftBan("did:plc:unbanned123"); 109 110 // Must only set bannedByMod — never touch deleted (user-initiated deletes must not be resurrected) 111 expect(mockSet).toHaveBeenCalledWith({ bannedByMod: false }); 112 expect(mockSet).not.toHaveBeenCalledWith(expect.objectContaining({ deleted: expect.anything() })); 113 expect(mockWhere).toHaveBeenCalled(); 114 }); 115 116 it("re-throws when DB throws during liftBan", async () => { 117 const dbError = new Error("DB connection lost"); 118 (mockDb.update as ReturnType<typeof vi.fn>).mockReturnValue({ 119 set: vi.fn().mockReturnValue({ 120 where: vi.fn().mockRejectedValue(dbError), 121 }), 122 }); 123 124 await expect(enforcer.liftBan("did:plc:unbanned123")).rejects.toThrow("DB connection lost"); 125 }); 126 }); 127});