From b6eeb86c8af1a7f497c255ac73467dda036279e5 Mon Sep 17 00:00:00 2001 From: uo289689 Date: Wed, 27 Mar 2024 12:17:12 +0100 Subject: [PATCH 1/6] Adjusted user registration tests to use an in-memory SQLite database --- .env | 1 + users/__tests/services/user-service.test.js | 141 +++++--------------- users/models/user-model.js | 35 +++-- users/package-lock.json | 3 +- users/package.json | 3 +- 5 files changed, 62 insertions(+), 121 deletions(-) diff --git a/.env b/.env index 9a0df33e..9937ee34 100644 --- a/.env +++ b/.env @@ -1 +1,2 @@ teamname="wiq_es04a" +NODE_ENV='test' diff --git a/users/__tests/services/user-service.test.js b/users/__tests/services/user-service.test.js index 606a0965..cff4dc55 100644 --- a/users/__tests/services/user-service.test.js +++ b/users/__tests/services/user-service.test.js @@ -1,122 +1,49 @@ +const { User, Statistics, sequelize } = require('../../models/user-model.js'); +const bcrypt = require('bcrypt'); const request = require('supertest'); -const { Sequelize, DataTypes } = require('sequelize'); -const {router} = require('../../routes/user-routes.js'); - -// Configuration of the Sequelize to use a memory data base -const sequelize = new Sequelize({ - dialect: 'sqlite', - storage: ':memory:', - logging: false, -}); - -const User = sequelize.define('User', { - username: { - type: DataTypes.STRING, - primaryKey: true, - notEmpty: true, - }, - password: { - type: DataTypes.STRING, - allowNull: false, - }, - name: { - type: DataTypes.STRING, - allowNull: false, - notEmpty: true, - }, - surname: { - type: DataTypes.STRING, - allowNull: false, - notEmpty: true, - }, - createdAt: { - type: DataTypes.DATE, - defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), - }, - imageUrl: { - type: DataTypes.STRING, - defaultValue: "../../webapp/public/default_user.jpg", - } -}); - -const Statistics = sequelize.define('Statistics', { - username: { - type: DataTypes.STRING, - primaryKey: true, - allowNull: false - }, - earned_money: { - type: DataTypes.INTEGER, - defaultValue: 0, - }, - classic_correctly_answered_questions: { - type: DataTypes.INTEGER, - defaultValue: 0, - }, - classic_incorrectly_answered_questions: { - type: DataTypes.INTEGER, - defaultValue: 0, - }, - classic_total_time_played: { - type: DataTypes.INTEGER, - defaultValue: 0, - }, - classic_games_played: { - type: DataTypes.INTEGER, - defaultValue: 0, - } -}); - -User.hasOne(Statistics, { foreignKey: 'username' }); -Statistics.belongsTo(User, { foreignKey: 'username' }); - -beforeAll(async () => { - await sequelize.sync({ force: true }); -}); +const express = require('express'); +const bodyParser = require('body-parser'); +const userRoutes = require('../../routes/user-routes.js'); + +const app = express(); +app.use(bodyParser.json()); +app.use('/user', userRoutes); + +describe('User Routes', () => { + beforeAll(async () => { + await sequelize.authenticate(); + await sequelize.sync({ force: true }); + }); -afterAll(async () => { - await sequelize.close(); -}); + afterAll(async () => { + await sequelize.close(); + }); -// tests -describe('User Registration API', () => { - it('should register a new user', async () => { - const userData = { + it('should add a new user', async () => { + const newUser = { username: 'testuser', password: 'Test1234', name: 'Test', surname: 'User' }; - const response = await request(router) - .post('/add') - .send(userData); + const response = await request(app) + .post('/user/add') + .send(newUser); expect(response.status).toBe(200); - expect(response.body).toHaveProperty('username', userData.username); - expect(response.body).toHaveProperty('name', userData.name); - expect(response.body).toHaveProperty('surname', userData.surname); - - const userStats = await Statistics.findOne({ where: { username: userData.username } }); - expect(userStats).toBeTruthy(); - expect(userStats.classic_correctly_answered_questions).toBe(0); - }); - - it('should return an error if trying to register with an existing username', async () => { - const existingUser = { - username: 'existinguser', - password: 'Test1234', - name: 'Existing', - surname: 'User' - }; + expect(response.body.username).toBe(newUser.username); - await User.create(existingUser); + // Check if the user exists in the database + const user = await User.findOne({ where: { username: newUser.username } }); + expect(user).toBeDefined(); - const response = await request(router) - .post('/add') - .send(existingUser); + // Check if the password is hashed + const isPasswordCorrect = await bcrypt.compare(newUser.password, user.password); + expect(isPasswordCorrect).toBe(true); - expect(response.status).toBe(400); - expect(response.body.error).toBe('An account with that username already exists'); + // Check if statistics for the user are created + const statistics = await Statistics.findOne({ where: { username: newUser.username } }); + expect(statistics).toBeDefined(); }); -}); +}); \ No newline at end of file diff --git a/users/models/user-model.js b/users/models/user-model.js index 5508be79..cdc19aff 100644 --- a/users/models/user-model.js +++ b/users/models/user-model.js @@ -1,18 +1,31 @@ const { Sequelize, DataTypes } = require('sequelize'); +// Function to create Sequelize instance with appropriate configurations +function createSequelizeInstance() { + if (process.env.NODE_ENV === 'test') { + // Use SQLite in-memory database for testing + return new Sequelize({ + dialect: 'sqlite', + storage: ':memory:' + }); + } else { + // Normal database configuration for production + return new Sequelize({ + host: 'mariadb', + username: 'root', + password: 'R#9aLp2sWu6y', + database: 'base_de_datos_de_usuarios', + port: 3306, + dialect: 'mariadb', + dialectOptions: { + connectTimeout: 20000 + } + }); + } +} // Database connection configuration -const sequelize = new Sequelize({ - host: 'mariadb', - username: 'root', - password: 'R#9aLp2sWu6y', - database: 'base_de_datos_de_usuarios', - port: 3306, - dialect: 'mariadb', - dialectOptions: { - connectTimeout: 20000 - } -}); +const sequelize = createSequelizeInstance(); // Define the user model const User = sequelize.define('User', { diff --git a/users/package-lock.json b/users/package-lock.json index c0ea546b..796db0b4 100644 --- a/users/package-lock.json +++ b/users/package-lock.json @@ -11,7 +11,6 @@ "dependencies": { "bcrypt": "^5.1.1", "body-parser": "^1.20.2", - "dotenv": "^16.4.5", "express": "^4.18.2", "express-session": "^1.18.0", "jose": "5.2.2", @@ -20,6 +19,7 @@ "sequelize": "^6.6.5" }, "devDependencies": { + "dotenv": "^16.4.5", "jest": "^29.7.0", "sqlite3": "^5.1.7", "supertest": "^6.3.4" @@ -2258,6 +2258,7 @@ "version": "16.4.5", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "dev": true, "engines": { "node": ">=12" }, diff --git a/users/package.json b/users/package.json index 6c703d52..ddc7c9dc 100644 --- a/users/package.json +++ b/users/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "start": "node index.js", - "test": "jest" + "test": "set NODE_ENV=test&& jest" }, "repository": { "type": "git", @@ -20,7 +20,6 @@ "dependencies": { "bcrypt": "^5.1.1", "body-parser": "^1.20.2", - "dotenv": "^16.4.5", "express": "^4.18.2", "express-session": "^1.18.0", "jose": "5.2.2", From cef32edbbd8d9747170b756a7a46be4c02bfd09e Mon Sep 17 00:00:00 2001 From: uo289689 Date: Wed, 27 Mar 2024 12:42:04 +0100 Subject: [PATCH 2/6] The new test case "Usuario already exists" has been added --- users/__tests/services/user-service.test.js | 27 +++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/users/__tests/services/user-service.test.js b/users/__tests/services/user-service.test.js index cff4dc55..426d58b9 100644 --- a/users/__tests/services/user-service.test.js +++ b/users/__tests/services/user-service.test.js @@ -46,4 +46,31 @@ describe('User Routes', () => { const statistics = await Statistics.findOne({ where: { username: newUser.username } }); expect(statistics).toBeDefined(); }); + + it('should not add a user if username already exists', async () => { + const existingUser = { + username: 'existinguser', + password: 'Test1234', + name: 'Existing', + surname: 'User' + }; + + // Create the existing user in the database + await User.create({ + username: existingUser.username, + password: await bcrypt.hash(existingUser.password, 10), + createdAt: new Date(), + updatedAt: new Date(), + name: existingUser.name, + surname: existingUser.surname + }); + + // Try to add the existing user again + const response = await request(app) + .post('/user/add') + .send(existingUser); + + expect(response.status).toBe(400); + expect(response.body.error).toBe('An account with that username already exists'); + }); }); \ No newline at end of file From b96adfa1531444ff69315d297f5c59f5f93ecc41 Mon Sep 17 00:00:00 2001 From: uo289689 Date: Wed, 27 Mar 2024 12:58:06 +0100 Subject: [PATCH 3/6] test package refactor --- .../{services/user-service.test.js => routes/user-routes.test.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename users/__tests/{services/user-service.test.js => routes/user-routes.test.js} (100%) diff --git a/users/__tests/services/user-service.test.js b/users/__tests/routes/user-routes.test.js similarity index 100% rename from users/__tests/services/user-service.test.js rename to users/__tests/routes/user-routes.test.js From 76931578a14eb16d1a8618b43c92603ff5986549 Mon Sep 17 00:00:00 2001 From: uo289689 Date: Wed, 27 Mar 2024 13:31:06 +0100 Subject: [PATCH 4/6] Fixing deplyment bug with NODE_ENV='test' --- .env | 2 +- gatewayservice/gateway-service.js | 1 + users/__tests/services/auth-service.test.js | 45 --------------------- users/package-lock.json | 32 +++++++++------ users/package.json | 4 +- users/routes/user-routes.js | 1 + 6 files changed, 25 insertions(+), 60 deletions(-) delete mode 100644 users/__tests/services/auth-service.test.js diff --git a/.env b/.env index 9937ee34..b2c6cd85 100644 --- a/.env +++ b/.env @@ -1,2 +1,2 @@ teamname="wiq_es04a" -NODE_ENV='test' +NODE_ENV='' diff --git a/gatewayservice/gateway-service.js b/gatewayservice/gateway-service.js index 1a4a323c..ba6d8015 100644 --- a/gatewayservice/gateway-service.js +++ b/gatewayservice/gateway-service.js @@ -40,6 +40,7 @@ app.post('/login', async (req, res) => { app.post('/user/add', async (req, res) => { try { + console.log(1); // Forward the add user request to the user service const userResponse = await axios.post(`${userServiceUrl}/user/add`, req.body); res.json(userResponse.data); diff --git a/users/__tests/services/auth-service.test.js b/users/__tests/services/auth-service.test.js deleted file mode 100644 index 7b55ee72..00000000 --- a/users/__tests/services/auth-service.test.js +++ /dev/null @@ -1,45 +0,0 @@ -const request = require('supertest'); -const { MongoMemoryServer } = require('mongodb-memory-server'); -const bcrypt = require('bcrypt'); -const User = require('./auth-model'); - -let mongoServer; -let app; - -//test user -const user = { - username: 'testuser', - password: 'testpassword', -}; - -async function addUser(user){ - const hashedPassword = await bcrypt.hash(user.password, 10); - const newUser = new User({ - username: user.username, - password: hashedPassword, - }); - - await newUser.save(); -} - -beforeAll(async () => { - mongoServer = await MongoMemoryServer.create(); - const mongoUri = mongoServer.getUri(); - process.env.MONGODB_URI = mongoUri; - app = require('./auth-service'); - //Load database with initial conditions - await addUser(user); -}); - -afterAll(async () => { - app.close(); - await mongoServer.stop(); -}); - -describe('Auth Service', () => { - it('Should perform a login operation /login', async () => { - const response = await request(app).post('/login').send(user); - expect(response.status).toBe(200); - expect(response.body).toHaveProperty('username', 'testuser'); - }); -}); diff --git a/users/package-lock.json b/users/package-lock.json index 796db0b4..09db4cb4 100644 --- a/users/package-lock.json +++ b/users/package-lock.json @@ -19,7 +19,7 @@ "sequelize": "^6.6.5" }, "devDependencies": { - "dotenv": "^16.4.5", + "cross-env": "^7.0.3", "jest": "^29.7.0", "sqlite3": "^5.1.7", "supertest": "^6.3.4" @@ -2094,6 +2094,24 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2254,18 +2272,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, "node_modules/dottie": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", diff --git a/users/package.json b/users/package.json index ddc7c9dc..150d2f16 100644 --- a/users/package.json +++ b/users/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "start": "node index.js", - "test": "set NODE_ENV=test&& jest" + "test": "cross-env NODE_ENV=test jest" }, "repository": { "type": "git", @@ -20,6 +20,7 @@ "dependencies": { "bcrypt": "^5.1.1", "body-parser": "^1.20.2", + "dotenv": "^16.4.5", "express": "^4.18.2", "express-session": "^1.18.0", "jose": "5.2.2", @@ -28,6 +29,7 @@ "sequelize": "^6.6.5" }, "devDependencies": { + "cross-env": "^7.0.3", "jest": "^29.7.0", "sqlite3": "^5.1.7", "supertest": "^6.3.4" diff --git a/users/routes/user-routes.js b/users/routes/user-routes.js index cccc43b8..062d9263 100644 --- a/users/routes/user-routes.js +++ b/users/routes/user-routes.js @@ -9,6 +9,7 @@ const apiRoutes = require('../services/user-api'); // Route for add a user router.post('/add', async (req, res) => { try { + console.log(1); const { username, password, name, surname } = req.body; const user = await User.findOne({ where: { username } }); From a306e4004e29553a25ffbf5145db6172e424d1c1 Mon Sep 17 00:00:00 2001 From: uo289689 Date: Wed, 27 Mar 2024 13:56:09 +0100 Subject: [PATCH 5/6] Added tests for statistics --- .../__tests/routes/statistics-routes.test.js | 87 +++++++++++++++++++ ...statics-routes.js => statistics-routes.js} | 0 2 files changed, 87 insertions(+) create mode 100644 users/__tests/routes/statistics-routes.test.js rename users/routes/{statics-routes.js => statistics-routes.js} (100%) diff --git a/users/__tests/routes/statistics-routes.test.js b/users/__tests/routes/statistics-routes.test.js new file mode 100644 index 00000000..1976fc48 --- /dev/null +++ b/users/__tests/routes/statistics-routes.test.js @@ -0,0 +1,87 @@ +const { Statistics, sequelize } = require('../../models/user-model.js'); +const request = require('supertest'); +const express = require('express'); +const bodyParser = require('body-parser'); +const statisticsRoutes = require('../../routes/statistics-routes.js'); +const { User } = require('../../models/user-model.js'); + +const app = express(); +app.use(bodyParser.json()); +app.use('/statistics', statisticsRoutes); + +describe('Statistics Routes', () => { + beforeAll(async () => { + await sequelize.authenticate(); + await sequelize.sync({ force: true }); + }); + + afterAll(async () => { + await sequelize.close(); + }); + + it('should update user statistics', async () => { + const newUser = { + username: 'testuser', + password: 'Test1234', + name: 'Test', + surname: 'User' + }; + + // Create user for the statistics + await User.create(newUser); + + const initialStatistics = { + username: 'testuser', + earned_money: 100, + classic_correctly_answered_questions: 5, + classic_incorrectly_answered_questions: 2, + classic_total_time_played: 3600, + classic_games_played: 3 + }; + + await Statistics.create(initialStatistics); + + const updatedStatistics = { + username: 'testuser', + earned_money: 50, + classic_correctly_answered_questions: 3, + classic_incorrectly_answered_questions: 1, + classic_total_time_played: 1800, + classic_games_played: 2 + }; + + const response = await request(app) + .post('/statistics/edit') + .send(updatedStatistics); + + expect(response.status).toBe(200); + expect(response.body.message).toBe('User statics updated successfully'); + + // Check if the statistics are updated in the database + const userStatistics = await Statistics.findOne({ where: { username: updatedStatistics.username } }); + + expect(userStatistics.earned_money).toBe(initialStatistics.earned_money + updatedStatistics.earned_money); + expect(userStatistics.classic_correctly_answered_questions).toBe(initialStatistics.classic_correctly_answered_questions + updatedStatistics.classic_correctly_answered_questions); + expect(userStatistics.classic_incorrectly_answered_questions).toBe(initialStatistics.classic_incorrectly_answered_questions + updatedStatistics.classic_incorrectly_answered_questions); + expect(userStatistics.classic_total_time_played).toBe(initialStatistics.classic_total_time_played + updatedStatistics.classic_total_time_played); + expect(userStatistics.classic_games_played).toBe(initialStatistics.classic_games_played + updatedStatistics.classic_games_played); + }); + + it('should return error if user not found', async () => { + const nonExistingUserStatistics = { + username: 'nonexistinguser', + earned_money: 50, + classic_correctly_answered_questions: 3, + classic_incorrectly_answered_questions: 1, + classic_total_time_played: 1800, + classic_games_played: 2 + }; + + const response = await request(app) + .post('/statistics/edit') + .send(nonExistingUserStatistics); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('User not found'); + }); +}); \ No newline at end of file diff --git a/users/routes/statics-routes.js b/users/routes/statistics-routes.js similarity index 100% rename from users/routes/statics-routes.js rename to users/routes/statistics-routes.js From e374b89b69d4eec85d201b70b3ac839558a72157 Mon Sep 17 00:00:00 2001 From: uo289689 Date: Wed, 27 Mar 2024 14:38:48 +0100 Subject: [PATCH 6/6] Added tests for the statistics API --- users/__tests/services/statistics-api.test.js | 53 +++++++++++++++++++ users/routes/statistics-routes.js | 2 +- .../{statics-api.js => statistics-api.js} | 0 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 users/__tests/services/statistics-api.test.js rename users/services/{statics-api.js => statistics-api.js} (100%) diff --git a/users/__tests/services/statistics-api.test.js b/users/__tests/services/statistics-api.test.js new file mode 100644 index 00000000..53da0fdf --- /dev/null +++ b/users/__tests/services/statistics-api.test.js @@ -0,0 +1,53 @@ +const { Statistics, sequelize } = require('../../models/user-model.js'); +const request = require('supertest'); +const express = require('express'); +const bodyParser = require('body-parser'); +const statisticsRoutes = require('../../routes/statistics-routes.js'); +const { User } = require('../../models/user-model.js'); + +const app = express(); +app.use(bodyParser.json()); +app.use('/statistics', statisticsRoutes); + +describe('Statistics Routes', () => { + beforeAll(async () => { + await sequelize.authenticate(); + await sequelize.sync({ force: true }); + }); + + afterAll(async () => { + await sequelize.close(); + }); + + it('should get user statistics by username', async () => { + const newUser = { + username: 'testuser', + password: 'Test1234', + name: 'Test', + surname: 'User' + }; + + // Create user for the statistics + await User.create(newUser); + + const initialStatistics = { + earned_money: 100, + classic_correctly_answered_questions: 5, + classic_incorrectly_answered_questions: 2, + classic_total_time_played: 3600, + classic_games_played: 3 + }; + + await Statistics.create({ username: newUser.username, ...initialStatistics }); + + const response = await request(app) + .get(`/statistics/api/${newUser.username}`); + + expect(response.status).toBe(200); + expect(response.body.earned_money).toEqual(initialStatistics.earned_money); + expect(response.body.classic_correctly_answered_questions).toEqual(initialStatistics.classic_correctly_answered_questions); + expect(response.body.classic_incorrectly_answered_questions).toEqual(initialStatistics.classic_incorrectly_answered_questions); + expect(response.body.classic_total_time_played).toEqual(initialStatistics.classic_total_time_played); + expect(response.body.classic_games_played).toEqual(initialStatistics.classic_games_played); + }); +}); \ No newline at end of file diff --git a/users/routes/statistics-routes.js b/users/routes/statistics-routes.js index fe67f5f4..14efc6cc 100644 --- a/users/routes/statistics-routes.js +++ b/users/routes/statistics-routes.js @@ -3,7 +3,7 @@ const router = express.Router(); const { Statistics } = require('../models/user-model'); //Group internal routes -const apiRoutes = require('../services/statics-api'); +const apiRoutes = require('../services/statistics-api'); // Route for edit the statics of a user router.post('/edit', async (req, res) => { diff --git a/users/services/statics-api.js b/users/services/statistics-api.js similarity index 100% rename from users/services/statics-api.js rename to users/services/statistics-api.js