280 lines
10 KiB
TypeScript
280 lines
10 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
|
// Mock the database module
|
|
vi.mock('./db', () => ({
|
|
getRequestById: vi.fn(),
|
|
updateRequest: vi.fn(),
|
|
createRequestHistory: vi.fn(),
|
|
createAuditLog: vi.fn(),
|
|
}));
|
|
|
|
import * as db from './db';
|
|
|
|
describe('DSU (Direction des Services aux Usagers) - Cadre réservé', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe('Merging DSU data into formData', () => {
|
|
it('should merge DSU data into existing formData for demande_salle', () => {
|
|
const existingFormData = {
|
|
nomAssociation: 'Association Test',
|
|
sallesSelectionnees: ['Salle TOUCAN'],
|
|
dateReservation: '2026-04-15',
|
|
motifReservation: 'Réunion annuelle',
|
|
formulaireType: 'reservation_salle_mjs',
|
|
};
|
|
|
|
const dsuData = {
|
|
dateReception: '2026-03-10',
|
|
avisDSU: 'favorable' as const,
|
|
conditionsParticulieres: 'Nettoyage obligatoire',
|
|
cautionRequise: true,
|
|
montantCaution: '500',
|
|
assuranceRequise: true,
|
|
horairesImposes: '8h00 - 22h00',
|
|
responsableDSU: 'Jean Dupont',
|
|
dateDecision: '2026-03-11',
|
|
observationsDSU: 'RAS',
|
|
};
|
|
|
|
// Simulate the merge logic from routers.ts
|
|
const merged = { ...existingFormData };
|
|
(merged as any).cadreDSU = {
|
|
...dsuData,
|
|
rempliPar: 1,
|
|
dateRemplissage: new Date().toISOString(),
|
|
};
|
|
|
|
const result = JSON.parse(JSON.stringify(merged));
|
|
|
|
// Verify original data is preserved
|
|
expect(result.nomAssociation).toBe('Association Test');
|
|
expect(result.sallesSelectionnees).toEqual(['Salle TOUCAN']);
|
|
expect(result.dateReservation).toBe('2026-04-15');
|
|
expect(result.motifReservation).toBe('Réunion annuelle');
|
|
|
|
// Verify DSU data is added
|
|
expect(result.cadreDSU).toBeDefined();
|
|
expect(result.cadreDSU.avisDSU).toBe('favorable');
|
|
expect(result.cadreDSU.dateReception).toBe('2026-03-10');
|
|
expect(result.cadreDSU.conditionsParticulieres).toBe('Nettoyage obligatoire');
|
|
expect(result.cadreDSU.cautionRequise).toBe(true);
|
|
expect(result.cadreDSU.montantCaution).toBe('500');
|
|
expect(result.cadreDSU.assuranceRequise).toBe(true);
|
|
expect(result.cadreDSU.horairesImposes).toBe('8h00 - 22h00');
|
|
expect(result.cadreDSU.responsableDSU).toBe('Jean Dupont');
|
|
expect(result.cadreDSU.dateDecision).toBe('2026-03-11');
|
|
expect(result.cadreDSU.observationsDSU).toBe('RAS');
|
|
expect(result.cadreDSU.rempliPar).toBe(1);
|
|
expect(result.cadreDSU.dateRemplissage).toBeDefined();
|
|
});
|
|
|
|
it('should not add DSU data for non-salle request types', () => {
|
|
const requestType = 'subvention_fonctionnement';
|
|
const dsuData = {
|
|
avisDSU: 'favorable' as const,
|
|
responsableDSU: 'Jean Dupont',
|
|
};
|
|
|
|
// Simulate the condition check from routers.ts
|
|
const shouldAddDsu = requestType === 'demande_salle';
|
|
expect(shouldAddDsu).toBe(false);
|
|
});
|
|
|
|
it('should handle empty existing formData gracefully', () => {
|
|
const existingFormData = null;
|
|
const dsuData = {
|
|
avisDSU: 'defavorable' as const,
|
|
responsableDSU: 'Marie Martin',
|
|
observationsDSU: 'Salle non disponible',
|
|
};
|
|
|
|
// Simulate the merge logic
|
|
const parsed = existingFormData ? JSON.parse(existingFormData) : {};
|
|
parsed.cadreDSU = {
|
|
...dsuData,
|
|
rempliPar: 2,
|
|
dateRemplissage: new Date().toISOString(),
|
|
};
|
|
|
|
const result = JSON.parse(JSON.stringify(parsed));
|
|
expect(result.cadreDSU).toBeDefined();
|
|
expect(result.cadreDSU.avisDSU).toBe('defavorable');
|
|
expect(result.cadreDSU.responsableDSU).toBe('Marie Martin');
|
|
});
|
|
|
|
it('should handle favorable_avec_reserves avis correctly', () => {
|
|
const existingFormData = { nomAssociation: 'Test' };
|
|
const dsuData = {
|
|
avisDSU: 'favorable_avec_reserves' as const,
|
|
conditionsParticulieres: 'Sous réserve de présentation de l\'attestation d\'assurance',
|
|
cautionRequise: true,
|
|
montantCaution: '1000',
|
|
};
|
|
|
|
const merged = { ...existingFormData, cadreDSU: { ...dsuData, rempliPar: 1, dateRemplissage: new Date().toISOString() } };
|
|
const result = JSON.parse(JSON.stringify(merged));
|
|
|
|
expect(result.cadreDSU.avisDSU).toBe('favorable_avec_reserves');
|
|
expect(result.cadreDSU.conditionsParticulieres).toContain('attestation d\'assurance');
|
|
expect(result.cadreDSU.cautionRequise).toBe(true);
|
|
expect(result.cadreDSU.montantCaution).toBe('1000');
|
|
});
|
|
|
|
it('should merge financial decision into an existing salle request dossier', () => {
|
|
const existingFormData = {
|
|
nomAssociation: 'Association Test',
|
|
sallesSelectionnees: ['Salle TOUCAN'],
|
|
dateReservation: '2026-06-20',
|
|
cadreDSU: {
|
|
responsableDSU: 'Agent DSU',
|
|
},
|
|
};
|
|
|
|
const financialDecision = {
|
|
financialMode: 'location_payante' as const,
|
|
depositRequired: true,
|
|
depositAmountCents: 45000,
|
|
rentalAmountCents: 12500,
|
|
pricingNotes: 'Facturation exceptionnelle pour occupation prolongée',
|
|
contractStatus: 'a_generer' as const,
|
|
};
|
|
|
|
const merged = structuredClone(existingFormData) as any;
|
|
merged.cadreDSU = {
|
|
...merged.cadreDSU,
|
|
cautionRequise: financialDecision.depositRequired,
|
|
montantCaution: String(financialDecision.depositAmountCents / 100),
|
|
materielEvent: {
|
|
financialDecision,
|
|
},
|
|
};
|
|
|
|
expect(merged.cadreDSU.cautionRequise).toBe(true);
|
|
expect(merged.cadreDSU.montantCaution).toBe('450');
|
|
expect(merged.cadreDSU.materielEvent.financialDecision.financialMode).toBe('location_payante');
|
|
expect(merged.cadreDSU.materielEvent.financialDecision.rentalAmountCents).toBe(12500);
|
|
});
|
|
|
|
it('should merge material financial decision into the existing material request data', () => {
|
|
const existingFormData = {
|
|
commune: 'Kourou',
|
|
service: 'Vie associative',
|
|
quantitesDemandees: { tente3x3: '2' },
|
|
cadreDSU: {
|
|
dateReception: '2026-05-10',
|
|
materielEvent: {
|
|
itemsAccordes: { tente3x3: true },
|
|
quantitesAccordees: { tente3x3: '2' },
|
|
},
|
|
},
|
|
};
|
|
|
|
const financialDecision = {
|
|
financialMode: 'location_payante' as const,
|
|
depositRequired: true,
|
|
depositAmountCents: 25000,
|
|
rentalAmountCents: 9500,
|
|
pricingNotes: 'Tarif exceptionnel pour événement intercommunal',
|
|
contractStatus: 'a_generer' as const,
|
|
};
|
|
|
|
const merged = structuredClone(existingFormData) as any;
|
|
merged.cadreDSU = {
|
|
...merged.cadreDSU,
|
|
responsableDSU: 'Admin Logistique',
|
|
materielEvent: {
|
|
...merged.cadreDSU.materielEvent,
|
|
financialDecision,
|
|
},
|
|
};
|
|
|
|
expect(merged.cadreDSU.materielEvent.itemsAccordes.tente3x3).toBe(true);
|
|
expect(merged.cadreDSU.materielEvent.quantitesAccordees.tente3x3).toBe('2');
|
|
expect(merged.cadreDSU.materielEvent.financialDecision.financialMode).toBe('location_payante');
|
|
expect(merged.cadreDSU.materielEvent.financialDecision.depositRequired).toBe(true);
|
|
expect(merged.cadreDSU.materielEvent.financialDecision.depositAmountCents).toBe(25000);
|
|
expect(merged.cadreDSU.materielEvent.financialDecision.rentalAmountCents).toBe(9500);
|
|
expect(merged.cadreDSU.materielEvent.financialDecision.pricingNotes).toContain('Tarif exceptionnel');
|
|
});
|
|
});
|
|
|
|
describe('DSU avis labels mapping', () => {
|
|
const avisDSULabels: Record<string, { label: string; className: string }> = {
|
|
favorable: { label: 'Favorable', className: 'bg-green-100 text-green-800 border-green-200' },
|
|
defavorable: { label: 'Défavorable', className: 'bg-red-100 text-red-800 border-red-200' },
|
|
favorable_avec_reserves: { label: 'Favorable avec réserves', className: 'bg-amber-100 text-amber-800 border-amber-200' },
|
|
};
|
|
|
|
it('should have correct labels for all avis types', () => {
|
|
expect(avisDSULabels.favorable.label).toBe('Favorable');
|
|
expect(avisDSULabels.defavorable.label).toBe('Défavorable');
|
|
expect(avisDSULabels.favorable_avec_reserves.label).toBe('Favorable avec réserves');
|
|
});
|
|
|
|
it('should have appropriate CSS classes for visual distinction', () => {
|
|
expect(avisDSULabels.favorable.className).toContain('green');
|
|
expect(avisDSULabels.defavorable.className).toContain('red');
|
|
expect(avisDSULabels.favorable_avec_reserves.className).toContain('amber');
|
|
});
|
|
});
|
|
|
|
describe('Process mutation with DSU data', () => {
|
|
it('should call updateRequest with merged formData when DSU data is provided', async () => {
|
|
const mockRequest = {
|
|
id: 1,
|
|
type: 'demande_salle',
|
|
status: 'soumise',
|
|
formData: JSON.stringify({
|
|
nomAssociation: 'Test Asso',
|
|
sallesSelectionnees: ['Salle TOUCAN'],
|
|
}),
|
|
};
|
|
|
|
(db.getRequestById as any).mockResolvedValue(mockRequest);
|
|
(db.updateRequest as any).mockResolvedValue(undefined);
|
|
(db.createRequestHistory as any).mockResolvedValue(undefined);
|
|
(db.createAuditLog as any).mockResolvedValue(undefined);
|
|
|
|
const dsuData = {
|
|
avisDSU: 'favorable' as const,
|
|
responsableDSU: 'Admin Test',
|
|
dateReception: '2026-03-10',
|
|
dateDecision: '2026-03-11',
|
|
};
|
|
|
|
// Simulate the process logic
|
|
const existingFormData = JSON.parse(mockRequest.formData);
|
|
existingFormData.cadreDSU = {
|
|
...dsuData,
|
|
rempliPar: 1,
|
|
dateRemplissage: new Date().toISOString(),
|
|
};
|
|
const updatedFormData = JSON.stringify(existingFormData);
|
|
|
|
await db.updateRequest(mockRequest.id, {
|
|
status: 'validee' as any,
|
|
commentaireAdmin: 'Approuvé',
|
|
traitePar: 1,
|
|
dateTraitement: new Date(),
|
|
formData: updatedFormData,
|
|
});
|
|
|
|
expect(db.updateRequest).toHaveBeenCalledWith(
|
|
1,
|
|
expect.objectContaining({
|
|
status: 'validee',
|
|
formData: expect.stringContaining('cadreDSU'),
|
|
})
|
|
);
|
|
|
|
// Verify the formData contains the DSU data
|
|
const callArgs = (db.updateRequest as any).mock.calls[0][1];
|
|
const parsedFormData = JSON.parse(callArgs.formData);
|
|
expect(parsedFormData.cadreDSU.avisDSU).toBe('favorable');
|
|
expect(parsedFormData.cadreDSU.responsableDSU).toBe('Admin Test');
|
|
expect(parsedFormData.nomAssociation).toBe('Test Asso');
|
|
});
|
|
});
|
|
});
|