File size: 5,202 Bytes
1477a90 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | // Code generated by @openmeter/typespec-typescript. DO NOT EDIT.
import fetchMock from '@fetch-mock/vitest'
import { beforeEach, describe, expect, it } from 'vitest'
import { Client, HTTPError, funcs } from '../src/index.js'
beforeEach(() => {
fetchMock.mockReset()
})
function client() {
return new Client({
baseUrl: 'https://eu.api.konghq.com/v3',
apiKey: 'k',
fetch: fetchMock.fetchHandler,
// Several of these tests mock retryable statuses (429, 500); disable ky's
// built-in retry so each test hits the mock exactly once and isn't slowed
// down (or, for a 30s Retry-After, timed out) by ky's own backoff.
retry: 0,
})
}
function mockProblem(status: number, body: Record<string, unknown>) {
fetchMock.route('*', {
status,
body,
headers: { 'Content-Type': 'application/problem+json; charset=utf-8' },
})
}
describe('error mapping', () => {
it('maps problem+json to a typed HTTPError with parsed fields', async () => {
mockProblem(404, {
type: 't',
title: 'Not Found',
status: 404,
detail: 'nope',
instance: '/x',
})
const result = await funcs.getMeter(client(), { meterId: 'x' })
expect(result.ok).toBe(false)
expect(result.error).toBeInstanceOf(HTTPError)
const httpError = result.error as HTTPError
expect(httpError.status).toBe(404)
expect(httpError.title).toBe('Not Found')
expect(httpError.message).toBe('nope')
})
it('sets the error name so it reads correctly in logs', async () => {
mockProblem(404, {
type: 't',
title: 'Not Found',
status: 404,
detail: 'nope',
instance: '/x',
})
const result = await funcs.getMeter(client(), { meterId: 'x' })
const httpError = result.error as HTTPError
expect(httpError.name).toBe('HTTPError')
})
it('exposes invalid_parameters via getField', async () => {
mockProblem(400, {
type: 't',
title: 'Bad Request',
status: 400,
detail: 'validation failed',
instance: '/x',
invalid_parameters: [{ field: 'name', reason: 'is required' }],
})
const result = await funcs.getMeter(client(), { meterId: 'x' })
const httpError = result.error as HTTPError
expect(httpError.getField('invalid_parameters')).toEqual([
{ field: 'name', reason: 'is required' },
])
})
it('exposes invalid_parameters via the typed invalidParameters accessor', async () => {
mockProblem(400, {
type: 't',
title: 'Bad Request',
status: 400,
detail: 'validation failed',
instance: '/x',
invalid_parameters: [
{ field: 'name', reason: 'is required' },
{
field: 'quantity',
rule: 'min',
minimum: 1,
reason: 'must be at least 1',
},
],
})
const result = await funcs.getMeter(client(), { meterId: 'x' })
const httpError = result.error as HTTPError
expect(httpError.invalidParameters).toEqual([
{ field: 'name', reason: 'is required' },
{
field: 'quantity',
rule: 'min',
minimum: 1,
reason: 'must be at least 1',
},
])
})
it('falls back to a status-only error for non-problem responses', async () => {
fetchMock.route('*', {
status: 500,
body: 'oops',
headers: { 'Content-Type': 'text/plain' },
})
const result = await funcs.getMeter(client(), { meterId: 'x' })
expect(result.error).toBeInstanceOf(HTTPError)
const httpError = result.error as HTTPError
expect(httpError.status).toBe(500)
expect(httpError.invalidParameters).toBeUndefined()
})
})
describe('retryAfter', () => {
it('parses a delta-seconds Retry-After header', async () => {
fetchMock.route('*', {
status: 429,
body: {
type: 't',
title: 'Too Many Requests',
status: 429,
detail: 'slow down',
instance: '/x',
},
headers: {
'Content-Type': 'application/problem+json; charset=utf-8',
'Retry-After': '30',
},
})
const result = await funcs.getMeter(client(), { meterId: 'x' })
const httpError = result.error as HTTPError
expect(httpError.retryAfter).toBe(30)
})
it('is undefined when the header is absent', async () => {
mockProblem(500, {
type: 't',
title: 'Internal Server Error',
status: 500,
detail: 'boom',
instance: '/x',
})
const result = await funcs.getMeter(client(), { meterId: 'x' })
const httpError = result.error as HTTPError
expect(httpError.retryAfter).toBeUndefined()
})
it('is undefined for an HTTP-date Retry-After header', async () => {
fetchMock.route('*', {
status: 429,
body: {
type: 't',
title: 'Too Many Requests',
status: 429,
detail: 'slow down',
instance: '/x',
},
headers: {
'Content-Type': 'application/problem+json; charset=utf-8',
'Retry-After': 'Wed, 21 Oct 2015 07:28:00 GMT',
},
})
const result = await funcs.getMeter(client(), { meterId: 'x' })
const httpError = result.error as HTTPError
expect(httpError.retryAfter).toBeUndefined()
})
})
|