Compare commits

...

4 Commits

Author SHA1 Message Date
Gitea Actions
73484d3eb4 ci: Bump version to 0.9.11 [skip ci] 2026-01-03 23:52:31 +05:00
b3253d5bbc more test improvements
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 17m17s
2026-01-03 10:51:44 -08:00
Gitea Actions
54f3769e90 ci: Bump version to 0.9.10 [skip ci] 2026-01-03 13:34:20 +05:00
bad6f74ee6 more test improvements
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 19m21s
2026-01-03 00:33:47 -08:00
6 changed files with 49 additions and 5 deletions

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "flyer-crawler",
"version": "0.9.9",
"version": "0.9.11",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "flyer-crawler",
"version": "0.9.9",
"version": "0.9.11",
"dependencies": {
"@bull-board/api": "^6.14.2",
"@bull-board/express": "^6.14.2",

View File

@@ -1,7 +1,7 @@
{
"name": "flyer-crawler",
"private": true,
"version": "0.9.9",
"version": "0.9.11",
"type": "module",
"scripts": {
"dev": "concurrently \"npm:start:dev\" \"vite\"",

View File

@@ -208,6 +208,34 @@ router.post(
},
);
/**
* POST /api/ai/upload-legacy - Process a flyer upload from a legacy client.
* This is an authenticated route that processes the flyer synchronously.
* This is used for integration testing the legacy upload flow.
*/
router.post(
'/upload-legacy',
passport.authenticate('jwt', { session: false }),
uploadToDisk.single('flyerFile'),
async (req: Request, res: Response, next: NextFunction) => {
try {
if (!req.file) {
return res.status(400).json({ message: 'No flyer file uploaded.' });
}
const userProfile = req.user as UserProfile;
const newFlyer = await aiService.processLegacyFlyerUpload(req.file, req.body, userProfile, req.log);
res.status(200).json(newFlyer);
} catch (error) {
await cleanupUploadedFile(req.file);
if (error instanceof DuplicateFlyerError) {
logger.warn(`Duplicate legacy flyer upload attempt blocked.`);
return res.status(409).json({ message: error.message, flyerId: error.flyerId });
}
next(error);
}
},
);
/**
* NEW ENDPOINT: Checks the status of a background job.
*/

View File

@@ -1136,7 +1136,7 @@ describe('AI Service (Server)', () => {
expect(dbModule.adminRepo.logActivity).toHaveBeenCalledWith(
expect.objectContaining({
action: 'flyer_processed',
userId: 'u1',
userId: mockProfile.user.user_id,
}),
mockLoggerInstance,
);

View File

@@ -7,6 +7,10 @@ import { ValidationError, NotFoundError } from './db/errors.db';
import type { Job } from 'bullmq';
import type { TokenCleanupJobData } from '../types/job-data';
// Un-mock the service under test to ensure we are testing the real implementation,
// not the global mock from `tests/setup/tests-setup-unit.ts`.
vi.unmock('./userService');
// --- Hoisted Mocks ---
const mocks = vi.hoisted(() => {
// Create mock implementations for the repository methods we'll be using.
@@ -212,9 +216,12 @@ describe('UserService', () => {
describe('updateUserAvatar', () => {
it('should construct avatar URL and update profile', async () => {
const { logger } = await import('./logger.server');
const testBaseUrl = 'http://localhost:3001';
vi.stubEnv('FRONTEND_URL', testBaseUrl);
const userId = 'user-123';
const file = { filename: 'avatar.jpg' } as Express.Multer.File;
const expectedUrl = '/uploads/avatars/avatar.jpg';
const expectedUrl = `${testBaseUrl}/uploads/avatars/avatar.jpg`;
mocks.mockUpdateUserProfile.mockResolvedValue({} as any);
@@ -225,6 +232,8 @@ describe('UserService', () => {
{ avatar_url: expectedUrl },
logger,
);
vi.unstubAllEnvs();
});
});

View File

@@ -88,6 +88,10 @@ describe('Gamification Flow Integration Test', () => {
it(
'should award the "First Upload" achievement after a user successfully uploads and processes their first flyer',
async () => {
// --- Arrange: Stub environment variables for URL generation in the background worker ---
const testBaseUrl = 'http://localhost:3001'; // Use a fixed port for predictability
vi.stubEnv('FRONTEND_URL', testBaseUrl);
// --- Arrange: Prepare a unique flyer file for upload ---
const imagePath = path.resolve(__dirname, '../assets/test-flyer-image.jpg');
const imageBuffer = await fs.readFile(imagePath);
@@ -174,6 +178,9 @@ describe('Gamification Flow Integration Test', () => {
expect(Number(userOnLeaderboard?.points)).toBeGreaterThanOrEqual(
firstUploadAchievement!.points_value,
);
// --- Cleanup ---
vi.unstubAllEnvs();
},
240000, // Increase timeout to 240s to match other long-running processing tests
);