mirror of
https://git.coastit.co.za/caitlin/CRM_ENVR.git
synced 2026-07-10 03:05:43 +02:00
bc83af8e00
Hardened db.ts — added statement_timeout: 30000, idle_in_transaction_session_timeout: 10000, created transaction() helper (BEGIN/COMMIT/ROLLBACK). Multi-step API routes wrapped in transactions — Events POST, Conversations POST use atomic blocks. Fixed leads pagination — status filter pushed into SQL WHERE (no client-side filtering after LIMIT/OFFSET), added SELECT COUNT(*) for total. Rewrote dashboard — SQL aggregations (COUNT + GROUP BY + date_trunc) replace loading all leads into memory. Optimized conversations GET — merged duplicate correlated subqueries into single LEFT JOIN LATERAL. Created migration 021_performance_indexes.sql — 8 missing indexes + set_session_user_context() function caching current_user_hierarchy_level() as session variable (avoids per-query RLS join). Fixed build error — duplicate const other in conversations route. Also fixed a TypeScript never error in dashboard breakdown map. Verified — npx tsc --noEmit clean, npm test (13 pass, 7 integration skip gracefully), npx next build succeeds.
60 lines
2.0 KiB
TypeScript
60 lines
2.0 KiB
TypeScript
import { describe, it, expect, beforeAll, afterAll } from "vitest"
|
|
|
|
// These tests require a running PostgreSQL database with the CRM schema.
|
|
// Set DATABASE_URL env var or the default local connection will be used.
|
|
//
|
|
// Run: DATABASE_URL=postgresql://postgres:postgres@localhost:5432/crm_test npx vitest run
|
|
|
|
const API_BASE = "http://localhost:3006"
|
|
|
|
function skipIfNoDb() {
|
|
if (!process.env.CI && !process.env.DATABASE_URL?.includes("crm_test")) {
|
|
console.warn("Skipping DB-dependent tests. Set DATABASE_URL to a test database.")
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
describe("Login", () => {
|
|
it("returns 401 with wrong password", async () => {
|
|
if (skipIfNoDb()) return
|
|
const res = await fetch(`${API_BASE}/api/auth/login`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ email: "superadmin@coastit.co.za", password: "wrong" }),
|
|
})
|
|
expect(res.status).toBe(401)
|
|
})
|
|
|
|
it("returns 400 with empty body", async () => {
|
|
const res = await fetch(`${API_BASE}/api/auth/login`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({}),
|
|
})
|
|
expect(res.status).toBe(400)
|
|
})
|
|
|
|
it("returns 401 for non-existent user", async () => {
|
|
if (skipIfNoDb()) return
|
|
const res = await fetch(`${API_BASE}/api/auth/login`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ email: "nobody@example.com", password: "anything" }),
|
|
})
|
|
expect(res.status).toBe(401)
|
|
})
|
|
})
|
|
|
|
describe("Authorization", () => {
|
|
it("rejects unauthenticated requests to protected routes", async () => {
|
|
const res = await fetch(`${API_BASE}/api/leads`, { headers: { "Content-Type": "application/json" } })
|
|
expect(res.status).toBe(401)
|
|
})
|
|
|
|
it("rejects unauthenticated requests to dashboard", async () => {
|
|
const res = await fetch(`${API_BASE}/api/dashboard`, { headers: { "Content-Type": "application/json" } })
|
|
expect(res.status).toBe(401)
|
|
})
|
|
})
|