ronantakizawa/codereview-bench huggingface dataset:
Repository: freeCodeCamp/freeCodeCamp
File: api/src/exam-environment/utils/exam-environment.test.ts (TypeScript)

Code before:

  describe('shuffleArray()', () => {
    it('reasonably shuffles an array', () => {
      const unshuff = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
      const shuff = shuffleArray(unshuff);

      expect(shuff).not.toEqual(unshuff);
    });

    it('does not mutate the input', () => {
      const unshuff = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
      shuffleArray(unshuff);

      expect(unshuff).toEqual(unshuff);
    });
  });
});

describe('Exam Environment Schema', () => {
  setupServer();
  describe('ExamEnvironmentExam', () => {
    afterAll(async () => {
      await fastifyTestInstance.prisma.examEnvironmentExam.deleteMany({});
    });

    it('should throw if the schema changes, and remind to increment the `version` field by 1', () => {
      const configQuestionSets = [
        {
          numberOfCorrectAnswers: 0,
          numberOfIncorrectAnswers: 0,
          numberOfQuestions: 0,
          numberOfSet: 0,
          type: ExamEnvironmentQuestionType.MultipleChoice
        }
      ];
      const tags = [
        {
          group: [''],
          numberOfQuestions: 0
        }
      ];
      const config = {
        name: '',
        note: '',
        passingPercent: 0.0,
        questionSets: configQuestionSets,
        retakeTimeInMS: 0,
        tags,
        totalTimeInMS: 0
      };


Review comment (bug):
```suggestion
    it('If this test fails and you've deliberately altered the schema, then increment the `version` field by 1', async () => {
```

Code after:

  describe('shuffleArray()', () => {
    it('reasonably shuffles an array', () => {
      const unshuff = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
      const shuff = shuffleArray(unshuff);

      expect(shuff).not.toEqual(unshuff);
    });

    it('does not mutate the input', () => {
      const unshuff = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
      shuffleArray(unshuff);

      expect(unshuff).toEqual(unshuff);
    });
  });
});

describe('Exam Environment Schema', () => {
  setupServer();
  describe('ExamEnvironmentExam', () => {
    afterAll(async () => {
      await fastifyTestInstance.prisma.examEnvironmentExam.deleteMany({});
    });

    it("If this test fails and you've deliberately altered the schema, then increment the `version` field by 1", async () => {
      const configQuestionSets = [
        {
          numberOfCorrectAnswers: 0,
          numberOfIncorrectAnswers: 0,
          numberOfQuestions: 0,
          numberOfSet: 0,
          type: ExamEnvironmentQuestionType.MultipleChoice
        }
      ];
      const tags = [
        {
          group: [''],
          numberOfQuestions: 0
        }
      ];
      const config = {
        name: '',
        note: '',
        passingPercent: 0.0,
        questionSets: configQuestionSets,
        retakeTimeInMS: 0,
        tags,
        totalTimeInMS: 0
      };
Repository: freeCodeCamp/freeCodeCamp
File: api/src/exam-environment/utils/exam-environment.test.ts (TypeScript)

Code before:
    afterAll(async () => {
      await fastifyTestInstance.prisma.examEnvironmentGeneratedExam.deleteMany(
        {}
      );
    });
    it('should throw if the schema changes, and remind to increment the `version` field by 1', () => {
      expect(async () => {
        await fastifyTestInstance.prisma.examEnvironmentGeneratedExam.create({
          data: {
            deprecated: false,
            examId: oid(),
            questionSets: [
              { id: oid(), questions: [{ answers: [oid()], id: oid() }] }
            ]
          }
        });
      }).not.toThrow();
    });
  });
  describe('ExamEnvironmentExamAttempt', () => {
    afterAll(async () => {
      await fastifyTestInstance.prisma.examEnvironmentExamAttempt.deleteMany(
        {}
      );
    });
    it('should throw if the schema changes, and remind to increment the `version` field by 1', () => {
      expect(async () => {
        await fastifyTestInstance.prisma.examEnvironmentExamAttempt.create({
          data: {
            examId: oid(),
            generatedExamId: oid(),
            questionSets: [
              {
                id: oid(),
                questions: [
                  { answers: [oid()], id: oid(), submissionTimeInMS: 0 }
                ]
              }
            ],
            startTimeInMS: 0,
            userId: oid()
          }
        });
      }).not.toThrow();
    });
  });
});


Review comment (bug):
```suggestion
    it('If this test fails and you've deliberately altered the schema, then increment the `version` field by 1', async () => {
```

Code after:
        {}
      );
    });
    it("If this test fails and you've deliberately altered the schema, then increment the `version` field by 1", async () => {
      await fastifyTestInstance.prisma.examEnvironmentGeneratedExam.create({
        data: {
          deprecated: false,
          examId: oid(),
          questionSets: [
            { id: oid(), questions: [{ answers: [oid()], id: oid() }] }
          ]
        }
      });
    });
  });
  describe('ExamEnvironmentExamAttempt', () => {
    afterAll(async () => {
      await fastifyTestInstance.prisma.examEnvironmentExamAttempt.deleteMany(
        {}
      );
    });
    it("If this test fails and you've deliberately altered the schema, then increment the `version` field by 1", async () => {
      await fastifyTestInstance.prisma.examEnvironmentExamAttempt.create({
        data: {
          examId: oid(),
          generatedExamId: oid(),
          questionSets: [
            {
              id: oid(),
              questions: [
                { answers: [oid()], id: oid(), submissionTimeInMS: 0 }
              ]
            }
          ],
          startTimeInMS: 0,
          userId: oid()
        }
      });
    });
  });
});
Repository: freeCodeCamp/freeCodeCamp
File: client/utils/sort-challengefiles.test.ts (TypeScript)

