fix(api): handle concurrent deletion requests (#60430)

This commit is contained in:
Oliver Eyton-Williams
2025-05-19 16:25:22 +02:00
committed by GitHub
parent a5ec9e1ee7
commit 7003362fef
2 changed files with 68 additions and 3 deletions
+49
View File
@@ -464,6 +464,55 @@ describe('userRoutes', () => {
expect(countAfter).toBe(0);
expect(res.status).toBe(200);
});
test('handles concurrent requests to delete the same user', async () => {
const deletePromises = Array.from({ length: 2 }, () =>
superPost('/account/delete')
);
const responses = await Promise.all(deletePromises);
const userCount = await fastifyTestInstance.prisma.user.count({
where: { email: testUserData.email }
});
responses.forEach(response => {
expect(response.status).toBe(200);
expect(response.body).toStrictEqual({});
});
expect(userCount).toBe(0);
});
test("only deletes the logged in user's data", async () => {
await fastifyTestInstance.prisma.user.create({
data: {
...testUserData,
email: 'an.random@user'
}
});
expect(await fastifyTestInstance.prisma.user.count()).toBe(2);
await superPost('/account/delete');
const userCount = await fastifyTestInstance.prisma.user.count();
expect(userCount).toBe(1);
});
test('logs if it is asked to delete a non-existent user', async () => {
const spy = jest.spyOn(fastifyTestInstance.log, 'warn');
const deletePromises = Array.from({ length: 2 }, () =>
superPost('/account/delete')
);
await Promise.all(deletePromises);
expect(spy).toHaveBeenCalledTimes(1);
expect(spy.mock.calls[0]).toEqual(
expect.arrayContaining([
`User with id ${defaultUserId} not found for deletion.`
])
);
});
});
describe('/account/reset-progress', () => {
+19 -3
View File
@@ -3,6 +3,7 @@ import { ObjectId } from 'mongodb';
import _ from 'lodash';
import { FastifyInstance, FastifyReply } from 'fastify';
import jwt from 'jsonwebtoken';
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library';
import * as schemas from '../../schemas';
import { createResetProperties } from '../../utils/create-user';
@@ -84,9 +85,24 @@ export const userRoutes: FastifyPluginCallbackTypebox = (
await fastify.prisma.survey.deleteMany({
where: { userId: req.user!.id }
});
await fastify.prisma.user.delete({
where: { id: req.user!.id }
});
try {
await fastify.prisma.user.delete({
where: { id: req.user!.id }
});
} catch (err) {
if (
err instanceof PrismaClientKnownRequestError &&
err.code === 'P2025'
) {
logger.warn(
err,
`User with id ${req.user?.id} not found for deletion.`
);
} else {
logger.error(err, 'Error deleting user account');
throw err;
}
}
reply.clearOurCookies();
return {};