Deploy stack su Proxmox / deploy (push) Failing after 3s
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
168 lines
8.3 KiB
TypeScript
168 lines
8.3 KiB
TypeScript
import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest"
|
|
import { buildTestApp, cookieFrom, createUserAndLogin, TEST_PASSWORD, type TestApp, truncateAll } from "./helpers.js"
|
|
|
|
let t: TestApp
|
|
|
|
beforeAll(async () => {
|
|
t = await buildTestApp()
|
|
})
|
|
afterAll(async () => {
|
|
await t.app.close()
|
|
})
|
|
beforeEach(async () => {
|
|
await truncateAll()
|
|
t.medusa.reset()
|
|
})
|
|
|
|
describe("PATCH /me", () => {
|
|
it("aggiorna nome e cognome, senza toccare il resto", async () => {
|
|
const { headers } = await createUserAndLogin(t.app, "mario@example.com")
|
|
const res = await t.app.inject({ method: "PATCH", url: "/me", headers, payload: { firstName: " Mario ", lastName: "Bianchi" } })
|
|
expect(res.statusCode, res.body).toBe(200)
|
|
expect(res.json()).toMatchObject({ firstName: "Mario", lastName: "Bianchi", email: "mario@example.com", role: "customer" })
|
|
expect((await t.app.inject({ method: "GET", url: "/me", headers })).json().lastName).toBe("Bianchi")
|
|
})
|
|
|
|
it("un privato non ha dati aziendali; email e ruolo non si cambiano", async () => {
|
|
const { headers } = await createUserAndLogin(t.app, "mario@example.com")
|
|
const res = await t.app.inject({ method: "PATCH", url: "/me", headers, payload: { companyName: "Mario Srl" } })
|
|
expect(res.statusCode).toBe(400)
|
|
expect(res.json().error.details[0].path).toBe("body.companyName")
|
|
const sneaky = await t.app.inject({ method: "PATCH", url: "/me", headers, payload: { role: "admin", email: "x@example.com", firstName: "M" } })
|
|
expect(sneaky.statusCode).toBe(200)
|
|
expect(sneaky.json()).toMatchObject({ role: "customer", email: "mario@example.com" })
|
|
})
|
|
|
|
it("azienda: ragione sociale e P.IVA modificabili ma non svuotabili", async () => {
|
|
const { headers } = await createUserAndLogin(t.app, "srl@example.com", "company", { companyName: "Vecchia Srl", vatNumber: "12345678903" })
|
|
const ok = await t.app.inject({ method: "PATCH", url: "/me", headers, payload: { companyName: "Nuova Srl" } })
|
|
expect(ok.json()).toMatchObject({ companyName: "Nuova Srl", vatNumber: "12345678903" })
|
|
const empty = await t.app.inject({ method: "PATCH", url: "/me", headers, payload: { vatNumber: "" } })
|
|
expect(empty.statusCode).toBe(400)
|
|
expect(empty.json().error.details[0].path).toBe("body.vatNumber")
|
|
})
|
|
|
|
it("installatore: P.IVA facoltativa, ragione sociale obbligatoria", async () => {
|
|
const { headers } = await createUserAndLogin(t.app, "inst@example.com", "installer", { companyName: "Impianti Srl", vatNumber: "12345678903" })
|
|
expect((await t.app.inject({ method: "PATCH", url: "/me", headers, payload: { vatNumber: null } })).json().vatNumber).toBeNull()
|
|
expect((await t.app.inject({ method: "PATCH", url: "/me", headers, payload: { companyName: " " } })).statusCode).toBe(400)
|
|
})
|
|
|
|
it("serve l'autenticazione", async () => {
|
|
expect((await t.app.inject({ method: "PATCH", url: "/me", payload: { firstName: "X" } })).statusCode).toBe(401)
|
|
})
|
|
})
|
|
|
|
describe("POST /auth/change-password", () => {
|
|
async function loginCookie(email: string, password = TEST_PASSWORD) {
|
|
const res = await t.app.inject({ method: "POST", url: "/auth/login", payload: { email, password } })
|
|
return { status: res.statusCode, cookie: cookieFrom(res.headers["set-cookie"], "iti_rt") }
|
|
}
|
|
|
|
it("con la password attuale: nuova sessione qui, le altre chiuse", async () => {
|
|
const { headers } = await createUserAndLogin(t.app, "mario@example.com")
|
|
const other = await loginCookie("mario@example.com")
|
|
const res = await t.app.inject({
|
|
method: "POST",
|
|
url: "/auth/change-password",
|
|
headers,
|
|
payload: { currentPassword: TEST_PASSWORD, newPassword: "Nuova-password-123" },
|
|
})
|
|
expect(res.statusCode, res.body).toBe(200)
|
|
expect(res.json()).toMatchObject({ accessToken: expect.any(String), user: { email: "mario@example.com" } })
|
|
const fresh = cookieFrom(res.headers["set-cookie"], "iti_rt")
|
|
expect(fresh).toBeTruthy()
|
|
|
|
// l'altro dispositivo perde la sessione, questo no
|
|
const otherRefresh = await t.app.inject({ method: "POST", url: "/auth/refresh", cookies: { iti_rt: other.cookie! } })
|
|
expect(otherRefresh.statusCode).toBe(401)
|
|
const thisRefresh = await t.app.inject({ method: "POST", url: "/auth/refresh", cookies: { iti_rt: fresh! } })
|
|
expect(thisRefresh.statusCode).toBe(200)
|
|
|
|
expect((await loginCookie("mario@example.com")).status).toBe(401)
|
|
expect((await loginCookie("mario@example.com", "Nuova-password-123")).status).toBe(200)
|
|
})
|
|
|
|
it("password attuale errata → 400 sul campo, nulla cambia", async () => {
|
|
const { headers } = await createUserAndLogin(t.app, "mario@example.com")
|
|
const res = await t.app.inject({
|
|
method: "POST",
|
|
url: "/auth/change-password",
|
|
headers,
|
|
payload: { currentPassword: "sbagliata-123", newPassword: "Nuova-password-123" },
|
|
})
|
|
expect(res.statusCode).toBe(400)
|
|
expect(res.json().error).toMatchObject({ code: "INVALID_CURRENT_PASSWORD", details: [{ path: "body.currentPassword" }] })
|
|
expect((await loginCookie("mario@example.com")).status).toBe(200)
|
|
})
|
|
|
|
it("nuova password troppo corta o uguale all'attuale → 400", async () => {
|
|
const { headers } = await createUserAndLogin(t.app, "mario@example.com")
|
|
for (const newPassword of ["corta", TEST_PASSWORD]) {
|
|
const res = await t.app.inject({ method: "POST", url: "/auth/change-password", headers, payload: { currentPassword: TEST_PASSWORD, newPassword } })
|
|
expect(res.statusCode).toBe(400)
|
|
}
|
|
})
|
|
})
|
|
|
|
describe("GET /me/orders/:number", () => {
|
|
const summary = (id: string, displayId: number) => ({
|
|
id,
|
|
display_id: displayId,
|
|
status: "pending",
|
|
created_at: "2026-10-01T10:00:00.000Z",
|
|
payment_status: "captured",
|
|
fulfillment_status: "shipped",
|
|
item_total: 1190,
|
|
shipping_total: 15,
|
|
tax_total: 217.29,
|
|
total: 1205,
|
|
items: [],
|
|
})
|
|
|
|
it("dettaglio del proprio ordine con indirizzo, spedizione e tracking; 404 per gli altri", async () => {
|
|
const mario = await createUserAndLogin(t.app, "mario@example.com")
|
|
const luigi = await createUserAndLogin(t.app, "luigi@example.com")
|
|
await t.app.inject({ method: "GET", url: "/me/orders", headers: mario.headers })
|
|
const customerId = t.medusa.accountByEmail("mario@example.com")!.id
|
|
t.medusa.customerOrderList.set(customerId, [summary("order_1", 12)])
|
|
t.medusa.orderDetails.set("order_1", {
|
|
...summary("order_1", 12),
|
|
email: "mario@example.com",
|
|
customer_id: customerId,
|
|
currency_code: "eur",
|
|
discount_total: 0,
|
|
items: [
|
|
{ title: "Standard", product_title: "Addolcitore", variant_title: "Standard", variant_sku: "ADD-20", thumbnail: null, quantity: 1, unit_price: 1190, total: 1190 },
|
|
],
|
|
shipping_address: { first_name: "Mario", last_name: "Rossi", address_1: "Via Roma 1", city: "Milano", postal_code: "20100", province: "MI" },
|
|
shipping_methods: [{ name: "Spedizione standard" }],
|
|
fulfillments: [{ id: "ful_1", labels: [{ tracking_number: "BRT123", tracking_url: "https://track.example.com/BRT123" }] }],
|
|
})
|
|
|
|
const res = await t.app.inject({ method: "GET", url: "/me/orders/12", headers: mario.headers })
|
|
expect(res.statusCode, res.body).toBe(200)
|
|
expect(res.json()).toMatchObject({
|
|
number: 12,
|
|
fulfillmentStatus: "shipped",
|
|
totals: { total: 120500, shipping: 1500, discount: 0 },
|
|
items: [{ title: "Addolcitore - Standard", sku: "ADD-20", unitPrice: 119000 }],
|
|
shippingAddress: ["Mario Rossi", "Via Roma 1", "20100 Milano (MI)"],
|
|
shippingMethods: ["Spedizione standard"],
|
|
tracking: [{ number: "BRT123", url: "https://track.example.com/BRT123" }],
|
|
})
|
|
expect(res.body).not.toContain("order_1")
|
|
expect(res.body).not.toContain(customerId)
|
|
|
|
expect((await t.app.inject({ method: "GET", url: "/me/orders/12", headers: luigi.headers })).statusCode).toBe(404)
|
|
expect((await t.app.inject({ method: "GET", url: "/me/orders/99", headers: mario.headers })).statusCode).toBe(404)
|
|
expect((await t.app.inject({ method: "GET", url: "/me/orders/abc", headers: mario.headers })).statusCode).toBe(400)
|
|
})
|
|
|
|
it("Medusa giù → 503", async () => {
|
|
const mario = await createUserAndLogin(t.app, "mario@example.com")
|
|
t.medusa.down = true
|
|
expect((await t.app.inject({ method: "GET", url: "/me/orders/1", headers: mario.headers })).statusCode).toBe(503)
|
|
})
|
|
})
|