diff --git a/backend/test/contract.test.js b/backend/test/contract.test.js new file mode 100644 index 0000000..9edc29a --- /dev/null +++ b/backend/test/contract.test.js @@ -0,0 +1,222 @@ +import request from 'supertest'; +import app from '../src/server.js'; +import { pool } from '../src/db/connection.js'; + +describe('API Contract Tests', () => { + let authToken; + let testUserId; + + beforeAll(async () => { + // Clean up any existing test data + await pool.query('DELETE FROM users WHERE email = ?', ['test@example.com']); + + // Register a test user + const registerResponse = await request(app) + .post('/auth/register') + .send({ + email: 'test@example.com', + password: 'password123', + displayName: 'Test User' + }); + + expect(registerResponse.status).toBe(201); + authToken = registerResponse.body.token; + + // Get the user ID + const [rows] = await pool.query('SELECT id FROM users WHERE email = ?', ['test@example.com']); + testUserId = rows[0].id; + }); + + afterAll(async () => { + // Clean up test data + await pool.query('DELETE FROM users WHERE email = ?', ['test@example.com']); + await pool.end(); + }); + + describe('Auth Endpoints', () => { + it('should register a new user', async () => { + const response = await request(app) + .post('/auth/register') + .send({ + email: 'register-test@example.com', + password: 'password123', + displayName: 'Register Test User' + }); + + expect(response.status).toBe(201); + expect(response.body).toHaveProperty('token'); + }); + + it('should fail to register with invalid data', async () => { + const response = await request(app) + .post('/auth/register') + .send({ + email: 'invalid-email', + password: 'short', + displayName: '' + }); + + expect(response.status).toBe(400); + }); + + it('should login with valid credentials', async () => { + const response = await request(app) + .post('/auth/login') + .send({ + email: 'test@example.com', + password: 'password123' + }); + + expect(response.status).toBe(200); + expect(response.body).toHaveProperty('token'); + }); + + it('should fail to login with invalid credentials', async () => { + const response = await request(app) + .post('/auth/login') + .send({ + email: 'test@example.com', + password: 'wrongpassword' + }); + + expect(response.status).toBe(401); + }); + }); + + describe('Help Requests Endpoints', () => { + let helpRequestId; + + it('should fetch all help requests', async () => { + const response = await request(app) + .get('/requests') + .set('Authorization', `Bearer ${authToken}`); + + expect(response.status).toBe(200); + expect(Array.isArray(response.body)).toBe(true); + }); + + it('should create a new help request', async () => { + const response = await request(app) + .post('/requests') + .set('Authorization', `Bearer ${authToken}`) + .send({ + title: 'Test Help Request', + description: 'This is a test help request', + valueChf: 50.0 + }); + + expect(response.status).toBe(201); + expect(response.body).toHaveProperty('id'); + helpRequestId = response.body.id; + }); + + it('should fail to create a help request with invalid data', async () => { + const response = await request(app) + .post('/requests') + .set('Authorization', `Bearer ${authToken}`) + .send({ + title: 'T', + description: 'Short', + valueChf: -10 + }); + + expect(response.status).toBe(400); + }); + + it('should update a help request', async () => { + const response = await request(app) + .put(`/requests/${helpRequestId}`) + .set('Authorization', `Bearer ${authToken}`) + .send({ + title: 'Updated Test Help Request', + description: 'This is an updated test help request', + valueChf: 75.0, + status: 'in_progress' + }); + + expect(response.status).toBe(200); + }); + + it('should fail to update a help request with invalid data', async () => { + const response = await request(app) + .put(`/requests/${helpRequestId}`) + .set('Authorization', `Bearer ${authToken}`) + .send({ + title: 'T', + description: 'Short', + valueChf: -10 + }); + + expect(response.status).toBe(400); + }); + + it('should delete a help request', async () => { + const response = await request(app) + .delete(`/requests/${helpRequestId}`) + .set('Authorization', `Bearer ${authToken}`); + + expect(response.status).toBe(200); + }); + }); + + describe('Offers Endpoints', () => { + let offerId; + + it('should fetch all offers', async () => { + const response = await request(app) + .get('/offers') + .set('Authorization', `Bearer ${authToken}`); + + expect(response.status).toBe(200); + expect(Array.isArray(response.body)).toBe(true); + }); + + it('should create a new offer', async () => { + // First create a help request to offer against + const requestResponse = await request(app) + .post('/requests') + .set('Authorization', `Bearer ${authToken}`) + .send({ + title: 'Test Help Request for Offer', + description: 'This is a test help request for an offer', + valueChf: 50.0 + }); + + const helpRequestId = requestResponse.body.id; + + const response = await request(app) + .post('/offers') + .set('Authorization', `Bearer ${authToken}`) + .send({ + helpRequestId, + description: 'This is a test offer', + valueChf: 45.0 + }); + + expect(response.status).toBe(201); + expect(response.body).toHaveProperty('id'); + offerId = response.body.id; + }); + + it('should fail to create an offer with invalid data', async () => { + const response = await request(app) + .post('/offers') + .set('Authorization', `Bearer ${authToken}`) + .send({ + helpRequestId: 99999, + description: 'Short', + valueChf: -10 + }); + + expect(response.status).toBe(400); + }); + + it('should delete an offer', async () => { + const response = await request(app) + .delete(`/offers/${offerId}`) + .set('Authorization', `Bearer ${authToken}`); + + expect(response.status).toBe(200); + }); + }); +}); \ No newline at end of file