112 lines
3.7 KiB
TypeScript
112 lines
3.7 KiB
TypeScript
// src/tests/e2e/user-journey.e2e.test.ts
|
|
import { describe, it, expect, afterAll } from 'vitest';
|
|
import supertest from 'supertest';
|
|
import app from '../../../server';
|
|
import { getPool } from '../../services/db/connection.db';
|
|
|
|
/**
|
|
* @vitest-environment node
|
|
*/
|
|
|
|
const request = supertest(app);
|
|
|
|
describe('E2E User Journey', () => {
|
|
// Use a unique email for every run to avoid collisions
|
|
const uniqueId = Date.now();
|
|
const userEmail = `e2e-test-${uniqueId}@example.com`;
|
|
const userPassword = 'StrongPassword123!';
|
|
|
|
let authToken: string;
|
|
let userId: string | null = null;
|
|
let shoppingListId: number;
|
|
|
|
afterAll(async () => {
|
|
// Safety cleanup: Ensure the user is deleted from the DB if the test fails mid-way.
|
|
// If the test succeeds, the user deletes their own account, so this acts as a fallback.
|
|
if (userId) {
|
|
try {
|
|
await getPool().query('DELETE FROM public.users WHERE user_id = $1', [userId]);
|
|
} catch (err) {
|
|
console.error('Error cleaning up E2E test user:', err);
|
|
}
|
|
}
|
|
});
|
|
|
|
it('should complete a full user lifecycle: Register -> Login -> Manage List -> Delete Account', async () => {
|
|
// 1. Register a new user
|
|
const registerResponse = await request.post('/api/auth/register').send({
|
|
email: userEmail,
|
|
password: userPassword,
|
|
full_name: 'E2E Traveler',
|
|
});
|
|
|
|
expect(registerResponse.status).toBe(201);
|
|
expect(registerResponse.body.message).toBe('User registered successfully!');
|
|
|
|
// 2. Login to get the access token
|
|
const loginResponse = await request.post('/api/auth/login').send({
|
|
email: userEmail,
|
|
password: userPassword,
|
|
});
|
|
|
|
expect(loginResponse.status).toBe(200);
|
|
authToken = loginResponse.body.token;
|
|
userId = loginResponse.body.userprofile.user.user_id;
|
|
|
|
expect(authToken).toBeDefined();
|
|
expect(userId).toBeDefined();
|
|
|
|
// 3. Create a Shopping List
|
|
const createListResponse = await request
|
|
.post('/api/users/shopping-lists')
|
|
.set('Authorization', `Bearer ${authToken}`)
|
|
.send({ name: 'E2E Party List' });
|
|
|
|
expect(createListResponse.status).toBe(201);
|
|
shoppingListId = createListResponse.body.shopping_list_id;
|
|
expect(shoppingListId).toBeDefined();
|
|
|
|
// 4. Add an item to the list
|
|
const addItemResponse = await request
|
|
.post(`/api/users/shopping-lists/${shoppingListId}/items`)
|
|
.set('Authorization', `Bearer ${authToken}`)
|
|
.send({ customItemName: 'Chips' });
|
|
|
|
expect(addItemResponse.status).toBe(201);
|
|
expect(addItemResponse.body.custom_item_name).toBe('Chips');
|
|
|
|
// 5. Verify the list and item exist via GET
|
|
const getListsResponse = await request
|
|
.get('/api/users/shopping-lists')
|
|
.set('Authorization', `Bearer ${authToken}`);
|
|
|
|
expect(getListsResponse.status).toBe(200);
|
|
const myLists = getListsResponse.body;
|
|
const targetList = myLists.find((l: any) => l.shopping_list_id === shoppingListId);
|
|
|
|
expect(targetList).toBeDefined();
|
|
expect(targetList.items).toHaveLength(1);
|
|
expect(targetList.items[0].custom_item_name).toBe('Chips');
|
|
|
|
// 6. Delete the User Account (Self-Service)
|
|
const deleteAccountResponse = await request
|
|
.delete('/api/users/account')
|
|
.set('Authorization', `Bearer ${authToken}`)
|
|
.send({ password: userPassword });
|
|
|
|
expect(deleteAccountResponse.status).toBe(200);
|
|
expect(deleteAccountResponse.body.message).toBe('Account deleted successfully.');
|
|
|
|
// 7. Verify Login is no longer possible
|
|
const failLoginResponse = await request.post('/api/auth/login').send({
|
|
email: userEmail,
|
|
password: userPassword,
|
|
});
|
|
|
|
expect(failLoginResponse.status).toBe(401);
|
|
|
|
// Mark userId as null so afterAll doesn't attempt to delete it again
|
|
userId = null;
|
|
});
|
|
});
|