Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions app/test/e2e/instance-create.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,11 @@ test('add ssh key from instance create form', async ({ page }) => {
await page.getByRole('link', { name: 'SSH Keys' }).click()
await expectRowVisible(page.getByRole('table'), { Name: newKey, Description: 'hi' })
})

test('shows object not found error on no default pool', async ({ page }) => {
await page.goto('/projects/mock-project/instances-new')
await page.getByRole('textbox', { name: 'Name', exact: true }).fill('no-default-pool')
await page.getByRole('button', { name: 'Create instance' }).click()

await expect(page.getByText('Not found: default IP pool')).toBeVisible()
})
6 changes: 4 additions & 2 deletions libs/api-mocks/msw/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ import { internalError, json } from './util'

const notFoundBody = { error_code: 'ObjectNotFound' } as const
export type NotFound = typeof notFoundBody
export const notFoundErr = () =>
json({ error_code: 'ObjectNotFound' } as const, { status: 404 })
export const notFoundErr = (msg?: string) => {
const message = msg ? `not found: ${msg}` : 'not found'
return json({ error_code: 'ObjectNotFound', message } as const, { status: 404 })
}

export const lookupById = <T extends { id: string }>(table: T[], id: string) => {
const item = table.find((i) => i.id === id)
Expand Down
4 changes: 4 additions & 0 deletions libs/api-mocks/msw/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,10 @@ export const handlers = makeHandlers({
instanceCreate({ body, query }) {
const project = lookup.project(query)

if (body.name === 'no-default-pool') {
throw notFoundErr('default IP pool for current silo')
}

errIfExists(db.instances, { name: body.name, project_id: project.id }, 'instance')

const instanceId = uuid()
Expand Down
17 changes: 16 additions & 1 deletion libs/api/__tests__/errors.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,21 @@ describe('processServerError', () => {
})
})

describe('ObjectNotFound', () => {
it('passes through the API error', () => {
const error = makeError({
errorCode: 'ObjectNotFound',
message: 'not found: whatever',
statusCode: 404,
})
expect(processServerError('fakeThingCreate', error)).toEqual({
errorCode: 'ObjectNotFound',
message: 'Not found: whatever',
statusCode: 404,
})
})
})

it('falls back to server error message if code not found', () => {
const error = makeError({ errorCode: 'WeirdError', message: 'whatever' })
expect(processServerError('womp', error)).toEqual({
Expand All @@ -100,6 +115,6 @@ it.each([
['instanceNetworkInterfaceCreate', '', 'interface'],
['instanceNetworkInterfaceCreate', 'already exists: something else', 'something else'],
['doesNotContainC-reate', '', null],
])('getResourceName', (method, message, resource) => {
])('getResourceName: %s', (method, message, resource) => {
expect(getResourceName(method, message)).toEqual(resource)
})
4 changes: 2 additions & 2 deletions libs/api/__tests__/hooks.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ describe('useApiQuery', () => {
const error = onError.mock.lastCall?.[0]
expect(error).toEqual({
errorCode: 'ObjectNotFound',
message: 'Object not found',
message: 'Not found',
statusCode: 404,
})
})
Expand Down Expand Up @@ -208,7 +208,7 @@ describe('useApiMutation', () => {

expect(result.current.error).toEqual({
errorCode: 'ObjectNotFound',
message: 'Object not found',
message: 'Not found',
statusCode: 404,
})
})
Expand Down
4 changes: 2 additions & 2 deletions libs/api/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/
import { camelCaseToWords, capitalize } from '@oxide/util'

import type { ErrorResult } from '.'
import type { ErrorResult } from './__generated__/Api'

/**
* Processed API error ready for display in the console. Note that it's possible
Expand Down Expand Up @@ -56,7 +56,7 @@ export function processServerError(method: string, resp: ErrorResult): ApiError
if (code === 'Forbidden') {
message = 'Action not authorized'
} else if (code === 'ObjectNotFound') {
message = 'Object not found'
message = capitalize(resp.data.message)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the entire change. Everything else is tests.

} else if (code === 'ObjectAlreadyExists') {
const resource = getResourceName(method, resp.data.message)
if (resource) {
Expand Down