diff --git a/gatewayservice/gateway-service.test.js b/gatewayservice/gateway-service.test.js index abe0dac..71e217d 100644 --- a/gatewayservice/gateway-service.test.js +++ b/gatewayservice/gateway-service.test.js @@ -82,6 +82,37 @@ describe('Gateway Service', () => { ], } }); + } else if(url.endsWith('getAllUsers')) { + return Promise.resolve({ + status: 200, + data: { + users: + [{ + "_id":"65e604d6b9b58650687ee809", + "username":"tomas", + "password":"$2b$10$cD6x4mUOP9Q16SfMpbWXGugPczuYcV/HMaJbVTWJ.guZoy.LmEe6e", + "email":"tomas@gmail.com", + "questions_answered":500, + "correctly_answered_questions":200, + "createdAt":"2024-03-03T17:30:54.385Z", + "cheeseCount":1 + }] + } + }); + } else if(url.endsWith('/getUser')){ + return Promise.resolve({ + status: 200, + data: { + "_id":"65e604d6b9b58650687ee809", + "username":"tomas", + "password":"$2b$10$cD6x4mUOP9Q16SfMpbWXGugPczuYcV/HMaJbVTWJ.guZoy.LmEe6e", + "email":"tomas@gmail.com", + "questions_answered":500, + "correctly_answered_questions":200, + "createdAt":"2024-03-03T17:30:54.385Z", + "cheeseCount":1 + } + }); } @@ -99,9 +130,11 @@ describe('Gateway Service', () => { return Promise.resolve({ data: { token: 'mockedToken' } }); } else if (url.endsWith('/adduser')) { return Promise.resolve({ data: { userId: 'mockedUserId' } }); - } else if(url.endsWith('/edituser')){ + } else if(url.endsWith('/sumNormalStats')){ return Promise.resolve({ data: { message: 'User updated' } }); - } + } else if(url.endsWith('/sumTrivialStats')){ + return Promise.resolve({ data: { message: 'User updated' } }); + } }); // Test health @@ -198,5 +231,61 @@ describe('Gateway Service', () => { const response = await request(app).get("/api-doc"); expect(response.status).toBe(301); }); + + // Test /sumStats + it('should respond with a successful update operation of normal game mode stats', async () => { + + await sumStats('/sumNormalStats', { + username: 'newuser', + questions_answered:500, + correctly_answered_questions:200 + }); + }, 6000); + + it('should respond with a successful update operation of trivial game mode stats', async () => { + await sumStats('/sumTrivialStats', { + username: 'newuser', + cheeseCount:10 + }); + }, 6000); + + + async function sumStats(endpoint, user){ + const response = await request(app) + .post(endpoint) + .send(user); + expect(response.status).toBe(200); + expect(response.body.message).toBe("User updated"); + } + + // Test getUser(s) + + it('should retrieve the specified user', async () => { + const response = await request(app).post('/getUser').send({username: "tomas"}); + expect(response.status).toBe(200); + console.log(response.body); + checkCorrectUserFields(response.body); + + }, 6000); + + it('should retrieve all the users', async () => { + const response = await request(app).get('/getAllUsers'); + expect(response.status).toBe(200); + expect(response.body.users.length).toBe(1); + console.log(response.body); + await checkCorrectUserFields(response.body.users[0]); + + }, 6000); + + async function checkCorrectUserFields(user){ + expect(user).toHaveProperty("_id"); + expect(user).toHaveProperty("username"); + expect(user).toHaveProperty("password"); + expect(user).toHaveProperty("createdAt"); + expect(user).toHaveProperty("email"); + expect(user).toHaveProperty("questions_answered"); + expect(user).toHaveProperty("correctly_answered_questions"); + expect(user).toHaveProperty("cheeseCount"); + } }); \ No newline at end of file diff --git a/users/authservice/auth-service.js b/users/authservice/auth-service.js index 488d409..d1b6bfe 100644 --- a/users/authservice/auth-service.js +++ b/users/authservice/auth-service.js @@ -27,25 +27,13 @@ router.post('/login', async (req, res) => { // Check if required fields are present in the request body validateRequiredFields(req, ['username', 'password']); - const username = req.body.username.toString(); - const password = req.body.password.toString(); - - - // access to the database - const db = mongoose.connection.useDb("UsersDB"); - - // access to the collection of the database - const userCollection = db.collection('User'); - + const { username, password } = req.body; let user; - - await userCollection.findOne({ username }, function(err, result) { - if (err) { - console.error('Error finding user. There is not user with that username:', err); - } else { - user = result; - } - }); + try { + user = await User.findOne({ username }); + } catch (err) { + throw new Error('Error finding the user') + } // Check if the user exists and verify the password if (user && await bcrypt.compare(password, user.password)) { diff --git a/webapp/src/components/leaderboard/RankingTable.test.tsx b/webapp/src/components/leaderboard/RankingTable.test.tsx index 2c3e685..9de944f 100644 --- a/webapp/src/components/leaderboard/RankingTable.test.tsx +++ b/webapp/src/components/leaderboard/RankingTable.test.tsx @@ -1,46 +1,65 @@ import React from 'react'; -import { render, screen, waitFor } from '@testing-library/react'; -import { act } from 'react-dom/test-utils'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import RankingTable, { User } from './RankingTable'; -import { getAllUsers } from '../../services/auth-service'; +import { getAllUsers } from "../../services/auth-service"; +import { act } from 'react-dom/test-utils'; // import act + jest.mock('../../services/auth-service'); describe('RankingTable', () => { - it('true', () => {expect(true).toBe(true);}); - // it('renders users correctly', async () => { - // const mockUsers: { [key: string]: User } = { - // '1': { username: 'user1', correctAnswers: 5, totalAnswers: 10 }, - // '2': { username: 'user2', correctAnswers: 7, totalAnswers: 10 }, - // '3': { username: 'user3', correctAnswers: 3, totalAnswers: 10 }, - // }; - - // (getAllUsers as jest.Mock).mockResolvedValue(mockUsers); - - // await act(async () => { - // render(); - // }); - - // await waitFor(() => { - // expect(screen.getByText('user1')).toBeInTheDocument(); - // expect(screen.getByText('user2')).toBeInTheDocument(); - // expect(screen.getByText('user3')).toBeInTheDocument(); - // }); - // }); - - // it('handles error during fetching users', async () => { - // const consoleError = jest.spyOn(console, 'error').mockImplementation(() => {}); + beforeEach(() => { + (getAllUsers as jest.Mock).mockResolvedValue({ + user1: { username: 'user1', correctly_answered_questions: 5, questions_answered: 10 }, + user2: { username: 'user2', correctly_answered_questions: 3, questions_answered: 10 }, + user3: { username: 'user3', correctly_answered_questions: 7, questions_answered: 10 }, + }); + }); - // (getAllUsers as jest.Mock).mockRejectedValue(new Error('Error during fetching users')); + it('renders without crashing', () => { + render(); + }); - // await act(async () => { - // render(); - // }); + it('displays users sorted by correct answers', async () => { + await act(async () => { // wrap render in act + render(); + }); + + // Wait for users to load + const userNames = await screen.findAllByRole('cell', { name: /user\d/i }); + expect(userNames[0]).toHaveTextContent('user3'); + expect(userNames[1]).toHaveTextContent('user1'); + expect(userNames[2]).toHaveTextContent('user2'); + }); - // await waitFor(() => { - // expect(consoleError).toHaveBeenCalledWith('Error during retrieving all the users', new Error('Error during fetching users')); - // }); + it('displays correct answers for each user', async () => { + await act(async () => { // wrap render in act + render(); + }); + + // Wait for users to load + const correctAnswersUser0 = await screen.findByTestId('user-0-correct-answers'); + const correctAnswersUser1 = await screen.findByTestId('user-1-correct-answers'); + const correctAnswersUser2 = await screen.findByTestId('user-2-correct-answers'); + + expect(correctAnswersUser0).toHaveTextContent('7'); + expect(correctAnswersUser1).toHaveTextContent('5'); + expect(correctAnswersUser2).toHaveTextContent('3'); +}); - // consoleError.mockRestore(); - // }); + it('displays correct percentage of correct answers for each user', async () => { + await act(async () => { // wrap render in act + render(); + }); + + // Wait for users to load + const percentageUser0 = await screen.findByTestId('user-0-percentage'); + const percentageUser1 = await screen.findByTestId('user-1-percentage'); + const percentageUser2 = await screen.findByTestId('user-2-percentage'); + + expect(percentageUser0).toHaveTextContent('70%'); + expect(percentageUser1).toHaveTextContent('50%'); + expect(percentageUser2).toHaveTextContent('30%'); + }); }); \ No newline at end of file diff --git a/webapp/src/components/leaderboard/RankingTable.tsx b/webapp/src/components/leaderboard/RankingTable.tsx index f016831..e9c00b4 100644 --- a/webapp/src/components/leaderboard/RankingTable.tsx +++ b/webapp/src/components/leaderboard/RankingTable.tsx @@ -57,32 +57,34 @@ export default function RankingTable() { - { - users.map((user, index) => { - return ( - - + { + users.map((user, index) => { + return ( + + - {user.username.toUpperCase().charAt(0)}{user.username.toUpperCase().charAt(1)} + {user.username.toUpperCase().charAt(0)}{user.username.toUpperCase().charAt(1)} {user.username.toLowerCase()} - - - {index === 0 || index === 1 || index === 2 ? podium[index] : index + 1} - - {user.correctAnswers} - - 0 ? (Math.round(((user.correctAnswers / user.totalAnswers) * 100) * 100) / 100) : 0} - color='#00A078' thickness='.4rem' - size={"3.6rem"}> - {user.totalAnswers > 0 ? (Math.round(((user.correctAnswers / user.totalAnswers) * 100) * 100) / 100).toFixed(0) : 0}% - - - - ) - }) - } + + + {index === 0 || index === 1 || index === 2 ? podium[index] : index + 1} + + {user.correctAnswers} + + 0 ? (Math.round(((user.correctAnswers / user.totalAnswers) * 100) * 100) / 100) : 0} + color='#00A078' thickness='.4rem' + size={"3.6rem"}> + + {user.totalAnswers > 0 ? (Math.round(((user.correctAnswers / user.totalAnswers) * 100) * 100) / 100).toFixed(0) : 0}% + + + + + ) + }) + } ) diff --git a/webapp/src/components/leaderboard/TrivialRankingTable.test.tsx b/webapp/src/components/leaderboard/TrivialRankingTable.test.tsx new file mode 100644 index 0000000..150e98e --- /dev/null +++ b/webapp/src/components/leaderboard/TrivialRankingTable.test.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { render, waitFor, screen } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; +import TrivialRankingTable, { User } from './TrivialRankingTable'; +import { getAllUsers } from '../../services/auth-service'; +import { act } from '@testing-library/react'; + +jest.mock('../../services/auth-service'); + +describe('TrivialRankingTable', () => { + const mockUsers: User[] = [ + { username: 'user1', correctAnswers: 10, totalAnswers: 20, cheeseCount: 5 }, + { username: 'user2', correctAnswers: 15, totalAnswers: 30, cheeseCount: 10 }, + { username: 'user3', correctAnswers: 5, totalAnswers: 10, cheeseCount: 0 }, + ]; + + beforeEach(() => { + (getAllUsers as jest.Mock).mockResolvedValue(mockUsers); + }); + + it('renders without crashing', async () => { + await act(async () => { + render(); + }); + }); + + it('displays users with cheeseCount greater than 0', async () => { + await act(async () => { + render(); + }); + await waitFor(() => expect(getAllUsers).toHaveBeenCalled()); + + expect(screen.getByText('user1')).toBeInTheDocument(); + expect(screen.getByText('user2')).toBeInTheDocument(); + expect(screen.queryByText('user3')).not.toBeInTheDocument(); + }); + + it('sorts users by cheeseCount in descending order', async () => { + await act(async () => { + render(); + }); + await waitFor(() => expect(getAllUsers).toHaveBeenCalled()); + + const users = screen.getAllByTestId('user-row'); + expect(users[0]).toHaveTextContent('user2'); + expect(users[1]).toHaveTextContent('user1'); + }); +}); \ No newline at end of file diff --git a/webapp/src/components/leaderboard/TrivialRankingTable.tsx b/webapp/src/components/leaderboard/TrivialRankingTable.tsx index d05086b..0522a6b 100644 --- a/webapp/src/components/leaderboard/TrivialRankingTable.tsx +++ b/webapp/src/components/leaderboard/TrivialRankingTable.tsx @@ -58,25 +58,25 @@ export default function TrivialRankingTable() { - { - users.map((user, index) => { - return ( - - - - {user.username.toUpperCase().charAt(0)}{user.username.toUpperCase().charAt(1)} - - {user.username.toLowerCase()} - - - {index === 0 || index === 1 || index === 2 ? podium[index] : index + 1} - - {user.cheeseCount} - - - ) - }) - } + { + users.map((user, index) => { + return ( + + + + {user.username.toUpperCase().charAt(0)}{user.username.toUpperCase().charAt(1)} + + {user.username.toLowerCase()} + + + {index === 0 || index === 1 || index === 2 ? podium[index] : index + 1} + + {user.cheeseCount} + + + ) + }) + } ) diff --git a/webapp/src/components/stats/Statistics.test.tsx b/webapp/src/components/stats/Statistics.test.tsx new file mode 100644 index 0000000..28f30fc --- /dev/null +++ b/webapp/src/components/stats/Statistics.test.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import Statistics from './Statistics'; + +jest.mock('./StatsTable', () => () =>
Mock StatsTable
); + +describe('Statistics component', () => { + it('renders without crashing', () => { + render(); + expect(screen.getByText('Statistics page')).toBeInTheDocument(); + }); + + it('renders the StatsTable component', () => { + render(); + expect(screen.getByText('Mock StatsTable')).toBeInTheDocument(); + }); +}); \ No newline at end of file