Skip to content

Commit 7003362

Browse files
fix(api): handle concurrent deletion requests (freeCodeCamp#60430)
1 parent a5ec9e1 commit 7003362

2 files changed

Lines changed: 68 additions & 3 deletions

File tree

api/src/routes/protected/user.test.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,55 @@ describe('userRoutes', () => {
464464
expect(countAfter).toBe(0);
465465
expect(res.status).toBe(200);
466466
});
467+
468+
test('handles concurrent requests to delete the same user', async () => {
469+
const deletePromises = Array.from({ length: 2 }, () =>
470+
superPost('/account/delete')
471+
);
472+
473+
const responses = await Promise.all(deletePromises);
474+
475+
const userCount = await fastifyTestInstance.prisma.user.count({
476+
where: { email: testUserData.email }
477+
});
478+
responses.forEach(response => {
479+
expect(response.status).toBe(200);
480+
expect(response.body).toStrictEqual({});
481+
});
482+
expect(userCount).toBe(0);
483+
});
484+
485+
test("only deletes the logged in user's data", async () => {
486+
await fastifyTestInstance.prisma.user.create({
487+
data: {
488+
...testUserData,
489+
email: 'an.random@user'
490+
}
491+
});
492+
expect(await fastifyTestInstance.prisma.user.count()).toBe(2);
493+
494+
await superPost('/account/delete');
495+
496+
const userCount = await fastifyTestInstance.prisma.user.count();
497+
expect(userCount).toBe(1);
498+
});
499+
500+
test('logs if it is asked to delete a non-existent user', async () => {
501+
const spy = jest.spyOn(fastifyTestInstance.log, 'warn');
502+
503+
const deletePromises = Array.from({ length: 2 }, () =>
504+
superPost('/account/delete')
505+
);
506+
507+
await Promise.all(deletePromises);
508+
509+
expect(spy).toHaveBeenCalledTimes(1);
510+
expect(spy.mock.calls[0]).toEqual(
511+
expect.arrayContaining([
512+
`User with id ${defaultUserId} not found for deletion.`
513+
])
514+
);
515+
});
467516
});
468517

469518
describe('/account/reset-progress', () => {

api/src/routes/protected/user.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { ObjectId } from 'mongodb';
33
import _ from 'lodash';
44
import { FastifyInstance, FastifyReply } from 'fastify';
55
import jwt from 'jsonwebtoken';
6+
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library';
67

78
import * as schemas from '../../schemas';
89
import { createResetProperties } from '../../utils/create-user';
@@ -84,9 +85,24 @@ export const userRoutes: FastifyPluginCallbackTypebox = (
8485
await fastify.prisma.survey.deleteMany({
8586
where: { userId: req.user!.id }
8687
});
87-
await fastify.prisma.user.delete({
88-
where: { id: req.user!.id }
89-
});
88+
try {
89+
await fastify.prisma.user.delete({
90+
where: { id: req.user!.id }
91+
});
92+
} catch (err) {
93+
if (
94+
err instanceof PrismaClientKnownRequestError &&
95+
err.code === 'P2025'
96+
) {
97+
logger.warn(
98+
err,
99+
`User with id ${req.user?.id} not found for deletion.`
100+
);
101+
} else {
102+
logger.error(err, 'Error deleting user account');
103+
throw err;
104+
}
105+
}
90106
reply.clearOurCookies();
91107

92108
return {};

0 commit comments

Comments
 (0)