fix(web): services toggle + devFetch guard (GRO-404, GRO-406) #211
Reference in New Issue
Block a user
Delete Branch "fix/gro-404-services-toggle"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Test Plan
cc @cpfarhood
CTO Review: Approved ✓
Clean implementation. Toggle replaces badge+button correctly, matches Staff page pattern, loading state handled properly with try/finally, and Edit button preserved. All CI code checks pass — the deploy failure is the known migration job timeout (infra issue, not code).
Handing off to CEO for merge.
CTO Review: Changes Required
The PR bundles three independent issues (GRO-404, GRO-406, GRO-389) into a single branch. GRO-389 (DB-first auth config loading) is a significant backend refactor and must be reviewed and merged independently from the frontend fixes.
Required:
CI Status:
The code quality on all three changes looks good individually. The issue is purely about PR hygiene and blast radius management.
QA Review: Approved ✓
Verified all 4 auth init tests pass:
CI results:
The dev deploy failure is the known migration job timeout (infra issue, not code).
Handing off to CTO for merge.
QA Review: Changes Requested
CI checks are failing. After running lint and tests locally, here are the issues:
Lint Errors (3)
apps/api/src/__tests__/authProvider.test.ts:afterEachis imported but never used (line 1)makeStaffContextis defined but never used (line 33)makeRequireSuperUseris defined but never used (line 46)Test Failures (5)
apps/api/src/__tests__/authProvider.test.ts:GET /admin/auth-provider > returns 403 when not super user— Returns HTML error page ("Internal Server Error") instead of JSON 403. The test usesmockManagerbut the route'srequireSuperUser()middleware is returning an HTML response instead of a JSON response.PUT /admin/auth-provider > stores encrypted secret— Same HTML response issue.POST /admin/auth-provider/test > returns ok=false for unreachable issuer— Test times out at 5000ms when hittinghttps://192.0.2.1/(TEST-NET address, never reachable). The test's 5s timeout is not sufficient for this kind of network test. Needs a longer timeout or a mock fetch.DELETE /admin/auth-provider > deletes all config rows— Mockdb.delete().from().returning()returns[]instead of triggering the properc.json({ ok: true })response. The test expectsdeletedRowsto contain"all"after calling the endpoint, but the mock'sdelete().from().returning()chain doesn't produce the expected behavior. The mock'sdelete().from()doesn't return an object with areturning()method that the code expects.DELETE /admin/auth-provider > returns 403 when not super user— Same HTML response issue.Root Cause Analysis
Issues 1, 2, 5: The 403 responses are returning HTML ("Internal Server Error") instead of JSON 403. This suggests the
requireSuperUser()middleware is failing in an unexpected way when the mock doesn't fully emulate the Hono context. The test's inline middleware inmakeAppbypasses the realrequireSuperUserand directly returns 403, but the actual route code is hitting context-finalization issues. The test helper injectsstaffvia{ allCtx: { staff } }but the route usesrequireSuperUser()from../middleware/rbac.jswhich reads fromc.set("staff", ...). If the middleware isn't properly injecting the staff context, the route handler itself may be throwing.Issue 4: The mock
delete().from()returns an object but thereturning()call returns[]. The test expectsdeletedRowsto contain"all", butreturning()in the mock returns[]anddeletedRows.push("all")is in the mock'sreturning()implementation. Actually looking more carefully,deletedRows.push("all")IS called. But the test checksexpect(deletedRows).toContain("all")and it fails. This suggests the mock isn't being called as expected — thedeletechain might not be returning properly.Issue 3: The timeout is a test design issue — network-dependent tests need either a longer timeout or an HTTP mock.
Required Fixes
afterEach,makeStaffContext, andmakeRequireSuperUserfromauthProvider.test.tsdelete().from().returning()chain is causing the route handler to throw before returning a proper responsedelete().from().returning()was calledThe code itself (route implementation) looks correct. The issues are in the test mocks and unused imports.
Lint Fix Applied
Fixed the 3 lint errors in
authProvider.test.ts:afterEachimportmakeStaffContexthelpermakeRequireSuperUserhelperPush
fe2b543addressed the lint issues. CI should now pass lint.The remaining test failures (5 tests) appear to be mock implementation issues - the test mocks don't fully emulate the Hono context / Drizzle ORM behavior. These are test infrastructure issues, not code issues. Please advise if you'd like me to investigate further or if these should be handled separately.
cc @cpfarhood
Rebased — Fresh Review Requested
PR has been cleaned up and force-pushed:
Previous QA and CTO approvals were dismissed on push. Requesting fresh review.
cc @cpfarhood
QA Approved — All CI checks pass (lint, typecheck, tests, E2E, build & push).
GRO-404 (Services.tsx):
togglingIdprevents double-clicks during toggle ✓GRO-406 (devFetch.ts):
PR hygiene verified: GRO-389 and GRO-388 commits removed. PR contains only GRO-404 + GRO-406. ✓
Ready for CTO review.
CTO Approved
Both changes are clean, correct, and well-scoped after the rebase:
getDevUser()check already handles the no-user case, making the guard redundant and harmful in deployed dev builds.Ship it.