Files
flyer-crawler.projectium.com/src/tests/integration/auth.integration.test.ts

136 lines
5.5 KiB
TypeScript

// src/tests/integration/auth.integration.test.ts
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import supertest from 'supertest';
import app from '../../../server';
import { getPool } from '../../services/db/connection.db';
import { createAndLoginUser, TEST_PASSWORD } from '../utils/testHelpers';
import type { UserProfile } from '../../types';
/**
* @vitest-environment node
*/
const request = supertest(app);
/**
* These are integration tests that verify the authentication flow against a running backend server.
* Make sure your Express server is running before executing these tests.
*
* To run only these tests: `vitest run src/tests/auth.integration.test.ts`
*/
describe('Authentication API Integration', () => {
let testUserEmail: string;
let testUser: UserProfile;
beforeAll(async () => {
({ user: testUser } = await createAndLoginUser({ fullName: 'Auth Test User' }));
testUserEmail = testUser.user.email;
});
afterAll(async () => {
if (testUserEmail) {
await getPool().query('DELETE FROM public.users WHERE email = $1', [testUserEmail]);
}
});
// This test migrates the logic from the old DevTestRunner.tsx component.
it('should successfully log in a registered user', async () => {
// The `rememberMe` parameter is required. For a test, `false` is a safe default.
const response = await request
.post('/api/auth/login')
.send({ email: testUserEmail, password: TEST_PASSWORD, rememberMe: false });
const data = response.body;
// Assert that the API returns the expected structure
expect(data).toBeDefined();
expect(response.status).toBe(200);
expect(data.userprofile).toBeDefined();
expect(data.userprofile.user.email).toBe(testUserEmail);
expect(data.userprofile.user.user_id).toBeTypeOf('string');
expect(data.token).toBeTypeOf('string');
});
it('should fail to log in with an incorrect password', async () => {
// Use the user we just created
const adminEmail = testUserEmail;
const wrongPassword = 'wrongpassword';
// The loginUser function returns a Response object. We check its status.
const response = await request
.post('/api/auth/login')
.send({ email: adminEmail, password: wrongPassword, rememberMe: false });
expect(response.status).toBe(401);
const errorData = response.body;
expect(errorData.message).toBe('Incorrect email or password.');
});
it('should fail to log in with a non-existent email', async () => {
const nonExistentEmail = 'nobody-here@example.com';
const anyPassword = 'any-password';
// The loginUser function returns a Response object. We check its status.
const response = await request
.post('/api/auth/login')
.send({ email: nonExistentEmail, password: anyPassword, rememberMe: false });
expect(response.status).toBe(401);
const errorData = response.body;
// Security best practice: the error message should be identical for wrong password and wrong email
// to prevent user enumeration attacks.
expect(errorData.message).toBe('Incorrect email or password.');
});
it('should successfully refresh an access token using a refresh token cookie', async () => {
// Arrange: Log in to get a fresh, valid refresh token cookie for this specific test.
// This ensures the test is self-contained and not affected by other tests.
const loginResponse = await request
.post('/api/auth/login')
.send({ email: testUserEmail, password: TEST_PASSWORD, rememberMe: true });
const refreshTokenCookie = loginResponse.headers['set-cookie'][0].split(';')[0];
expect(refreshTokenCookie).toBeDefined();
// Act: Make a request to the refresh-token endpoint, including the cookie.
const response = await request
.post('/api/auth/refresh-token')
.set('Cookie', refreshTokenCookie!);
// Assert: Check for a successful response and a new access token.
expect(response.status).toBe(200);
const data = response.body;
expect(data.token).toBeTypeOf('string');
});
it('should fail to refresh an access token with an invalid refresh token cookie', async () => {
// Arrange: Create a fake/invalid cookie.
const invalidRefreshTokenCookie = 'refreshToken=this-is-not-a-valid-token';
// Act: Make a request to the refresh-token endpoint with the invalid cookie.
const response = await request
.post('/api/auth/refresh-token')
.set('Cookie', invalidRefreshTokenCookie);
// Assert: Check for a 403 Forbidden response.
expect(response.status).toBe(403);
const data = response.body;
expect(data.message).toBe('Invalid or expired refresh token.');
});
it('should successfully log out and clear the refresh token cookie', async () => {
// Arrange: Log in to get a valid refresh token cookie.
const loginResponse = await request
.post('/api/auth/login')
.send({ email: testUserEmail, password: TEST_PASSWORD, rememberMe: true });
const refreshTokenCookie = loginResponse.headers['set-cookie'][0].split(';')[0];
expect(refreshTokenCookie).toBeDefined();
// Act: Make a request to the new logout endpoint, including the cookie.
const response = await request.post('/api/auth/logout').set('Cookie', refreshTokenCookie!);
// Assert: Check for a successful response and a cookie-clearing header.
expect(response.status).toBe(200);
const logoutSetCookieHeader = response.headers['set-cookie'][0];
expect(logoutSetCookieHeader).toContain('refreshToken=;');
expect(logoutSetCookieHeader).toContain('Max-Age=0');
});
});