Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/freeCodeCamp/freeCodeCamp i…
Browse files Browse the repository at this point in the history
…nto test/editor-theme
  • Loading branch information
huyenltnguyen committed Apr 26, 2024
2 parents 21c1a90 + 84a81c8 commit d5aeb79
Show file tree
Hide file tree
Showing 7,899 changed files with 92,488 additions and 14,580 deletions.
The diff you're trying to view is too large. We only load the first 3000 changed files.
4 changes: 0 additions & 4 deletions api-server/src/common/models/challenge.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,6 @@
"checksum": {
"type": "number"
},
"isBeta": {
"type": "boolean",
"description": "Show only during dev or on beta site. Completely omitted otherwise"
},
"isComingSoon": {
"type": "boolean",
"description": "Challenge shows in production, but is unreachable and disabled. Is reachable in beta/dev only if isBeta flag is set"
Expand Down
2 changes: 1 addition & 1 deletion api-server/src/server/middlewares/csurf.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import csurf from 'csurf';

export const csrfOptions = {
domain: process.env.COOKIE_DOMAIN || 'localhost',
domain: process.env.COOKIE_DOMAIN,
sameSite: 'strict',
secure: process.env.FREECODECAMP_NODE_ENV === 'production'
};
Expand Down
2 changes: 1 addition & 1 deletion api-server/src/server/utils/getSetAccessToken.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const jwtCookieNS = 'jwt_access_token';
export function createCookieConfig(req) {
return {
signed: !!req.signedCookies,
domain: process.env.COOKIE_DOMAIN || 'localhost'
domain: process.env.COOKIE_DOMAIN
};
}

Expand Down
15 changes: 14 additions & 1 deletion api-server/src/server/utils/getSetAccessToken.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,27 @@ describe('getSetAccessToken', () => {
const invalidJWTSecret = 'This is not correct secret';
const now = new Date(Date.now());
const theBeginningOfTime = new Date(0);
const domain = process.env.COOKIE_DOMAIN || 'localhost';
const domain = 'www.example.com';
const accessToken = {
id: '123abc',
userId: '456def',
ttl: 60000,
created: now
};

// https://stackoverflow.com/questions/48033841/test-process-env-with-jest
const OLD_ENV = process.env;

beforeEach(() => {
jest.resetModules(); // process is implicitly cached by Jest, so hence the reset
process.env = { ...OLD_ENV }; // Shallow clone that we can modify
process.env.COOKIE_DOMAIN = domain;
});

afterAll(() => {
process.env = OLD_ENV;
});

describe('getAccessTokenFromRequest', () => {
it('return `no token` error if no token is found', () => {
const req = mockReq({ headers: {}, cookie: {} });
Expand Down
19 changes: 19 additions & 0 deletions api/src/routes/certificate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,25 @@ describe('certificate routes', () => {
});
expect(response.status).toBe(200);
});

test('should return cert-not-found if there is no cert with that slug', async () => {
const response = await superRequest(
'/certificate/showCert/foobar/not-a-valid-cert-slug',
{
method: 'GET'
}
);
expect(response.body).toEqual({
messages: [
{
type: 'info',
message: 'flash.cert-not-found',
variables: { certSlug: 'not-a-valid-cert-slug' }
}
]
});
expect(response.status).toBe(404);
});
});
});
});
Expand Down
21 changes: 8 additions & 13 deletions api/src/routes/certificate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import {
} from '../../../shared/config/certification-settings';
import { normalizeChallenges, removeNulls } from '../utils/normalize';
import { SHOW_UPCOMING_CHANGES } from '../utils/env';
import { formatCertificationValidation } from '../utils/error-formatting';