Code before:
import { challengeFiles } from './__fixtures__/challenges';
import { sortChallengeFiles } from './sort-challengefiles';

describe('sort-files', () => {
  describe('sortChallengeFiles', () => {
    it('should return an array', () => {
      const sorted = sortChallengeFiles(challengeFiles);
      expect(Array.isArray(sorted)).toBe(true);
    });
    it('should not modify the challenges', () => {
      const sorted = sortChallengeFiles(challengeFiles);
      const expected = challengeFiles;
      expect(sorted).toEqual(expect.arrayContaining(expected));
      expect(sorted.length).toEqual(expected.length);
    });

    it('should sort the objects into jsx, html, css, js, ts,tsx order', () => {
      const sorted = sortChallengeFiles(challengeFiles);
      const sortedKeys = sorted.map(({ fileKey }) => fileKey);
      const expected = [
        'indexjsx',
        'indexhtml',
        'stylescss',
        'scriptjs',
        'indexts',
        'indextsx'
      ];
      expect(sortedKeys).toStrictEqual(expected);
    });
  });
});


Review comment (suggestion):
```suggestion
    it('should sort the objects into jsx, html, css, js, ts, tsx order', () => {
```

Code after:
import { challengeFiles } from './__fixtures__/challenges';
import { sortChallengeFiles } from './sort-challengefiles';

describe('sort-files', () => {
  describe('sortChallengeFiles', () => {
    it('should return an array', () => {
      const sorted = sortChallengeFiles(challengeFiles);
      expect(Array.isArray(sorted)).toBe(true);
    });
    it('should not modify the challenges', () => {
      const sorted = sortChallengeFiles(challengeFiles);
      const expected = challengeFiles;
      expect(sorted).toEqual(expect.arrayContaining(expected));
      expect(sorted.length).toEqual(expected.length);
    });

    it('should sort the objects into jsx, tsx, html, css, js, ts order', () => {
      const sorted = sortChallengeFiles(challengeFiles);
      const sortedKeys = sorted.map(({ fileKey }) => fileKey);
      const expected = [
        'indexjsx',
        'indextsx',
        'indexhtml',
        'stylescss',
        'scriptjs',
        'indexts'
      ];
      expect(sortedKeys).toStrictEqual(expected);
    });
  });
});
Repository: freeCodeCamp/freeCodeCamp
File: client/utils/sort-challengefiles.ts (TypeScript)

