linting done now fix unit tests
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 8m19s
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 8m19s
This commit is contained in:
@@ -23,12 +23,20 @@ import { backgroundJobService } from '../services/backgroundJobService';
|
||||
import { flyerQueue, emailQueue, analyticsQueue, cleanupQueue, weeklyAnalyticsQueue, flyerWorker, emailWorker, analyticsWorker, cleanupWorker, weeklyAnalyticsWorker } from '../services/queueService.server'; // Import your queues
|
||||
import { getSimpleWeekAndYear } from '../utils/dateUtils';
|
||||
|
||||
const uuidParamSchema = (key: string) => z.object({
|
||||
params: z.object({ [key]: z.string().uuid() }),
|
||||
/**
|
||||
* A factory for creating a Zod schema that validates a UUID in the request parameters.
|
||||
* @param key The name of the parameter key (e.g., 'userId').
|
||||
* @param message A custom error message for invalid UUIDs.
|
||||
*/
|
||||
const uuidParamSchema = (key: string, message = `Invalid UUID for parameter '${key}'.`) => z.object({
|
||||
params: z.object({ [key]: z.string().uuid({ message }) }),
|
||||
});
|
||||
|
||||
const numericIdParamSchema = (key: string) => z.object({
|
||||
params: z.object({ [key]: z.coerce.number().int().positive() }),
|
||||
/**
|
||||
* A factory for creating a Zod schema that validates a numeric ID in the request parameters.
|
||||
*/
|
||||
const numericIdParamSchema = (key: string, message = `Invalid ID for parameter '${key}'. Must be a positive integer.`) => z.object({
|
||||
params: z.object({ [key]: z.coerce.number().int({ message }).positive({ message }) }),
|
||||
});
|
||||
|
||||
const updateCorrectionSchema = numericIdParamSchema('id').extend({
|
||||
@@ -49,8 +57,7 @@ const updateCommentStatusSchema = numericIdParamSchema('id').extend({
|
||||
}),
|
||||
});
|
||||
|
||||
const updateUserRoleSchema = z.object({
|
||||
params: z.object({ id: z.string().uuid() }),
|
||||
const updateUserRoleSchema = uuidParamSchema('id', 'A valid user ID is required.').extend({
|
||||
body: z.object({
|
||||
role: z.enum(['user', 'admin']),
|
||||
}),
|
||||
@@ -64,7 +71,10 @@ const activityLogSchema = z.object({
|
||||
});
|
||||
|
||||
const jobRetrySchema = z.object({
|
||||
params: z.object({ queueName: z.string(), jobId: z.string() }),
|
||||
params: z.object({
|
||||
queueName: z.enum(['flyer-processing', 'email-sending', 'analytics-reporting', 'file-cleanup', 'weekly-analytics-reporting']),
|
||||
jobId: z.string().min(1, 'A valid Job ID is required.'),
|
||||
}),
|
||||
});
|
||||
|
||||
const router = Router();
|
||||
@@ -281,7 +291,7 @@ router.get('/activity-log', validateRequest(activityLogSchema), async (req: Requ
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/users/:id', validateRequest(uuidParamSchema('id')), async (req: Request, res: Response, next: NextFunction) => {
|
||||
router.get('/users/:id', validateRequest(uuidParamSchema('id', 'A valid user ID is required.')), async (req: Request, res: Response, next: NextFunction) => {
|
||||
// Apply ADR-003 pattern for type safety
|
||||
const { params } = req as unknown as z.infer<ReturnType<typeof uuidParamSchema>>;
|
||||
try {
|
||||
@@ -304,7 +314,7 @@ router.put('/users/:id', validateRequest(updateUserRoleSchema), async (req: Requ
|
||||
}
|
||||
});
|
||||
|
||||
router.delete('/users/:id', validateRequest(uuidParamSchema('id')), async (req: Request, res: Response, next: NextFunction) => {
|
||||
router.delete('/users/:id', validateRequest(uuidParamSchema('id', 'A valid user ID is required.')), async (req: Request, res: Response, next: NextFunction) => {
|
||||
const adminUser = req.user as UserProfile;
|
||||
// Apply ADR-003 pattern for type safety
|
||||
const { params } = req as unknown as z.infer<ReturnType<typeof uuidParamSchema>>;
|
||||
@@ -365,10 +375,9 @@ router.post('/trigger/analytics-report', async (req: Request, res: Response, nex
|
||||
* This is triggered by an admin after they have verified the flyer processing was successful.
|
||||
*/
|
||||
router.post('/flyers/:flyerId/cleanup', validateRequest(numericIdParamSchema('flyerId')), async (req: Request, res: Response, next: NextFunction) => {
|
||||
// Define schema locally to simplify type inference
|
||||
const schema = numericIdParamSchema('flyerId');
|
||||
const adminUser = req.user as UserProfile;
|
||||
const { params } = req as unknown as z.infer<typeof schema>;
|
||||
// Infer type from the schema generator for type safety, as per ADR-003.
|
||||
const { params } = req as unknown as z.infer<ReturnType<typeof numericIdParamSchema>>;
|
||||
logger.info(`[Admin] Manual trigger for flyer file cleanup received from user: ${adminUser.user_id} for flyer ID: ${params.flyerId}`);
|
||||
|
||||
// Enqueue the cleanup job. The worker will handle the file deletion.
|
||||
|
||||
@@ -163,7 +163,8 @@ describe('User Routes (/api/users)', () => {
|
||||
.post('/api/users/watched-items')
|
||||
.send({ category: 'Produce' });
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.body.message).toBe("Field 'itemName' is required.");
|
||||
// Check the 'errors' array for the specific validation message.
|
||||
expect(response.body.errors[0].message).toBe("Field 'itemName' is required.");
|
||||
});
|
||||
|
||||
it('should return 400 if category is missing', async () => {
|
||||
@@ -171,7 +172,8 @@ describe('User Routes (/api/users)', () => {
|
||||
.post('/api/users/watched-items')
|
||||
.send({ itemName: 'Apples' });
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.body.message).toBe("Field 'category' is required.");
|
||||
// Check the 'errors' array for the specific validation message.
|
||||
expect(response.body.errors[0].message).toBe("Field 'category' is required.");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -215,7 +217,8 @@ describe('User Routes (/api/users)', () => {
|
||||
it('should return 400 if name is missing', async () => {
|
||||
const response = await supertest(app).post('/api/users/shopping-lists').send({});
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.body.message).toBe("Field 'name' is required.");
|
||||
// Check the 'errors' array for the specific validation message.
|
||||
expect(response.body.errors[0].message).toBe("Field 'name' is required.");
|
||||
});
|
||||
|
||||
it('should return 400 on foreign key constraint error', async () => {
|
||||
@@ -228,7 +231,8 @@ describe('User Routes (/api/users)', () => {
|
||||
it('should return 400 for an invalid listId on DELETE', async () => {
|
||||
const response = await supertest(app).delete('/api/users/shopping-lists/abc');
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.body.message).toBe("Invalid ID for parameter 'listId'. Must be a number.");
|
||||
// Check the 'errors' array for the specific validation message.
|
||||
expect(response.body.errors[0].message).toBe("Invalid ID for parameter 'listId'. Must be a number.");
|
||||
});
|
||||
|
||||
describe('DELETE /shopping-lists/:listId', () => {
|
||||
@@ -375,7 +379,7 @@ describe('User Routes (/api/users)', () => {
|
||||
});
|
||||
|
||||
it('should return 404 if the user to delete is not found', async () => {
|
||||
vi.mocked(db.userRepo.findUserWithPasswordHashById).mockResolvedValue(undefined);
|
||||
vi.mocked(db.userRepo.findUserWithPasswordHashById).mockRejectedValue(new NotFoundError('User not found'));
|
||||
const response = await supertest(app)
|
||||
.delete('/api/users/account')
|
||||
.send({ password: 'any-password' });
|
||||
@@ -422,7 +426,8 @@ describe('User Routes (/api/users)', () => {
|
||||
it('should return 400 for an invalid masterItemId', async () => {
|
||||
const response = await supertest(app).delete('/api/users/watched-items/abc');
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.body.message).toBe("Invalid ID for parameter 'masterItemId'. Must be a number.");
|
||||
// Check the 'errors' array for the specific validation message.
|
||||
expect(response.body.errors[0].message).toBe("Invalid ID for parameter 'masterItemId'. Must be a number.");
|
||||
});
|
||||
|
||||
it('PUT should successfully set the restrictions', async () => {
|
||||
|
||||
Reference in New Issue
Block a user