diff --git a/.env b/.env index 9a0df33e..b2c6cd85 100644 --- a/.env +++ b/.env @@ -1 +1,2 @@ teamname="wiq_es04a" +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/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/__tests/routes/user-routes.test.js b/users/__tests/routes/user-routes.test.js new file mode 100644 index 00000000..426d58b9 --- /dev/null +++ b/users/__tests/routes/user-routes.test.js @@ -0,0 +1,76 @@ +const { User, Statistics, sequelize } = require('../../models/user-model.js'); +const bcrypt = require('bcrypt'); +const request = require('supertest'); +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(); + }); + + it('should add a new user', async () => { + const newUser = { + username: 'testuser', + password: 'Test1234', + name: 'Test', + surname: 'User' + }; + + const response = await request(app) + .post('/user/add') + .send(newUser); + + expect(response.status).toBe(200); + expect(response.body.username).toBe(newUser.username); + + // Check if the user exists in the database + const user = await User.findOne({ where: { username: newUser.username } }); + expect(user).toBeDefined(); + + // Check if the password is hashed + const isPasswordCorrect = await bcrypt.compare(newUser.password, user.password); + expect(isPasswordCorrect).toBe(true); + + // Check if statistics for the user are created + 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 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/__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/__tests/services/user-service.test.js b/users/__tests/services/user-service.test.js deleted file mode 100644 index 606a0965..00000000 --- a/users/__tests/services/user-service.test.js +++ /dev/null @@ -1,122 +0,0 @@ -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 }); -}); - -afterAll(async () => { - await sequelize.close(); -}); - -// tests -describe('User Registration API', () => { - it('should register a new user', async () => { - const userData = { - username: 'testuser', - password: 'Test1234', - name: 'Test', - surname: 'User' - }; - - const response = await request(router) - .post('/add') - .send(userData); - - 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' - }; - - await User.create(existingUser); - - const response = await request(router) - .post('/add') - .send(existingUser); - - expect(response.status).toBe(400); - expect(response.body.error).toBe('An account with that username already exists'); - }); -}); 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..09db4cb4 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": { + "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,17 +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==", - "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 6c703d52..150d2f16 100644 --- a/users/package.json +++ b/users/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "start": "node index.js", - "test": "jest" + "test": "cross-env NODE_ENV=test jest" }, "repository": { "type": "git", @@ -29,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/statics-routes.js b/users/routes/statistics-routes.js similarity index 97% rename from users/routes/statics-routes.js rename to users/routes/statistics-routes.js index fe67f5f4..14efc6cc 100644 --- a/users/routes/statics-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/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 } }); 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