portail-associations/server/calendar.test.ts

231 lines
7.7 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest';
// Mock db module
vi.mock('./db', () => ({
getRequestById: vi.fn(),
updateRequestSalles: vi.fn(),
createRequestHistory: vi.fn(),
getAssociationById: vi.fn(),
getApprovedReservations: vi.fn(),
}));
// Mock notification
vi.mock('./_core/notification', () => ({
notifyOwner: vi.fn().mockResolvedValue(true),
}));
import * as db from './db';
import { notifyOwner } from './_core/notification';
describe('Calendrier des réservations - Backend', () => {
beforeEach(() => {
vi.clearAllMocks();
});
describe('getApprovedReservations', () => {
it('should return approved reservations with association info', async () => {
const mockReservations = [
{
id: 1,
associationId: 10,
type: 'demande_salle',
titre: 'Réservation Salle TOUCAN',
status: 'validee',
formData: JSON.stringify({
sallesSelectionnees: ['Salle TOUCAN'],
dateReservation: '2026-04-15',
heureDebut: '09:00',
heureFin: '17:00',
motifReservation: 'AG annuelle',
}),
associationName: 'Association Test',
associationEmail: 'test@asso.fr',
createdAt: new Date('2026-03-01'),
},
{
id: 2,
associationId: 11,
type: 'demande_salle',
titre: 'Réservation DOJO',
status: 'validee',
formData: JSON.stringify({
sallesSelectionnees: ['DOJO'],
dateReservation: '2026-04-20',
heureDebut: '18:00',
heureFin: '20:00',
motifReservation: 'Entraînement',
}),
associationName: 'Club Judo',
associationEmail: 'judo@club.fr',
createdAt: new Date('2026-03-05'),
},
];
vi.mocked(db.getApprovedReservations).mockResolvedValue(mockReservations as any);
const result = await db.getApprovedReservations();
expect(result).toHaveLength(2);
expect(result[0].associationName).toBe('Association Test');
expect(result[1].associationName).toBe('Club Judo');
});
it('should return empty array when no approved reservations', async () => {
vi.mocked(db.getApprovedReservations).mockResolvedValue([]);
const result = await db.getApprovedReservations();
expect(result).toHaveLength(0);
});
});
describe('changeSalles - logic simulation', () => {
it('should update salles in formData correctly', async () => {
const existingRequest = {
id: 1,
type: 'demande_salle',
associationId: 10,
status: 'validee',
formData: JSON.stringify({
sallesSelectionnees: ['Salle TOUCAN'],
sallesIds: ['salle_toucan'],
dateReservation: '2026-04-15',
heureDebut: '09:00',
heureFin: '17:00',
}),
};
vi.mocked(db.getRequestById).mockResolvedValue(existingRequest as any);
vi.mocked(db.updateRequestSalles).mockResolvedValue(undefined);
vi.mocked(db.createRequestHistory).mockResolvedValue(undefined as any);
vi.mocked(db.getAssociationById).mockResolvedValue({
id: 10,
nomAssociation: 'Association Test',
emailContact: 'test@asso.fr',
} as any);
// Simulate the changeSalles logic
const request = await db.getRequestById(1);
expect(request).toBeDefined();
expect(request!.type).toBe('demande_salle');
const formData = JSON.parse(request!.formData!);
const anciennesSalles = formData.sallesSelectionnees;
// Update salles
formData.sallesSelectionnees = ['Salle IBIS', 'Salle PELICAN'];
formData.sallesIds = ['salle_ibis', 'salle_pelican'];
// Add change history
formData.historiqueChangementsSalles = [{
date: new Date().toISOString(),
anciennesSalles,
nouvellesSalles: ['Salle IBIS', 'Salle PELICAN'],
raison: 'Besoin d\'une salle plus grande',
parAdmin: 1,
}];
await db.updateRequestSalles(1, JSON.stringify(formData));
expect(db.updateRequestSalles).toHaveBeenCalledWith(
1,
expect.stringContaining('Salle IBIS')
);
// Verify the updated formData
const updatedFormData = JSON.parse(
vi.mocked(db.updateRequestSalles).mock.calls[0][1]
);
expect(updatedFormData.sallesSelectionnees).toEqual(['Salle IBIS', 'Salle PELICAN']);
expect(updatedFormData.sallesIds).toEqual(['salle_ibis', 'salle_pelican']);
expect(updatedFormData.historiqueChangementsSalles).toHaveLength(1);
expect(updatedFormData.historiqueChangementsSalles[0].anciennesSalles).toEqual(['Salle TOUCAN']);
expect(updatedFormData.historiqueChangementsSalles[0].raison).toBe('Besoin d\'une salle plus grande');
});
it('should create request history entry for salle change', async () => {
vi.mocked(db.createRequestHistory).mockResolvedValue(undefined as any);
await db.createRequestHistory({
requestId: 1,
action: 'changement_salle',
ancienStatut: 'validee',
nouveauStatut: 'validee',
userId: 1,
commentaire: 'Changement de salle(s) : Salle TOUCAN → Salle IBIS. Raison : Besoin d\'une salle plus grande',
});
expect(db.createRequestHistory).toHaveBeenCalledWith(
expect.objectContaining({
requestId: 1,
action: 'changement_salle',
commentaire: expect.stringContaining('Salle TOUCAN'),
})
);
});
it('should reject change for non-salle requests', async () => {
const nonSalleRequest = {
id: 2,
type: 'subvention_fonctionnement',
associationId: 10,
status: 'validee',
formData: '{}',
};
vi.mocked(db.getRequestById).mockResolvedValue(nonSalleRequest as any);
const request = await db.getRequestById(2);
expect(request!.type).not.toBe('demande_salle');
// In the actual procedure, this would throw a TRPCError
});
it('should send notification to association on salle change', async () => {
vi.mocked(db.getAssociationById).mockResolvedValue({
id: 10,
nomAssociation: 'Association Test',
emailContact: 'test@asso.fr',
} as any);
const association = await db.getAssociationById(10);
expect(association).toBeDefined();
await notifyOwner({
title: 'Changement de salle pour votre réservation',
content: `Bonjour ${association!.nomAssociation}, votre salle a été modifiée.`,
});
expect(notifyOwner).toHaveBeenCalledWith(
expect.objectContaining({
title: 'Changement de salle pour votre réservation',
content: expect.stringContaining('Association Test'),
})
);
});
it('should preserve change history across multiple changes', () => {
const formData = {
sallesSelectionnees: ['Salle PELICAN'],
sallesIds: ['salle_pelican'],
historiqueChangementsSalles: [
{
date: '2026-03-10T10:00:00Z',
anciennesSalles: ['Salle TOUCAN'],
nouvellesSalles: ['Salle IBIS'],
raison: 'Premier changement',
parAdmin: 1,
},
{
date: '2026-03-11T14:00:00Z',
anciennesSalles: ['Salle IBIS'],
nouvellesSalles: ['Salle PELICAN'],
raison: 'Deuxième changement',
parAdmin: 1,
},
],
};
expect(formData.historiqueChangementsSalles).toHaveLength(2);
expect(formData.historiqueChangementsSalles[0].anciennesSalles).toEqual(['Salle TOUCAN']);
expect(formData.historiqueChangementsSalles[1].nouvellesSalles).toEqual(['Salle PELICAN']);
expect(formData.sallesSelectionnees).toEqual(['Salle PELICAN']);
});
});
});