diff --git a/package.json b/package.json index a2afadec3..1d956c24e 100644 --- a/package.json +++ b/package.json @@ -114,6 +114,7 @@ "@vue/test-utils": "2.4.6", "axe-core": "4.11.1", "fast-check": "4.5.3", + "h3": "1.15.5", "knip": "5.83.0", "lint-staged": "16.2.7", "oxfmt": "0.27.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c464f3f31..79d94fb3a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -240,6 +240,9 @@ importers: fast-check: specifier: 4.5.3 version: 4.5.3 + h3: + specifier: 1.15.5 + version: 1.15.5 knip: specifier: 5.83.0 version: 5.83.0(@types/node@24.10.9)(typescript@5.9.3) diff --git a/test/unit/server/utils/error-handler.spec.ts b/test/unit/server/utils/error-handler.spec.ts new file mode 100644 index 000000000..79024132f --- /dev/null +++ b/test/unit/server/utils/error-handler.spec.ts @@ -0,0 +1,47 @@ +import { describe, expect, it } from 'vitest' +import { createError } from 'h3' +import * as v from 'valibot' +import { handleApiError } from '../../../../server/utils/error-handler' + +describe('handleApiError', () => { + const fallback = { message: 'Something went wrong', statusCode: 500 } + + it('re-throws H3 errors as-is', () => { + const h3Err = createError({ statusCode: 404, message: 'Not found' }) + + expect(() => handleApiError(h3Err, fallback)).toThrow(h3Err) + }) + + it('throws a 404 with the first issue message for valibot errors', () => { + const schema = v.object({ name: v.pipe(v.string(), v.minLength(1, 'Name is required')) }) + + let valibotError: unknown + try { + v.parse(schema, { name: '' }) + } catch (e) { + valibotError = e + } + + expect(() => handleApiError(valibotError, fallback)).toThrow( + expect.objectContaining({ statusCode: 404, message: 'Name is required' }), + ) + }) + + it('throws a fallback error with the given statusCode and message', () => { + expect(() => + handleApiError(new Error('unexpected'), { message: 'Bad gateway', statusCode: 502 }), + ).toThrow(expect.objectContaining({ statusCode: 502, message: 'Bad gateway' })) + }) + + it('defaults fallback statusCode to 502 when not provided', () => { + expect(() => handleApiError('some string error', { message: 'Upstream failed' })).toThrow( + expect.objectContaining({ statusCode: 502, message: 'Upstream failed' }), + ) + }) + + it('uses the custom fallback statusCode when provided', () => { + expect(() => handleApiError(null, { message: 'Service unavailable', statusCode: 503 })).toThrow( + expect.objectContaining({ statusCode: 503, message: 'Service unavailable' }), + ) + }) +})