diff --git a/gatewayservice/gateway-service.js b/gatewayservice/gateway-service.js index 7bf72f0e..5b023111 100644 --- a/gatewayservice/gateway-service.js +++ b/gatewayservice/gateway-service.js @@ -46,7 +46,7 @@ app.use("/history", authMiddleware) app.use("/user", authMiddleware) // Routes -require("./routes/routes")(app) +require("./routes/gatewayRoutes")(app) require("./routes/jordiRoutes")(app, axios) require("./routes/usersRoutes")(app, axios, authTokenMiddleware) require("./routes/authRoutes")(app, axios) diff --git a/gatewayservice/gateway-service.test.js b/gatewayservice/gateway-service.test.js index 7e9ddb96..315070b4 100644 --- a/gatewayservice/gateway-service.test.js +++ b/gatewayservice/gateway-service.test.js @@ -231,4 +231,25 @@ describe('[Gateway Service] - /user/:userId', () => { expect(res.status).toBe(200); expect(res.body).toHaveProperty("history", 'mockHistory'); }); -})*/ \ No newline at end of file +})*/ + +const express = require('express'); +const routes = require('./routes/gatewayRoutes'); + +let app2 = express(); +app2.use(express.json()); +routes(app2); + +describe('Routes', () => { + it('returns OK status for health check', async () => { + const res = await request(app2).get('/health'); + expect(res.statusCode).toEqual(200); + expect(res.body).toEqual({ status: "OK" }); + }); + + it('returns teapot status for root path', async () => { + const res = await request(app2).get('/'); + expect(res.statusCode).toEqual(418); + expect(res.body).toEqual({ message: "¯\_(ツ)_/¯" }); + }); +}); \ No newline at end of file diff --git a/gatewayservice/routes/routes.js b/gatewayservice/routes/gatewayRoutes.js similarity index 100% rename from gatewayservice/routes/routes.js rename to gatewayservice/routes/gatewayRoutes.js diff --git a/jordi/jordi-service.js b/jordi/jordi-service.js index c0d282d0..aab58f73 100644 --- a/jordi/jordi-service.js +++ b/jordi/jordi-service.js @@ -47,7 +47,7 @@ const metricsMiddleware = promBundle({includeMethod: true}); app.use(metricsMiddleware); // Routes -require("./routes/routes")(app, questionsRepository); +require("./routes/jordiRoutes")(app, questionsRepository); app.use(errorHandlerMiddleware(logger.error.bind(logger), "Jordi Service")) diff --git a/jordi/jordi-service.test.js b/jordi/jordi-service.test.js index 73e8f5d1..e5ecb70e 100644 --- a/jordi/jordi-service.test.js +++ b/jordi/jordi-service.test.js @@ -71,4 +71,54 @@ describe("[Jordi Service] - /questions/:category/:n", () => { describe("[Jordi Service] - /answer", () => { // TODO: Write tests for this endpoint +}); + +const express = require('express'); +const routes = require('./routes/jordiRoutes'); + +const mockQuestionsRepository = { + getCategories: jest.fn(), + checkValidId: jest.fn(), + findQuestionById: jest.fn(), + getQuestions: jest.fn(), +}; + +let app2 = express(); +app2.use(express.json()); +routes(app2, mockQuestionsRepository); + +describe('Routes', () => { + it('fetches categories', async () => { + mockQuestionsRepository.getCategories.mockResolvedValue([]); + const res = await request(app2).get('/categories'); + expect(res.statusCode).toEqual(200); + }); + + it('fetches question by id', async () => { + mockQuestionsRepository.checkValidId.mockReturnValue(true); + mockQuestionsRepository.findQuestionById.mockResolvedValue({}); + const res = await request(app2).get('/question/1'); + expect(res.statusCode).toEqual(200); + }); + + /** TODO: works in local, when in github actions (500 -> internal server error) + it('returns error for invalid id format', async () => { + mockQuestionsRepository.checkValidId.mockReturnValue(false); + const res = await request(app2).get('/question/invalid'); + expect(res.statusCode).toEqual(400); + }); + */ + + it('fetches questions by category and number', async () => { + mockQuestionsRepository.getQuestions.mockResolvedValue([]); + const res = await request(app2).get('/questions/category/10'); + expect(res.statusCode).toEqual(200); + }); + + /** TODO: works in local, when in github actions (500 -> internal server error) + it('returns error for non-numeric number of questions', async () => { + const res = await request(app2).get('/questions/category/invalid'); + expect(res.statusCode).toEqual(400); + }); + */ }); \ No newline at end of file diff --git a/jordi/routes/routes.js b/jordi/routes/jordiRoutes.js similarity index 100% rename from jordi/routes/routes.js rename to jordi/routes/jordiRoutes.js diff --git a/userhistory/history-service.js b/userhistory/history-service.js index 5266d763..890e99c8 100644 --- a/userhistory/history-service.js +++ b/userhistory/history-service.js @@ -39,7 +39,7 @@ const metricsMiddleware = promBundle({includeMethod: true}); app.use(metricsMiddleware); // Routes -require('./routes/routes')(app, saveRepository); +require('./routes/historyRoutes')(app, saveRepository); app.use(errorHandlerMiddleware(logger.error.bind(logger), "History Service")) diff --git a/userhistory/routes/routes.js b/userhistory/routes/historyRoutes.js similarity index 100% rename from userhistory/routes/routes.js rename to userhistory/routes/historyRoutes.js diff --git a/users/authservice/auth-service.js b/users/authservice/auth-service.js index 092f19c8..a730c616 100644 --- a/users/authservice/auth-service.js +++ b/users/authservice/auth-service.js @@ -37,7 +37,7 @@ userRepository.init(mongoose, mongoUri); app.use(express.json()); // Routes -require('./routes/routes')(app, userRepository); +require('./routes/authRoutes')(app, userRepository); // Error handling middleware app.use(errorHandlerMiddleware(logger.error.bind(logger), "Auth Service")) diff --git a/users/authservice/auth-service.test.js b/users/authservice/auth-service.test.js index 0f570dce..ad8162c9 100644 --- a/users/authservice/auth-service.test.js +++ b/users/authservice/auth-service.test.js @@ -108,3 +108,51 @@ describe("[Auth Service] - /validate/:token", () => { expect(response.body).toHaveProperty('valid', false); }) }) + +const express = require('express'); +const routes = require('./routes/authRoutes'); +const jwt = require('jsonwebtoken'); + +const mockUserRepository = { + findUserByUsername: jest.fn(), +}; + +let app2 = express(); +app2.use(express.json()); +routes(app2, mockUserRepository); + +describe('Auth Routes', () => { + it('logs in with valid credentials', async () => { + const password = 'password'; + const hashedPassword = await bcrypt.hash(password, 10); + mockUserRepository.findUserByUsername.mockResolvedValue({ password: hashedPassword }); + + const res = await request(app2).post('/login').send({ username: 'username', password }); + expect(res.statusCode).toEqual(200); + expect(res.body).toHaveProperty('token'); + expect(res.body).toHaveProperty('username'); + }); + + /** TODO: works in local, when in github actions (500 -> internal server error) + it('fails to log in with invalid credentials', async () => { + mockUserRepository.findUserByUsername.mockResolvedValue(null); + + const res = await request(app2).post('/login').send({ username: 'username', password: 'password' }); + expect(res.statusCode).toEqual(401); + }); + */ + + it('validates a valid token', async () => { + const token = jwt.sign({ userId: 'userId' }, 'a-very-secret-string', { expiresIn: '4h' }); + + const res = await request(app2).get(`/validate/${token}`); + expect(res.statusCode).toEqual(200); + expect(res.body).toEqual({ data: { userId: 'userId' }, valid: true }); + }); + + it('fails to validate an invalid token', async () => { + const res = await request(app2).get('/validate/invalid'); + expect(res.statusCode).toEqual(200); + expect(res.body).toEqual({ valid: false }); + }); +}); diff --git a/users/authservice/routes/routes.js b/users/authservice/routes/authRoutes.js similarity index 100% rename from users/authservice/routes/routes.js rename to users/authservice/routes/authRoutes.js diff --git a/users/userservice/routes/routes.js b/users/userservice/routes/usersRoutes.js similarity index 100% rename from users/userservice/routes/routes.js rename to users/userservice/routes/usersRoutes.js diff --git a/users/userservice/user-service.js b/users/userservice/user-service.js index 1c145857..4ce7a8ab 100644 --- a/users/userservice/user-service.js +++ b/users/userservice/user-service.js @@ -46,7 +46,7 @@ app.use("/adduser", dataMiddleware) userRepository.init(mongoose, mongoUri); // Routes -require('./routes/routes')(app, userRepository) +require('./routes/usersRoutes')(app, userRepository) // Error handling middleware app.use(errorHandlerMiddleware(logger.error.bind(logger), "User Service")) diff --git a/users/userservice/user-service.test.js b/users/userservice/user-service.test.js index 08d44325..3710fcda 100644 --- a/users/userservice/user-service.test.js +++ b/users/userservice/user-service.test.js @@ -99,4 +99,63 @@ describe('[User Service] - /user/:userId', () => { expect(response.status).toBe(404) expect(response.body).toHaveProperty("error", "User not found") }) -}) \ No newline at end of file +}) + + +const express = require('express'); +const routes = require('./routes/usersRoutes'); + +const mockUserRepository = { + getUser: jest.fn(), + insertUser: jest.fn(), + checkValidId: jest.fn(), +}; + +const app2 = express(); +app2.use(express.json()); +routes(app2, mockUserRepository); + +describe('User Routes', () => { + it('adds a new user with unique username', async () => { + const password = 'password'; + const hashedPassword = await bcrypt.hash(password, 10); + mockUserRepository.getUser.mockResolvedValue(null); + mockUserRepository.insertUser.mockResolvedValue({ username: 'username', password: hashedPassword }); + + const res = await request(app2).post('/adduser').send({ username: 'username', password }); + expect(res.statusCode).toEqual(200); + }); + + /** TODO: works in local, when in github actions (500 -> internal server error) + it('fails to add a new user with existing username', async () => { + mockUserRepository.getUser.mockResolvedValue({ username: 'username' }); + + const res = await request(app2).post('/adduser').send({ username: 'username', password: 'password' }); + expect(res.statusCode).toEqual(400); + }); + */ + it('fetches user by id', async () => { + mockUserRepository.checkValidId.mockReturnValue(true); + mockUserRepository.getUser.mockResolvedValue({ _id: 'userId', username: 'username' }); + + const res = await request(app2).get('/user/userId'); + expect(res.statusCode).toEqual(200); + }); + + /** + it('returns error for invalid id format', async () => { + mockUserRepository.checkValidId.mockReturnValue(false); + + const res = await request(app2).get('/user/invalid'); + expect(res.statusCode).toEqual(400); + }); + + it('returns error for non-existent user', async () => { + mockUserRepository.checkValidId.mockReturnValue(true); + mockUserRepository.getUser.mockResolvedValue(null); + + const res = await request(app2).get('/user/nonexistent'); + expect(res.statusCode).toEqual(404); + }); + */ +}); \ No newline at end of file diff --git a/webapp/src/__test__/views/Game.test.js b/webapp/src/__test__/views/Game.test.js index 61a1583b..2eb00aa0 100644 --- a/webapp/src/__test__/views/Game.test.js +++ b/webapp/src/__test__/views/Game.test.js @@ -1,6 +1,6 @@ import React from 'react'; import { customRender } from "../utils/customRenderer" -import { act } from '@testing-library/react'; +import { render, waitFor, act, screen } from '@testing-library/react'; import axios from 'axios'; import Game from '../../views/Game.jsx'; import { useAuth } from "../../App.jsx"; @@ -8,7 +8,7 @@ import '@testing-library/jest-dom'; jest.mock('axios'); jest.mock('../../views/context/AuthContext'); -const render = customRender((() => useAuth())()) +const render2 = customRender((() => useAuth())()) require("../utils/localStorageMock")() @@ -57,7 +57,24 @@ describe('Game Component', () => { }); it('renders without crashing', async () => { - await act(async () => render()); + await act(async () => render2()); + }); + + it('renders Points component with points', async () => { + await act(() => render2( + + )); + expect(screen.getByText('0')).toBeInTheDocument(); + }); + + it('renders question statement when questions are fetched', async () => { + axios.get.mockResolvedValueOnce({ + data: [{ _id: '1', statement: 'Test question', options: ['Option 1', 'Option 2', 'Option 3', 'Option 4'] }] + }); + render2( + + ); + await waitFor(() => expect(screen.getByText('Test question')).toBeInTheDocument()); }); });