xpeditis2.0/apps/backend/test/carrier-portal.e2e-spec.ts
David a1e255e816
Some checks failed
CI/CD Pipeline / Discord Notification (Failure) (push) Blocked by required conditions
CI/CD Pipeline / Integration Tests (push) Blocked by required conditions
CI/CD Pipeline / Deployment Summary (push) Blocked by required conditions
CI/CD Pipeline / Deploy to Portainer (push) Blocked by required conditions
CI/CD Pipeline / Discord Notification (Success) (push) Blocked by required conditions
CI/CD Pipeline / Backend - Build, Test & Push (push) Failing after 1m20s
CI/CD Pipeline / Frontend - Build, Test & Push (push) Has been cancelled
fix v1.0.0
2025-12-23 11:49:57 +01:00

363 lines
13 KiB
TypeScript

/**
* 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);
});
});
});
});