Code before:
export function sortChallengeFiles<File extends { fileKey: string }>(
  challengeFiles: File[]
): File[] {
  return challengeFiles.toSorted((a, b) => {
    if (a.fileKey === 'indexjsx') return -1;
    if (b.fileKey === 'indexjsx') return 1;
    if (a.fileKey === 'indexhtml') return -1;
    if (b.fileKey === 'indexhtml') return 1;
    if (a.fileKey === 'stylescss') return -1;
    if (b.fileKey === 'stylescss') return 1;
    if (a.fileKey === 'scriptjs') return -1;
    if (b.fileKey === 'scriptjs') return 1;
    if (a.fileKey === 'indexts') return -1;
    if (b.fileKey === 'indexts') return 1;
    if (a.fileKey === 'indextsx') return -1;
    if (b.fileKey === 'indextsx') return 1;
    return 0;
  });
}


Review comment (suggestion):
We should move these up and above `indexhtml`. 

By default, the challenge opens the first file in the list, so with `index.tsx` being below `index.html`, the HTML file is opened first.

Code after:
export function sortChallengeFiles<File extends { fileKey: string }>(
  challengeFiles: File[]
): File[] {
  return challengeFiles.toSorted((a, b) => {
    if (a.fileKey === 'indexjsx') return -1;
    if (b.fileKey === 'indexjsx') return 1;
    if (a.fileKey === 'indextsx') return -1;
    if (b.fileKey === 'indextsx') return 1;
    if (a.fileKey === 'indexhtml') return -1;
    if (b.fileKey === 'indexhtml') return 1;
    if (a.fileKey === 'stylescss') return -1;
    if (b.fileKey === 'stylescss') return 1;
    if (a.fileKey === 'scriptjs') return -1;
    if (b.fileKey === 'scriptjs') return 1;
    if (a.fileKey === 'indexts') return -1;
    if (b.fileKey === 'indexts') return 1;
    return 0;
  });
}
Repository: freeCodeCamp/freeCodeCamp
File: api/src/routes/protected/classroom.ts (TypeScript)

Code before:
import { FastifyPluginCallback } from 'fastify';
import { Type } from '@fastify/type-provider-typebox';
import { normalizeChallenges } from '../../utils/normalize.js';

/**
 * Fastify plugin for classroom-related protected routes.
 * Provides endpoint for retrieving user data for classrooms.
 * @param fastify - The Fastify instance.
 * @param _options - Plugin options (unused).
 * @param done - Callback to signal plugin registration is complete.
 */
