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
at root/atb-56-theme-caching-layer 123 lines 3.3 kB view raw
1import { describe, it, expect, vi, beforeEach } from "vitest"; 2import { createAppContext, destroyAppContext } from "../app-context.js"; 3import type { AppConfig } from "../config.js"; 4 5// Mock dependencies 6vi.mock("@atbb/db", () => ({ 7 createDb: vi.fn(() => ({})), 8})); 9 10vi.mock("../firehose.js", () => ({ 11 FirehoseService: vi.fn(() => ({ 12 start: vi.fn(), 13 stop: vi.fn(), 14 })), 15})); 16 17vi.mock("@atproto/oauth-client-node", () => ({ 18 NodeOAuthClient: vi.fn(() => ({ 19 clientMetadata: {}, 20 })), 21})); 22 23vi.mock("../oauth-stores.js", () => ({ 24 OAuthStateStore: vi.fn(() => ({ destroy: vi.fn() })), 25 OAuthSessionStore: vi.fn(() => ({ destroy: vi.fn() })), 26})); 27 28vi.mock("../cookie-session-store.js", () => ({ 29 CookieSessionStore: vi.fn(() => ({ destroy: vi.fn() })), 30})); 31 32vi.mock("@atbb/atproto", () => ({ 33 ForumAgent: vi.fn(() => ({ 34 initialize: vi.fn(), 35 shutdown: vi.fn(), 36 isAuthenticated: vi.fn(() => true), 37 })), 38})); 39 40vi.mock("@atbb/logger", () => ({ 41 createLogger: vi.fn(() => ({ 42 debug: vi.fn(), 43 info: vi.fn(), 44 warn: vi.fn(), 45 error: vi.fn(), 46 fatal: vi.fn(), 47 child: vi.fn(() => ({ 48 debug: vi.fn(), 49 info: vi.fn(), 50 warn: vi.fn(), 51 error: vi.fn(), 52 fatal: vi.fn(), 53 child: vi.fn(), 54 shutdown: vi.fn(), 55 })), 56 shutdown: vi.fn(), 57 })), 58})); 59 60describe("AppContext", () => { 61 let config: AppConfig; 62 63 beforeEach(() => { 64 config = { 65 port: 3000, 66 forumDid: "did:plc:test123", 67 databaseUrl: "postgres://localhost/test", 68 jetstreamUrl: "wss://jetstream.example.com", 69 pdsUrl: "https://pds.example.com", 70 logLevel: "info", 71 oauthPublicUrl: "http://localhost:3000", 72 sessionSecret: "test-secret-with-minimum-32-chars-for-validation", 73 sessionTtlDays: 7, 74 forumHandle: "forum.example.com", 75 forumPassword: "test-password", 76 backfillRateLimit: 10, 77 backfillConcurrency: 10, 78 backfillCursorMaxAgeHours: 48, 79 }; 80 }); 81 82 describe("createAppContext", () => { 83 it("creates ForumAgent when credentials are provided", async () => { 84 const ctx = await createAppContext(config); 85 86 expect(ctx.forumAgent).toBeDefined(); 87 expect(ctx.forumAgent).not.toBeNull(); 88 expect(ctx.forumAgent!.initialize).toHaveBeenCalled(); 89 }); 90 91 it("sets forumAgent to null when credentials are missing", async () => { 92 config.forumHandle = undefined; 93 config.forumPassword = undefined; 94 95 const ctx = await createAppContext(config); 96 97 expect(ctx.forumAgent).toBeNull(); 98 expect(ctx.logger.warn).toHaveBeenCalledWith( 99 "ForumAgent credentials missing", 100 expect.objectContaining({ operation: "createAppContext" }) 101 ); 102 }); 103 }); 104 105 describe("destroyAppContext", () => { 106 it("shuts down ForumAgent if present", async () => { 107 const ctx = await createAppContext(config); 108 const shutdownSpy = vi.spyOn(ctx.forumAgent!, "shutdown"); 109 110 await destroyAppContext(ctx); 111 112 expect(shutdownSpy).toHaveBeenCalled(); 113 }); 114 115 it("handles null ForumAgent gracefully", async () => { 116 config.forumHandle = undefined; 117 config.forumPassword = undefined; 118 const ctx = await createAppContext(config); 119 120 await expect(destroyAppContext(ctx)).resolves.not.toThrow(); 121 }); 122 }); 123});