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 264 lines 7.7 kB view raw view rendered
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)