const {
legacyFrontEndChallengeId,
Expand Down Expand Up @@ -287,15 +286,7 @@ export const unprotectedCertificateRoutes: FastifyPluginCallbackTypebox = (
fastify.get(
'/certificate/showCert/:username/:certSlug',
{
schema: schemas.certSlug,
errorHandler(error, request, reply) {
if (error.validation) {
void reply.code(400);
return formatCertificationValidation(error.validation);
} else {
fastify.errorHandler(error, request, reply);
}
}
schema: schemas.certSlug
},
async (req, reply) => {
try {
Expand All @@ -307,9 +298,13 @@ export const unprotectedCertificateRoutes: FastifyPluginCallbackTypebox = (
if (!isKnownCertSlug(certSlug)) {
void reply.code(404);
return reply.send({
type: 'info',
message: 'flash.cert-not-found',
variables: { certSlug }
messages: [
{
type: 'info',
message: 'flash.cert-not-found',
variables: { certSlug }
}
]
});
}

Expand Down
83 changes: 74 additions & 9 deletions api/src/routes/donate.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
/* eslint-disable @typescript-eslint/naming-convention */
import type { Prisma } from '@prisma/client';
import {
createSuperRequest,
devLogin,
setupServer,
superRequest
superRequest,
defaultUserEmail
} from '../../jest.utils';
import { createUserInput } from '../utils/create-user';

const chargeStripeCardReqBody = {
paymentMethodId: 'UID',
Expand Down Expand Up @@ -45,6 +47,39 @@ jest.mock('stripe', () => {
});
});

const userWithoutProgress: Prisma.userCreateInput =
createUserInput(defaultUserEmail);

const userWithProgress: Prisma.userCreateInput = {
...createUserInput(defaultUserEmail),
completedChallenges: [
{
id: 'a6b0bb188d873cb2c8729495',
completedDate: 1520002973119,
solution: null,
challengeType: 5
},
{
id: '33b0bb188d873cb2c8729433',
completedDate: 4420002973122,
solution: null,
challengeType: 5
},
{
id: 'a5229172f011153519423690',
completedDate: 1520440323273,
solution: null,
challengeType: 5
},
{
id: 'a5229172f011153519423692',
completedDate: 1520440323274,
githubLink: '',
challengeType: 5
}
]
};

describe('Donate', () => {
setupServer();

Expand All @@ -54,6 +89,10 @@ describe('Donate', () => {
beforeEach(async () => {
const setCookies = await devLogin();
superPost = createSuperRequest({ method: 'POST', setCookies });
await fastifyTestInstance.prisma.user.updateMany({
where: { email: userWithProgress.email },
data: userWithProgress
});
});

describe('POST /donate/charge-stripe-card', () => {
Expand All @@ -77,9 +116,11 @@ describe('Donate', () => {
);

expect(response.body).toEqual({
type: 'UserActionRequired',
message: 'Payment requires user action',
client_secret: 'superSecret'
error: {
type: 'UserActionRequired',
message: 'Payment requires user action',
client_secret: 'superSecret'
}
});
expect(response.status).toBe(402);
});
Expand All @@ -93,8 +134,10 @@ describe('Donate', () => {
);

expect(response.body).toEqual({
type: 'PaymentMethodRequired',
message: 'Card has been declined'
error: {
type: 'PaymentMethodRequired',
message: 'Card has been declined'
}
});
expect(response.status).toBe(402);
});
Expand All @@ -111,6 +154,12 @@ describe('Donate', () => {
const failResponse = await superPost('/donate/charge-stripe-card').send(
chargeStripeCardReqBody
);
expect(failResponse.body).toEqual({
error: {
type: 'AlreadyDonatingError',
message: 'User is already donating.'
}
});
expect(failResponse.status).toBe(400);
});

Expand All @@ -121,10 +170,26 @@ describe('Donate', () => {
);
expect(response.status).toBe(500);
expect(response.body).toEqual({
type: 'danger',
message: 'Donation failed due to a server error.'
error: 'Donation failed due to a server error.'
});
});

it('should return 400 if user has not completed challenges', async () => {
await fastifyTestInstance.prisma.user.updateMany({
where: { email: userWithProgress.email },
data: userWithoutProgress
});
const failResponse = await superPost('/donate/charge-stripe-card').send(
chargeStripeCardReqBody
);
expect(failResponse.body).toEqual({
error: {
type: 'MethodRestrictionError',
message: `Donate using another method`
}
});
expect(failResponse.status).toBe(400);
});
});

describe('POST /donate/add-donation', () => {
Expand Down
55 changes: 35 additions & 20 deletions api/src/routes/donate.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/naming-convention */
import {
Type,
type FastifyPluginCallbackTypebox
Expand Down Expand Up @@ -103,15 +102,28 @@ export const donateRoutes: FastifyPluginCallbackTypebox = (
});

const { email, name } = user;
const threeChallengesCompleted = user.completedChallenges.length >= 3;

if (user.isDonating) {
if (!threeChallengesCompleted) {
void reply.code(400);
return {
type: 'info',
message: 'User is already donating.'
error: {
type: 'MethodRestrictionError',
message: `Donate using another method`
}
} as const;
}

if (user.isDonating) {
void reply.code(400);
return reply.send({
error: {
type: 'AlreadyDonatingError',
message: 'User is already donating.'
}
});
}

// Create Stripe Customer
const { id: customerId } = await stripe.customers.create({
email,
Expand Down Expand Up @@ -141,18 +153,22 @@ export const donateRoutes: FastifyPluginCallbackTypebox = (
});
if (status === 'requires_source_action') {
void reply.code(402);
return {
type: 'UserActionRequired',
message: 'Payment requires user action',
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
client_secret
} as const;
return reply.send({
error: {
type: 'UserActionRequired',
message: 'Payment requires user action',
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
client_secret
}
});
} else if (status === 'requires_source') {
void reply.code(402);
return {
type: 'PaymentMethodRequired',
message: 'Card has been declined'
} as const;
return reply.send({
error: {
type: 'PaymentMethodRequired',
message: 'Card has been declined'
}
});
}

// update record in database
Expand Down Expand Up @@ -182,17 +198,16 @@ export const donateRoutes: FastifyPluginCallbackTypebox = (
}
});

return {
return reply.send({
type: 'success',
isDonating: true
} as const;
});
} catch (error) {
fastify.log.error(error);
void reply.code(500);
return {
type: 'danger',
message: 'Donation failed due to a server error.'
} as const;
return reply.send({
error: 'Donation failed due to a server error.'
});
}
}
);
Expand Down
18 changes: 9 additions & 9 deletions api/src/schemas/certificate/cert-slug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,16 +108,16 @@ export const certSlug = {
)
})
]),
400: Type.Object({
type: Type.Literal('error'),
message: Type.String()
}),
404: Type.Object({
message: Type.Literal('flash.cert-not-found'),
type: Type.Literal('info'),
variables: Type.Object({
certSlug: Type.String()
})
messages: Type.Array(
Type.Object({
message: Type.Literal('flash.cert-not-found'),
type: Type.Literal('info'),
variables: Type.Object({
certSlug: Type.String()
})
})
)
}),
500: Type.Object({
type: Type.Literal('danger'),
Expand Down

0 comments on commit d5aeb79

Please sign in to comment.