From 38165bdb9a58fae8d10c349969222386b0a2678d Mon Sep 17 00:00:00 2001 From: Torben Sorensen Date: Mon, 5 Jan 2026 19:10:46 -0800 Subject: [PATCH] get rid of localhost in tests - not a qualified URL - we'll see --- .gitea/workflows/deploy-to-test.yml | 4 +-- src/routes/user.routes.test.ts | 36 +++++++++---------- src/services/aiService.server.test.ts | 6 ++-- src/services/authService.test.ts | 2 +- src/services/db/flyer.db.test.ts | 8 ++--- src/services/flyerAiProcessor.server.test.ts | 2 +- src/services/flyerDataTransformer.test.ts | 4 +-- .../flyerProcessingService.server.test.ts | 8 ++--- src/services/userService.test.ts | 2 +- .../flyer-processing.integration.test.ts | 2 +- .../gamification.integration.test.ts | 4 +-- src/tests/utils/mockFactories.ts | 2 +- src/tests/utils/testHelpers.ts | 4 +-- src/utils/serverUtils.test.ts | 16 +++------ src/utils/serverUtils.ts | 2 +- 15 files changed, 47 insertions(+), 55 deletions(-) diff --git a/.gitea/workflows/deploy-to-test.yml b/.gitea/workflows/deploy-to-test.yml index 5f6581d6..6f623030 100644 --- a/.gitea/workflows/deploy-to-test.yml +++ b/.gitea/workflows/deploy-to-test.yml @@ -113,7 +113,7 @@ jobs: REDIS_PASSWORD: ${{ secrets.REDIS_PASSWORD_TEST }} # --- Integration test specific variables --- - FRONTEND_URL: 'http://localhost:3000' + FRONTEND_URL: 'http://example.com' VITE_API_BASE_URL: 'http://localhost:3001/api' GEMINI_API_KEY: ${{ secrets.VITE_GOOGLE_GENAI_API_KEY }} @@ -389,7 +389,7 @@ jobs: REDIS_PASSWORD: ${{ secrets.REDIS_PASSWORD_TEST }} # Application Secrets - FRONTEND_URL: 'https://flyer-crawler-test.projectium.com' + FRONTEND_URL: 'http://example.com' JWT_SECRET: ${{ secrets.JWT_SECRET }} GEMINI_API_KEY: ${{ secrets.VITE_GOOGLE_GENAI_API_KEY_TEST }} GOOGLE_MAPS_API_KEY: ${{ secrets.GOOGLE_MAPS_API_KEY }} diff --git a/src/routes/user.routes.test.ts b/src/routes/user.routes.test.ts index b7277b56..0e63d7d7 100644 --- a/src/routes/user.routes.test.ts +++ b/src/routes/user.routes.test.ts @@ -1030,7 +1030,7 @@ describe('User Routes (/api/users)', () => { it('should upload an avatar and update the user profile', async () => { const mockUpdatedProfile = createMockUserProfile({ ...mockUserProfile, - avatar_url: 'http://localhost:3001/uploads/avatars/new-avatar.png', + avatar_url: 'http://example.com/uploads/avatars/new-avatar.png', }); vi.mocked(userService.updateUserAvatar).mockResolvedValue(mockUpdatedProfile); @@ -1042,7 +1042,7 @@ describe('User Routes (/api/users)', () => { .attach('avatar', Buffer.from('dummy-image-content'), dummyImagePath); expect(response.status).toBe(200); - expect(response.body.avatar_url).toContain('http://localhost:3001/uploads/avatars/'); + expect(response.body.avatar_url).toContain('http://example.com/uploads/avatars/'); expect(userService.updateUserAvatar).toHaveBeenCalledWith( mockUserProfile.user.user_id, expect.any(Object), @@ -1299,32 +1299,32 @@ describe('User Routes (/api/users)', () => { expect(response.headers).toHaveProperty('ratelimit-limit'); expect(parseInt(response.headers['ratelimit-limit'])).toBe(20); }); - }); - it('should apply userSensitiveUpdateLimiter to DELETE /account and block after limit', async () => { + it('should apply userSensitiveUpdateLimiter to DELETE /account and block after limit', async () => { // Explicitly advance time to ensure the rate limiter window has reset from previous tests vi.advanceTimersByTime(60 * 60 * 1000 + 5000); - const limit = 5; - vi.mocked(userService.deleteUserAccount).mockResolvedValue(undefined); + const limit = 5; + vi.mocked(userService.deleteUserAccount).mockResolvedValue(undefined); - // Consume the limit - for (let i = 0; i < limit; i++) { + // Consume the limit + for (let i = 0; i < limit; i++) { + const response = await supertest(app) + .delete('/api/users/account') + .set('X-Test-Rate-Limit-Enable', 'true') + .send({ password: 'correct-password' }); + expect(response.status).toBe(200); + } + + // Next request should be blocked const response = await supertest(app) .delete('/api/users/account') .set('X-Test-Rate-Limit-Enable', 'true') .send({ password: 'correct-password' }); - expect(response.status).toBe(200); - } - // Next request should be blocked - const response = await supertest(app) - .delete('/api/users/account') - .set('X-Test-Rate-Limit-Enable', 'true') - .send({ password: 'correct-password' }); - - expect(response.status).toBe(429); - expect(response.text).toContain('Too many sensitive requests'); + expect(response.status).toBe(429); + expect(response.text).toContain('Too many sensitive requests'); + }); }); }); }); diff --git a/src/services/aiService.server.test.ts b/src/services/aiService.server.test.ts index 66a45c54..29e58335 100644 --- a/src/services/aiService.server.test.ts +++ b/src/services/aiService.server.test.ts @@ -116,7 +116,7 @@ interface MockFlyer { updated_at: string; } -const baseUrl = 'http://localhost:3001'; +const baseUrl = 'http://example.com'; describe('AI Service (Server)', () => { // Create mock dependencies that will be injected into the service @@ -1015,7 +1015,7 @@ describe('AI Service (Server)', () => { userId: 'user123', submitterIp: '127.0.0.1', userProfileAddress: '123 St, City, Country', // Partial address match based on filter(Boolean) - baseUrl: 'http://localhost:3000', + baseUrl: 'http://example.com', }); expect(result.id).toBe('job123'); }); @@ -1037,7 +1037,7 @@ describe('AI Service (Server)', () => { expect.objectContaining({ userId: undefined, userProfileAddress: undefined, - baseUrl: 'http://localhost:3000', + baseUrl: 'http://example.com', }), ); }); diff --git a/src/services/authService.test.ts b/src/services/authService.test.ts index ee7bc490..8b08e377 100644 --- a/src/services/authService.test.ts +++ b/src/services/authService.test.ts @@ -59,7 +59,7 @@ describe('AuthService', () => { // Set environment variables before any modules are imported vi.stubEnv('JWT_SECRET', 'test-secret'); - vi.stubEnv('FRONTEND_URL', 'http://localhost:3000'); + vi.stubEnv('FRONTEND_URL', 'http://example.com'); // Mock all dependencies before dynamically importing the service // Core modules like bcrypt, jsonwebtoken, and crypto are now mocked globally in tests-setup-unit.ts diff --git a/src/services/db/flyer.db.test.ts b/src/services/db/flyer.db.test.ts index f0f47e8b..dc682cb2 100644 --- a/src/services/db/flyer.db.test.ts +++ b/src/services/db/flyer.db.test.ts @@ -132,8 +132,8 @@ describe('Flyer DB Service', () => { it('should execute an INSERT query and return the new flyer', async () => { const flyerData: FlyerDbInsert = { file_name: 'test.jpg', - image_url: 'http://localhost:3001/images/test.jpg', - icon_url: 'http://localhost:3001/images/icons/test.jpg', + image_url: 'http://example.com/images/test.jpg', + icon_url: 'http://example.com/images/icons/test.jpg', checksum: 'checksum123', store_id: 1, valid_from: '2024-01-01', @@ -155,8 +155,8 @@ describe('Flyer DB Service', () => { expect.stringContaining('INSERT INTO flyers'), [ 'test.jpg', - 'http://localhost:3001/images/test.jpg', - 'http://localhost:3001/images/icons/test.jpg', + 'http://example.com/images/test.jpg', + 'http://example.com/images/icons/test.jpg', 'checksum123', 1, '2024-01-01', diff --git a/src/services/flyerAiProcessor.server.test.ts b/src/services/flyerAiProcessor.server.test.ts index 2de4965a..9b5285e9 100644 --- a/src/services/flyerAiProcessor.server.test.ts +++ b/src/services/flyerAiProcessor.server.test.ts @@ -21,7 +21,7 @@ const createMockJobData = (data: Partial): FlyerJobData => ({ filePath: '/tmp/flyer.jpg', originalFileName: 'flyer.jpg', checksum: 'checksum-123', - baseUrl: 'http://localhost:3000', + baseUrl: 'http://example.com', ...data, }); diff --git a/src/services/flyerDataTransformer.test.ts b/src/services/flyerDataTransformer.test.ts index df609896..ab49cc55 100644 --- a/src/services/flyerDataTransformer.test.ts +++ b/src/services/flyerDataTransformer.test.ts @@ -28,13 +28,13 @@ describe('FlyerDataTransformer', () => { transformer = new FlyerDataTransformer(); // Stub environment variables to ensure consistency and predictability. // Prioritize FRONTEND_URL to match the updated service logic. - vi.stubEnv('FRONTEND_URL', 'http://localhost:3000'); + vi.stubEnv('FRONTEND_URL', 'http://example.com'); vi.stubEnv('BASE_URL', ''); // Ensure this is not used to confirm priority logic vi.stubEnv('PORT', ''); // Ensure this is not used // Provide a default mock implementation for generateFlyerIcon vi.mocked(generateFlyerIcon).mockResolvedValue('icon-flyer-page-1.webp'); - vi.mocked(getBaseUrl).mockReturnValue('http://localhost:3000'); + vi.mocked(getBaseUrl).mockReturnValue('http://example.com'); }); it('should transform AI data into database-ready format with a user ID', async () => { diff --git a/src/services/flyerProcessingService.server.test.ts b/src/services/flyerProcessingService.server.test.ts index 06fa6950..46fcbb55 100644 --- a/src/services/flyerProcessingService.server.test.ts +++ b/src/services/flyerProcessingService.server.test.ts @@ -189,7 +189,7 @@ describe('FlyerProcessingService', () => { filePath: '/tmp/flyer.jpg', originalFileName: 'flyer.jpg', checksum: 'checksum-123', - baseUrl: 'http://localhost:3000', + baseUrl: 'http://example.com', ...data, }, updateProgress: vi.fn(), @@ -241,7 +241,7 @@ describe('FlyerProcessingService', () => { 'checksum-123', // checksum undefined, // userId expect.any(Object), // logger - 'http://localhost:3000', // baseUrl + 'http://example.com', // baseUrl ); // 5. DB transaction was initiated @@ -695,8 +695,8 @@ describe('FlyerProcessingService', () => { it('should derive paths from DB and delete files if job paths are empty', async () => { const job = createMockCleanupJob({ flyerId: 1, paths: [] }); // Empty paths const mockFlyer = createMockFlyer({ - image_url: 'http://localhost:3000/flyer-images/flyer-abc.jpg', - icon_url: 'http://localhost:3000/flyer-images/icons/icon-flyer-abc.webp', + image_url: 'http://example.com/flyer-images/flyer-abc.jpg', + icon_url: 'http://example.com/flyer-images/icons/icon-flyer-abc.webp', }); // Mock DB call to return a flyer vi.mocked(mockedDb.flyerRepo.getFlyerById).mockResolvedValue(mockFlyer); diff --git a/src/services/userService.test.ts b/src/services/userService.test.ts index 3a928ce1..affc9053 100644 --- a/src/services/userService.test.ts +++ b/src/services/userService.test.ts @@ -241,7 +241,7 @@ describe('UserService', () => { describe('updateUserAvatar', () => { it('should construct avatar URL and update profile', async () => { const { logger } = await import('./logger.server'); - const testBaseUrl = getTestBaseUrl(3001); + const testBaseUrl = getTestBaseUrl(); vi.stubEnv('FRONTEND_URL', testBaseUrl); const userId = 'user-123'; diff --git a/src/tests/integration/flyer-processing.integration.test.ts b/src/tests/integration/flyer-processing.integration.test.ts index 8cb60b95..8670be11 100644 --- a/src/tests/integration/flyer-processing.integration.test.ts +++ b/src/tests/integration/flyer-processing.integration.test.ts @@ -57,7 +57,7 @@ describe('Flyer Processing Background Job Integration Test', () => { beforeAll(async () => { // FIX: Stub FRONTEND_URL to ensure valid absolute URLs (http://...) are generated // for the database, satisfying the 'url_check' constraint. - vi.stubEnv('FRONTEND_URL', 'http://localhost:3000'); + vi.stubEnv('FRONTEND_URL', 'http://example.com'); }); // FIX: Reset mocks before each test to ensure isolation. diff --git a/src/tests/integration/gamification.integration.test.ts b/src/tests/integration/gamification.integration.test.ts index 4ec4d53e..2c68ec8b 100644 --- a/src/tests/integration/gamification.integration.test.ts +++ b/src/tests/integration/gamification.integration.test.ts @@ -69,7 +69,7 @@ describe('Gamification Flow Integration Test', () => { // Stub environment variables for URL generation in the background worker. // This needs to be in beforeAll to ensure it's set before any code that might use it is imported. - vi.stubEnv('FRONTEND_URL', 'http://localhost:3001'); + vi.stubEnv('FRONTEND_URL', 'http://example.com'); // Setup default mock response for the AI service's extractCoreDataFromFlyerImage method. mockExtractCoreData.mockResolvedValue({ @@ -253,7 +253,7 @@ describe('Gamification Flow Integration Test', () => { // 8. Assert that the URLs are fully qualified. expect(savedFlyer.image_url).to.equal(newFlyer.image_url); expect(savedFlyer.icon_url).to.equal(newFlyer.icon_url); - const expectedBaseUrl = getTestBaseUrl(3001); + const expectedBaseUrl = getTestBaseUrl(); expect(newFlyer.image_url).toContain(`${expectedBaseUrl}/flyer-images/`); }); }); diff --git a/src/tests/utils/mockFactories.ts b/src/tests/utils/mockFactories.ts index 75df5969..c36e98b1 100644 --- a/src/tests/utils/mockFactories.ts +++ b/src/tests/utils/mockFactories.ts @@ -178,7 +178,7 @@ export const createMockFlyer = ( store_id: overrides.store_id ?? overrides.store?.store_id, }); - const baseUrl = 'http://localhost:3001'; // A reasonable default for tests + const baseUrl = 'http://example.com'; // A reasonable default for tests // Determine the final file_name to generate dependent properties from. const fileName = overrides.file_name ?? `flyer-${flyerId}.jpg`; diff --git a/src/tests/utils/testHelpers.ts b/src/tests/utils/testHelpers.ts index 1d91b64a..92b69e17 100644 --- a/src/tests/utils/testHelpers.ts +++ b/src/tests/utils/testHelpers.ts @@ -7,8 +7,8 @@ import supertest from 'supertest'; export const TEST_PASSWORD = 'a-much-stronger-password-for-testing-!@#$'; export const TEST_EXAMPLE_DOMAIN = 'https://example.com'; -export const getTestBaseUrl = (fallbackPort = 3000): string => { - const url = process.env.FRONTEND_URL || `http://localhost:${fallbackPort}`; +export const getTestBaseUrl = (): string => { + const url = process.env.FRONTEND_URL || `http://example.com`; return url.endsWith('/') ? url.slice(0, -1) : url; }; diff --git a/src/utils/serverUtils.test.ts b/src/utils/serverUtils.test.ts index 5acc2649..b50cc25b 100644 --- a/src/utils/serverUtils.test.ts +++ b/src/utils/serverUtils.test.ts @@ -56,29 +56,21 @@ describe('serverUtils', () => { expect(mockLogger.warn).not.toHaveBeenCalled(); }); - it('should fall back to localhost with default port 3000 if no URL is provided', () => { + it('should fall back to example.com with default port 3000 if no URL is provided', () => { delete process.env.FRONTEND_URL; delete process.env.BASE_URL; delete process.env.PORT; const baseUrl = getBaseUrl(mockLogger); - expect(baseUrl).toBe('http://localhost:3000'); + expect(baseUrl).toBe('http://example.com:3000'); expect(mockLogger.warn).not.toHaveBeenCalled(); }); - it('should fall back to localhost with the specified PORT if no URL is provided', () => { - delete process.env.FRONTEND_URL; - delete process.env.BASE_URL; - process.env.PORT = '8888'; - const baseUrl = getBaseUrl(mockLogger); - expect(baseUrl).toBe('http://localhost:8888'); - }); - it('should log a warning and fall back if FRONTEND_URL is invalid (does not start with http)', () => { process.env.FRONTEND_URL = 'invalid.url.com'; const baseUrl = getBaseUrl(mockLogger); - expect(baseUrl).toBe('http://localhost:3000'); + expect(baseUrl).toBe('http://example.com'); expect(mockLogger.warn).toHaveBeenCalledWith( - "[getBaseUrl] FRONTEND_URL/BASE_URL is invalid or incomplete ('invalid.url.com'). Falling back to default local URL: http://localhost:3000", + "[getBaseUrl] FRONTEND_URL/BASE_URL is invalid or incomplete ('invalid.url.com'). Falling back to default local URL: http://example.com", ); }); }); diff --git a/src/utils/serverUtils.ts b/src/utils/serverUtils.ts index 52662f1e..c9d244e3 100644 --- a/src/utils/serverUtils.ts +++ b/src/utils/serverUtils.ts @@ -14,7 +14,7 @@ export function getBaseUrl(logger: Logger): string { let baseUrl = (process.env.FRONTEND_URL || process.env.BASE_URL || '').trim(); if (!baseUrl || !baseUrl.startsWith('http')) { const port = process.env.PORT || 3000; - const fallbackUrl = `http://localhost:${port}`; + const fallbackUrl = `http://example.com:${port}`; if (baseUrl) { logger.warn( `[getBaseUrl] FRONTEND_URL/BASE_URL is invalid or incomplete ('${baseUrl}'). Falling back to default local URL: ${fallbackUrl}`,