From 6936c007c729323243510728b0ff722b6955a312 Mon Sep 17 00:00:00 2001 From: Saravana Achu Mac Date: Fri, 3 Apr 2026 14:09:52 -0700 Subject: [PATCH] fix(cowork-service): preserve budget policy auth errors --- .../src/modules/usage/routes.test.ts | 16 ++++++++++ .../src/modules/usage/routes.ts | 30 +++++++++++++++---- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/services/cowork-service/src/modules/usage/routes.test.ts b/services/cowork-service/src/modules/usage/routes.test.ts index 5b508962..2937d4d3 100644 --- a/services/cowork-service/src/modules/usage/routes.test.ts +++ b/services/cowork-service/src/modules/usage/routes.test.ts @@ -133,6 +133,22 @@ describe('usage proxy routes', () => { 'Bearer test-token' ); }); + + it('forwards budget-policy authorization failures', async () => { + mockFetch.mockResolvedValue({ + ok: false, + status: 403, + json: async () => ({ error: 'Admin access required' }), + }); + + const res = await app.inject({ + method: 'GET', + url: '/api/usage/budget-policy', + }); + + expect(res.statusCode).toBe(403); + expect(JSON.parse(res.payload).error).toBe('Admin access required'); + }); }); describe('PUT /api/usage/budget-policy', () => { diff --git a/services/cowork-service/src/modules/usage/routes.ts b/services/cowork-service/src/modules/usage/routes.ts index e2ddc682..65f23104 100644 --- a/services/cowork-service/src/modules/usage/routes.ts +++ b/services/cowork-service/src/modules/usage/routes.ts @@ -54,7 +54,10 @@ async function listBudgetPolicies( headers: buildProxyHeaders(req), }); if (!res.ok) { - throw new Error(`Platform returned ${res.status}`); + const body = (await res.json().catch(() => null)) as { error?: string } | null; + const error = new Error(body?.error ?? `Platform returned ${res.status}`); + (error as Error & { statusCode?: number }).statusCode = res.status; + throw error; } return (await res.json()) as PlatformBudgetPolicy[]; } @@ -89,7 +92,10 @@ async function upsertPolicy( body: JSON.stringify(body), }); if (!res.ok) { - throw new Error(`Platform returned ${res.status}`); + const body = (await res.json().catch(() => null)) as { error?: string } | null; + const error = new Error(body?.error ?? `Platform returned ${res.status}`); + (error as Error & { statusCode?: number }).statusCode = res.status; + throw error; } return (await res.json()) as PlatformBudgetPolicy; } @@ -170,8 +176,14 @@ export async function usageRoutes(app: FastifyInstance) { }; } catch (err) { req.log.warn({ err }, 'Failed to load budget policy from platform-service'); - reply.code(502); - return { error: 'Platform-service unavailable' }; + const statusCode = + err instanceof Error && 'statusCode' in err && typeof err.statusCode === 'number' + ? err.statusCode + : 502; + reply.code(statusCode); + return { + error: err instanceof Error ? err.message : 'Platform-service unavailable', + }; } }); @@ -207,8 +219,14 @@ export async function usageRoutes(app: FastifyInstance) { }; } catch (err) { req.log.warn({ err }, 'Failed to save budget policy to platform-service'); - reply.code(502); - return { error: 'Platform-service unavailable' }; + const statusCode = + err instanceof Error && 'statusCode' in err && typeof err.statusCode === 'number' + ? err.statusCode + : 502; + reply.code(statusCode); + return { + error: err instanceof Error ? err.message : 'Platform-service unavailable', + }; } }); }