turbo-monorepo-test/apps/backend/test/demo/pet.integration.test.ts

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);
});
});