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 main 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});