diff --git a/app/schemas/customer.schema.js b/app/schemas/customer.schema.js index 9ba6be9..6a9aeb3 100644 --- a/app/schemas/customer.schema.js +++ b/app/schemas/customer.schema.js @@ -22,9 +22,15 @@ const intIdParam = z.object({ * zod issue. */ const createCustomerBody = z.object({ - custCompanyName: z.string().max(255).optional(), - custFName: z.string().max(255).optional(), - custLName: z.string().max(255).optional(), + // custCompanyName / custFName / custLName mirror the DB columns + // (text NOT NULL on each — see setup/TimeTracker.sql). Without + // the required flag, callers omitting them passed zod and tripped + // the postgres "null value violates not-null constraint" path, + // surfacing as a 500 instead of a clean 400. Same drift class as + // #265's custState fix. + custCompanyName: z.string().max(255), + custFName: z.string().max(255), + custLName: z.string().max(255), custAddress1: z.string().max(255).optional(), custAddress2: z.string().max(255).optional(), custCity: z.string().max(255).optional(), diff --git a/tests/api/customer-bulk.test.js b/tests/api/customer-bulk.test.js index 861fefe..98b0b8c 100644 --- a/tests/api/customer-bulk.test.js +++ b/tests/api/customer-bulk.test.js @@ -50,9 +50,11 @@ beforeAll(async () => { describe('POST /v1/customer/bulk auth contract', () => { test('returns 403 when authKey header is missing', async () => { + // Full required-field body so body-validation passes and the + // authKey check inside makeBulkCreate is reached. const res = await request(app) .post('/v1/customer/bulk') - .send({ customers: [{ custCompanyName: 'Acme' }] }); + .send({ customers: [{ custCompanyName: 'Acme', custFName: 'Test', custLName: 'User' }] }); expect(res.status).toBe(403); }); }); diff --git a/tests/api/customer-create.test.js b/tests/api/customer-create.test.js index 1b6c4b0..791f89f 100644 --- a/tests/api/customer-create.test.js +++ b/tests/api/customer-create.test.js @@ -36,9 +36,12 @@ beforeAll(async () => { describe('POST /v1/customer', () => { test('returns 403 when authKey header is missing', async () => { + // Send a full required-field body so we exit body validation + // and reach the controller's authKey check. custCompanyName / + // custFName / custLName are required (NOT NULL in the DB). const res = await request(app) .post('/v1/customer') - .send({ custCompanyName: 'Acme' }); + .send({ custCompanyName: 'Acme', custFName: 'Test', custLName: 'User' }); expect(res.status).toBe(403); expect(res.body).toMatchObject({ message: expect.stringMatching(/Authorization key not sent/i), @@ -59,7 +62,7 @@ describe('POST /v1/customer', () => { const res = await request(app) .post('/v1/customer') .set('authKey', 'definitely-not-a-real-key') - .send({ custCompanyName: 'Acme', custCompId: 1 }); + .send({ custCompanyName: 'Acme', custFName: 'Test', custLName: 'User', custCompId: 1 }); // With no DB, IsMaster returns false, GetCompanyId returns -1, // and the controller emits 403 "Invalid Authorization Key." expect(res.status).toBe(403); diff --git a/tests/api/idempotency.test.js b/tests/api/idempotency.test.js index d01ab32..44ae174 100644 --- a/tests/api/idempotency.test.js +++ b/tests/api/idempotency.test.js @@ -78,7 +78,10 @@ describe('Idempotency middleware: mounted on POST routes', () => { .post('/v1/customer') .set('authKey', 'any') .set('Idempotency-Key', '01HFTESTKEY12345') - .send({ custCompanyName: 'Acme' }); + // Full required-field body so the schema validator passes + // and we exercise the idempotency middleware itself rather + // than short-circuiting on a 400 from zod. + .send({ custCompanyName: 'Acme', custFName: 'Test', custLName: 'User' }); // Whatever the controller decides (likely 403 from inline // auth, since the mock returns []), the idempotency layer // should NOT have short-circuited with 400/409. That's the diff --git a/tests/api/validation.test.js b/tests/api/validation.test.js index 3b08502..efc44c6 100644 --- a/tests/api/validation.test.js +++ b/tests/api/validation.test.js @@ -85,7 +85,10 @@ describe('body validation — POST /v1/customer', () => { const res = await request(app) .post('/v1/customer') .set('authKey', 'unknown-key') // will still 403 at controller - .send({ custCompanyName: 'Acme', custCompId: 1 }); + // Full required-field body — custCompanyName / custFName / + // custLName are NOT NULL in the DB and required in the + // zod schema. + .send({ custCompanyName: 'Acme', custFName: 'Test', custLName: 'User', custCompId: 1 }); // After zod passes, the controller's auth check 403s on the // unknown key — that's the expected post-validation path. // We just need to confirm we got past the 400 validator.