4.5 KiB
Codecov Patch Coverage Fix Plan: user_handler.go (AcceptInvite constant-time branch)
Problem Statement
Codecov patch coverage is failing at 66.66667% due to 3 uncovered patch lines in:
backend/internal/api/handlers/user_handler.go
Codecov reports 2 lines missing and 1 line partially covered.
What’s Uncovered (Exact Lines + Behavior)
The uncovered patch lines are in (*UserHandler).AcceptInvite:
- Partial line: the branch condition
if !util.ConstantTimeCompare(user.InviteToken, req.Token) {
- Missing lines: the branch body
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid invite token"})return
What behavior this represents
This branch is intended as defense-in-depth: if the stored invite token and the request token differ, reject the request with HTTP 401.
Root Cause Analysis (Hypothesis)
These lines are currently effectively unreachable under normal execution:
- The handler looks up the user via
WHERE invite_token = req.Token. - If a row is found,
user.InviteTokenshould already equalreq.Token. - Therefore,
!util.ConstantTimeCompare(user.InviteToken, req.Token)will never be true, leaving its body uncovered.
This yields the exact Codecov pattern we see:
- the
ifline is hit (partial) - its body is never hit (2 lines missing)
Existing Test Harness (Repo Patterns)
Handler tests (unit)
The repository already has strong patterns for handler unit tests:
- Use Gin in test mode:
gin.SetMode(gin.TestMode) - Build a minimal router:
r := gin.New() - Register a single route per test:
r.POST("/invite/accept", handler.AcceptInvite) - Use
httptest.NewRecorder()+httptest.NewRequest(...) - Use SQLite in-memory via GORM for isolated tests
Relevant existing files:
backend/internal/api/handlers/user_handler_test.go(many UserHandler endpoint tests)backend/internal/api/handlers/user_handler_coverage_test.go(targeted “error/coverage” tests usinggin.CreateTestContext)backend/internal/api/handlers/testdb_test.go(sharedOpenTestDB*helpers used across handler tests)
Minimal Fix Strategy (Supervisor-Recommended)
Because the DB query already enforces invite_token = req.Token, the constant-time compare mismatch branch in AcceptInvite is unreachable. The smallest, behavior-preserving fix (and the one that avoids creating a response oracle) is to delete the dead mismatch branch rather than trying to make it testable.
Why this is safe
- If the token doesn’t match, the DB lookup already fails and returns 404 Not Found (
"Invalid or expired invite token"). - Keeping a separate 401 Unauthorized path for a mismatch (even if unreachable today) risks future “oracle” behavior differences.
- Removing the branch is a strict simplification: it does not introduce any new acceptance conditions.
Proposed Code Change (Minimal, Behavior-Preserving)
Edit
backend/internal/api/handlers/user_handler.go
Change
In (*UserHandler).AcceptInvite, remove the redundant constant-time compare mismatch block (currently the only util. usage in this file):
- Remove the comment and
if !util.ConstantTimeCompare(user.InviteToken, req.Token) { ... }block at lines ~827–834 (theifis at line 830). - Remove the now-unused import
github.com/Wikid82/charon/backend/internal/util(line 18) if it becomes unused after deleting the block.
Unit Tests
No new unit tests are expected/required for this change because it only deletes unreachable code.
If any existing tests assert a 401 for invite-token mismatch (none are expected), update those tests to assert the existing invalid-token behavior (404) instead.
Verification Steps
- Run VS Code task: Test: Backend with Coverage
- Run VS Code task: Build: Backend (or equivalent:
cd backend && go build ./...) - (Optional but recommended) Run: Lint: Go Vet
- Confirm Codecov/coverage gate remains >= 85% and the patch no longer reports uncovered lines for the removed branch.
Security Note (Avoiding an Oracle)
This approach intentionally avoids distinguishing “token exists but mismatched” vs “token not found” via different status codes/messages, which can become a token-validity oracle. After the change, invalid tokens continue to get the existing 404 response path.
Acceptance Criteria
- Codecov patch coverage for the change is >= 85%
- No uncovered lines remain in
backend/internal/api/handlers/user_handler.go(specifically theConstantTimeComparemismatch branch) Test: Backend with Coveragepasses