fix for integration tests 404 ? not sure this is right

This commit is contained in:
2025-12-23 19:08:53 -08:00
parent a6a484d432
commit 10a379c5e3
10 changed files with 313 additions and 392 deletions

View File

@@ -1,6 +1,7 @@
// src/tests/integration/user.integration.test.ts
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import * as apiClient from '../../services/apiClient';
import supertest from 'supertest';
import app from '../../../server';
import { logger } from '../../services/logger.server';
import { getPool } from '../../services/db/connection.db';
import type { UserProfile, MasterGroceryItem, ShoppingList } from '../../types';
@@ -10,25 +11,12 @@ import { createAndLoginUser, TEST_PASSWORD } from '../utils/testHelpers';
* @vitest-environment node
*/
const request = supertest(app);
describe('User API Routes Integration Tests', () => {
let testUser: UserProfile;
let authToken: string;
// --- START DEBUG LOGGING ---
// Query the DB from within the test file to see its state.
beforeAll(async () => {
const res = await getPool().query(
'SELECT u.user_id, u.email, p.role FROM public.users u JOIN public.profiles p ON u.user_id = p.user_id',
);
console.log(
'\n--- [user.integration.test.ts] Users found in DB from TEST perspective (beforeAll): ---',
);
console.table(res.rows);
console.log(
'-------------------------------------------------------------------------------------\n',
);
});
// --- END DEBUG LOGGING ---
// Before any tests run, create a new user and log them in.
// The token will be used for all subsequent API calls in this test suite.
beforeAll(async () => {
@@ -62,11 +50,13 @@ describe('User API Routes Integration Tests', () => {
it('should fetch the authenticated user profile via GET /api/users/profile', async () => {
// Act: Call the API endpoint using the authenticated token.
const response = await apiClient.getAuthenticatedUserProfile({ tokenOverride: authToken });
const profile = await response.json();
const response = await request
.get('/api/users/profile')
.set('Authorization', `Bearer ${authToken}`);
const profile = response.body;
// Assert: Verify the profile data matches the created user.
expect(profile).toBeDefined();
expect(response.status).toBe(200);
expect(profile.user.user_id).toBe(testUser.user.user_id);
expect(profile.user.email).toBe(testUser.user.email); // This was already correct
expect(profile.full_name).toBe('Test User');
@@ -80,20 +70,21 @@ describe('User API Routes Integration Tests', () => {
};
// Act: Call the update endpoint with the new data and the auth token.
const response = await apiClient.updateUserProfile(profileUpdates, {
tokenOverride: authToken,
});
const updatedProfile = await response.json();
const response = await request
.put('/api/users/profile')
.set('Authorization', `Bearer ${authToken}`)
.send(profileUpdates);
const updatedProfile = response.body;
// Assert: Check that the returned profile reflects the changes.
expect(updatedProfile).toBeDefined();
expect(response.status).toBe(200);
expect(updatedProfile.full_name).toBe('Updated Test User');
// Also, fetch the profile again to ensure the change was persisted.
const refetchResponse = await apiClient.getAuthenticatedUserProfile({
tokenOverride: authToken,
});
const refetchedProfile = await refetchResponse.json();
const refetchResponse = await request
.get('/api/users/profile')
.set('Authorization', `Bearer ${authToken}`);
const refetchedProfile = refetchResponse.body;
expect(refetchedProfile.full_name).toBe('Updated Test User');
});
@@ -104,14 +95,14 @@ describe('User API Routes Integration Tests', () => {
};
// Act: Call the update endpoint.
const response = await apiClient.updateUserPreferences(preferenceUpdates, {
tokenOverride: authToken,
});
const updatedProfile = await response.json();
const response = await request
.put('/api/users/profile/preferences')
.set('Authorization', `Bearer ${authToken}`)
.send(preferenceUpdates);
const updatedProfile = response.body;
// Assert: Check that the preferences object in the returned profile is updated.
expect(updatedProfile).toBeDefined();
expect(updatedProfile.preferences).toBeDefined();
expect(response.status).toBe(200);
expect(updatedProfile.preferences?.darkMode).toBe(true);
});
@@ -122,9 +113,14 @@ describe('User API Routes Integration Tests', () => {
// Act & Assert: Attempt to register and expect the promise to reject
// with an error message indicating the password is too weak.
const response = await apiClient.registerUser(email, weakPassword, 'Weak Password User');
expect(response.ok).toBe(false);
const errorData = (await response.json()) as { message: string; errors: { message: string }[] };
const response = await request.post('/api/auth/register').send({
email,
password: weakPassword,
full_name: 'Weak Password User',
});
expect(response.status).toBe(400);
const errorData = response.body as { message: string; errors: { message: string }[] };
// For validation errors, the detailed messages are in the `errors` array.
// We join them to check for the specific feedback from the password strength checker.
const detailedErrorMessage = errorData.errors?.map((e) => e.message).join(' ');
@@ -137,18 +133,22 @@ describe('User API Routes Integration Tests', () => {
const { token: deletionToken } = await createAndLoginUser({ email: deletionEmail });
// Act: Call the delete endpoint with the correct password and token.
const response = await apiClient.deleteUserAccount(TEST_PASSWORD, {
tokenOverride: deletionToken,
});
const deleteResponse = await response.json();
const response = await request
.delete('/api/users/account')
.set('Authorization', `Bearer ${deletionToken}`)
.send({ password: TEST_PASSWORD });
const deleteResponse = response.body;
// Assert: Check for a successful deletion message.
expect(response.status).toBe(200);
expect(deleteResponse.message).toBe('Account deleted successfully.');
// Assert (Verification): Attempting to log in again with the same credentials should now fail.
const loginResponse = await apiClient.loginUser(deletionEmail, TEST_PASSWORD, false);
expect(loginResponse.ok).toBe(false);
const errorData = await loginResponse.json();
const loginResponse = await request
.post('/api/auth/login')
.send({ email: deletionEmail, password: TEST_PASSWORD });
expect(loginResponse.status).toBe(401);
const errorData = loginResponse.body;
expect(errorData.message).toBe('Incorrect email or password.');
});
@@ -158,12 +158,14 @@ describe('User API Routes Integration Tests', () => {
const { user: resetUser } = await createAndLoginUser({ email: resetEmail });
// Act 1: Request a password reset. In our test environment, the token is returned in the response.
const resetRequestRawResponse = await apiClient.requestPasswordReset(resetEmail);
if (!resetRequestRawResponse.ok) {
const errorData = await resetRequestRawResponse.json();
const resetRequestRawResponse = await request
.post('/api/auth/forgot-password')
.send({ email: resetEmail });
if (resetRequestRawResponse.status !== 200) {
const errorData = resetRequestRawResponse.body;
throw new Error(errorData.message || 'Password reset request failed');
}
const resetRequestResponse = await resetRequestRawResponse.json();
const resetRequestResponse = resetRequestRawResponse.body;
const resetToken = resetRequestResponse.token;
// Assert 1: Check that we received a token.
@@ -172,19 +174,23 @@ describe('User API Routes Integration Tests', () => {
// Act 2: Use the token to set a new password.
const newPassword = 'my-new-secure-password-!@#$';
const resetRawResponse = await apiClient.resetPassword(resetToken!, newPassword);
if (!resetRawResponse.ok) {
const errorData = await resetRawResponse.json();
const resetRawResponse = await request
.post('/api/auth/reset-password')
.send({ token: resetToken!, newPassword });
if (resetRawResponse.status !== 200) {
const errorData = resetRawResponse.body;
throw new Error(errorData.message || 'Password reset failed');
}
const resetResponse = await resetRawResponse.json();
const resetResponse = resetRawResponse.body;
// Assert 2: Check for a successful password reset message.
expect(resetResponse.message).toBe('Password has been reset successfully.');
// Act 3 & Assert 3 (Verification): Log in with the NEW password to confirm the change.
const loginResponse = await apiClient.loginUser(resetEmail, newPassword, false);
const loginData = await loginResponse.json();
const loginResponse = await request
.post('/api/auth/login')
.send({ email: resetEmail, password: newPassword });
const loginData = loginResponse.body;
expect(loginData.userprofile).toBeDefined();
expect(loginData.userprofile.user.user_id).toBe(resetUser.user.user_id);
});
@@ -192,20 +198,21 @@ describe('User API Routes Integration Tests', () => {
describe('User Data Routes (Watched Items & Shopping Lists)', () => {
it('should allow a user to add and remove a watched item', async () => {
// Act 1: Add a new watched item. The API returns the created master item.
const addResponse = await apiClient.addWatchedItem(
'Integration Test Item',
'Other/Miscellaneous',
authToken,
);
const newItem = await addResponse.json();
const addResponse = await request
.post('/api/users/watched-items')
.set('Authorization', `Bearer ${authToken}`)
.send({ itemName: 'Integration Test Item', category: 'Other/Miscellaneous' });
const newItem = addResponse.body;
// Assert 1: Check that the item was created correctly.
expect(newItem).toBeDefined();
expect(addResponse.status).toBe(201);
expect(newItem.name).toBe('Integration Test Item');
// Act 2: Fetch all watched items for the user.
const watchedItemsResponse = await apiClient.fetchWatchedItems(authToken);
const watchedItems = await watchedItemsResponse.json();
const watchedItemsResponse = await request
.get('/api/users/watched-items')
.set('Authorization', `Bearer ${authToken}`);
const watchedItems = watchedItemsResponse.body;
// Assert 2: Verify the new item is in the user's watched list.
expect(
@@ -216,11 +223,16 @@ describe('User API Routes Integration Tests', () => {
).toBe(true);
// Act 3: Remove the watched item.
await apiClient.removeWatchedItem(newItem.master_grocery_item_id, authToken);
const removeResponse = await request
.delete(`/api/users/watched-items/${newItem.master_grocery_item_id}`)
.set('Authorization', `Bearer ${authToken}`);
expect(removeResponse.status).toBe(204);
// Assert 3: Fetch again and verify the item is gone.
const finalWatchedItemsResponse = await apiClient.fetchWatchedItems(authToken);
const finalWatchedItems = await finalWatchedItemsResponse.json();
const finalWatchedItemsResponse = await request
.get('/api/users/watched-items')
.set('Authorization', `Bearer ${authToken}`);
const finalWatchedItems = finalWatchedItemsResponse.body;
expect(
finalWatchedItems.some(
(item: MasterGroceryItem) =>
@@ -231,31 +243,33 @@ describe('User API Routes Integration Tests', () => {
it('should allow a user to manage a shopping list', async () => {
// Act 1: Create a new shopping list.
const createListResponse = await apiClient.createShoppingList(
'My Integration Test List',
authToken,
);
const newList = await createListResponse.json();
const createListResponse = await request
.post('/api/users/shopping-lists')
.set('Authorization', `Bearer ${authToken}`)
.send({ name: 'My Integration Test List' });
const newList = createListResponse.body;
// Assert 1: Check that the list was created.
expect(newList).toBeDefined();
expect(createListResponse.status).toBe(201);
expect(newList.name).toBe('My Integration Test List');
// Act 2: Add an item to the new list.
const addItemResponse = await apiClient.addShoppingListItem(
newList.shopping_list_id,
{ customItemName: 'Custom Test Item' },
authToken,
);
const addedItem = await addItemResponse.json();
const addItemResponse = await request
.post(`/api/users/shopping-lists/${newList.shopping_list_id}/items`)
.set('Authorization', `Bearer ${authToken}`)
.send({ customItemName: 'Custom Test Item' });
const addedItem = addItemResponse.body;
// Assert 2: Check that the item was added.
expect(addedItem).toBeDefined();
expect(addItemResponse.status).toBe(201);
expect(addedItem.custom_item_name).toBe('Custom Test Item');
// Assert 3: Fetch all lists and verify the new item is present in the correct list.
const fetchResponse = await apiClient.fetchShoppingLists(authToken);
const lists = await fetchResponse.json();
const fetchResponse = await request
.get('/api/users/shopping-lists')
.set('Authorization', `Bearer ${authToken}`);
const lists = fetchResponse.body;
expect(fetchResponse.status).toBe(200);
const updatedList = lists.find(
(l: ShoppingList) => l.shopping_list_id === newList.shopping_list_id,
);