export const classroomRoutes: FastifyPluginCallback = (
  fastify,
  _options,
  done
) => {
  // Endpoint to retrieve a user's ID from a user's email.
  fastify.post(
    '/api/protected/classroom/get-user-id',
    {
      schema: {
        body: Type.Object({
          email: Type.String({ format: 'email' })
        }),
        response: {
          200: Type.Object({
            userId: Type.String()

Review comment (suggestion):
```suggestion
import { FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
```
We need this type because it uses the `schema` to generate types for the request and reply objects.

Code after:
import { FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
import {
  normalizeChallenges,
  NormalizedChallenge
} from '../../utils/normalize.js';
import * as schemas from '../../schemas/classroom/classroom.js';

/**
 * Fastify plugin for classroom-related protected routes.
 * Provides endpoint for retrieving user data for classrooms.
 * @param fastify - The Fastify instance.
 * @param _options - Plugin options (unused).
 * @param done - Callback to signal plugin registration is complete.
 */
export const classroomRoutes: FastifyPluginCallbackTypebox = (
  fastify,
  _options,
  done
) => {
  // Endpoint to retrieve a user's ID from a user's email.
  // If we send a 404 error here, it will stop the entire classroom process from working.
  // Instead, we indicate that the user was not found through a null response and continue.
  fastify.post(
    '/api/protected/classroom/get-user-id',
    {
      schema: schemas.classroomGetUserIdSchema
    },
Repository: freeCodeCamp/freeCodeCamp
File: api/src/routes/protected/classroom.ts (TypeScript)

Code before:
 */
export const classroomRoutes: FastifyPluginCallback = (
  fastify,
  _options,
  done
) => {
  // Endpoint to retrieve a user's ID from a user's email.
  fastify.post(
    '/api/protected/classroom/get-user-id',
    {
      schema: {
        body: Type.Object({
          email: Type.String({ format: 'email' })
        }),
        response: {
          200: Type.Object({
            userId: Type.String()
          }),
          404: Type.Object({
            error: Type.String()
          })
        }
      }
    },
    async (request, reply) => {
      const { email } = request.body as { email: string };

      try {
        // Find the user by email
        const user = await fastify.prisma.user.findFirst({
          where: { email },
          select: {
            id: true,
            isClassroomAccount: true
          }
        });

        if (!user || !user.isClassroomAccount) {
          return reply.code(404).send({ error: 'User not found' });
        }

        return reply.send({
          userId: user.id
        });
      } catch (error) {
        fastify.log.error(error);
        return reply.code(500).send({ error: 'Failed to retrieve user id' });
      }
    }
  );


Review comment (suggestion):
```suggestion
      const { email } = request.body;
```
This won't be necessary if `FastifyPluginCallbackTypebox` is used.


Code after:
 * @param fastify - The Fastify instance.
 * @param _options - Plugin options (unused).
 * @param done - Callback to signal plugin registration is complete.
 */
export const classroomRoutes: FastifyPluginCallbackTypebox = (
  fastify,
  _options,
  done
) => {
  // Endpoint to retrieve a user's ID from a user's email.
  // If we send a 404 error here, it will stop the entire classroom process from working.
  // Instead, we indicate that the user was not found through a null response and continue.
  fastify.post(
    '/api/protected/classroom/get-user-id',
    {
      schema: schemas.classroomGetUserIdSchema
    },
    async (request, reply) => {
      const { email } = request.body;

      // Basic email validation - return empty userId for invalid emails
      const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
      if (!emailRegex.test(email)) {
        return reply.send({ userId: '' });
      }

      try {
        // Find the user by email
        const user = await fastify.prisma.user.findFirst({
          where: { email, isClassroomAccount: true },
          select: { id: true }
        });

        if (!user) {
          return reply.send({ userId: '' });
        }

        return reply.send({
          userId: user.id
        });
      } catch (error) {
        fastify.log.error(error);
        return reply.code(500).send({ error: 'Failed to retrieve user id' });
      }
    }
  );

  // Endpoint to retrieve user(s) data from a list of user ids
  fastify.post(
    '/api/protected/classroom/get-user-data',
    {
Repository: freeCodeCamp/freeCodeCamp
File: api/src/routes/protected/classroom.ts (TypeScript)

Code before:
import { FastifyPluginCallback } from 'fastify';
import { Type } from '@fastify/type-provider-typebox';
import { normalizeChallenges } from '../../utils/normalize.js';

/**
 * Fastify plugin for classroom-related protected routes.
 * Provides endpoint for retrieving user data for classrooms.
 * @param fastify - The Fastify instance.
 * @param _options - Plugin options (unused).
 * @param done - Callback to signal plugin registration is complete.
 */
export const classroomRoutes: FastifyPluginCallback = (
  fastify,
  _options,
  done
) => {
  // Endpoint to retrieve a user's ID from a user's email.
  fastify.post(
    '/api/protected/classroom/get-user-id',
    {
      schema: {
        body: Type.Object({
          email: Type.String({ format: 'email' })
        }),
        response: {
          200: Type.Object({
            userId: Type.String()
          }),
          404: Type.Object({
            error: Type.String()
          })
        }
      }
    },
    async (request, reply) => {
      const { email } = request.body as { email: string };


Review comment (suggestion):
```suggestion
export const classroomRoutes: FastifyPluginCallbackTypebox = (
```

Code after:
import { FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
import {
  normalizeChallenges,
  NormalizedChallenge
} from '../../utils/normalize.js';
import * as schemas from '../../schemas/classroom/classroom.js';

/**
 * Fastify plugin for classroom-related protected routes.
 * Provides endpoint for retrieving user data for classrooms.
 * @param fastify - The Fastify instance.
 * @param _options - Plugin options (unused).
 * @param done - Callback to signal plugin registration is complete.
 */
export const classroomRoutes: FastifyPluginCallbackTypebox = (
  fastify,
  _options,
  done
) => {
  // Endpoint to retrieve a user's ID from a user's email.
  // If we send a 404 error here, it will stop the entire classroom process from working.
  // Instead, we indicate that the user was not found through a null response and continue.
  fastify.post(
    '/api/protected/classroom/get-user-id',
    {
      schema: schemas.classroomGetUserIdSchema
    },
    async (request, reply) => {
      const { email } = request.body;

      // Basic email validation - return empty userId for invalid emails
      const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
      if (!emailRegex.test(email)) {
        return reply.send({ userId: '' });
      }

      try {
Repository: freeCodeCamp/freeCodeCamp
File: api/src/routes/protected/classroom.ts (TypeScript)

Code before:
          200: Type.Object({
            data: Type.Record(
              Type.String(), // Id as key
              Type.Array(
                Type.Object({
                  id: Type.String(),
                  completedDate: Type.Number(),
                  challengeName: Type.Optional(Type.String()),
                  files: Type.Optional(Type.Array(Type.Object({}))),
                  githubLink: Type.Optional(Type.String()),
                  solution: Type.Optional(Type.String())
                })
              )
            )
          }),
          400: Type.Object({
            error: Type.String()
          }),
          500: Type.Object({
            error: Type.String()
          })
        }
      }
    },
    async (request, reply) => {
      const { userIds = [] } = request.body as { userIds: string[] };

      // Limit number of users per request for performance
      if (userIds.length > 50) {
        return reply.code(400).send({
          error: 'Too many users requested. Maximum 50 allowed.'
        });
      }

      try {
        // Find all the requested users by user id
        const users = await fastify.prisma.user.findMany({
          where: {
            id: { in: userIds }
          },
          select: {
            id: true,
            completedChallenges: true,
            isClassroomAccount: true
          }
        });

        // Map to transform user data into the required format
        const userData: Record<string, object[]> = {};

        users.forEach(user => {

Review comment (suggestion):
```suggestion
      const { userIds = [] } = request.body
```
Also not necessary when using a schema.

Code after:
          error: 'Too many users requested. Maximum 50 allowed.'
        });
      }

      try {
        // Find all the requested users by user id
        const users = await fastify.prisma.user.findMany({
          where: {
            id: { in: userIds },
            isClassroomAccount: true
          },
          select: {
            id: true,
            completedChallenges: true
          }
        });

        // Map to transform user data into the required format
        const userData: Record<string, NormalizedChallenge[]> = {};

        users.forEach(user => {
          // Normalize challenges
          const normalizedChallenges = normalizeChallenges(
            user.completedChallenges
          );

          userData[user.id] = normalizedChallenges;
        });

        return reply.send({
          data: userData
        });
      } catch (error) {
        fastify.log.error(error);
        return reply.code(500).send({ error: 'Failed to retrieve user data' });
      }
    }
  );

  done();
};
Repository: freeCodeCamp/freeCodeCamp
File: api/src/routes/protected/classroom.ts (TypeScript)

Code before:
            )
          }),
          400: Type.Object({
            error: Type.String()
          }),
          500: Type.Object({
            error: Type.String()
          })
        }
      }
    },
    async (request, reply) => {
      const { userIds = [] } = request.body as { userIds: string[] };

      // Limit number of users per request for performance
      if (userIds.length > 50) {
        return reply.code(400).send({
          error: 'Too many users requested. Maximum 50 allowed.'
        });
      }

      try {
        // Find all the requested users by user id
        const users = await fastify.prisma.user.findMany({
          where: {
            id: { in: userIds }
          },
          select: {
            id: true,
            completedChallenges: true,
            isClassroomAccount: true
          }
        });

        // Map to transform user data into the required format
        const userData: Record<string, object[]> = {};

        users.forEach(user => {
          if (!user.isClassroomAccount) {
            return;
          }

          // Normalize challenges
          const normalizedChallenges = normalizeChallenges(
            user.completedChallenges
          );

          if (user.id !== null) {
            userData[user.id] = normalizedChallenges;
          }
        });

Review comment (suggestion):
```suggestion
            id: { in: userIds },
            isClassroomAccount: true
```
Rather than fetching them all and then filtering.

Code after:
            completedChallenges: true
          }
        });

        // Map to transform user data into the required format
        const userData: Record<string, NormalizedChallenge[]> = {};

        users.forEach(user => {
          // Normalize challenges
          const normalizedChallenges = normalizeChallenges(
            user.completedChallenges
          );

          userData[user.id] = normalizedChallenges;
        });

        return reply.send({
          data: userData
        });
      } catch (error) {
        fastify.log.error(error);
        return reply.code(500).send({ error: 'Failed to retrieve user data' });
      }
    }
  );

  done();
};