const BASE = "http://localhost:5173/api"; let pass = 0, fail = 0; async function req(method, path, body, token) { const headers = { "Content-Type": "application/json" }; if (token) headers.authorization = `Bearer ${token}`; const res = await fetch(`${BASE}${path}`, { method, headers, body: body ? JSON.stringify(body) : undefined }); const text = await res.text(); let json; try { json = JSON.parse(text); } catch { json = text; } return { status: res.status, json }; } function check(name, cond, extra = "") { if (cond) { pass++; console.log(` ✓ ${name}`); } else { fail++; console.log(` ✗ ${name} ${extra}`); } } // Fetch the first available slot (iso) for a service over the next `maxDays` days. // Slots already filter out non-working days and double-booked specialists, so any // returned iso is safe to use as start_at for either autoAssign or self-assign paths. async function findSlot(slug, serviceId, maxDays = 30) { for (let i = 1; i <= maxDays; i++) { const d = new Date(Date.now() + i * 86400000); const date = d.toISOString().slice(0, 10); const { json } = await req("GET", `/public/${slug}/slots?service_id=${serviceId}&date=${date}`); if (Array.isArray(json.slots) && json.slots.length > 0) return json.slots[0].iso; } return null; } // /me now works (the bug we fixed) — login first to get a real token const { json: login } = await req("POST", "/auth/login", { email: "owner@agendapro.demo", password: "demo1234" }); check("login owner", !!login.token && login.user?.role === "owner", JSON.stringify(login).slice(0, 100)); const t = login.token; const { json: me } = await req("GET", "/auth/me", null, t); check("/api/auth/me returns user", me.user?.email === "owner@agendapro.demo", JSON.stringify(me).slice(0, 100)); // Slug del negocio (para consultar slots públicos) const { json: settings } = await req("GET", "/settings", null, t); const slug = settings.settings?.slug; check("tiene slug", !!slug, String(slug)); const checks = [ ["business", "/business", (j) => j.business?.name === "Lumière Estética & Spa"], ["employees+stats", "/employees", (j) => j.employees?.length === 6 && !!j.employees[0].stats], ["services", "/services", (j) => j.services?.length === 12 && Array.isArray(j.services[0].employee_ids)], ["clients+stats", "/clients", (j) => j.clients?.length > 0 && j.clients[0].stats?.visits !== undefined], ["appointments joined", "/appointments?limit=5", (j) => j.appointments?.[0]?.service && j.appointments[0].client], ["dashboard overview", "/dashboard/overview", (j) => j.revenue_month > 0], ["best-employees", "/dashboard/best-employees", (j) => j.employees?.length === 6], ["best-services", "/dashboard/best-services", (j) => j.services?.length > 0], ["top-tickets", "/dashboard/top-tickets", (j) => j.tickets?.length > 0], ["top-clients", "/dashboard/top-clients", (j) => j.clients?.length > 0], ["frequent-clients", "/dashboard/frequent-clients", (j) => j.clients?.length > 0], ["revenue-trend", "/dashboard/revenue-trend?days=30", (j) => j.points?.length === 30], ["revenue-by-category", "/dashboard/revenue-by-category", (j) => j.slices?.length > 0], ["tickets list", "/dashboard/tickets", (j) => j.tickets?.length > 0], ]; for (const [name, path, cond] of checks) { const { json, status } = await req("GET", path, null, t); check(name, status === 200 && cond(json), `status=${status}`); } // Lifecycle const slotOwner = await findSlot(slug, 1); check("found slot for service 1", !!slotOwner); const { json: created } = await req("POST", "/appointments", { service_id: 1, client_id: 1, start_at: slotOwner }, t); check("create appointment", !!created.appointment?.id); const { json: completed } = await req("POST", `/appointments/${created.appointment.id}/complete`, { tip: 50, payment_method: "cash" }, t); check("complete -> ticket", completed.appointment?.status === "completed"); const { json: empLogin } = await req("POST", "/auth/login", { email: "mateo.herrera@lumiere.mx", password: "demo1234" }); // Mateo offers services "Corte caballero" (5) and "Corte + arreglo de barba" (6). // Try 5 first (keeps the original assertion intent); fall back to 6 if 5 has no slots. let empSvcId = 5; let slotEmp = await findSlot(slug, empSvcId); if (!slotEmp) { empSvcId = 6; slotEmp = await findSlot(slug, empSvcId); } check("found slot for employee service", !!slotEmp); const { json: empAppt } = await req("POST", "/appointments", { service_id: empSvcId, client_id: 2, start_at: slotEmp }, empLogin.token); check("employee auto-assign", empAppt.appointment?.employee_id === empLogin.user?.employee_id, JSON.stringify(empAppt).slice(0, 150)); const { status: badLogin } = await req("POST", "/auth/login", { email: "x@x.com", password: "wrong" }); check("bad login 401", badLogin === 401); const { status: noAuth } = await req("GET", "/employees"); check("no auth 401", noAuth === 401); const { status: empForb } = await req("GET", "/dashboard/overview", null, empLogin.token); check("employee blocked dashboard 403", empForb === 403); console.log(`\n${pass}/${pass + fail} passed${fail ? `, ${fail} FAILED` : ""}`); process.exit(fail ? 1 : 0);