258 lines
8.9 KiB
TypeScript
258 lines
8.9 KiB
TypeScript
import { prisma } from '../../src/db/prisma';
|
|
import { resetDatabase } from '../setup/resetDatabase';
|
|
import {
|
|
changeStato,
|
|
getListaHome,
|
|
getListaSearch,
|
|
getListMy,
|
|
getOne,
|
|
save,
|
|
} from '../../src/modules/attivita/attivita.service';
|
|
import { AuthContext } from '../../src/middlewares/auth.types';
|
|
import { AttivitaSaveInput } from '../../src/types/validation';
|
|
|
|
// Questi test girano contro un DB Postgres di test reale (vedi tests/setup),
|
|
// non contro dei mock: il dominio ha query relazionali/JSON troppo specifiche
|
|
// per essere simulate in modo affidabile.
|
|
|
|
const AUTH: AuthContext = { userId: 'user-test', email: 'test@example.com', name: 'Autore Test', roles: [] };
|
|
const MARIO: AuthContext = { userId: 'user-mario', email: 'mario@example.com', name: 'Mario', roles: [] };
|
|
const LUIGI: AuthContext = { userId: 'user-luigi', email: 'luigi@example.com', name: 'Luigi', roles: [] };
|
|
|
|
function baseAttivita(overrides: Partial<AttivitaSaveInput> = {}): AttivitaSaveInput {
|
|
return {
|
|
nome: 'Attività di test',
|
|
stato: { id: 'BO', nome: 'Bozza' },
|
|
brancaList: [],
|
|
categoriaList: [],
|
|
materialeList: [],
|
|
periodoAnnoList: [],
|
|
paragrafoList: [],
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
beforeEach(async () => {
|
|
await resetDatabase();
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await resetDatabase();
|
|
});
|
|
|
|
describe('attivita.service', () => {
|
|
test('save collega una branca esistente per nome senza duplicarla', async () => {
|
|
await save(baseAttivita({ brancaList: [{ nome: 'E/G' }] }), AUTH);
|
|
|
|
const brancheEG = await prisma.branca.findMany({ where: { nome: 'E/G' } });
|
|
expect(brancheEG).toHaveLength(1);
|
|
|
|
const attivita = await prisma.attivita.findFirstOrThrow();
|
|
const link = await prisma.brancaAttivita.findUnique({
|
|
where: { attivitaId_brancaId: { attivitaId: attivita.id, brancaId: brancheEG[0].id } },
|
|
});
|
|
expect(link).not.toBeNull();
|
|
expect(link?.cancellato).toBe(false);
|
|
|
|
const dto = await getOne(attivita.id);
|
|
expect(dto?.brancaList.map((b) => b.nome)).toEqual(['E/G']);
|
|
});
|
|
|
|
test('save crea branca/categoria/materiale/periodoAnno nuovi quando non esistono per nome', async () => {
|
|
await save(
|
|
baseAttivita({
|
|
materialeList: [{ nome: 'Corda 10m', proprieta: { lunghezza: 10 } }],
|
|
}),
|
|
AUTH,
|
|
);
|
|
|
|
const materiale = await prisma.materiale.findFirst({ where: { nome: 'Corda 10m' } });
|
|
expect(materiale).not.toBeNull();
|
|
|
|
const attivita = await prisma.attivita.findFirstOrThrow();
|
|
const link = await prisma.materialeAttivita.findUnique({
|
|
where: { attivitaId_materialeId: { attivitaId: attivita.id, materialeId: materiale!.id } },
|
|
});
|
|
expect(link).not.toBeNull();
|
|
expect(link?.cancellato).toBe(false);
|
|
expect(link?.proprieta).toEqual({ lunghezza: 10 });
|
|
});
|
|
|
|
test('save in aggiornamento marca come cancellato (soft-delete) il collegamento non più presente', async () => {
|
|
const lc = await prisma.branca.findFirstOrThrow({ where: { nome: 'L/C' } });
|
|
const eg = await prisma.branca.findFirstOrThrow({ where: { nome: 'E/G' } });
|
|
|
|
await save(baseAttivita({ brancaList: [{ nome: 'L/C' }, { nome: 'E/G' }] }), AUTH);
|
|
const created = await prisma.attivita.findFirstOrThrow();
|
|
|
|
await save(baseAttivita({ id: created.id, brancaList: [{ nome: 'E/G' }] }), AUTH);
|
|
|
|
const linkLc = await prisma.brancaAttivita.findUniqueOrThrow({
|
|
where: { attivitaId_brancaId: { attivitaId: created.id, brancaId: lc.id } },
|
|
});
|
|
const linkEg = await prisma.brancaAttivita.findUniqueOrThrow({
|
|
where: { attivitaId_brancaId: { attivitaId: created.id, brancaId: eg.id } },
|
|
});
|
|
|
|
expect(linkLc.cancellato).toBe(true);
|
|
expect(linkEg.cancellato).toBe(false);
|
|
});
|
|
|
|
test('save sostituisce correttamente i paragrafi (modifica, rimozione, aggiunta)', async () => {
|
|
await save(
|
|
baseAttivita({
|
|
paragrafoList: [
|
|
{ corpo: 'Paragrafo uno', tipo: { id: 'PARAGRAFO' }, ordine: 1 },
|
|
{ corpo: 'Paragrafo due', tipo: { id: 'PARAGRAFO' }, ordine: 2 },
|
|
],
|
|
}),
|
|
AUTH,
|
|
);
|
|
|
|
const created = await prisma.attivita.findFirstOrThrow();
|
|
const paragrafi = await prisma.paragrafo.findMany({
|
|
where: { attivitaId: created.id },
|
|
orderBy: { ordine: 'asc' },
|
|
});
|
|
expect(paragrafi).toHaveLength(2);
|
|
const [primo, secondo] = paragrafi;
|
|
|
|
await save(
|
|
baseAttivita({
|
|
id: created.id,
|
|
paragrafoList: [
|
|
{
|
|
id: primo.id,
|
|
corpo: 'Paragrafo uno modificato',
|
|
tipo: { id: 'PARAGRAFO' },
|
|
ordine: 1,
|
|
},
|
|
{ corpo: 'Paragrafo nuovo', tipo: { id: 'PARAGRAFO' }, ordine: 2 },
|
|
],
|
|
}),
|
|
AUTH,
|
|
);
|
|
|
|
const paragrafiFinali = await prisma.paragrafo.findMany({
|
|
where: { attivitaId: created.id },
|
|
orderBy: { ordine: 'asc' },
|
|
});
|
|
|
|
expect(paragrafiFinali).toHaveLength(2);
|
|
expect(paragrafiFinali.map((p) => p.corpo)).toEqual(['Paragrafo uno modificato', 'Paragrafo nuovo']);
|
|
expect(paragrafiFinali.find((p) => p.id === secondo.id)).toBeUndefined();
|
|
});
|
|
|
|
test('save su id inesistente lancia HttpError 404', async () => {
|
|
await expect(save(baseAttivita({ id: 999999 }), AUTH)).rejects.toMatchObject({ statusCode: 404 });
|
|
});
|
|
|
|
test("save su un'attività di un altro autore lancia HttpError 403", async () => {
|
|
await save(baseAttivita(), MARIO);
|
|
const created = await prisma.attivita.findFirstOrThrow();
|
|
|
|
await expect(save(baseAttivita({ id: created.id }), LUIGI)).rejects.toMatchObject({ statusCode: 403 });
|
|
});
|
|
|
|
test('getListaHome restituisce solo attività pubblicate', async () => {
|
|
await save(baseAttivita({ nome: 'Bozza', stato: { id: 'BO', nome: 'Bozza' } }), AUTH);
|
|
await save(baseAttivita({ nome: 'Pubblicata', stato: { id: 'PU', nome: 'Pubblicato' } }), AUTH);
|
|
await save(baseAttivita({ nome: 'Privata', stato: { id: 'PR', nome: 'Privato' } }), AUTH);
|
|
|
|
const home = await getListaHome();
|
|
expect(home).toHaveLength(1);
|
|
expect(home[0].nome).toBe('Pubblicata');
|
|
});
|
|
|
|
test('getListMy filtra per autore', async () => {
|
|
await save(baseAttivita({ nome: 'Di Mario' }), MARIO);
|
|
await save(baseAttivita({ nome: 'Di Luigi' }), LUIGI);
|
|
|
|
const mie = await getListMy(MARIO.userId);
|
|
expect(mie).toHaveLength(1);
|
|
expect(mie[0].nome).toBe('Di Mario');
|
|
expect(mie[0].autore).toBe('Mario');
|
|
});
|
|
|
|
test('changeStato su id inesistente lancia HttpError 404', async () => {
|
|
await expect(changeStato(999999, 'PU', AUTH)).rejects.toMatchObject({ statusCode: 404 });
|
|
});
|
|
|
|
test("changeStato su un'attività di un altro autore lancia HttpError 403", async () => {
|
|
await save(baseAttivita(), MARIO);
|
|
const created = await prisma.attivita.findFirstOrThrow();
|
|
|
|
await expect(changeStato(created.id, 'PU', LUIGI)).rejects.toMatchObject({ statusCode: 403 });
|
|
});
|
|
|
|
test('changeStato aggiorna lo stato e lo restituisce come TipologicaDto', async () => {
|
|
await save(baseAttivita(), AUTH);
|
|
const created = await prisma.attivita.findFirstOrThrow();
|
|
|
|
const stato = await changeStato(created.id, 'PU', AUTH);
|
|
expect(stato).toEqual({ id: 'PU', nome: 'Pubblicato' });
|
|
|
|
const aggiornata = await prisma.attivita.findUniqueOrThrow({ where: { id: created.id } });
|
|
expect(aggiornata.statoId).toBe('PU');
|
|
});
|
|
|
|
test('getListaSearch combina filtri di gruppi diversi in AND e valori dello stesso gruppo in OR', async () => {
|
|
const lc = await prisma.branca.findFirstOrThrow({ where: { nome: 'L/C' } });
|
|
const gioco = await prisma.categoria.findFirstOrThrow({ where: { nome: 'gioco' } });
|
|
|
|
await save(
|
|
baseAttivita({
|
|
nome: 'L/C gioco',
|
|
brancaList: [{ nome: 'L/C' }],
|
|
categoriaList: [{ nome: 'gioco' }],
|
|
}),
|
|
AUTH,
|
|
);
|
|
await save(
|
|
baseAttivita({
|
|
nome: 'E/G gioco',
|
|
brancaList: [{ nome: 'E/G' }],
|
|
categoriaList: [{ nome: 'gioco' }],
|
|
}),
|
|
AUTH,
|
|
);
|
|
await save(
|
|
baseAttivita({
|
|
nome: 'L/C danza',
|
|
brancaList: [{ nome: 'L/C' }],
|
|
categoriaList: [{ nome: 'danza' }],
|
|
}),
|
|
AUTH,
|
|
);
|
|
|
|
const risultati = await getListaSearch([
|
|
{ gruppo: 'branca', id: lc.id, nome: null },
|
|
{ gruppo: 'categoria', id: gioco.id, nome: null },
|
|
]);
|
|
|
|
expect(risultati).toHaveLength(1);
|
|
expect(risultati[0].nome).toBe('L/C gioco');
|
|
});
|
|
|
|
test('getListaSearch con filtro testo trova sia nel nome sia nei paragrafi', async () => {
|
|
await save(baseAttivita({ nome: 'Caccia al tesoro' }), AUTH);
|
|
await save(
|
|
baseAttivita({
|
|
nome: 'Attività generica',
|
|
paragrafoList: [
|
|
{
|
|
corpo: "C'è un tesoro nascosto nel bosco",
|
|
tipo: { id: 'PARAGRAFO' },
|
|
ordine: 1,
|
|
},
|
|
],
|
|
}),
|
|
AUTH,
|
|
);
|
|
|
|
const risultati = await getListaSearch([{ gruppo: 'testo', nome: 'tesoro', id: null }]);
|
|
|
|
expect(risultati.map((r) => r.nome).sort()).toEqual(['Attività generica', 'Caccia al tesoro'].sort());
|
|
});
|
|
});
|