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
1# Contributing to atBB
2
3This document covers development workflow, testing standards, and coding patterns for contributors. For project-specific gotchas and non-obvious quirks, see [`CLAUDE.md`](CLAUDE.md).
4
5## Testing Workflow
6
7### When to Run Tests
8
9**Before every commit:**
10```sh
11pnpm test
12git add <specific files>
13git commit -m "feat: your changes"
14```
15
16**Before requesting code review:**
17```sh
18pnpm build # Ensure clean build
19pnpm test # Verify all tests pass
20# Only then push and request review
21```
22
23**After fixing review feedback:**
24```sh
25# Make fixes
26pnpm test # Verify tests still pass
27# Push updates
28```
29
30### Test Requirements
31
32All new features must include tests:
33- **API endpoints:** Success cases, error cases, edge cases
34- **Business logic:** All code paths and error conditions
35- **Error handling:** All catch blocks tested
36- **Security features:** Authentication, authorization, input validation
37
38### Test Quality Standards
39
40- Tests must be independent (no shared state between tests)
41- Use descriptive test names that explain what is being tested
42- Mock external dependencies (databases, APIs, network calls)
43- Test error paths, not just happy paths
44- Verify logging and error messages are correct
45
46### Test Coverage Expectations
47
48While we don't enforce strict coverage percentages, aim for:
49- **Critical paths:** 100% coverage (authentication, authorization, data integrity)
50- **Error handling:** All catch blocks should be tested
51- **API endpoints:** All routes should have tests
52- **Business logic:** All functions with branching logic should be tested
53
54Do not:
55- Skip writing tests to "move faster" — untested code breaks in production
56- Write tests after requesting review — tests inform implementation
57- Rely on manual testing alone — automated tests catch regressions
58
59## Error Handling Patterns
60
61### Defensive Programming
62
63All list queries must include defensive limits:
64```typescript
65.from(categories)
66.orderBy(categories.sortOrder)
67.limit(1000); // Prevent memory exhaustion on unbounded queries
68```
69
70Always filter soft-deleted records in read queries:
71```typescript
72.where(and(
73 eq(posts.rootPostId, topicId),
74 eq(posts.deleted, false),
75 eq(posts.bannedByMod, false)
76))
77```
78
79Use explicit ordering for consistent, reproducible results:
80```typescript
81.orderBy(asc(posts.createdAt)) // Chronological order for replies
82```
83
84### Global Error Handler
85
86Every Hono app must have a global error handler as a safety net. See `apps/appview/src/create-app.ts` for the production implementation:
87
88```typescript
89app.onError((err, c) => {
90 console.error("Unhandled error in route handler", {
91 path: c.req.path,
92 method: c.req.method,
93 error: err.message,
94 stack: err.stack,
95 });
96 return c.json(
97 {
98 error: "An internal error occurred. Please try again later.",
99 ...(process.env.NODE_ENV !== "production" && {
100 details: err.message,
101 }),
102 },
103 500
104 );
105});
106```
107
108### Testing Error Classification
109
110Test that errors return the right status code — users need actionable feedback:
111
112| Status | When to return |
113|--------|----------------|
114| 400 | Invalid input, missing required fields, malformed JSON |
115| 404 | Resource doesn't exist |
116| 503 | Network errors, PDS connection failures — user should retry |
117| 500 | Unexpected errors, database errors — needs investigation |
118
119```typescript
120// ✅ Network errors should return 503 (user should retry)
121it("returns 503 when PDS connection fails", async () => {
122 mockPutRecord.mockRejectedValueOnce(new Error("fetch failed"));
123 const res = await app.request("/api/topics", {
124 method: "POST",
125 body: JSON.stringify({ text: "Test" })
126 });
127 expect(res.status).toBe(503);
128});
129
130// ✅ Server errors should return 500 (needs investigation)
131it("returns 500 for unexpected database errors", async () => {
132 mockPutRecord.mockRejectedValueOnce(new Error("Database connection lost"));
133 const res = await app.request("/api/topics", {
134 method: "POST",
135 body: JSON.stringify({ text: "Test" })
136 });
137 expect(res.status).toBe(500);
138});
139
140// ✅ Input validation should return 400
141it("returns 400 for malformed JSON", async () => {
142 const res = await app.request("/api/topics", {
143 method: "POST",
144 headers: { "Content-Type": "application/json" },
145 body: "{ invalid json }"
146 });
147 expect(res.status).toBe(400);
148});
149```
150
151## Security Code Review Checklist
152
153Before requesting review for authentication/authorization code:
154
155- [ ] All permission checks fail closed (deny access on error)
156- [ ] Database errors in security checks are caught and logged
157- [ ] Programming errors (TypeError) are re-thrown, not caught
158- [ ] Privilege escalation is prevented (equal/higher authority blocked)
159- [ ] Tests cover unauthorized (401), forbidden (403), and error cases
160- [ ] Error messages don't leak internal details (priority values, permission names)
161- [ ] Middleware composition is correct (auth before permission checks)
162- [ ] Startup fails fast if security infrastructure is unavailable
163
164**Example test suite structure for security-critical endpoints:**
165```typescript
166describe("POST /api/admin/members/:did/role (security-critical)", () => {
167 describe("Authorization", () => {
168 it("returns 401 when not authenticated", async () => { /* ... */ });
169 it("returns 403 when user lacks manageRoles permission", async () => { /* ... */ });
170 });
171
172 describe("Privilege Escalation Prevention", () => {
173 it("prevents admin from assigning owner role (higher authority)", async () => {
174 // Admin (priority 10) tries to assign Owner (priority 0) → 403
175 });
176 it("allows admin to assign moderator role (lower authority)", async () => {
177 // Admin (priority 10) assigns Moderator (priority 20) → 200
178 });
179 });
180
181 describe("Error Handling", () => {
182 it("returns 503 when PDS connection fails (network error)", async () => { /* ... */ });
183 it("returns 500 when database query fails (server error)", async () => { /* ... */ });
184 it("returns 400 for malformed roleUri (input validation)", async () => { /* ... */ });
185 });
186});
187```
188
189## Bruno API Collections
190
191The `bruno/` directory contains [Bruno](https://www.usebruno.com/) collections for API documentation and interactive testing.
192
193### When to Update
194
195| Event | Action |
196|-------|--------|
197| New endpoint | Create a `.bru` file in `bruno/AppView API/<feature>/` |
198| Modified endpoint | Update the corresponding `.bru` file |
199| Removed endpoint | Delete the `.bru` file; update `bruno/README.md` if referenced |
200| New environment variable | Update `bruno/environments/local.bru` and `bruno/environments/dev.bru`; document in `bruno/README.md` |
201
202Always update Bruno files **in the same commit** as the route implementation.
203
204### File Template
205
206```bru
207meta {
208 name: Endpoint Name
209 type: http
210 seq: 1
211}
212
213get {
214 url: {{appview_url}}/api/path
215}
216
217params:query {
218 param1: {{variable}}
219}
220
221headers {
222 Content-Type: application/json
223}
224
225body:json {
226 {
227 "field": "value"
228 }
229}
230
231assert {
232 res.status: eq 200
233 res.body.field: isDefined
234}
235
236docs {
237 Brief description of what this endpoint does.
238
239 Path/query/body params:
240 - param1: Description (type, required/optional)
241
242 Returns:
243 {
244 "field": "value"
245 }
246
247 Error codes:
248 - 400: Bad request (invalid input)
249 - 401: Unauthorized (requires auth)
250 - 404: Not found
251 - 500: Server error
252
253 Notes:
254 - Any special considerations or validation rules
255}
256```
257
258### Before Committing
259
2601. Open the collection in Bruno
2612. Test each modified request against your local dev server (`pnpm dev`)
2623. Verify assertions pass (green checkmarks)
2634. Verify documentation is accurate and complete
2645. Check that error scenarios are documented (not just happy path)