324 lines
8.0 KiB
TypeScript
324 lines
8.0 KiB
TypeScript
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
|
import { getConfig } from '@/config/config.js';
|
|
import type { Server } from '@/config/types.js';
|
|
import type { Pet } from '@/features/demo/demo.db-schema.js';
|
|
import { getServer } from '@/server.js';
|
|
|
|
let server: Server;
|
|
|
|
beforeAll(async () => {
|
|
const config = getConfig();
|
|
server = getServer(config);
|
|
await server.ready();
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await server.close();
|
|
});
|
|
|
|
// Helper to create valid metadata
|
|
const createMetadata = () => ({
|
|
login_at: new Date().toISOString(),
|
|
ip: '192.168.1.1',
|
|
agent: 'Mozilla/5.0',
|
|
plan: 'premium' as const,
|
|
});
|
|
|
|
// Helper to create a person
|
|
async function createPerson(first_name: string, gender: 'man' | 'woman' | 'other' = 'man') {
|
|
const response = await server.inject({
|
|
method: 'POST',
|
|
url: '/demo/persons',
|
|
payload: {
|
|
first_name,
|
|
gender,
|
|
metadata: createMetadata(),
|
|
},
|
|
});
|
|
return response.json();
|
|
}
|
|
|
|
describe('Pet CRUD', () => {
|
|
it('should create a pet', async () => {
|
|
const owner = await createPerson('PetOwner');
|
|
|
|
const response = await server.inject({
|
|
method: 'POST',
|
|
url: '/demo/pets',
|
|
payload: { name: 'Buddy', species: 'dog', owner_id: owner.id },
|
|
});
|
|
|
|
expect(response.statusCode).toBe(201);
|
|
const body = response.json();
|
|
expect(body).toMatchObject({
|
|
name: 'Buddy',
|
|
species: 'dog',
|
|
owner_id: owner.id,
|
|
});
|
|
expect(body.id).toBeDefined();
|
|
});
|
|
|
|
it('should get a pet by ID', async () => {
|
|
const owner = await createPerson('Owner');
|
|
|
|
// Create a pet
|
|
const createResponse = await server.inject({
|
|
method: 'POST',
|
|
url: '/demo/pets',
|
|
payload: { name: 'Mittens', species: 'cat', owner_id: owner.id },
|
|
});
|
|
const created = createResponse.json();
|
|
|
|
// Get the pet
|
|
const response = await server.inject({
|
|
method: 'GET',
|
|
url: `/demo/pets/${created.id}`,
|
|
});
|
|
|
|
expect(response.statusCode).toBe(200);
|
|
const body = response.json();
|
|
expect(body).toMatchObject({
|
|
id: created.id,
|
|
name: 'Mittens',
|
|
species: 'cat',
|
|
owner_id: owner.id,
|
|
});
|
|
});
|
|
|
|
it('should return 404 for non-existent pet', async () => {
|
|
const response = await server.inject({
|
|
method: 'GET',
|
|
url: '/demo/pets/999999',
|
|
});
|
|
|
|
expect(response.statusCode).toBe(404);
|
|
expect(response.json()).toEqual({ message: 'Pet not found' });
|
|
});
|
|
|
|
it('should list pets', async () => {
|
|
const owner = await createPerson('MultiPetOwner');
|
|
|
|
// Create multiple pets
|
|
await server.inject({
|
|
method: 'POST',
|
|
url: '/demo/pets',
|
|
payload: { name: 'Spot', species: 'dog', owner_id: owner.id },
|
|
});
|
|
await server.inject({
|
|
method: 'POST',
|
|
url: '/demo/pets',
|
|
payload: { name: 'Whiskers', species: 'cat', owner_id: owner.id },
|
|
});
|
|
|
|
const response = await server.inject({
|
|
method: 'GET',
|
|
url: '/demo/pets',
|
|
});
|
|
|
|
expect(response.statusCode).toBe(200);
|
|
const body = response.json();
|
|
expect(Array.isArray(body)).toBe(true);
|
|
expect(body.length).toBeGreaterThanOrEqual(2);
|
|
});
|
|
|
|
it('should filter pets by species', async () => {
|
|
const owner = await createPerson('SpeciesOwner');
|
|
|
|
// Create pets with different species
|
|
await server.inject({
|
|
method: 'POST',
|
|
url: '/demo/pets',
|
|
payload: { name: 'Fido', species: 'dog', owner_id: owner.id },
|
|
});
|
|
await server.inject({
|
|
method: 'POST',
|
|
url: '/demo/pets',
|
|
payload: { name: 'Kitty', species: 'cat', owner_id: owner.id },
|
|
});
|
|
|
|
const response = await server.inject({
|
|
method: 'GET',
|
|
url: '/demo/pets?species=dog',
|
|
});
|
|
|
|
expect(response.statusCode).toBe(200);
|
|
const body = response.json();
|
|
expect(body.every((p: Pet) => p.species === 'dog')).toBe(true);
|
|
});
|
|
|
|
it('should filter pets by owner_id', async () => {
|
|
const owner1 = await createPerson('Owner1');
|
|
const owner2 = await createPerson('Owner2', 'woman');
|
|
|
|
// Create pets for different owners
|
|
await server.inject({
|
|
method: 'POST',
|
|
url: '/demo/pets',
|
|
payload: { name: 'Pet1', species: 'dog', owner_id: owner1.id },
|
|
});
|
|
await server.inject({
|
|
method: 'POST',
|
|
url: '/demo/pets',
|
|
payload: { name: 'Pet2', species: 'cat', owner_id: owner2.id },
|
|
});
|
|
|
|
const response = await server.inject({
|
|
method: 'GET',
|
|
url: `/demo/pets?owner_id=${owner1.id}`,
|
|
});
|
|
|
|
expect(response.statusCode).toBe(200);
|
|
const body = response.json();
|
|
expect(body.every((p: Pet) => p.owner_id === owner1.id)).toBe(true);
|
|
});
|
|
|
|
it('should update a pet', async () => {
|
|
const owner = await createPerson('UpdateOwner');
|
|
|
|
// Create a pet
|
|
const createResponse = await server.inject({
|
|
method: 'POST',
|
|
url: '/demo/pets',
|
|
payload: { name: 'OldName', species: 'dog', owner_id: owner.id },
|
|
});
|
|
const created = createResponse.json();
|
|
|
|
// Update the pet
|
|
const updateResponse = await server.inject({
|
|
method: 'PATCH',
|
|
url: `/demo/pets/${created.id}`,
|
|
payload: { name: 'NewName' },
|
|
});
|
|
|
|
expect(updateResponse.statusCode).toBe(204);
|
|
|
|
// Verify the update
|
|
const getResponse = await server.inject({
|
|
method: 'GET',
|
|
url: `/demo/pets/${created.id}`,
|
|
});
|
|
|
|
const updated = getResponse.json();
|
|
expect(updated.name).toBe('NewName');
|
|
expect(updated.species).toBe('dog'); // Should remain unchanged
|
|
});
|
|
|
|
it('should return 404 when updating non-existent pet', async () => {
|
|
const response = await server.inject({
|
|
method: 'PATCH',
|
|
url: '/demo/pets/999999',
|
|
payload: { name: 'Does Not Exist' },
|
|
});
|
|
|
|
expect(response.statusCode).toBe(404);
|
|
});
|
|
|
|
it('should delete a pet', async () => {
|
|
const owner = await createPerson('DeleteOwner');
|
|
|
|
// Create a pet
|
|
const createResponse = await server.inject({
|
|
method: 'POST',
|
|
url: '/demo/pets',
|
|
payload: { name: 'ToDelete', species: 'cat', owner_id: owner.id },
|
|
});
|
|
const created = createResponse.json();
|
|
|
|
// Delete the pet
|
|
const deleteResponse = await server.inject({
|
|
method: 'DELETE',
|
|
url: `/demo/pets/${created.id}`,
|
|
});
|
|
|
|
expect(deleteResponse.statusCode).toBe(204);
|
|
|
|
// Verify deletion
|
|
const getResponse = await server.inject({
|
|
method: 'GET',
|
|
url: `/demo/pets/${created.id}`,
|
|
});
|
|
|
|
expect(getResponse.statusCode).toBe(404);
|
|
});
|
|
|
|
it('should return 404 when deleting non-existent pet', async () => {
|
|
const response = await server.inject({
|
|
method: 'DELETE',
|
|
url: '/demo/pets/999999',
|
|
});
|
|
|
|
expect(response.statusCode).toBe(404);
|
|
});
|
|
});
|
|
|
|
describe('Pet with Owner', () => {
|
|
it('should get pet with owner details', async () => {
|
|
const owner = await createPerson('DetailedOwner', 'other');
|
|
|
|
// Create a pet
|
|
const petResponse = await server.inject({
|
|
method: 'POST',
|
|
url: '/demo/pets',
|
|
payload: { name: 'Max', species: 'dog', owner_id: owner.id },
|
|
});
|
|
const pet = petResponse.json();
|
|
|
|
// Get pet with owner
|
|
const response = await server.inject({
|
|
method: 'GET',
|
|
url: `/demo/pets/${pet.id}/with-owner`,
|
|
});
|
|
|
|
expect(response.statusCode).toBe(200);
|
|
const body = response.json();
|
|
expect(body.pet.id).toBe(pet.id);
|
|
expect(body.pet.name).toBe('Max');
|
|
expect(body.owner).toBeDefined();
|
|
expect(body.owner.id).toBe(owner.id);
|
|
expect(body.owner.first_name).toBe('DetailedOwner');
|
|
expect(body.owner.gender).toBe('other');
|
|
});
|
|
});
|
|
|
|
describe('Cascade Delete', () => {
|
|
it('should delete all pets when owner is deleted', async () => {
|
|
const owner = await createPerson('CascadeOwner');
|
|
|
|
// Create multiple pets
|
|
const pet1Response = await server.inject({
|
|
method: 'POST',
|
|
url: '/demo/pets',
|
|
payload: { name: 'Pet1', species: 'dog', owner_id: owner.id },
|
|
});
|
|
const pet2Response = await server.inject({
|
|
method: 'POST',
|
|
url: '/demo/pets',
|
|
payload: { name: 'Pet2', species: 'cat', owner_id: owner.id },
|
|
});
|
|
|
|
const pet1 = pet1Response.json();
|
|
const pet2 = pet2Response.json();
|
|
|
|
// Delete the owner
|
|
const deleteResponse = await server.inject({
|
|
method: 'DELETE',
|
|
url: `/demo/persons/${owner.id}`,
|
|
});
|
|
|
|
expect(deleteResponse.statusCode).toBe(204);
|
|
|
|
// Verify pets are also deleted
|
|
const pet1Check = await server.inject({
|
|
method: 'GET',
|
|
url: `/demo/pets/${pet1.id}`,
|
|
});
|
|
const pet2Check = await server.inject({
|
|
method: 'GET',
|
|
url: `/demo/pets/${pet2.id}`,
|
|
});
|
|
|
|
expect(pet1Check.statusCode).toBe(404);
|
|
expect(pet2Check.statusCode).toBe(404);
|
|
});
|
|
});
|