mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-04-09 19:00:53 -04:00
feat(api): reset progress endpoint (#50432)
--------- Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
@@ -1,17 +1,48 @@
|
||||
import request from 'supertest';
|
||||
|
||||
import { setupServer, superRequest } from '../../jest.utils';
|
||||
|
||||
const baseProgressData = {
|
||||
currentChallengeId: '',
|
||||
isRespWebDesignCert: false,
|
||||
is2018DataVisCert: false,
|
||||
isFrontEndLibsCert: false,
|
||||
isJsAlgoDataStructCert: false,
|
||||
isApisMicroservicesCert: false,
|
||||
isInfosecQaCert: false,
|
||||
isQaCertV7: false,
|
||||
isInfosecCertV7: false,
|
||||
is2018FullStackCert: false,
|
||||
isFrontEndCert: false,
|
||||
isBackEndCert: false,
|
||||
isDataVisCert: false,
|
||||
isFullStackCert: false,
|
||||
isSciCompPyCertV7: false,
|
||||
isDataAnalysisPyCertV7: false,
|
||||
isMachineLearningPyCertV7: false,
|
||||
isRelationalDatabaseCertV8: false,
|
||||
isCollegeAlgebraPyCertV8: false,
|
||||
completedChallenges: [],
|
||||
savedChallenges: [],
|
||||
partiallyCompletedChallenges: [],
|
||||
needsModeration: false
|
||||
};
|
||||
|
||||
const modifiedProgressData = {
|
||||
...baseProgressData,
|
||||
currentChallengeId: 'hello there',
|
||||
isRespWebDesignCert: true,
|
||||
isJsAlgoDataStructCert: true,
|
||||
isRelationalDatabaseCertV8: true,
|
||||
needsModeration: true
|
||||
};
|
||||
|
||||
describe('userRoutes', () => {
|
||||
setupServer();
|
||||
|
||||
describe('Authenticated user', () => {
|
||||
let setCookies: string[];
|
||||
|
||||
beforeAll(async () => {
|
||||
const res = await request(fastifyTestInstance?.server).get(
|
||||
'/auth/dev-callback'
|
||||
);
|
||||
beforeEach(async () => {
|
||||
const res = await superRequest('/auth/dev-callback', { method: 'GET' });
|
||||
setCookies = res.get('Set-Cookie');
|
||||
});
|
||||
|
||||
@@ -22,7 +53,7 @@ describe('userRoutes', () => {
|
||||
setCookies
|
||||
});
|
||||
|
||||
const userCount = await fastifyTestInstance?.prisma.user.count({
|
||||
const userCount = await fastifyTestInstance.prisma.user.count({
|
||||
where: { email: 'foo@bar.com' }
|
||||
});
|
||||
|
||||
@@ -31,6 +62,30 @@ describe('userRoutes', () => {
|
||||
expect(userCount).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('/account/reset-progress', () => {
|
||||
test('POST returns 200 status code with empty object', async () => {
|
||||
await fastifyTestInstance.prisma.user.updateMany({
|
||||
where: { email: 'foo@bar.com' },
|
||||
data: modifiedProgressData
|
||||
});
|
||||
|
||||
const response = await superRequest('/account/reset-progress', {
|
||||
method: 'POST',
|
||||
setCookies
|
||||
});
|
||||
|
||||
const user = await fastifyTestInstance.prisma.user.findFirst({
|
||||
where: { email: 'foo@bar.com' }
|
||||
});
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body).toStrictEqual({});
|
||||
|
||||
expect(user?.progressTimestamps).toHaveLength(1);
|
||||
expect(user).toMatchObject(baseProgressData);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Unauthenticated user', () => {
|
||||
@@ -44,5 +99,15 @@ describe('userRoutes', () => {
|
||||
expect(response?.statusCode).toBe(401);
|
||||
});
|
||||
});
|
||||
|
||||
describe('/account/reset-progress', () => {
|
||||
test('POST returns 401 status code with error message', async () => {
|
||||
const response = await superRequest('/account/reset-progress', {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
expect(response?.statusCode).toBe(401);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import {
|
||||
Type,
|
||||
type FastifyPluginCallbackTypebox
|
||||
} from '@fastify/type-provider-typebox';
|
||||
import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
||||
|
||||
import { schemas } from '../schemas';
|
||||
|
||||
export const userRoutes: FastifyPluginCallbackTypebox = (
|
||||
fastify,
|
||||
@@ -13,17 +12,7 @@ export const userRoutes: FastifyPluginCallbackTypebox = (
|
||||
fastify.post(
|
||||
'/account/delete',
|
||||
{
|
||||
schema: {
|
||||
response: {
|
||||
200: Type.Object({}),
|
||||
500: Type.Object({
|
||||
message: Type.Literal(
|
||||
'Oops! Something went wrong. Please try again in a moment or contact support@freecodecamp.org if the error persists.'
|
||||
),
|
||||
type: Type.Literal('danger')
|
||||
})
|
||||
}
|
||||
}
|
||||
schema: schemas.deleteMyAccount
|
||||
},
|
||||
async (req, reply) => {
|
||||
try {
|
||||
@@ -49,5 +38,58 @@ export const userRoutes: FastifyPluginCallbackTypebox = (
|
||||
}
|
||||
);
|
||||
|
||||
fastify.post(
|
||||
'/account/reset-progress',
|
||||
{
|
||||
schema: schemas.resetMyProgress
|
||||
},
|
||||
async (req, reply) => {
|
||||
try {
|
||||
await fastify.prisma.userToken.deleteMany({
|
||||
where: { userId: req.session.user.id }
|
||||
});
|
||||
await fastify.prisma.user.update({
|
||||
where: { id: req.session.user.id },
|
||||
data: {
|
||||
progressTimestamps: [Date.now()],
|
||||
currentChallengeId: '',
|
||||
isRespWebDesignCert: false,
|
||||
is2018DataVisCert: false,
|
||||
isFrontEndLibsCert: false,
|
||||
isJsAlgoDataStructCert: false,
|
||||
isApisMicroservicesCert: false,
|
||||
isInfosecQaCert: false,
|
||||
isQaCertV7: false,
|
||||
isInfosecCertV7: false,
|
||||
is2018FullStackCert: false,
|
||||
isFrontEndCert: false,
|
||||
isBackEndCert: false,
|
||||
isDataVisCert: false,
|
||||
isFullStackCert: false,
|
||||
isSciCompPyCertV7: false,
|
||||
isDataAnalysisPyCertV7: false,
|
||||
isMachineLearningPyCertV7: false,
|
||||
isRelationalDatabaseCertV8: false,
|
||||
isCollegeAlgebraPyCertV8: false,
|
||||
completedChallenges: [],
|
||||
savedChallenges: [],
|
||||
partiallyCompletedChallenges: [],
|
||||
needsModeration: false
|
||||
}
|
||||
});
|
||||
|
||||
return {};
|
||||
} catch (err) {
|
||||
fastify.log.error(err);
|
||||
void reply.code(500);
|
||||
return {
|
||||
message:
|
||||
'Oops! Something went wrong. Please try again in a moment or contact support@freecodecamp.org if the error persists.',
|
||||
type: 'danger'
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
done();
|
||||
};
|
||||
|
||||
@@ -123,6 +123,29 @@ export const schemas = {
|
||||
})
|
||||
}
|
||||
},
|
||||
// User:
|
||||
deleteMyAccount: {
|
||||
response: {
|
||||
200: Type.Object({}),
|
||||
500: Type.Object({
|
||||
message: Type.Literal(
|
||||
'Oops! Something went wrong. Please try again in a moment or contact support@freecodecamp.org if the error persists.'
|
||||
),
|
||||
type: Type.Literal('danger')
|
||||
})
|
||||
}
|
||||
},
|
||||
resetMyProgress: {
|
||||
response: {
|
||||
200: Type.Object({}),
|
||||
500: Type.Object({
|
||||
message: Type.Literal(
|
||||
'Oops! Something went wrong. Please try again in a moment or contact support@freecodecamp.org if the error persists.'
|
||||
),
|
||||
type: Type.Literal('danger')
|
||||
})
|
||||
}
|
||||
},
|
||||
// Deprecated endpoints:
|
||||
deprecatedEndpoints: {
|
||||
response: {
|
||||
|
||||
Reference in New Issue
Block a user