Files
iti-technology/apps/api/test/tickets.test.ts
T
Lorenzo SanesiandClaude Opus 5.5 b83ce039fb
Deploy stack su Proxmox / deploy (push) Failing after 3s
Piattaforma ITI TECHNOLOGY: monorepo, Medusa, API, sito, area riservata e deploy
Monorepo npm con apps/public (Angular SSR), apps/private (Angular SPA),
apps/api (Fastify), packages/shared e packages/styles; Medusa 2.21 in medusa/;
infrastruttura Docker (Postgres PostGIS+pgvector, Redis, Caddy, backup) e
workflow Gitea Actions per il deploy via SSH a ogni push su master.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018jQyT6dR9czzGwqb7FkG6u
2026-10-20 09:30:48 +02:00

82 lines
4.0 KiB
TypeScript

import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest"
import { buildTestApp, createUserAndLogin, type TestApp, truncateAll } from "./helpers.js"
let t: TestApp
let admin: Record<string, string>
let mario: Record<string, string>
let luigi: Record<string, string>
const call = (method: "GET" | "POST" | "PATCH", url: string, headers: Record<string, string>, payload?: object) =>
t.app.inject({ method, url, headers, ...(payload ? { payload } : {}) })
const openTicket = async (headers = mario) => {
const res = await call("POST", "/me/tickets", headers, { subject: "Addolcitore in allarme", body: "Lampeggia la spia rossa" })
expect(res.statusCode, res.body).toBe(201)
return res.json()
}
beforeAll(async () => {
t = await buildTestApp()
})
afterAll(() => t.app.close())
beforeEach(async () => {
await truncateAll()
t.email.clear()
admin = (await createUserAndLogin(t.app, "admin@iti.local", "admin")).headers
mario = (await createUserAndLogin(t.app, "mario@example.com")).headers
luigi = (await createUserAndLogin(t.app, "luigi@example.com")).headers
})
describe("ticket", () => {
it("apertura con primo messaggio e notifica allo staff", async () => {
const tk = await openTicket()
expect(tk).toMatchObject({ subject: "Addolcitore in allarme", status: "open", priority: "normal" })
expect(tk.messages).toHaveLength(1)
expect(tk.messages[0]).toMatchObject({ body: "Lampeggia la spia rossa", fromStaff: false })
expect(t.email.last("staff-notification")?.to).toBe("admin@iti.local")
// priorità urgent riservata all'assistenza
expect((await call("POST", "/me/tickets", mario, { subject: "Urgente!", body: "x", priority: "urgent" })).statusCode).toBe(400)
})
it("un cliente non può leggere né scrivere nei ticket di un altro (404)", async () => {
const tk = await openTicket()
expect((await call("GET", `/me/tickets/${tk.id}`, luigi)).statusCode).toBe(404)
expect((await call("POST", `/me/tickets/${tk.id}/messages`, luigi, { body: "intrusione" })).statusCode).toBe(404)
expect((await call("GET", "/me/tickets", luigi)).json().total).toBe(0)
expect((await call("GET", "/me/tickets", mario)).json().total).toBe(1)
expect((await call("GET", "/admin/tickets", mario)).statusCode).toBe(403)
})
it("risposta admin: email al cliente, stato in attesa del cliente; la risposta del cliente riapre", async () => {
const tk = await openTicket()
t.email.clear()
const reply = await call("POST", `/admin/tickets/${tk.id}/messages`, admin, { body: "Il tecnico passa giovedì" })
expect(reply.statusCode).toBe(201)
expect(reply.json()).toMatchObject({ status: "waiting_customer", customer: { email: "mario@example.com" } })
expect(reply.json().messages.map((m: { fromStaff: boolean }) => m.fromStaff)).toEqual([false, true])
const mail = t.email.last("ticket-reply")
expect(mail?.to).toBe("mario@example.com")
expect(mail?.text).toContain("Il tecnico passa giovedì")
expect(mail?.text).toContain(`/assistenza/${tk.id}`)
const back = await call("POST", `/me/tickets/${tk.id}/messages`, mario, { body: "Va bene, grazie" })
expect(back.json().status).toBe("open")
expect(back.json().messages).toHaveLength(3)
})
it("admin: elenco con filtri, cambio stato e priorità; chiuso e riaperto dal cliente", async () => {
const tk = await openTicket()
await openTicket(luigi)
expect((await call("GET", "/admin/tickets?q=luigi", admin)).json().total).toBe(1)
const patched = await call("PATCH", `/admin/tickets/${tk.id}`, admin, { priority: "urgent" })
expect(patched.json()).toMatchObject({ priority: "urgent", status: "open" })
await call("PATCH", `/admin/tickets/${tk.id}`, admin, { status: "closed" })
expect((await call("GET", "/admin/tickets?status=closed", admin)).json().items.map((x: { id: string }) => x.id)).toEqual([tk.id])
const reopened = await call("POST", `/me/tickets/${tk.id}/messages`, mario, { body: "Il problema si è ripresentato" })
expect(reopened.json().status).toBe("open")
})
})