Add interface design references and principles; implement sport service tests

- Introduced critique, example, principles, and validation documents for interface design.
- Enhanced backend service with coverage threshold and fixed variable declaration in sport service.
- Added comprehensive tests for create, update, and list sports functionalities.
This commit is contained in:
Jose Selesan
2026-04-24 10:40:25 -03:00
parent 21d0c27f4d
commit c9ecdd2c77
10 changed files with 1135 additions and 1 deletions

View File

@@ -0,0 +1,97 @@
import { beforeEach, expect, mock, test } from 'bun:test';
import { Errors } from '@/lib/errors';
import { err, ok } from '@/lib/result';
import type { CreateSportInput } from '@repo/api-contract';
const createSportMock = mock(async (_input: CreateSportInput) =>
ok({
id: 'sport-1',
name: 'Tenis',
slug: 'tenis',
isActive: true,
createdAt: '2026-04-23T00:00:00.000Z',
updatedAt: '2026-04-23T00:00:00.000Z',
})
);
mock.module('@/modules/sport/services/sport.service', () => ({
__esModule: true,
createSport: createSportMock,
}));
const { createSportHandler } = await import(
'@/modules/sport/handlers/create-sport.handler'
);
type HandlerContext = {
get: (key: 'requestId') => string | undefined;
req: {
valid: () => unknown;
};
json: ReturnType<typeof mock>;
};
function createContext(body: unknown) {
const json = mock((payload: unknown, init?: unknown) => ({
payload,
init,
}));
const c = {
get: () => undefined,
req: {
valid: () => body,
},
json,
} as unknown as HandlerContext;
return { c, json };
}
beforeEach(() => {
createSportMock.mockReset();
mock.clearAllMocks();
});
test('returns 201 with the created sport', async () => {
const createdSport = {
id: 'sport-1',
name: 'Tenis',
slug: 'tenis',
isActive: true,
createdAt: '2026-04-23T00:00:00.000Z',
updatedAt: '2026-04-23T00:00:00.000Z',
} as const;
createSportMock.mockResolvedValue(ok(createdSport));
const payload: CreateSportInput = { name: ' Tenis ' };
const { c, json } = createContext(payload);
const response = await createSportHandler(c as never);
expect(createSportMock).toHaveBeenCalledWith(payload);
expect(json.mock.calls[0]?.[0]).toEqual(createdSport);
expect(json.mock.calls[0]?.[1]).toBe(201);
expect(response).toBeDefined();
});
test('returns a conflict problem when the service rejects with a conflict error', async () => {
createSportMock.mockResolvedValue(err(Errors.conflict('Ya existe un deporte con ese nombre.')));
const payload: CreateSportInput = { name: 'Tenis' };
const { c, json } = createContext(payload);
const response = await createSportHandler(c as never);
expect(createSportMock).toHaveBeenCalledWith(payload);
expect(json.mock.calls[0]?.[0]).toEqual({
type: 'https://api.myapp.dev/problems/conflict',
title: 'Conflict',
status: 409,
detail: 'Ya existe un deporte con ese nombre.',
});
expect(json.mock.calls[0]?.[1]).toBe(409);
expect(response).toBeDefined();
});