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
4
fork

Configure Feed

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

1# Test Coverage Analysis 2 3## Current State: No Tests Exist 4 5The monorepo has **zero test infrastructure**. No testing framework is installed, no test scripts exist in any `package.json`, and `turbo.json` has no `test` task. There are approximately **530 lines of source code** across 4 packages with 0% test coverage. 6 7--- 8 9## Recommended Test Framework Setup 10 11**Vitest** is the best fit for this project: 12- Native ESM support (the repo uses `"type": "module"` everywhere) 13- Built-in TypeScript support via `tsx`/`esbuild` (no separate ts-jest config) 14- Workspace-aware — can share a root config while per-package configs override as needed 15- Fast, with watch mode out of the box 16 17### Infrastructure needed 18 191. Install `vitest` as a root devDependency 202. Add a root `vitest.workspace.ts` pointing at each package 213. Add `"test": "vitest run"` scripts to each package's `package.json` 224. Add a `"test"` task to `turbo.json` (with `dependsOn: ["^build"]` since appview/web depend on lexicon types) 235. Add `pnpm test` as a root script 24 25--- 26 27## Package-by-Package Gaps and Recommendations 28 29### 1. `@atbb/appview` — API Server (highest priority) 30 31This is the most complex package (260 LOC) and where most business logic will land as stubs are implemented. It has the database schema, config loading, AT Protocol agent creation, and all API routes. 32 33#### a) Route handler tests (high value) 34 35**Files:** `src/routes/health.ts`, `src/routes/forum.ts`, `src/routes/categories.ts`, `src/routes/topics.ts`, `src/routes/posts.ts` 36 37**What to test:** 38- `GET /api/healthz` returns `200` with `{ status: "ok", version: "0.1.0" }` 39- `GET /api/healthz/ready` returns `200` with `{ status: "ready" }` 40- `GET /api/forum` returns `200` with the expected forum shape 41- `GET /api/categories` returns `200` with `{ categories: [] }` 42- `GET /api/categories/:id/topics` returns `200` and echoes the `id` param 43- `POST /api/topics` returns `501` (not implemented) 44- `POST /api/posts` returns `501` (not implemented) 45 46**How:** Use Hono's built-in `app.request()` test helper — no HTTP server needed: 47```ts 48import { describe, it, expect } from "vitest"; 49import { apiRoutes } from "../src/routes/index.js"; 50import { Hono } from "hono"; 51 52const app = new Hono().route("/api", apiRoutes); 53 54describe("GET /api/healthz", () => { 55 it("returns ok status", async () => { 56 const res = await app.request("/api/healthz"); 57 expect(res.status).toBe(200); 58 expect(await res.json()).toEqual({ status: "ok", version: "0.1.0" }); 59 }); 60}); 61``` 62 63**Why it matters:** As stubs are replaced with real implementations, having route-level tests in place catches regressions in response shape, status codes, and content-type headers. These tests are cheap to write now and will grow in value. 64 65#### b) Config loading tests (medium value) 66 67**File:** `src/lib/config.ts` 68 69**What to test:** 70- Returns correct defaults when env vars are absent (`PORT` defaults to `3000`, `PDS_URL` defaults to `https://bsky.social`) 71- Parses `PORT` as an integer (not a string) 72- Returns provided env var values when set 73- Handles `PORT` set to a non-numeric string (currently `parseInt` would return `NaN` — should this throw?) 74 75**Why it matters:** Config loading is the root of most "it works on my machine" bugs. The current implementation silently accepts empty strings for `forumDid` and `databaseUrl`, which will cause hard-to-debug runtime failures. Tests would document this behavior and motivate adding validation. 76 77#### c) Database schema tests (medium value) 78 79**File:** `src/db/schema.ts` 80 81**What to test:** 82- Schema definitions export the expected table names 83- Column types match expectations (e.g., `posts.deleted` defaults to `false`) 84- Foreign key references are correct (`posts.did``users.did`, `posts.rootPostId``posts.id`, etc.) 85- Index names are correct and unique constraints are in place 86 87**How:** These can be pure unit tests against the Drizzle schema objects — no database connection needed. Drizzle table objects expose `._.columns` and other metadata you can assert against. 88 89**Why it matters:** Schema is the foundation. If someone accidentally removes an index or changes a foreign key, these tests catch it before it hits a migration. 90 91#### d) Database integration tests (high value, but requires infrastructure) 92 93**What to test:** 94- Insert/select/update/delete for each table 95- Foreign key constraints are enforced (e.g., inserting a post with a non-existent `did` fails) 96- Unique index violations behave as expected 97- The `createDb()` factory produces a working Drizzle client 98 99**How:** Use a test PostgreSQL instance. Options: 100- **Testcontainers** (Docker-based, spins up a real Postgres per test suite) 101- **pg-mem** (in-memory Postgres emulator, faster but not 100% compatible) 102- A shared test database with transaction rollback between tests 103 104**Why it matters:** This is where the most subtle bugs live — constraint violations, bad joins, missing indexes. As the appview stubs are fleshed out with real queries, these tests become critical. 105 106#### e) AT Protocol agent factory test (low value now, higher later) 107 108**File:** `src/lib/atproto.ts` 109 110Currently just `new AtpAgent({ service: config.pdsUrl })` — not much to test. But as authentication and record-writing logic is added, this module should have tests verifying: 111- Agent is created with the correct service URL 112- Authentication errors are handled gracefully 113- Record write/read operations produce expected AT URI formats 114 115--- 116 117### 2. `@atbb/web` — Server-Rendered Web UI 118 119#### a) API client tests (high value) 120 121**File:** `src/lib/api.ts` 122 123**What to test:** 124- `fetchApi("/categories")` calls the correct URL (`${appviewUrl}/api/categories`) 125- Throws an `Error` with status code and status text on non-2xx responses 126- Returns parsed JSON on success 127- Handles network failures (fetch throws) 128 129**How:** Mock `global.fetch` with `vi.fn()` or use `msw` (Mock Service Worker): 130```ts 131import { describe, it, expect, vi, beforeEach } from "vitest"; 132 133// Mock fetch globally 134const mockFetch = vi.fn(); 135vi.stubGlobal("fetch", mockFetch); 136 137describe("fetchApi", () => { 138 it("throws on non-ok response", async () => { 139 mockFetch.mockResolvedValueOnce({ 140 ok: false, status: 500, statusText: "Internal Server Error", 141 }); 142 const { fetchApi } = await import("../src/lib/api.js"); 143 await expect(fetchApi("/test")).rejects.toThrow("AppView API error: 500"); 144 }); 145}); 146``` 147 148**Why it matters:** `fetchApi` is the single point of contact between the web UI and the appview. Error handling here determines whether users see useful error messages or blank pages. 149 150#### b) JSX component / layout tests (medium value) 151 152**File:** `src/layouts/base.tsx`, `src/routes/home.tsx` 153 154**What to test:** 155- `BaseLayout` renders valid HTML with the provided title 156- `BaseLayout` uses the default title "atBB Forum" when none is provided 157- `BaseLayout` includes the HTMX script tag 158- Home route returns `200` with `text/html` content type 159- Home page includes "Welcome to atBB" heading 160 161**How:** Use Hono's `app.request()` and assert against the HTML string, or use a lightweight HTML parser. Hono JSX components can be tested by rendering them and checking the output string. 162 163#### c) Config loading tests (low-medium value) 164 165**File:** `src/lib/config.ts` 166 167Same pattern as the appview config tests — verify defaults, parsing, and presence of required values. 168 169--- 170 171### 3. `@atbb/lexicon` — Lexicon Definitions 172 173#### a) YAML-to-JSON build script tests (medium value) 174 175**File:** `scripts/build.ts` 176 177**What to test:** 178- Each YAML file in `lexicons/` produces valid JSON 179- Output JSON matches the expected Lexicon schema structure (has `lexicon`, `id`, `defs` fields) 180- No duplicate lexicon IDs across files 181- The `id` field in each lexicon matches its file path (e.g., `space/atbb/post.yaml` has `id: "space.atbb.post"`) 182 183**How:** Rather than testing the build script directly (it's I/O-heavy), write validation tests that run against the YAML source files: 184```ts 185import { parse } from "yaml"; 186import { readFileSync } from "fs"; 187import { glob } from "glob"; 188 189describe("lexicon definitions", () => { 190 const files = glob.sync("**/*.yaml", { cwd: "lexicons" }); 191 192 it.each(files)("%s has a valid lexicon structure", (file) => { 193 const content = readFileSync(`lexicons/${file}`, "utf-8"); 194 const parsed = parse(content); 195 expect(parsed).toHaveProperty("lexicon", 1); 196 expect(parsed).toHaveProperty("id"); 197 expect(parsed).toHaveProperty("defs"); 198 }); 199}); 200``` 201 202**Why it matters:** Lexicon definitions are the API contract for the entire AT Protocol integration. A malformed lexicon causes downstream build failures in type generation and runtime validation errors. Catching issues at the YAML level is far cheaper than debugging them at the API level. 203 204#### b) Schema contract tests (high value) 205 206**What to test:** 207- `space.atbb.post` has `text` as a required string field 208- `space.atbb.post` has optional `reply` with `root` and `parent` refs 209- `space.atbb.forum.forum` uses `key: literal:self` 210- `space.atbb.forum.category` uses `key: tid` 211- All `strongRef` usages have both `uri` and `cid` fields 212- `knownValues` are used (not `enum`) for extensible fields like `modAction.action` 213 214**Why it matters:** These are the **contract tests** of the system. If a lexicon field is accidentally renamed or a required field becomes optional, it breaks interoperability with any PDS that stores atBB records. These tests protect the public API surface. 215 216--- 217 218### 4. `@atbb/spike` — PDS Integration Script 219 220The spike is a manual integration test. It doesn't need unit tests itself, but: 221 222#### Extractable test utilities (medium value) 223 224The spike contains reusable patterns for: 225- Authenticating with a PDS 226- Creating/reading/deleting AT Protocol records 227- Generating TIDs 228 229These should be extracted into a shared test utility module (e.g., `packages/test-utils/`) that integration tests across the monorepo can use. 230 231--- 232 233## Priority Matrix 234 235| Priority | Area | Package | Effort | Impact | 236|----------|------|---------|--------|--------| 237| **P0** | Test infrastructure setup (vitest, turbo task, CI) | root | Low | Unblocks everything | 238| **P0** | Appview route handler tests | appview | Low | Catches regressions as stubs are implemented | 239| **P1** | Web API client tests (`fetchApi`) | web | Low | Validates the only web→appview boundary | 240| **P1** | Lexicon schema contract tests | lexicon | Low | Protects the AT Protocol API surface | 241| **P1** | Config loading tests (both packages) | appview, web | Low | Documents defaults, catches parse bugs | 242| **P2** | Database schema unit tests | appview | Medium | Catches accidental schema changes | 243| **P2** | JSX layout/component tests | web | Medium | Ensures correct HTML output | 244| **P2** | Lexicon build script validation | lexicon | Low | Catches YAML/JSON conversion issues | 245| **P3** | Database integration tests | appview | High | Requires Postgres test infra (Docker/testcontainers) | 246| **P3** | AT Protocol integration tests | appview | High | Requires PDS test instance or mock | 247| **P3** | Extract spike utilities into shared test-utils | spike | Medium | Enables reuse across integration tests | 248 249--- 250 251## Suggested Implementation Order 252 2531. **Set up vitest** at the root + per-package, add `test` task to turbo.json 2542. **Appview route tests** — quick wins since Hono has a built-in test helper and the routes are simple right now 2553. **Lexicon contract tests** — validate YAML schema structure to protect the AT Protocol API 2564. **Web `fetchApi` tests** — mock fetch, verify URL construction and error handling 2575. **Config tests** for both packages — small but catches real bugs 2586. **Database schema tests** — assert on Drizzle metadata objects 2597. **Database integration tests** — add testcontainers or similar once there are real queries to test