feat(tests): add unit and integration test suite (node:test) #2
No Reviewers
Labels
Clear labels
Agentic
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Agent
Fable
Work made by a Claude Fable agent
Agent
gpt-5.6-luna
Work made by a gpt-5.6-luna agent
Agent
gpt-5.6-sol
Work made by a gpt-5.6-sol agent
Agent
gpt-5.6-terra
Work made by a gpt-5.6-terra agent
Work made by an agent
Agent
Opus
Work made by a Claude Opus agent
Agent
Sonnet
Work made by a Claude Sonnet agent
Breaking change that won't be backward compatible
Something is not working
Documentation changes
Improve existing functionality
New functionality
This is security issue
Issue or pull request related to testing
Priority
Critical
1
The priority is critical
Priority
High
2
The priority is high
Priority
Low
4
The priority is low
Priority
Medium
3
The priority is medium
Reviewed
Confirmed
1
Issue has been confirmed
Reviewed
Duplicate
2
This issue or pull request already exists
Reviewed
Invalid
3
Invalid issue
Reviewed
Won't Fix
3
This issue won't be fixed
Status
Abandoned
3
Somebody has started to work on this but abandoned work
Status
Blocked
1
Something is blocking this issue or pull request
Status
Need More Info
2
Feedback is required to reproduce issue or to continue work
Milestone
No items
No Milestone
Projects
Clear projects
No projects
Notifications
Due Date
No due date set.
Reference: codinget/abode#2
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Adds a comprehensive test suite (215 tests, 0 failures) using Node.js built-ins
node:test+node:assert/strict— no new test framework dependencies. TypeScript is run viatsx(already a devDependency).Test structure
test/tools/test/shared/DbInterface/BackendDbInterfacecontract suites reusable across backendstest/backends/sqlite/test/backends/api/npm scripts
Bugs fixed by tests
Four bugs in the production code were uncovered:
ApiInterfacepath params leaked into query string —remaining.delete(part)used the:uidkey (with colon) instead ofpart.slice(1), so path parameters also appeared as query params.ApiInterfacecalledres.json()on 204 responses — DELETE endpoints return 204 No Content; calling.json()threw "Unexpected end of JSON input".SqliteInterface.updateResidentSQL syntax error — missing comma betweenupdated_by = ?andjoinSql(updates)meant resident flags could never be updated.WrappedBetterSqlite3Dbrejectedreadonly: undefined—better-sqlite3requiresreadonlyto be a boolean; passingundefinedthrew on construction.Review: approve, minor gaps only
Ran the suite: 215/215 passing, 48 suites, 0 flaky across repeated runs.
npx tsc --noEmitpasses. package.json only adds npm scripts (test/test:backends/test:shared/test:tools), no new deps — matches the "no new framework dependencies" claim.All 4 production bug fixes have real regression coverage:
ApiInterface.#urlpath-param leak → directly asserted intest/backends/api/api-interface.test.ts.SqliteInterface.updateResidentmissing comma → covered byupdateResident updates flagsintest/shared/residents.ts(fails with a SQL syntax error without the fix).better-sqlite3.tsreadonly fix → exercised by every sqlite-backed test (constructor always passes thereadonlykey).Issues:
tsconfig.json'sincludeonly coverssrc/**/*.ts, sotest/is never type-checked. Building a parallel tsconfig surfaces real type errors:test/shared/users.ts—hashedPwtyped as plainstringinstead of the hash template-literal type (TS2322 at several call sites), andfound.emailaccessed on aPartialUser | ClientUserunion wherePartialUserhas noemail(TS2339). Harmless at runtime but means the test suite currently ships with an un-type-checked blind spot. Suggest wiringtest/**/*.tsinto a typecheck script.test/backends/api/index.test.tsonly wires uprunUserTests/runAbodeTests/runResidentTests/runApikeyTestsagainst the live Koa server —runSessionTests/runAuthTestsnever run over HTTP, only against sqlite directly. Given this PR's main value is catching integration bugs like the 204/path-param issues, this is the most likely place a similar bug could hide (login, session cookies, apikey-auth header parsing on the server side).test/shared/users.ts— the "createUser on readonly db throws" test starts withif (!db.readonly) return;, but no test helper ever constructs a readonly db instance for either backend, so this test body never actually executes for sqlite or api. It's currently a no-op that looks like real coverage.None of these block merging — the suite as-is already catches real bugs (see the 4 fixes above) and resource cleanup (Koa server close, in-memory sqlite) looks correct. Worth merging first among the three open PRs since #3 depends on the same fixes.
Addressed all three issues from the review, plus a security-relevant bug that turned up while fixing them:
Review issues (
4d9a0cf):Type-checking blind spot: Added
tsconfig.test.json(extends the base config, includestest/**/*.ts) plus atypecheck:testnpm script. Fixed the real type errors it surfaced intest/shared/users.ts—hashedPwnow typed viaAwaited<ReturnType<typeof hashPassword>>instead of plainstring, andfound.emailaccess now goes through an"email" in foundnarrowing check against thePartialUser | ClientUserunion.Session/auth not exercised over HTTP for the api backend:
runSessionTests/runAuthTestscan't be wired up againstApiInterfacedirectly — it doesn't implementgetUserBySession/createSession/getUserByApikey(server-only methods, not part of the client-facingDbInterfacesurfaceApiInterfaceimplements). Instead addedtest/backends/api/auth-http.test.ts, driving the live Koa server directly viafetch: login sets a session cookie, cookie auth works on protected routes,/auth/selfwithout credentials returns 401, logout clears the cookie,/auth/clear-sessionsinvalidates it, and Bearer apikey auth (valid + invalid) is exercised.No-op readonly test: Added
createReadonlyTestDb()/getReadonlyApiDb()helpers that construct real readonly db instances, wired intorunUserTestsvia a new optionalgetReadonlyDbparameter for both backends. The test now explicitlyt.skip()s if the helper isn't provided instead of silently short-circuiting.Bug found by the new HTTP test (
73c4b16):/auth/logoutonly cleared the client's cookie — the session token stayed valid server-side, so a cookie captured before logout (e.g. via XSS or a synced device) would keep working after the user logged out. AddedBackendDbInterface#deleteSession(implemented inSqliteInterface) and wired it into the logout route so the session is actually invalidated, not just forgotten client-side. The auth-http test now asserts this directly.Full suite: 222/222 passing,
tsc --noEmitclean on bothtsconfig.jsonandtsconfig.test.json.Follow-up: all three gaps addressed, plus a real security fix surfaced along the way
Re-checked the branch — 222/222 tests pass,
npm run typecheckand the newnpm run typecheck:testare both clean.All three issues from my earlier review are resolved:
tsconfig.test.json+typecheck:testnow type-checkstest/, and the real type errors it surfaced (hashedPwtyping,PartialUser | ClientUsernarrowing on.email) are fixed.test/backends/api/auth-http.test.tsnow covers login/session-cookie/logout/clear-sessions/bearer-apikey over real HTTP against the api backend (previously only sqlite exercised these).test/shared/users.tsnow actually constructs a readonly instance viagetReadonlyDbfor both backends, so it's no longer a silent no-op.And that new HTTP-level logout test caught a genuine security bug (commit
73c4b16):POST /auth/logoutonly cleared the client's cookie — the session token stayed valid in thesessionstable. A cookie captured before logout (XSS, log leakage, shared machine, proxy capture, etc.) would keep working indefinitely after the user "logged out." Fixed by addingBackendDbInterface#deleteSessionand calling it from the logout route with the token from the cookie before clearing it. Verified the fix:auth-http.test.tsnow assertsGET /auth/selfreturns 401 with the old cookie post-logout, and I confirmed#checkReadonly()/ cookie name (abode_session) / read-before-clear ordering are all correct inapirouter.ts.This is a good example of exactly the kind of bug this test suite is meant to catch (real bug, live HTTP layer, not visible from a sqlite-only unit test) — nice find. Approving.