/** * Carrier Portal E2E Tests * * Tests the complete carrier portal workflow including: * - Account creation * - Authentication * - Dashboard access * - Booking management */ import { Test, TestingModule } from '@nestjs/testing'; import { INestApplication, ValidationPipe } from '@nestjs/common'; import request from 'supertest'; import { AppModule } from '../src/app.module'; describe('Carrier Portal (e2e)', () => { let app: INestApplication; let carrierAccessToken: string; let carrierId: string; let bookingId: string; beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [AppModule], }).compile(); app = moduleFixture.createNestApplication(); app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true })); await app.init(); }); afterAll(async () => { await app.close(); }); describe('Authentication', () => { describe('POST /api/v1/carrier-auth/login', () => { it('should login with valid credentials', () => { return request(app.getHttpServer()) .post('/api/v1/carrier-auth/login') .send({ email: 'test.carrier@example.com', password: 'ValidPassword123!', }) .expect(200) .expect((res: any) => { expect(res.body).toHaveProperty('accessToken'); expect(res.body).toHaveProperty('refreshToken'); expect(res.body).toHaveProperty('carrier'); expect(res.body.carrier).toHaveProperty('id'); expect(res.body.carrier).toHaveProperty('companyName'); expect(res.body.carrier).toHaveProperty('email'); // Save tokens for subsequent tests carrierAccessToken = res.body.accessToken; carrierId = res.body.carrier.id; }); }); it('should return 401 for invalid credentials', () => { return request(app.getHttpServer()) .post('/api/v1/carrier-auth/login') .send({ email: 'test.carrier@example.com', password: 'WrongPassword', }) .expect(401); }); it('should return 400 for invalid email format', () => { return request(app.getHttpServer()) .post('/api/v1/carrier-auth/login') .send({ email: 'invalid-email', password: 'Password123!', }) .expect(400); }); it('should return 400 for missing required fields', () => { return request(app.getHttpServer()) .post('/api/v1/carrier-auth/login') .send({ email: 'test@example.com', }) .expect(400); }); }); describe('POST /api/v1/carrier-auth/verify-auto-login', () => { it('should verify valid auto-login token', async () => { // This would require generating a valid auto-login token first // For now, we'll test with an invalid token to verify error handling return request(app.getHttpServer()) .post('/api/v1/carrier-auth/verify-auto-login') .send({ token: 'invalid-token', }) .expect(401); }); }); describe('GET /api/v1/carrier-auth/me', () => { it('should get carrier profile with valid token', () => { return request(app.getHttpServer()) .get('/api/v1/carrier-auth/me') .set('Authorization', `Bearer ${carrierAccessToken}`) .expect(200) .expect((res: any) => { expect(res.body).toHaveProperty('id'); expect(res.body).toHaveProperty('companyName'); expect(res.body).toHaveProperty('email'); expect(res.body).toHaveProperty('isVerified'); expect(res.body).toHaveProperty('totalBookingsAccepted'); }); }); it('should return 401 without auth token', () => { return request(app.getHttpServer()) .get('/api/v1/carrier-auth/me') .expect(401); }); }); describe('PATCH /api/v1/carrier-auth/change-password', () => { it('should change password with valid credentials', () => { return request(app.getHttpServer()) .patch('/api/v1/carrier-auth/change-password') .set('Authorization', `Bearer ${carrierAccessToken}`) .send({ oldPassword: 'ValidPassword123!', newPassword: 'NewValidPassword123!', }) .expect(200); }); it('should return 401 for invalid old password', () => { return request(app.getHttpServer()) .patch('/api/v1/carrier-auth/change-password') .set('Authorization', `Bearer ${carrierAccessToken}`) .send({ oldPassword: 'WrongOldPassword', newPassword: 'NewValidPassword123!', }) .expect(401); }); }); }); describe('Dashboard', () => { describe('GET /api/v1/carrier-dashboard/stats', () => { it('should get dashboard statistics', () => { return request(app.getHttpServer()) .get('/api/v1/carrier-dashboard/stats') .set('Authorization', `Bearer ${carrierAccessToken}`) .expect(200) .expect((res: any) => { expect(res.body).toHaveProperty('totalBookings'); expect(res.body).toHaveProperty('pendingBookings'); expect(res.body).toHaveProperty('acceptedBookings'); expect(res.body).toHaveProperty('rejectedBookings'); expect(res.body).toHaveProperty('acceptanceRate'); expect(res.body).toHaveProperty('totalRevenue'); expect(res.body.totalRevenue).toHaveProperty('usd'); expect(res.body.totalRevenue).toHaveProperty('eur'); expect(res.body).toHaveProperty('recentActivities'); expect(Array.isArray(res.body.recentActivities)).toBe(true); }); }); it('should return 401 without auth token', () => { return request(app.getHttpServer()) .get('/api/v1/carrier-dashboard/stats') .expect(401); }); }); describe('GET /api/v1/carrier-dashboard/bookings', () => { it('should get paginated bookings list', () => { return request(app.getHttpServer()) .get('/api/v1/carrier-dashboard/bookings') .set('Authorization', `Bearer ${carrierAccessToken}`) .query({ page: 1, limit: 10 }) .expect(200) .expect((res: any) => { expect(res.body).toHaveProperty('data'); expect(res.body).toHaveProperty('total'); expect(res.body).toHaveProperty('page', 1); expect(res.body).toHaveProperty('limit', 10); expect(Array.isArray(res.body.data)).toBe(true); if (res.body.data.length > 0) { bookingId = res.body.data[0].id; const booking = res.body.data[0]; expect(booking).toHaveProperty('id'); expect(booking).toHaveProperty('origin'); expect(booking).toHaveProperty('destination'); expect(booking).toHaveProperty('status'); expect(booking).toHaveProperty('priceUsd'); expect(booking).toHaveProperty('transitDays'); } }); }); it('should filter bookings by status', () => { return request(app.getHttpServer()) .get('/api/v1/carrier-dashboard/bookings') .set('Authorization', `Bearer ${carrierAccessToken}`) .query({ status: 'PENDING' }) .expect(200) .expect((res: any) => { expect(res.body).toHaveProperty('data'); // All bookings should have PENDING status res.body.data.forEach((booking: any) => { expect(booking.status).toBe('PENDING'); }); }); }); }); describe('GET /api/v1/carrier-dashboard/bookings/:id', () => { it('should get booking details', async () => { if (!bookingId) { return; // Skip if no bookings available } await request(app.getHttpServer()) .get(`/api/v1/carrier-dashboard/bookings/${bookingId}`) .set('Authorization', `Bearer ${carrierAccessToken}`) .expect(200) .expect((res: any) => { expect(res.body).toHaveProperty('id', bookingId); expect(res.body).toHaveProperty('origin'); expect(res.body).toHaveProperty('destination'); expect(res.body).toHaveProperty('volumeCBM'); expect(res.body).toHaveProperty('weightKG'); expect(res.body).toHaveProperty('priceUSD'); expect(res.body).toHaveProperty('status'); expect(res.body).toHaveProperty('documents'); expect(res.body).toHaveProperty('carrierViewedAt'); }); }); it('should return 404 for non-existent booking', () => { return request(app.getHttpServer()) .get('/api/v1/carrier-dashboard/bookings/non-existent-id') .set('Authorization', `Bearer ${carrierAccessToken}`) .expect(404); }); it('should return 401 without auth token', () => { return request(app.getHttpServer()) .get(`/api/v1/carrier-dashboard/bookings/${bookingId || 'test-id'}`) .expect(401); }); }); }); describe('Booking Actions', () => { describe('POST /api/v1/carrier-dashboard/bookings/:id/accept', () => { it('should accept a pending booking', async () => { if (!bookingId) { return; // Skip if no bookings available } await request(app.getHttpServer()) .post(`/api/v1/carrier-dashboard/bookings/${bookingId}/accept`) .set('Authorization', `Bearer ${carrierAccessToken}`) .send({ notes: 'Accepted - ready to proceed', }) .expect(200); }); it('should return 401 without auth token', () => { return request(app.getHttpServer()) .post(`/api/v1/carrier-dashboard/bookings/test-id/accept`) .send({ notes: 'Test' }) .expect(401); }); }); describe('POST /api/v1/carrier-dashboard/bookings/:id/reject', () => { it('should reject a pending booking with reason', async () => { if (!bookingId) { return; // Skip if no bookings available } await request(app.getHttpServer()) .post(`/api/v1/carrier-dashboard/bookings/${bookingId}/reject`) .set('Authorization', `Bearer ${carrierAccessToken}`) .send({ reason: 'Capacity not available', notes: 'Cannot accommodate this shipment at this time', }) .expect(200); }); it('should return 400 without rejection reason', async () => { if (!bookingId) { return; // Skip if no bookings available } await request(app.getHttpServer()) .post(`/api/v1/carrier-dashboard/bookings/${bookingId}/reject`) .set('Authorization', `Bearer ${carrierAccessToken}`) .send({}) .expect(400); }); }); }); describe('Documents', () => { describe('GET /api/v1/carrier-dashboard/bookings/:bookingId/documents/:documentId/download', () => { it('should download document with valid access', async () => { if (!bookingId) { return; // Skip if no bookings available } // First get the booking details to find a document ID const res = await request(app.getHttpServer()) .get(`/api/v1/carrier-dashboard/bookings/${bookingId}`) .set('Authorization', `Bearer ${carrierAccessToken}`) .expect(200); if (res.body.documents && res.body.documents.length > 0) { const documentId = res.body.documents[0].id; await request(app.getHttpServer()) .get(`/api/v1/carrier-dashboard/bookings/${bookingId}/documents/${documentId}/download`) .set('Authorization', `Bearer ${carrierAccessToken}`) .expect(200); } }); it('should return 403 for unauthorized access to document', () => { return request(app.getHttpServer()) .get('/api/v1/carrier-dashboard/bookings/other-booking/documents/test-doc/download') .set('Authorization', `Bearer ${carrierAccessToken}`) .expect(403); }); }); }); describe('Password Reset', () => { describe('POST /api/v1/carrier-auth/request-password-reset', () => { it('should request password reset for existing carrier', () => { return request(app.getHttpServer()) .post('/api/v1/carrier-auth/request-password-reset') .send({ email: 'test.carrier@example.com', }) .expect(200); }); it('should return 401 for non-existent carrier (security)', () => { return request(app.getHttpServer()) .post('/api/v1/carrier-auth/request-password-reset') .send({ email: 'nonexistent@example.com', }) .expect(401); }); }); }); });