diff --git a/src/test/transactions.e2e.spec.ts b/src/test/transactions.e2e.spec.ts index 82bffbe..a3798f1 100644 --- a/src/test/transactions.e2e.spec.ts +++ b/src/test/transactions.e2e.spec.ts @@ -2,8 +2,9 @@ * TST-005: Transactions API Integration Tests (CC-047) * * Tests HTTP contract of TransactionsController: - * - validation of amountMinorUnits as integer - * - currency and evidenceStatus validation + * - validation of amountMinorUnits as integer >= 0 + * - currency length validation (3 chars) + * - evidenceStatus enum validation on create + update * - ParseUUIDPipe on path params * - response shapes */ @@ -64,7 +65,8 @@ const mockTxService = { list: jest.fn().mockResolvedValue([MOCK_TX]), getById: jest.fn().mockResolvedValue(MOCK_TX), create: jest.fn().mockResolvedValue(MOCK_TX), - updateEvidence: jest.fn().mockResolvedValue({ ...MOCK_TX, evidenceStatus: 'complete' }), + update: jest.fn().mockResolvedValue({ ...MOCK_TX, evidenceStatus: 'complete' }), + softDelete: jest.fn().mockResolvedValue({ deleted: true }), }; @Module({ @@ -96,6 +98,21 @@ describe('TST-005: Transactions API Integration', () => { afterAll(() => app.close()); beforeEach(() => jest.clearAllMocks()); + describe('GET /v1/transactions', () => { + it('returns 401 without auth', async () => { + await request(app.getHttpServer()).get('/v1/transactions').expect(401); + }); + + it('returns 200 with array when authenticated', async () => { + const res = await request(app.getHttpServer()) + .get('/v1/transactions') + .set('Authorization', AUTH) + .expect(200); + expect(Array.isArray(res.body)).toBe(true); + expect(mockTxService.list).toHaveBeenCalledTimes(1); + }); + }); + describe('POST /v1/transactions', () => { const VALID_BODY = { organizationId: VALID_ORG_UUID, @@ -103,7 +120,6 @@ describe('TST-005: Transactions API Integration', () => { amountMinorUnits: 123400, currency: 'EUR', transactionDate: '2026-07-31', - evidenceStatus: 'missing', }; it('returns 201 with valid body', async () => { @@ -131,7 +147,7 @@ describe('TST-005: Transactions API Integration', () => { .expect(400); }); - it('returns 400 when evidenceStatus is invalid', async () => { + it('returns 400 when evidenceStatus is invalid enum', async () => { await request(app.getHttpServer()) .post('/v1/transactions') .set('Authorization', AUTH) @@ -147,6 +163,14 @@ describe('TST-005: Transactions API Integration', () => { .expect(400); }); + it('returns 400 when currency is not 3 chars', async () => { + await request(app.getHttpServer()) + .post('/v1/transactions') + .set('Authorization', AUTH) + .send({ ...VALID_BODY, currency: 'EURO' }) + .expect(400); + }); + it('returns 400 when required field missing (currency)', async () => { const { currency: _c, ...bodyWithoutCurrency } = VALID_BODY; await request(app.getHttpServer()) @@ -157,21 +181,47 @@ describe('TST-005: Transactions API Integration', () => { }); }); - describe('PATCH /v1/transactions/:id/evidence', () => { + describe('PATCH /v1/transactions/:id', () => { it('returns 200 with valid UUID and valid body', async () => { await request(app.getHttpServer()) - .patch(`/v1/transactions/${VALID_UUID}/evidence`) + .patch(`/v1/transactions/${VALID_UUID}`) .set('Authorization', AUTH) .send({ evidenceStatus: 'complete' }) .expect(200); + expect(mockTxService.update).toHaveBeenCalledTimes(1); }); it('returns 400 for non-UUID path param', async () => { await request(app.getHttpServer()) - .patch('/v1/transactions/not-a-uuid/evidence') + .patch('/v1/transactions/not-a-uuid') .set('Authorization', AUTH) .send({ evidenceStatus: 'complete' }) .expect(400); }); + + it('returns 400 for invalid evidenceStatus in body', async () => { + await request(app.getHttpServer()) + .patch(`/v1/transactions/${VALID_UUID}`) + .set('Authorization', AUTH) + .send({ evidenceStatus: 'flying' }) + .expect(400); + }); + }); + + describe('DELETE /v1/transactions/:id', () => { + it('returns 200 with valid UUID', async () => { + await request(app.getHttpServer()) + .delete(`/v1/transactions/${VALID_UUID}`) + .set('Authorization', AUTH) + .expect(200); + expect(mockTxService.softDelete).toHaveBeenCalledTimes(1); + }); + + it('returns 400 for non-UUID path param', async () => { + await request(app.getHttpServer()) + .delete('/v1/transactions/not-a-uuid') + .set('Authorization', AUTH) + .expect(400); + }); }); });