From 2f9063b920e2b4a6c6453d4da487a6b908553a9e Mon Sep 17 00:00:00 2001 From: James Garbutt <43081j@users.noreply.github.com> Date: Thu, 5 Feb 2026 22:59:08 +0000 Subject: [PATCH 1/2] test: add tests for error-handler --- package.json | 1 + pnpm-lock.yaml | 3 ++ test/unit/server/utils/error-handler.spec.ts | 55 ++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 test/unit/server/utils/error-handler.spec.ts 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..fe6bd68db --- /dev/null +++ b/test/unit/server/utils/error-handler.spec.ts @@ -0,0 +1,55 @@ +import { describe, expect, it } from 'vitest' +import { createError } from 'h3' +import * as v from 'valibot' +import { handleApiError } from '../../../../server/utils/error-handler' + +function h3Error(statusCode: number, message: string) { + const error = createError({ statusCode, message }) + error.statusCode = statusCode + return error +} + +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 + } + + const expected = h3Error(404, 'Name is required') + expect(() => handleApiError(valibotError, fallback)).toThrow(expected) + }) + + it('throws a fallback error with the given statusCode and message', () => { + const expected = h3Error(502, 'Bad gateway') + expect(() => + handleApiError(new Error('unexpected'), { message: 'Bad gateway', statusCode: 502 }), + ).toThrow(expected) + }) + + it('defaults fallback statusCode to 502 when not provided', () => { + const expected = h3Error(502, 'Upstream failed') + expect(() => handleApiError('some string error', { message: 'Upstream failed' })).toThrow( + expected, + ) + }) + + it('uses the custom fallback statusCode when provided', () => { + const expected = h3Error(503, 'Service unavailable') + expect(() => handleApiError(null, { message: 'Service unavailable', statusCode: 503 })).toThrow( + expected, + ) + }) +}) From 8ecf89f3386f36b6a361d8665ea44aa8f03a8d64 Mon Sep 17 00:00:00 2001 From: James Garbutt <43081j@users.noreply.github.com> Date: Thu, 5 Feb 2026 23:47:22 +0000 Subject: [PATCH 2/2] test: use objectContaining --- test/unit/server/utils/error-handler.spec.ts | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/test/unit/server/utils/error-handler.spec.ts b/test/unit/server/utils/error-handler.spec.ts index fe6bd68db..79024132f 100644 --- a/test/unit/server/utils/error-handler.spec.ts +++ b/test/unit/server/utils/error-handler.spec.ts @@ -3,12 +3,6 @@ import { createError } from 'h3' import * as v from 'valibot' import { handleApiError } from '../../../../server/utils/error-handler' -function h3Error(statusCode: number, message: string) { - const error = createError({ statusCode, message }) - error.statusCode = statusCode - return error -} - describe('handleApiError', () => { const fallback = { message: 'Something went wrong', statusCode: 500 } @@ -28,28 +22,26 @@ describe('handleApiError', () => { valibotError = e } - const expected = h3Error(404, 'Name is required') - expect(() => handleApiError(valibotError, fallback)).toThrow(expected) + expect(() => handleApiError(valibotError, fallback)).toThrow( + expect.objectContaining({ statusCode: 404, message: 'Name is required' }), + ) }) it('throws a fallback error with the given statusCode and message', () => { - const expected = h3Error(502, 'Bad gateway') expect(() => handleApiError(new Error('unexpected'), { message: 'Bad gateway', statusCode: 502 }), - ).toThrow(expected) + ).toThrow(expect.objectContaining({ statusCode: 502, message: 'Bad gateway' })) }) it('defaults fallback statusCode to 502 when not provided', () => { - const expected = h3Error(502, 'Upstream failed') expect(() => handleApiError('some string error', { message: 'Upstream failed' })).toThrow( - expected, + expect.objectContaining({ statusCode: 502, message: 'Upstream failed' }), ) }) it('uses the custom fallback statusCode when provided', () => { - const expected = h3Error(503, 'Service unavailable') expect(() => handleApiError(null, { message: 'Service unavailable', statusCode: 503 })).toThrow( - expected, + expect.objectContaining({ statusCode: 503, message: 'Service unavailable' }), ) }) })