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

test(appview): add missing DB error 503 tests for board endpoints (ATB-45)

- POST /api/admin/boards: add "returns 503 when category lookup query fails"
- PUT /api/admin/boards/:id: add "returns 503 when board lookup query fails"
- PUT /api/admin/boards/:id: add "returns 503 when category CID lookup query fails"
(call-count pattern: first select passes, second throws)

+65
+65
apps/appview/src/routes/__tests__/admin.test.ts
··· 1640 1640 ctx.forumAgent = originalAgent; 1641 1641 }); 1642 1642 1643 + it("returns 503 when category lookup query fails", async () => { 1644 + const dbSelectSpy = vi.spyOn(ctx.db, "select").mockImplementationOnce(() => { 1645 + throw new Error("Database connection lost"); 1646 + }); 1647 + 1648 + const res = await app.request("/api/admin/boards", { 1649 + method: "POST", 1650 + headers: { "Content-Type": "application/json" }, 1651 + body: JSON.stringify({ name: "Test Board", categoryUri }), 1652 + }); 1653 + 1654 + expect(res.status).toBe(503); 1655 + const data = await res.json(); 1656 + expect(data.error).toBe("Database temporarily unavailable. Please try again later."); 1657 + expect(mockPutRecord).not.toHaveBeenCalled(); 1658 + 1659 + dbSelectSpy.mockRestore(); 1660 + }); 1661 + 1643 1662 it("returns 403 when user lacks manageCategories permission", async () => { 1644 1663 const { requirePermission } = await import("../../middleware/permissions.js"); 1645 1664 const mockRequirePermission = requirePermission as any; ··· 1894 1913 mockRequirePermission.mockImplementation(() => async (_c: any, next: any) => { 1895 1914 await next(); 1896 1915 }); 1916 + }); 1917 + 1918 + it("returns 503 when board lookup query fails", async () => { 1919 + const dbSelectSpy = vi.spyOn(ctx.db, "select").mockImplementationOnce(() => { 1920 + throw new Error("Database connection lost"); 1921 + }); 1922 + 1923 + const res = await app.request(`/api/admin/boards/${boardId}`, { 1924 + method: "PUT", 1925 + headers: { "Content-Type": "application/json" }, 1926 + body: JSON.stringify({ name: "Updated Name" }), 1927 + }); 1928 + 1929 + expect(res.status).toBe(503); 1930 + const data = await res.json(); 1931 + expect(data.error).toBe("Database temporarily unavailable. Please try again later."); 1932 + expect(mockPutRecord).not.toHaveBeenCalled(); 1933 + 1934 + dbSelectSpy.mockRestore(); 1935 + }); 1936 + 1937 + it("returns 503 when category CID lookup query fails", async () => { 1938 + const originalSelect = ctx.db.select.bind(ctx.db); 1939 + let callCount = 0; 1940 + const dbSelectSpy = vi.spyOn(ctx.db, "select").mockImplementation((...args: any[]) => { 1941 + callCount++; 1942 + if (callCount === 1) { 1943 + // First call: board lookup — pass through to real DB 1944 + return (originalSelect as any)(...args); 1945 + } 1946 + // Second call: category CID fetch — throw DB error 1947 + throw new Error("Database connection lost"); 1948 + }); 1949 + 1950 + const res = await app.request(`/api/admin/boards/${boardId}`, { 1951 + method: "PUT", 1952 + headers: { "Content-Type": "application/json" }, 1953 + body: JSON.stringify({ name: "Updated Name" }), 1954 + }); 1955 + 1956 + expect(res.status).toBe(503); 1957 + const data = await res.json(); 1958 + expect(data.error).toBe("Database temporarily unavailable. Please try again later."); 1959 + expect(mockPutRecord).not.toHaveBeenCalled(); 1960 + 1961 + dbSelectSpy.mockRestore(); 1897 1962 }); 1898 1963 }); 1899 1964