import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import { getConfig } from '@/config/config.js'; import type { Server } from '@/config/types.js'; import type { Person, 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, }); describe('Person CRUD', () => { it('should create a person', async () => { const response = await server.inject({ method: 'POST', url: '/demo/persons', payload: { first_name: 'John', last_name: 'Doe', gender: 'man', metadata: createMetadata(), }, }); expect(response.statusCode).toBe(201); const body = response.json(); expect(body.first_name).toBe('John'); expect(body.last_name).toBe('Doe'); expect(body.gender).toBe('man'); expect(body.id).toBeDefined(); expect(body.created_at).toBeDefined(); expect(body.metadata.plan).toBe('premium'); }); it('should get a person by ID', async () => { // Create a person first const createResponse = await server.inject({ method: 'POST', url: '/demo/persons', payload: { first_name: 'Jane', last_name: 'Smith', gender: 'woman', metadata: createMetadata(), }, }); const created = createResponse.json(); // Get the person const response = await server.inject({ method: 'GET', url: `/demo/persons/${created.id}`, }); expect(response.statusCode).toBe(200); const body = response.json(); expect(body).toMatchObject({ id: created.id, first_name: 'Jane', last_name: 'Smith', gender: 'woman', }); }); it('should return 404 for non-existent person', async () => { const response = await server.inject({ method: 'GET', url: '/demo/persons/999999', }); expect(response.statusCode).toBe(404); expect(response.json()).toEqual({ message: 'Person not found' }); }); it('should list persons', async () => { // Create multiple persons await server.inject({ method: 'POST', url: '/demo/persons', payload: { first_name: 'Alice', gender: 'woman', metadata: createMetadata(), }, }); await server.inject({ method: 'POST', url: '/demo/persons', payload: { first_name: 'Bob', gender: 'man', metadata: createMetadata(), }, }); const response = await server.inject({ method: 'GET', url: '/demo/persons', }); expect(response.statusCode).toBe(200); const body = response.json(); expect(Array.isArray(body)).toBe(true); expect(body.length).toBeGreaterThanOrEqual(2); }); it('should filter persons by first_name', async () => { // Create persons await server.inject({ method: 'POST', url: '/demo/persons', payload: { first_name: 'Charlie', gender: 'man', metadata: createMetadata(), }, }); const response = await server.inject({ method: 'GET', url: '/demo/persons?first_name=Charlie', }); expect(response.statusCode).toBe(200); const body = response.json(); expect(body.every((p: Person) => p.first_name === 'Charlie')).toBe(true); }); it('should update a person', async () => { // Create a person const createResponse = await server.inject({ method: 'POST', url: '/demo/persons', payload: { first_name: 'Original', gender: 'other', metadata: createMetadata(), }, }); const created = createResponse.json(); // Update the person const updateResponse = await server.inject({ method: 'PATCH', url: `/demo/persons/${created.id}`, payload: { first_name: 'Updated', }, }); expect(updateResponse.statusCode).toBe(204); // Verify the update const getResponse = await server.inject({ method: 'GET', url: `/demo/persons/${created.id}`, }); const updated = getResponse.json(); expect(updated.first_name).toBe('Updated'); expect(updated.gender).toBe('other'); // Should remain unchanged }); it('should return 404 when updating non-existent person', async () => { const response = await server.inject({ method: 'PATCH', url: '/demo/persons/999999', payload: { first_name: 'Does Not Exist' }, }); expect(response.statusCode).toBe(404); }); it('should delete a person', async () => { // Create a person const createResponse = await server.inject({ method: 'POST', url: '/demo/persons', payload: { first_name: 'ToDelete', gender: 'man', metadata: createMetadata(), }, }); const created = createResponse.json(); // Delete the person const deleteResponse = await server.inject({ method: 'DELETE', url: `/demo/persons/${created.id}`, }); expect(deleteResponse.statusCode).toBe(204); // Verify deletion const getResponse = await server.inject({ method: 'GET', url: `/demo/persons/${created.id}`, }); expect(getResponse.statusCode).toBe(404); }); it('should return 404 when deleting non-existent person', async () => { const response = await server.inject({ method: 'DELETE', url: '/demo/persons/999999', }); expect(response.statusCode).toBe(404); }); it('should create person with different plans', async () => { const freePlan = await server.inject({ method: 'POST', url: '/demo/persons', payload: { first_name: 'Free', gender: 'woman', metadata: { ...createMetadata(), plan: 'free' }, }, }); expect(freePlan.statusCode).toBe(201); expect(freePlan.json().metadata.plan).toBe('free'); const premiumPlan = await server.inject({ method: 'POST', url: '/demo/persons', payload: { first_name: 'Premium', gender: 'man', metadata: { ...createMetadata(), plan: 'premium' }, }, }); expect(premiumPlan.statusCode).toBe(201); expect(premiumPlan.json().metadata.plan).toBe('premium'); }); }); describe('Person with Pets', () => { it('should get person with their pets', async () => { // Create a person const personResponse = await server.inject({ method: 'POST', url: '/demo/persons', payload: { first_name: 'PetOwner', gender: 'other', metadata: createMetadata(), }, }); const person = personResponse.json(); // Create pets for this person await server.inject({ method: 'POST', url: '/demo/pets', payload: { name: 'Fluffy', species: 'cat', owner_id: person.id }, }); await server.inject({ method: 'POST', url: '/demo/pets', payload: { name: 'Rex', species: 'dog', owner_id: person.id }, }); // Get person with pets const response = await server.inject({ method: 'GET', url: `/demo/persons/${person.id}/with-pets`, }); expect(response.statusCode).toBe(200); const body = response.json(); expect(body.id).toBe(person.id); expect(body.first_name).toBe('PetOwner'); expect(Array.isArray(body.pets)).toBe(true); expect(body.pets.length).toBe(2); expect(body.pets.map((p: Pet) => p.name).sort()).toEqual(['Fluffy', 'Rex']); }); it('should return person with empty pets array when they have no pets', async () => { const personResponse = await server.inject({ method: 'POST', url: '/demo/persons', payload: { first_name: 'NoPets', gender: 'man', metadata: createMetadata(), }, }); const person = personResponse.json(); const response = await server.inject({ method: 'GET', url: `/demo/persons/${person.id}/with-pets`, }); expect(response.statusCode).toBe(200); const body = response.json(); expect(body.pets).toEqual([]); }); });