mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
Merge pull request #147 from ets-cfuhrman-pfe/backend-tests
Backend tests and refactorings
This commit is contained in:
commit
b1e9489ba9
29 changed files with 1509 additions and 481 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
name: Frontend Tests
|
name: Tests
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
@ -9,7 +9,7 @@ on:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
frontend-test:
|
tests:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
@ -21,10 +21,12 @@ jobs:
|
||||||
with:
|
with:
|
||||||
node-version: '18'
|
node-version: '18'
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies and Run Tests
|
||||||
run: npm ci
|
run: |
|
||||||
working-directory: ./client
|
npm ci
|
||||||
|
npm test
|
||||||
|
working-directory: ${{ matrix.directory }}
|
||||||
|
|
||||||
- name: Run Tests
|
strategy:
|
||||||
run: npm test
|
matrix:
|
||||||
working-directory: ./client
|
directory: [client, server]
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -128,3 +128,4 @@ dist
|
||||||
.yarn/build-state.yml
|
.yarn/build-state.yml
|
||||||
.yarn/install-state.gz
|
.yarn/install-state.gz
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
db-backup/
|
||||||
|
|
|
||||||
|
|
@ -98,8 +98,9 @@ const Dashboard: React.FC = () => {
|
||||||
setQuizzes(quizzes as QuizType[]);
|
setQuizzes(quizzes as QuizType[]);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.log("show some quizes")
|
console.log("show some quizzes")
|
||||||
const folderQuizzes = await ApiService.getFolderContent(selectedFolder);
|
const folderQuizzes = await ApiService.getFolderContent(selectedFolder);
|
||||||
|
console.log("folderQuizzes: ", folderQuizzes);
|
||||||
setQuizzes(folderQuizzes as QuizType[]);
|
setQuizzes(folderQuizzes as QuizType[]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -147,7 +148,7 @@ const Dashboard: React.FC = () => {
|
||||||
setQuizzes(quizzes as QuizType[]);
|
setQuizzes(quizzes as QuizType[]);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.log("show some quizes")
|
console.log("show some quizzes")
|
||||||
const folderQuizzes = await ApiService.getFolderContent(selectedFolder);
|
const folderQuizzes = await ApiService.getFolderContent(selectedFolder);
|
||||||
setQuizzes(folderQuizzes as QuizType[]);
|
setQuizzes(folderQuizzes as QuizType[]);
|
||||||
|
|
||||||
|
|
@ -292,7 +293,7 @@ const Dashboard: React.FC = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
setQuizzes(quizzes as QuizType[]);
|
setQuizzes(quizzes as QuizType[]);
|
||||||
|
setSelectedFolder('');
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error deleting folder:', error);
|
console.error('Error deleting folder:', error);
|
||||||
|
|
@ -317,9 +318,11 @@ const Dashboard: React.FC = () => {
|
||||||
try {
|
try {
|
||||||
// folderId: string GET THIS FROM CURRENT FOLDER
|
// folderId: string GET THIS FROM CURRENT FOLDER
|
||||||
await ApiService.duplicateFolder(selectedFolder);
|
await ApiService.duplicateFolder(selectedFolder);
|
||||||
|
// TODO set the selected folder to be the duplicated folder
|
||||||
const userFolders = await ApiService.getUserFolders();
|
const userFolders = await ApiService.getUserFolders();
|
||||||
setFolders(userFolders as FolderType[]);
|
setFolders(userFolders as FolderType[]);
|
||||||
|
const newlyCreatedFolder = userFolders[userFolders.length - 1] as FolderType;
|
||||||
|
setSelectedFolder(newlyCreatedFolder._id);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error duplicating folder:', error);
|
console.error('Error duplicating folder:', error);
|
||||||
}
|
}
|
||||||
|
|
@ -401,7 +404,6 @@ const Dashboard: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='actions'>
|
<div className='actions'>
|
||||||
|
|
||||||
<Tooltip title="Ajouter dossier" placement="top">
|
<Tooltip title="Ajouter dossier" placement="top">
|
||||||
<IconButton
|
<IconButton
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|
@ -425,7 +427,7 @@ const Dashboard: React.FC = () => {
|
||||||
> <ContentCopy /> </IconButton>
|
> <ContentCopy /> </IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
<Tooltip title="Suprimer dossier" placement="top">
|
<Tooltip title="Supprimer dossier" placement="top">
|
||||||
<IconButton
|
<IconButton
|
||||||
aria-label="delete"
|
aria-label="delete"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|
|
||||||
8
server/__mocks__/AppError.js
Normal file
8
server/__mocks__/AppError.js
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
class AppError extends Error {
|
||||||
|
constructor(message, statusCode) {
|
||||||
|
super(message);
|
||||||
|
this.statusCode = statusCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = AppError;
|
||||||
6
server/__mocks__/bcrypt.js
Normal file
6
server/__mocks__/bcrypt.js
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
const mockBcrypt = {
|
||||||
|
hash: jest.fn().mockResolvedValue('hashedPassword'),
|
||||||
|
compare: jest.fn().mockResolvedValue(true),
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = mockBcrypt;
|
||||||
20
server/__mocks__/db.js
Normal file
20
server/__mocks__/db.js
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
class MockDBConnection {
|
||||||
|
constructor() {
|
||||||
|
this.db = jest.fn().mockReturnThis();
|
||||||
|
this.collection = jest.fn().mockReturnThis();
|
||||||
|
this.insertOne = jest.fn();
|
||||||
|
this.findOne = jest.fn();
|
||||||
|
this.updateOne = jest.fn();
|
||||||
|
this.deleteOne = jest.fn();
|
||||||
|
}
|
||||||
|
|
||||||
|
async connect() {
|
||||||
|
// Simulate successful connection
|
||||||
|
}
|
||||||
|
|
||||||
|
getConnection() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = MockDBConnection;
|
||||||
8
server/__mocks__/folders.js
Normal file
8
server/__mocks__/folders.js
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
const mockFolders = {
|
||||||
|
create: jest.fn(),
|
||||||
|
find: jest.fn(),
|
||||||
|
update: jest.fn(),
|
||||||
|
delete: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = mockFolders;
|
||||||
441
server/__tests__/folders.test.js
Normal file
441
server/__tests__/folders.test.js
Normal file
|
|
@ -0,0 +1,441 @@
|
||||||
|
const { create } = require('../middleware/jwtToken');
|
||||||
|
const Folders = require('../models/folders');
|
||||||
|
const ObjectId = require('mongodb').ObjectId;
|
||||||
|
const Quizzes = require('../models/quiz');
|
||||||
|
|
||||||
|
describe('Folders', () => {
|
||||||
|
let folders;
|
||||||
|
let db;
|
||||||
|
let collection;
|
||||||
|
let quizzes;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks(); // Clear any previous mock calls
|
||||||
|
|
||||||
|
// Mock the collection object
|
||||||
|
collection = {
|
||||||
|
findOne: jest.fn(),
|
||||||
|
insertOne: jest.fn(),
|
||||||
|
find: jest.fn().mockReturnValue({ toArray: jest.fn() }), // Mock the find method
|
||||||
|
deleteOne: jest.fn(),
|
||||||
|
deleteMany: jest.fn(),
|
||||||
|
updateOne: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mock the database connection
|
||||||
|
db = {
|
||||||
|
connect: jest.fn(),
|
||||||
|
getConnection: jest.fn().mockReturnThis(), // Add getConnection method
|
||||||
|
collection: jest.fn().mockReturnValue(collection),
|
||||||
|
};
|
||||||
|
|
||||||
|
quizzes = new Quizzes(db);
|
||||||
|
folders = new Folders(db, quizzes);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// create
|
||||||
|
describe('create', () => {
|
||||||
|
it('should create a new folder and return the new folder ID', async () => {
|
||||||
|
const title = 'Test Folder';
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.findOne.mockResolvedValue(null);
|
||||||
|
collection.insertOne.mockResolvedValue({ insertedId: new ObjectId() });
|
||||||
|
|
||||||
|
const result = await folders.create(title, '12345');
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||||
|
expect(collection.findOne).toHaveBeenCalledWith({ title, userId: '12345' });
|
||||||
|
expect(collection.insertOne).toHaveBeenCalledWith(expect.objectContaining({ title, userId: '12345' }));
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
// throw an error if userId is undefined
|
||||||
|
it('should throw an error if userId is undefined', async () => {
|
||||||
|
const title = 'Test Folder';
|
||||||
|
|
||||||
|
await expect(folders.create(title, undefined)).rejects.toThrow('Missing required parameter(s)');
|
||||||
|
|
||||||
|
expect(db.connect).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if the folder already exists', async () => {
|
||||||
|
const title = 'Existing Folder';
|
||||||
|
const userId = '66fc70bea1b9e87655cf17c9';
|
||||||
|
|
||||||
|
// Mock the database response of a found folder
|
||||||
|
collection.findOne.mockResolvedValue(
|
||||||
|
// real result from mongosh
|
||||||
|
{
|
||||||
|
_id: ObjectId.createFromHexString('66fd33fd81758a882ce99aae'),
|
||||||
|
userId: userId,
|
||||||
|
title: title,
|
||||||
|
created_at: new Date('2024-10-02T11:52:29.797Z')
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(folders.create(title, userId)).rejects.toThrow('Folder already exists');
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||||
|
expect(collection.findOne).toHaveBeenCalledWith({ title, userId: userId });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// getUserFolders
|
||||||
|
describe('getUserFolders', () => {
|
||||||
|
it('should return all folders for a user', async () => {
|
||||||
|
const userId = '12345';
|
||||||
|
const userFolders = [
|
||||||
|
{ title: 'Folder 1', userId },
|
||||||
|
{ title: 'Folder 2', userId },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.find().toArray.mockResolvedValue(userFolders);
|
||||||
|
|
||||||
|
const result = await folders.getUserFolders(userId);
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||||
|
expect(collection.find).toHaveBeenCalledWith({ userId });
|
||||||
|
expect(result).toEqual(userFolders);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// getOwner
|
||||||
|
describe('getOwner', () => {
|
||||||
|
it('should return the owner of a folder', async () => {
|
||||||
|
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
const userId = '12345';
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.findOne.mockResolvedValue({ userId });
|
||||||
|
|
||||||
|
const result = await folders.getOwner(folderId);
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||||
|
expect(collection.findOne).toHaveBeenCalledWith({ _id: new ObjectId(folderId) });
|
||||||
|
expect(result).toBe(userId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// write a test for getContent
|
||||||
|
describe('getContent', () => {
|
||||||
|
it('should return the content of a folder', async () => {
|
||||||
|
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
const content = [
|
||||||
|
{ title: 'Quiz 1', content: [] },
|
||||||
|
{ title: 'Quiz 2', content: [] },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.find().toArray.mockResolvedValue(content);
|
||||||
|
|
||||||
|
const result = await folders.getContent(folderId);
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.collection).toHaveBeenCalledWith('files');
|
||||||
|
expect(collection.find).toHaveBeenCalledWith({ folderId });
|
||||||
|
expect(result).toEqual(content);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an empty array if the folder has no content', async () => {
|
||||||
|
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.find().toArray.mockResolvedValue([]);
|
||||||
|
|
||||||
|
const result = await folders.getContent(folderId);
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.collection).toHaveBeenCalledWith('files');
|
||||||
|
expect(collection.find).toHaveBeenCalledWith({ folderId });
|
||||||
|
expect(result).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// delete
|
||||||
|
describe('delete', () => {
|
||||||
|
it('should delete a folder and return true', async () => {
|
||||||
|
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.deleteOne.mockResolvedValue({ deletedCount: 1 });
|
||||||
|
|
||||||
|
|
||||||
|
// Mock the folders.quizModel.deleteQuizzesByFolderId()
|
||||||
|
jest.spyOn(quizzes, 'deleteQuizzesByFolderId').mockResolvedValue(true);
|
||||||
|
|
||||||
|
const result = await folders.delete(folderId);
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||||
|
expect(collection.deleteOne).toHaveBeenCalledWith({ _id: new ObjectId(folderId) });
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if the folder does not exist', async () => {
|
||||||
|
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.deleteOne.mockResolvedValue({ deletedCount: 0 });
|
||||||
|
|
||||||
|
const result = await folders.delete(folderId);
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||||
|
expect(collection.deleteOne).toHaveBeenCalledWith({ _id: new ObjectId(folderId) });
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// rename
|
||||||
|
describe('rename', () => {
|
||||||
|
it('should rename a folder and return true', async () => {
|
||||||
|
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
const newTitle = 'New Folder Name';
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.updateOne.mockResolvedValue({ modifiedCount: 1 });
|
||||||
|
|
||||||
|
const result = await folders.rename(folderId, newTitle);
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||||
|
expect(collection.updateOne).toHaveBeenCalledWith({ _id: new ObjectId(folderId) }, { $set: { title: newTitle } });
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if the folder does not exist', async () => {
|
||||||
|
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
const newTitle = 'New Folder Name';
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.updateOne.mockResolvedValue({ modifiedCount: 0 });
|
||||||
|
|
||||||
|
const result = await folders.rename(folderId, newTitle);
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||||
|
expect(collection.updateOne).toHaveBeenCalledWith({ _id: new ObjectId(folderId) }, { $set: { title: newTitle } });
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// duplicate
|
||||||
|
describe('duplicate', () => {
|
||||||
|
it('should duplicate a folder and return the new folder ID', async () => {
|
||||||
|
const userId = '12345';
|
||||||
|
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
const sourceFolder = {title: 'SourceFolder', userId: userId, content: []};
|
||||||
|
const duplicatedFolder = {title: 'SourceFolder (1)', userId: userId, created_at: expect.any(Date), content: []};
|
||||||
|
|
||||||
|
// Mock the database responses for the folder and the new folder (first one is found, second one is null)
|
||||||
|
// mock the findOne method
|
||||||
|
jest.spyOn(collection, 'findOne')
|
||||||
|
.mockResolvedValueOnce(sourceFolder) // source file exists
|
||||||
|
.mockResolvedValueOnce(null); // new name is not found
|
||||||
|
|
||||||
|
// Mock the folder create method
|
||||||
|
const createSpy = jest.spyOn(folders, 'create').mockResolvedValue(new ObjectId());
|
||||||
|
|
||||||
|
// mock the folder.getContent method
|
||||||
|
jest.spyOn(folders, 'getContent').mockResolvedValue([{ title: 'Quiz 1', content: [] }]);
|
||||||
|
|
||||||
|
// Mock the quizzes.create method
|
||||||
|
jest.spyOn(quizzes, 'create').mockResolvedValue(new ObjectId());
|
||||||
|
|
||||||
|
const result = await folders.duplicate(folderId, userId);
|
||||||
|
|
||||||
|
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||||
|
|
||||||
|
// expect folders.create method was called
|
||||||
|
expect(createSpy).toHaveBeenCalledWith(duplicatedFolder.title, userId);
|
||||||
|
// expect the getContent method was called
|
||||||
|
expect(folders.getContent).toHaveBeenCalledWith(folderId);
|
||||||
|
// expect the quizzes.create method was called
|
||||||
|
expect(quizzes.create).toHaveBeenCalledWith('Quiz 1', [], expect.any(String), userId);
|
||||||
|
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if the folder does not exist', async () => {
|
||||||
|
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
|
||||||
|
// Mock the database response for the source
|
||||||
|
collection.findOne.mockResolvedValue(null);
|
||||||
|
|
||||||
|
await expect(folders.duplicate(folderId, '54321')).rejects.toThrow(`Folder ${folderId} not found`);
|
||||||
|
|
||||||
|
// expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||||
|
expect(collection.findOne).toHaveBeenCalledWith({ _id: new ObjectId(folderId), userId: '54321' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('folderExists', () => {
|
||||||
|
it('should return true if folder exists', async () => {
|
||||||
|
const title = 'Test Folder';
|
||||||
|
const userId = '12345';
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.findOne.mockResolvedValue({ title, userId });
|
||||||
|
|
||||||
|
const result = await folders.folderExists(title, userId);
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||||
|
expect(collection.findOne).toHaveBeenCalledWith({ title, userId });
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if folder does not exist', async () => {
|
||||||
|
const title = 'Nonexistent Folder';
|
||||||
|
const userId = '12345';
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.findOne.mockResolvedValue(null);
|
||||||
|
|
||||||
|
const result = await folders.folderExists(title, userId);
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||||
|
expect(collection.findOne).toHaveBeenCalledWith({ title, userId });
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('copy', () => {
|
||||||
|
it('should copy a folder and return the new folder ID', async () => {
|
||||||
|
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
const userId = '12345';
|
||||||
|
const newFolderId = new ObjectId();
|
||||||
|
// Mock some quizzes that are in folder.content
|
||||||
|
const sourceFolder = {
|
||||||
|
title: 'Test Folder',
|
||||||
|
content: [
|
||||||
|
{ title: 'Quiz 1', content: [] },
|
||||||
|
{ title: 'Quiz 2', content: [] },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mock the response from getFolderWithContent
|
||||||
|
jest.spyOn(folders, 'getFolderWithContent').mockResolvedValue(sourceFolder);
|
||||||
|
jest.spyOn(folders, 'create').mockResolvedValue(newFolderId);
|
||||||
|
// Mock the response from Quiz.createQuiz
|
||||||
|
jest.spyOn(quizzes, 'create').mockImplementation(() => {});
|
||||||
|
|
||||||
|
const result = await folders.copy(folderId, userId);
|
||||||
|
|
||||||
|
// expect(db.connect).toHaveBeenCalled();
|
||||||
|
// expect(db.collection).toHaveBeenCalledWith('folders');
|
||||||
|
// expect(collection.findOne).toHaveBeenCalledWith({ _id: new ObjectId(folderId) });
|
||||||
|
// expect(collection.insertOne).toHaveBeenCalledWith(expect.objectContaining({ userId }));
|
||||||
|
expect(result).toBe(newFolderId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if the folder does not exist', async () => {
|
||||||
|
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
const userId = '12345';
|
||||||
|
|
||||||
|
// Mock the response from getFolderWithContent
|
||||||
|
jest.spyOn(folders, 'getFolderWithContent').mockImplementation(() => {
|
||||||
|
throw new Error(`Folder ${folderId} not found`);
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(folders.copy(folderId, userId)).rejects.toThrow(`Folder ${folderId} not found`);
|
||||||
|
|
||||||
|
// expect(db.connect).toHaveBeenCalled();
|
||||||
|
// expect(db.collection).toHaveBeenCalledWith('folders');
|
||||||
|
// expect(collection.findOne).toHaveBeenCalledWith({ _id: new ObjectId(folderId) });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// write a test for getFolderWithContent
|
||||||
|
describe('getFolderWithContent', () => {
|
||||||
|
it('should return a folder with content', async () => {
|
||||||
|
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
const folder = {
|
||||||
|
_id: new ObjectId(folderId),
|
||||||
|
title: 'Test Folder',
|
||||||
|
};
|
||||||
|
const content = {
|
||||||
|
content : [
|
||||||
|
{ title: 'Quiz 1', content: [] },
|
||||||
|
{ title: 'Quiz 2', content: [] },
|
||||||
|
]};
|
||||||
|
|
||||||
|
// Mock the response from getFolderById
|
||||||
|
jest.spyOn(folders, 'getFolderById').mockResolvedValue(folder);
|
||||||
|
|
||||||
|
// Mock the response from getContent
|
||||||
|
jest.spyOn(folders, 'getContent').mockResolvedValue(content);
|
||||||
|
|
||||||
|
const result = await folders.getFolderWithContent(folderId);
|
||||||
|
|
||||||
|
// expect(db.connect).toHaveBeenCalled();
|
||||||
|
// expect(db.collection).toHaveBeenCalledWith('folders');
|
||||||
|
// expect(collection.findOne).toHaveBeenCalledWith({ _id: new ObjectId(folderId) });
|
||||||
|
expect(result).toEqual({
|
||||||
|
...folder,
|
||||||
|
content: content
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if the folder does not exist', async () => {
|
||||||
|
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
|
||||||
|
// // Mock the database response
|
||||||
|
// collection.findOne.mockResolvedValue(null);
|
||||||
|
|
||||||
|
// Mock getFolderById to throw an error
|
||||||
|
jest.spyOn(folders, 'getFolderById').mockImplementation(() => {
|
||||||
|
throw new Error(`Folder ${folderId} not found`);
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(folders.getFolderWithContent(folderId)).rejects.toThrow(`Folder ${folderId} not found`);
|
||||||
|
|
||||||
|
// expect(db.connect).toHaveBeenCalled();
|
||||||
|
// expect(db.collection).toHaveBeenCalledWith('folders');
|
||||||
|
// expect(collection.findOne).toHaveBeenCalledWith({ _id: new ObjectId(folderId) });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// write a test for getFolderById
|
||||||
|
describe('getFolderById', () => {
|
||||||
|
it('should return a folder by ID', async () => {
|
||||||
|
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
const folder = {
|
||||||
|
_id: new ObjectId(folderId),
|
||||||
|
title: 'Test Folder',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.findOne.mockResolvedValue(folder);
|
||||||
|
|
||||||
|
const result = await folders.getFolderById(folderId);
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||||
|
expect(collection.findOne).toHaveBeenCalledWith({ _id: new ObjectId(folderId) });
|
||||||
|
expect(result).toEqual(folder);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if the folder does not exist', async () => {
|
||||||
|
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.findOne.mockResolvedValue(null);
|
||||||
|
|
||||||
|
await expect(folders.getFolderById(folderId)).resolves.toThrow(`Folder ${folderId} not found`);
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||||
|
expect(collection.findOne).toHaveBeenCalledWith({ _id: new ObjectId(folderId) });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
const request = require('supertest');
|
// const request = require('supertest');
|
||||||
const app = require('../app.js');
|
// const app = require('../app.js');
|
||||||
// const app = require('../routers/images.js');
|
// // const app = require('../routers/images.js');
|
||||||
const { response } = require('express');
|
// const { response } = require('express');
|
||||||
|
|
||||||
const BASE_URL = '/image'
|
// const BASE_URL = '/image'
|
||||||
|
|
||||||
describe("POST /upload", () => {
|
describe.skip("POST /upload", () => {
|
||||||
|
|
||||||
describe("when the jwt is not sent", () => {
|
describe("when the jwt is not sent", () => {
|
||||||
|
|
||||||
|
|
@ -44,7 +44,7 @@ describe("POST /upload", () => {
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("GET /get", () => {
|
describe.skip("GET /get", () => {
|
||||||
|
|
||||||
describe("when not give id", () => {
|
describe("when not give id", () => {
|
||||||
|
|
||||||
|
|
|
||||||
347
server/__tests__/quizzes.test.js
Normal file
347
server/__tests__/quizzes.test.js
Normal file
|
|
@ -0,0 +1,347 @@
|
||||||
|
const { ObjectId } = require('mongodb');
|
||||||
|
const Quizzes = require('../models/quiz'); // Adjust the path as necessary
|
||||||
|
|
||||||
|
describe('Quizzes', () => {
|
||||||
|
let db;
|
||||||
|
let quizzes;
|
||||||
|
let collection;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks(); // Clear any previous mock calls
|
||||||
|
|
||||||
|
// Mock the collection object
|
||||||
|
collection = {
|
||||||
|
findOne: jest.fn(),
|
||||||
|
insertOne: jest.fn(),
|
||||||
|
find: jest.fn().mockReturnValue({ toArray: jest.fn() }), // Mock the find method
|
||||||
|
deleteOne: jest.fn(),
|
||||||
|
deleteMany: jest.fn(),
|
||||||
|
updateOne: jest.fn(),
|
||||||
|
getContent: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mock the database connection
|
||||||
|
db = {
|
||||||
|
connect: jest.fn(),
|
||||||
|
getConnection: jest.fn().mockReturnValue({
|
||||||
|
collection: jest.fn().mockReturnValue(collection),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize the Quiz model with the mocked db
|
||||||
|
quizzes = new Quizzes(db);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('create', () => {
|
||||||
|
it('should create a new quiz if it does not exist', async () => {
|
||||||
|
const title = 'Test Quiz';
|
||||||
|
const content = 'This is a test quiz.';
|
||||||
|
const folderId = '507f1f77bcf86cd799439011';
|
||||||
|
const userId = '12345';
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.findOne.mockResolvedValue(null);
|
||||||
|
collection.insertOne.mockResolvedValue({ insertedId: new ObjectId() });
|
||||||
|
|
||||||
|
const result = await quizzes.create(title, content, folderId, userId);
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.getConnection).toHaveBeenCalled();
|
||||||
|
expect(collection.findOne).toHaveBeenCalledWith({ title, folderId, userId });
|
||||||
|
expect(collection.insertOne).toHaveBeenCalledWith(expect.objectContaining({
|
||||||
|
folderId,
|
||||||
|
userId,
|
||||||
|
title,
|
||||||
|
content,
|
||||||
|
created_at: expect.any(Date),
|
||||||
|
updated_at: expect.any(Date),
|
||||||
|
}));
|
||||||
|
expect(result).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw exception if the quiz already exists', async () => {
|
||||||
|
const title = 'Test Quiz';
|
||||||
|
const content = 'This is a test quiz.';
|
||||||
|
const folderId = '507f1f77bcf86cd799439011';
|
||||||
|
const userId = '12345';
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.findOne.mockResolvedValue({ title });
|
||||||
|
|
||||||
|
await expect(quizzes.create(title, content, folderId, userId)).rejects.toThrow(`Quiz already exists with title: ${title}, folderId: ${folderId}, userId: ${userId}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getOwner', () => {
|
||||||
|
it('should return the owner of the quiz', async () => {
|
||||||
|
const quizId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
const userId = '12345';
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.findOne.mockResolvedValue({ userId });
|
||||||
|
|
||||||
|
const result = await quizzes.getOwner(quizId);
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.getConnection).toHaveBeenCalled();
|
||||||
|
expect(collection.findOne).toHaveBeenCalledWith({ _id: ObjectId.createFromHexString(quizId) });
|
||||||
|
expect(result).toBe(userId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getContent', () => {
|
||||||
|
it('should return the content of the quiz', async () => {
|
||||||
|
const quizId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
const content = 'This is a test quiz.';
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.findOne.mockResolvedValue({ content });
|
||||||
|
|
||||||
|
const result = await quizzes.getContent(quizId);
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.getConnection).toHaveBeenCalled();
|
||||||
|
expect(collection.findOne).toHaveBeenCalledWith({ _id: ObjectId.createFromHexString(quizId) });
|
||||||
|
expect(result).toEqual({ content });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('delete', () => {
|
||||||
|
it('should delete the quiz', async () => {
|
||||||
|
const quizId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.deleteOne.mockResolvedValue({deletedCount: 1});
|
||||||
|
|
||||||
|
await quizzes.delete(quizId);
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.getConnection).toHaveBeenCalled();
|
||||||
|
expect(collection.deleteOne).toHaveBeenCalledWith({ _id: ObjectId.createFromHexString(quizId) });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if the quiz does not exist', async () => {
|
||||||
|
const quizId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.deleteOne.mockResolvedValue({deletedCount: 0});
|
||||||
|
|
||||||
|
const result = await quizzes.delete(quizId);
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.getConnection).toHaveBeenCalled();
|
||||||
|
expect(collection.deleteOne).toHaveBeenCalledWith({ _id: ObjectId.createFromHexString(quizId) });
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// deleteQuizzesByFolderId
|
||||||
|
describe('deleteQuizzesByFolderId', () => {
|
||||||
|
it('should delete all quizzes in a folder', async () => {
|
||||||
|
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.deleteMany.mockResolvedValue({deletedCount: 2});
|
||||||
|
|
||||||
|
await quizzes.deleteQuizzesByFolderId(folderId);
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.getConnection).toHaveBeenCalled();
|
||||||
|
expect(collection.deleteMany).toHaveBeenCalledWith({ folderId });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if no quizzes are deleted', async () => {
|
||||||
|
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.deleteMany.mockResolvedValue({deletedCount: 0});
|
||||||
|
|
||||||
|
const result = await quizzes.deleteQuizzesByFolderId(folderId);
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.getConnection).toHaveBeenCalled();
|
||||||
|
expect(collection.deleteMany).toHaveBeenCalledWith({ folderId });
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// update
|
||||||
|
describe('update', () => {
|
||||||
|
it('should update the title and content of the quiz', async () => {
|
||||||
|
const quizId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
const newTitle = 'Updated Quiz';
|
||||||
|
const newContent = 'This is an updated quiz.';
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.updateOne.mockResolvedValue({modifiedCount: 1});
|
||||||
|
|
||||||
|
await quizzes.update(quizId, newTitle, newContent);
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.getConnection).toHaveBeenCalled();
|
||||||
|
expect(collection.updateOne).toHaveBeenCalledWith(
|
||||||
|
{ _id: ObjectId.createFromHexString(quizId) },
|
||||||
|
{ $set: { title: newTitle, content: newContent, updated_at: expect.any(Date) } }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if the quiz does not exist', async () => {
|
||||||
|
const quizId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
const newTitle = 'Updated Quiz';
|
||||||
|
const newContent = 'This is an updated quiz.';
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.updateOne.mockResolvedValue({modifiedCount: 0});
|
||||||
|
|
||||||
|
const result = await quizzes.update(quizId, newTitle, newContent);
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.getConnection).toHaveBeenCalled();
|
||||||
|
expect(collection.updateOne).toHaveBeenCalledWith(
|
||||||
|
{ _id: ObjectId.createFromHexString(quizId) },
|
||||||
|
{ $set: { title: newTitle, content: newContent, updated_at: expect.any(Date) } }
|
||||||
|
);
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// move
|
||||||
|
describe('move', () => {
|
||||||
|
it('should move the quiz to a new folder', async () => {
|
||||||
|
const quizId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
const newFolderId = '507f1f77bcf86cd799439011';
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.updateOne.mockResolvedValue({modifiedCount: 1});
|
||||||
|
|
||||||
|
await quizzes.move(quizId, newFolderId);
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.getConnection).toHaveBeenCalled();
|
||||||
|
expect(collection.updateOne).toHaveBeenCalledWith(
|
||||||
|
{ _id: ObjectId.createFromHexString(quizId) },
|
||||||
|
{ $set: { folderId: newFolderId } }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if the quiz does not exist', async () => {
|
||||||
|
const quizId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
const newFolderId = '507f1f77bcf86cd799439011';
|
||||||
|
|
||||||
|
// Mock the database response
|
||||||
|
collection.updateOne.mockResolvedValue({modifiedCount: 0});
|
||||||
|
|
||||||
|
const result = await quizzes.move(quizId, newFolderId);
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.getConnection).toHaveBeenCalled();
|
||||||
|
expect(collection.updateOne).toHaveBeenCalledWith(
|
||||||
|
{ _id: ObjectId.createFromHexString(quizId) },
|
||||||
|
{ $set: { folderId: newFolderId } }
|
||||||
|
);
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// duplicate
|
||||||
|
describe('duplicate', () => {
|
||||||
|
|
||||||
|
it('should duplicate the quiz and return the new quiz ID', async () => {
|
||||||
|
const quizId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
const userId = '12345';
|
||||||
|
const newQuizId = ObjectId.createFromTime(Math.floor(Date.now() / 1000)); // Corrected ObjectId creation
|
||||||
|
const sourceQuiz = {
|
||||||
|
title: 'Test Quiz',
|
||||||
|
content: 'This is a test quiz.',
|
||||||
|
};
|
||||||
|
|
||||||
|
const createMock = jest.spyOn(quizzes, 'create').mockResolvedValue(newQuizId);
|
||||||
|
// mock the findOne method
|
||||||
|
jest.spyOn(collection, 'findOne')
|
||||||
|
.mockResolvedValueOnce(sourceQuiz) // source quiz exists
|
||||||
|
.mockResolvedValueOnce(null); // new name is not found
|
||||||
|
|
||||||
|
const result = await quizzes.duplicate(quizId, userId);
|
||||||
|
|
||||||
|
expect(result).toBe(newQuizId);
|
||||||
|
|
||||||
|
// Ensure mocks were called correctly
|
||||||
|
expect(createMock).toHaveBeenCalledWith(
|
||||||
|
sourceQuiz.title + ' (1)',
|
||||||
|
sourceQuiz.content,
|
||||||
|
undefined,
|
||||||
|
userId
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add test case for quizExists (name with number in parentheses)
|
||||||
|
it('should create a new title if the quiz title already exists and ends with " (1)"', async () => {
|
||||||
|
const quizId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
const userId = '12345';
|
||||||
|
const newQuizId = ObjectId.createFromTime(Math.floor(Date.now() / 1000));
|
||||||
|
const sourceQuiz = {
|
||||||
|
title: 'Test Quiz (1)',
|
||||||
|
content: 'This is a test quiz.',
|
||||||
|
};
|
||||||
|
|
||||||
|
const createMock = jest.spyOn(quizzes, 'create').mockResolvedValue(newQuizId);
|
||||||
|
// mock the findOne method
|
||||||
|
jest.spyOn(collection, 'findOne')
|
||||||
|
.mockResolvedValueOnce(sourceQuiz) // source quiz exists
|
||||||
|
.mockResolvedValueOnce(null); // new name is not found
|
||||||
|
|
||||||
|
const result = await quizzes.duplicate(quizId, userId);
|
||||||
|
|
||||||
|
expect(result).toBe(newQuizId);
|
||||||
|
|
||||||
|
// Ensure mocks were called correctly
|
||||||
|
expect(createMock).toHaveBeenCalledWith(
|
||||||
|
'Test Quiz (2)',
|
||||||
|
sourceQuiz.content,
|
||||||
|
undefined,
|
||||||
|
userId
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// test case for duplication of "C (1)" but "C (2)" already exists, so it should create "C (3)"
|
||||||
|
it('should create a new title if the quiz title already exists and ends with " (n)" but the incremented n also exists', async () => {
|
||||||
|
const quizId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
const userId = '12345';
|
||||||
|
const newQuizId = ObjectId.createFromTime(Math.floor(Date.now() / 1000));
|
||||||
|
const sourceQuiz = {
|
||||||
|
title: 'Test Quiz (1)',
|
||||||
|
content: 'This is a test quiz.',
|
||||||
|
};
|
||||||
|
|
||||||
|
const createMock = jest.spyOn(quizzes, 'create').mockResolvedValue(newQuizId);
|
||||||
|
|
||||||
|
// mock the findOne method
|
||||||
|
jest.spyOn(collection, 'findOne')
|
||||||
|
.mockResolvedValueOnce(sourceQuiz) // source quiz exists
|
||||||
|
.mockResolvedValueOnce({ title: 'Test Quiz (2)' }) // new name collision
|
||||||
|
.mockResolvedValueOnce(null); // final new name is not found
|
||||||
|
|
||||||
|
const result = await quizzes.duplicate(quizId, userId);
|
||||||
|
|
||||||
|
expect(result).toBe(newQuizId);
|
||||||
|
|
||||||
|
// Ensure mocks were called correctly
|
||||||
|
expect(createMock).toHaveBeenCalledWith(
|
||||||
|
'Test Quiz (3)',
|
||||||
|
sourceQuiz.content,
|
||||||
|
undefined,
|
||||||
|
userId
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if the quiz does not exist', async () => {
|
||||||
|
const quizId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||||
|
const userId = '12345';
|
||||||
|
|
||||||
|
// Mock the response from getContent
|
||||||
|
jest.spyOn(quizzes, 'getContent').mockResolvedValue(null);
|
||||||
|
|
||||||
|
await expect(quizzes.duplicate(quizId, userId)).rejects.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -5,7 +5,8 @@ const { setupWebsocket } = require("../socket/socket");
|
||||||
|
|
||||||
process.env.NODE_ENV = "test";
|
process.env.NODE_ENV = "test";
|
||||||
|
|
||||||
const BACKEND_PORT = 4400;
|
// pick a random port number for testing
|
||||||
|
const BACKEND_PORT = Math.ceil(Math.random() * 1000 + 3000);
|
||||||
const BACKEND_URL = "http://localhost";
|
const BACKEND_URL = "http://localhost";
|
||||||
|
|
||||||
const BACKEND_API = `${BACKEND_URL}:${BACKEND_PORT}`;
|
const BACKEND_API = `${BACKEND_URL}:${BACKEND_PORT}`;
|
||||||
|
|
|
||||||
86
server/__tests__/users.test.js
Normal file
86
server/__tests__/users.test.js
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
const Users = require('../models/users');
|
||||||
|
const bcrypt = require('bcrypt');
|
||||||
|
const Quizzes = require('../models/quiz');
|
||||||
|
const Folders = require('../models/folders');
|
||||||
|
const AppError = require('../middleware/AppError');
|
||||||
|
const { ObjectId } = require('mongodb');
|
||||||
|
|
||||||
|
jest.mock('bcrypt');
|
||||||
|
jest.mock('../middleware/AppError');
|
||||||
|
jest.mock('../models/folders');
|
||||||
|
|
||||||
|
describe('Users', () => {
|
||||||
|
let users;
|
||||||
|
let db;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks(); // Clear any previous mock calls
|
||||||
|
|
||||||
|
// Mock the database connection
|
||||||
|
db = {
|
||||||
|
connect: jest.fn(),
|
||||||
|
getConnection: jest.fn().mockReturnThis(), // Add getConnection method
|
||||||
|
collection: jest.fn().mockReturnThis(),
|
||||||
|
findOne: jest.fn(),
|
||||||
|
insertOne: jest.fn().mockResolvedValue({ insertedId: new ObjectId() }), // Mock insertOne to return an ObjectId
|
||||||
|
updateOne: jest.fn(),
|
||||||
|
deleteOne: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const quizModel = new Quizzes(db);
|
||||||
|
const foldersModel = new Folders(db, quizModel);
|
||||||
|
|
||||||
|
users = new Users(db, foldersModel);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should register a new user', async () => {
|
||||||
|
db.collection().findOne.mockResolvedValue(null); // No user found
|
||||||
|
db.collection().insertOne.mockResolvedValue({ insertedId: new ObjectId() });
|
||||||
|
bcrypt.hash.mockResolvedValue('hashedPassword');
|
||||||
|
users.folders.create.mockResolvedValue(true);
|
||||||
|
|
||||||
|
const email = 'test@example.com';
|
||||||
|
const password = 'password123';
|
||||||
|
const result = await users.register(email, password);
|
||||||
|
|
||||||
|
expect(db.connect).toHaveBeenCalled();
|
||||||
|
expect(db.collection().findOne).toHaveBeenCalledWith({ email });
|
||||||
|
expect(bcrypt.hash).toHaveBeenCalledWith(password, 10);
|
||||||
|
expect(db.collection().insertOne).toHaveBeenCalledWith({
|
||||||
|
email,
|
||||||
|
password: 'hashedPassword',
|
||||||
|
created_at: expect.any(Date),
|
||||||
|
});
|
||||||
|
expect(users.folders.create).toHaveBeenCalledWith('Dossier par Défaut', expect.any(String));
|
||||||
|
expect(result.insertedId).toBeDefined(); // Ensure result has insertedId
|
||||||
|
});
|
||||||
|
|
||||||
|
// it('should update the user password', async () => {
|
||||||
|
// db.collection().updateOne.mockResolvedValue({ modifiedCount: 1 });
|
||||||
|
// bcrypt.hash.mockResolvedValue('hashedPassword');
|
||||||
|
|
||||||
|
// const email = 'test@example.com';
|
||||||
|
// const newPassword = 'newPassword123';
|
||||||
|
// const result = await users.updatePassword(email, newPassword);
|
||||||
|
|
||||||
|
// expect(db.connect).toHaveBeenCalled();
|
||||||
|
// expect(db.collection().updateOne).toHaveBeenCalledWith(
|
||||||
|
// { email },
|
||||||
|
// { $set: { password: 'hashedPassword' } }
|
||||||
|
// );
|
||||||
|
// expect(result).toEqual(newPassword);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// it('should delete a user', async () => {
|
||||||
|
// db.collection().deleteOne.mockResolvedValue({ deletedCount: 1 });
|
||||||
|
|
||||||
|
// const email = 'test@example.com';
|
||||||
|
// const result = await users.delete(email);
|
||||||
|
|
||||||
|
// expect(db.connect).toHaveBeenCalled();
|
||||||
|
// expect(db.collection().deleteOne).toHaveBeenCalledWith({ email });
|
||||||
|
// expect(result).toBe(true);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// Add more tests as needed
|
||||||
|
});
|
||||||
|
|
@ -7,7 +7,35 @@ const dotenv = require('dotenv')
|
||||||
const { setupWebsocket } = require("./socket/socket");
|
const { setupWebsocket } = require("./socket/socket");
|
||||||
const { Server } = require("socket.io");
|
const { Server } = require("socket.io");
|
||||||
|
|
||||||
//import routers
|
// instantiate the db
|
||||||
|
const db = require('./config/db.js');
|
||||||
|
// instantiate the models
|
||||||
|
const quiz = require('./models/quiz.js');
|
||||||
|
const quizModel = new quiz(db);
|
||||||
|
const folders = require('./models/folders.js');
|
||||||
|
const foldersModel = new folders(db, quizModel);
|
||||||
|
const users = require('./models/users.js');
|
||||||
|
const userModel = new users(db, foldersModel);
|
||||||
|
const images = require('./models/images.js');
|
||||||
|
const imageModel = new images(db);
|
||||||
|
|
||||||
|
// instantiate the controllers
|
||||||
|
const usersController = require('./controllers/users.js');
|
||||||
|
const usersControllerInstance = new usersController(userModel);
|
||||||
|
const foldersController = require('./controllers/folders.js');
|
||||||
|
const foldersControllerInstance = new foldersController(foldersModel);
|
||||||
|
const quizController = require('./controllers/quiz.js');
|
||||||
|
const quizControllerInstance = new quizController(quizModel, foldersModel);
|
||||||
|
const imagesController = require('./controllers/images.js');
|
||||||
|
const imagesControllerInstance = new imagesController(imageModel);
|
||||||
|
|
||||||
|
// export the controllers
|
||||||
|
module.exports.users = usersControllerInstance;
|
||||||
|
module.exports.folders = foldersControllerInstance;
|
||||||
|
module.exports.quizzes = quizControllerInstance;
|
||||||
|
module.exports.images = imagesControllerInstance;
|
||||||
|
|
||||||
|
//import routers (instantiate controllers as side effect)
|
||||||
const userRouter = require('./routers/users.js');
|
const userRouter = require('./routers/users.js');
|
||||||
const folderRouter = require('./routers/folders.js');
|
const folderRouter = require('./routers/folders.js');
|
||||||
const quizRouter = require('./routers/quiz.js');
|
const quizRouter = require('./routers/quiz.js');
|
||||||
|
|
@ -15,7 +43,6 @@ const imagesRouter = require('./routers/images.js')
|
||||||
|
|
||||||
// Setup environement
|
// Setup environement
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
const db = require('./config/db.js');
|
|
||||||
const errorHandler = require("./middleware/errorHandler.js");
|
const errorHandler = require("./middleware/errorHandler.js");
|
||||||
|
|
||||||
// Start app
|
// Start app
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,18 @@
|
||||||
//controller
|
//controller
|
||||||
const model = require('../models/folders.js');
|
|
||||||
|
|
||||||
const AppError = require('../middleware/AppError.js');
|
const AppError = require('../middleware/AppError.js');
|
||||||
const { MISSING_REQUIRED_PARAMETER, NOT_IMPLEMENTED, FOLDER_NOT_FOUND, FOLDER_ALREADY_EXISTS, GETTING_FOLDER_ERROR, DELETE_FOLDER_ERROR, UPDATE_FOLDER_ERROR, MOVING_FOLDER_ERROR, DUPLICATE_FOLDER_ERROR, COPY_FOLDER_ERROR } = require('../constants/errorCodes');
|
const { MISSING_REQUIRED_PARAMETER, NOT_IMPLEMENTED, FOLDER_NOT_FOUND, FOLDER_ALREADY_EXISTS, GETTING_FOLDER_ERROR, DELETE_FOLDER_ERROR, UPDATE_FOLDER_ERROR, MOVING_FOLDER_ERROR, DUPLICATE_FOLDER_ERROR, COPY_FOLDER_ERROR } = require('../constants/errorCodes');
|
||||||
|
|
||||||
|
// controllers must use arrow functions to bind 'this' to the class instance in order to access class properties as callbacks in Express
|
||||||
class FoldersController {
|
class FoldersController {
|
||||||
|
|
||||||
|
constructor(foldersModel) {
|
||||||
|
this.folders = foldersModel;
|
||||||
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Basic queries
|
* Basic queries
|
||||||
*/
|
*/
|
||||||
async create(req, res, next) {
|
create = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { title } = req.body;
|
const { title } = req.body;
|
||||||
|
|
||||||
|
|
@ -17,7 +20,7 @@ class FoldersController {
|
||||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await model.create(title, req.user.userId);
|
const result = await this.folders.create(title, req.user.userId);
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
throw new AppError(FOLDER_ALREADY_EXISTS);
|
throw new AppError(FOLDER_ALREADY_EXISTS);
|
||||||
|
|
@ -27,16 +30,14 @@ class FoldersController {
|
||||||
message: 'Dossier créé avec succès.'
|
message: 'Dossier créé avec succès.'
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
return next(error);
|
return next(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getUserFolders(req, res, next) {
|
getUserFolders = async (req, res, next) => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const folders = await model.getUserFolders(req.user.userId);
|
const folders = await this.folders.getUserFolders(req.user.userId);
|
||||||
|
|
||||||
if (!folders) {
|
if (!folders) {
|
||||||
throw new AppError(FOLDER_NOT_FOUND);
|
throw new AppError(FOLDER_NOT_FOUND);
|
||||||
|
|
@ -46,13 +47,12 @@ class FoldersController {
|
||||||
data: folders
|
data: folders
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
return next(error);
|
return next(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getFolderContent(req, res, next) {
|
getFolderContent = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { folderId } = req.params;
|
const { folderId } = req.params;
|
||||||
|
|
||||||
|
|
@ -61,13 +61,13 @@ class FoldersController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is this folder mine
|
// Is this folder mine
|
||||||
const owner = await model.getOwner(folderId);
|
const owner = await this.folders.getOwner(folderId);
|
||||||
|
|
||||||
if (owner != req.user.userId) {
|
if (owner != req.user.userId) {
|
||||||
throw new AppError(FOLDER_NOT_FOUND);
|
throw new AppError(FOLDER_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = await model.getContent(folderId);
|
const content = await this.folders.getContent(folderId);
|
||||||
|
|
||||||
if (!content) {
|
if (!content) {
|
||||||
throw new AppError(GETTING_FOLDER_ERROR);
|
throw new AppError(GETTING_FOLDER_ERROR);
|
||||||
|
|
@ -77,13 +77,12 @@ class FoldersController {
|
||||||
data: content
|
data: content
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
return next(error);
|
return next(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(req, res, next) {
|
delete = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { folderId } = req.params;
|
const { folderId } = req.params;
|
||||||
|
|
||||||
|
|
@ -92,13 +91,13 @@ class FoldersController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is this folder mine
|
// Is this folder mine
|
||||||
const owner = await model.getOwner(folderId);
|
const owner = await this.folders.getOwner(folderId);
|
||||||
|
|
||||||
if (owner != req.user.userId) {
|
if (owner != req.user.userId) {
|
||||||
throw new AppError(FOLDER_NOT_FOUND);
|
throw new AppError(FOLDER_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await model.delete(folderId);
|
const result = await this.folders.delete(folderId);
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
throw new AppError(DELETE_FOLDER_ERROR);
|
throw new AppError(DELETE_FOLDER_ERROR);
|
||||||
|
|
@ -108,13 +107,12 @@ class FoldersController {
|
||||||
message: 'Dossier supprimé avec succès.'
|
message: 'Dossier supprimé avec succès.'
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
return next(error);
|
return next(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async rename(req, res, next) {
|
rename = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { folderId, newTitle } = req.body;
|
const { folderId, newTitle } = req.body;
|
||||||
|
|
||||||
|
|
@ -123,13 +121,13 @@ class FoldersController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is this folder mine
|
// Is this folder mine
|
||||||
const owner = await model.getOwner(folderId);
|
const owner = await this.folders.getOwner(folderId);
|
||||||
|
|
||||||
if (owner != req.user.userId) {
|
if (owner != req.user.userId) {
|
||||||
throw new AppError(FOLDER_NOT_FOUND);
|
throw new AppError(FOLDER_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await model.rename(folderId, newTitle);
|
const result = await this.folders.rename(folderId, newTitle);
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
throw new AppError(UPDATE_FOLDER_ERROR);
|
throw new AppError(UPDATE_FOLDER_ERROR);
|
||||||
|
|
@ -139,23 +137,21 @@ class FoldersController {
|
||||||
message: 'Dossier mis à jours avec succès.'
|
message: 'Dossier mis à jours avec succès.'
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
return next(error);
|
return next(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
duplicate = async (req, res, next) => {
|
||||||
async duplicate(req, res, next) {
|
|
||||||
try {
|
try {
|
||||||
const { folderId, } = req.body;
|
const { folderId } = req.body;
|
||||||
|
|
||||||
if (!folderId) {
|
if (!folderId) {
|
||||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is this folder mine
|
// Is this folder mine
|
||||||
const owner = await model.getOwner(folderId);
|
const owner = await this.folders.getOwner(folderId);
|
||||||
|
|
||||||
if (owner != req.user.userId) {
|
if (owner != req.user.userId) {
|
||||||
throw new AppError(FOLDER_NOT_FOUND);
|
throw new AppError(FOLDER_NOT_FOUND);
|
||||||
|
|
@ -163,7 +159,7 @@ class FoldersController {
|
||||||
|
|
||||||
const userId = req.user.userId;
|
const userId = req.user.userId;
|
||||||
|
|
||||||
const newFolderId = await model.duplicate(folderId, userId);
|
const newFolderId = await this.folders.duplicate(folderId, userId);
|
||||||
|
|
||||||
if (!newFolderId) {
|
if (!newFolderId) {
|
||||||
throw new AppError(DUPLICATE_FOLDER_ERROR);
|
throw new AppError(DUPLICATE_FOLDER_ERROR);
|
||||||
|
|
@ -178,7 +174,7 @@ class FoldersController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async copy(req, res, next) {
|
copy = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { folderId, newTitle } = req.body;
|
const { folderId, newTitle } = req.body;
|
||||||
|
|
||||||
|
|
@ -187,7 +183,7 @@ class FoldersController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is this folder mine
|
// Is this folder mine
|
||||||
const owner = await model.getOwner(folderId);
|
const owner = await this.folders.getOwner(folderId);
|
||||||
|
|
||||||
if (owner != req.user.userId) {
|
if (owner != req.user.userId) {
|
||||||
throw new AppError(FOLDER_NOT_FOUND);
|
throw new AppError(FOLDER_NOT_FOUND);
|
||||||
|
|
@ -195,7 +191,7 @@ class FoldersController {
|
||||||
|
|
||||||
const userId = req.user.userId; // Assuming userId is obtained from authentication
|
const userId = req.user.userId; // Assuming userId is obtained from authentication
|
||||||
|
|
||||||
const newFolderId = await model.copy(folderId, userId);
|
const newFolderId = await this.folders.copy(folderId, userId);
|
||||||
|
|
||||||
if (!newFolderId) {
|
if (!newFolderId) {
|
||||||
throw new AppError(COPY_FOLDER_ERROR);
|
throw new AppError(COPY_FOLDER_ERROR);
|
||||||
|
|
@ -210,7 +206,7 @@ class FoldersController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getFolderById(req, res, next) {
|
getFolderById = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { folderId } = req.params;
|
const { folderId } = req.params;
|
||||||
|
|
||||||
|
|
@ -219,13 +215,13 @@ class FoldersController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is this folder mine
|
// Is this folder mine
|
||||||
const owner = await model.getOwner(folderId);
|
const owner = await this.folders.getOwner(folderId);
|
||||||
|
|
||||||
if (owner != req.user.userId) {
|
if (owner != req.user.userId) {
|
||||||
throw new AppError(FOLDER_NOT_FOUND);
|
throw new AppError(FOLDER_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
const folder = await model.getFolderById(folderId);
|
const folder = await this.folders.getFolderById(folderId);
|
||||||
|
|
||||||
if (!folder) {
|
if (!folder) {
|
||||||
throw new AppError(FOLDER_NOT_FOUND);
|
throw new AppError(FOLDER_NOT_FOUND);
|
||||||
|
|
@ -239,7 +235,7 @@ class FoldersController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async folderExists(req, res, next) {
|
folderExists = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { title } = req.body;
|
const { title } = req.body;
|
||||||
|
|
||||||
|
|
@ -250,7 +246,7 @@ class FoldersController {
|
||||||
const userId = req.user.userId;
|
const userId = req.user.userId;
|
||||||
|
|
||||||
// Vérifie si le dossier existe pour l'utilisateur donné
|
// Vérifie si le dossier existe pour l'utilisateur donné
|
||||||
const exists = await model.folderExists(title, userId);
|
const exists = await this.folders.folderExists(title, userId);
|
||||||
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
exists: exists
|
exists: exists
|
||||||
|
|
@ -260,9 +256,8 @@ class FoldersController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = new FoldersController;
|
module.exports = FoldersController;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
const model = require('../models/images.js');
|
|
||||||
|
|
||||||
const AppError = require('../middleware/AppError.js');
|
const AppError = require('../middleware/AppError.js');
|
||||||
const { MISSING_REQUIRED_PARAMETER, IMAGE_NOT_FOUND } = require('../constants/errorCodes');
|
const { MISSING_REQUIRED_PARAMETER, IMAGE_NOT_FOUND } = require('../constants/errorCodes');
|
||||||
|
|
||||||
class ImagesController {
|
class ImagesController {
|
||||||
|
|
||||||
async upload(req, res, next) {
|
constructor(imagesModel) {
|
||||||
|
this.images = imagesModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
upload = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const file = req.file;
|
const file = req.file;
|
||||||
|
|
||||||
|
|
@ -13,19 +15,17 @@ class ImagesController {
|
||||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = await model.upload(file, req.user.userId);
|
const id = await this.images.upload(file, req.user.userId);
|
||||||
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
id: id
|
id: id
|
||||||
});
|
});
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
return next(error);
|
return next(error);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
get = async (req, res, next) => {
|
||||||
|
|
||||||
async get(req, res, next) {
|
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
|
||||||
|
|
@ -33,10 +33,10 @@ class ImagesController {
|
||||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||||
}
|
}
|
||||||
|
|
||||||
const image = await model.get(id);
|
const image = await this.images.get(id);
|
||||||
|
|
||||||
if (!image) {
|
if (!image) {
|
||||||
throw new AppError(IMAGE_NOT_FOUND)
|
throw new AppError(IMAGE_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set Headers for display in browser
|
// Set Headers for display in browser
|
||||||
|
|
@ -45,12 +45,11 @@ class ImagesController {
|
||||||
res.setHeader('Accept-Ranges', 'bytes');
|
res.setHeader('Accept-Ranges', 'bytes');
|
||||||
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
|
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
|
||||||
return res.send(image.file_content);
|
return res.send(image.file_content);
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
return next(error);
|
return next(error);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = new ImagesController;
|
module.exports = ImagesController;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
const model = require('../models/quiz.js');
|
|
||||||
const folderModel = require('../models/folders.js');
|
|
||||||
const emailer = require('../config/email.js');
|
const emailer = require('../config/email.js');
|
||||||
|
|
||||||
const AppError = require('../middleware/AppError.js');
|
const AppError = require('../middleware/AppError.js');
|
||||||
|
|
@ -7,7 +5,12 @@ const { MISSING_REQUIRED_PARAMETER, NOT_IMPLEMENTED, QUIZ_NOT_FOUND, FOLDER_NOT_
|
||||||
|
|
||||||
class QuizController {
|
class QuizController {
|
||||||
|
|
||||||
async create(req, res, next) {
|
constructor(quizModel, foldersModel) {
|
||||||
|
this.folders = foldersModel;
|
||||||
|
this.quizzes = quizModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
create = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { title, content, folderId } = req.body;
|
const { title, content, folderId } = req.body;
|
||||||
|
|
||||||
|
|
@ -16,13 +19,13 @@ class QuizController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is this folder mine
|
// Is this folder mine
|
||||||
const owner = await folderModel.getOwner(folderId);
|
const owner = await this.folders.getOwner(folderId);
|
||||||
|
|
||||||
if (owner != req.user.userId) {
|
if (owner != req.user.userId) {
|
||||||
throw new AppError(FOLDER_NOT_FOUND);
|
throw new AppError(FOLDER_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await model.create(title, content, folderId, req.user.userId);
|
const result = await this.quizzes.create(title, content, folderId, req.user.userId);
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
throw new AppError(QUIZ_ALREADY_EXISTS);
|
throw new AppError(QUIZ_ALREADY_EXISTS);
|
||||||
|
|
@ -32,13 +35,12 @@ class QuizController {
|
||||||
message: 'Quiz créé avec succès.'
|
message: 'Quiz créé avec succès.'
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
return next(error);
|
return next(error);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
async get(req, res, next) {
|
get = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { quizId } = req.params;
|
const { quizId } = req.params;
|
||||||
|
|
||||||
|
|
@ -46,8 +48,7 @@ class QuizController {
|
||||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const content = await this.quizzes.getContent(quizId);
|
||||||
const content = await model.getContent(quizId);
|
|
||||||
|
|
||||||
if (!content) {
|
if (!content) {
|
||||||
throw new AppError(GETTING_QUIZ_ERROR);
|
throw new AppError(GETTING_QUIZ_ERROR);
|
||||||
|
|
@ -62,13 +63,12 @@ class QuizController {
|
||||||
data: content
|
data: content
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
return next(error);
|
return next(error);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
async delete(req, res, next) {
|
delete = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { quizId } = req.params;
|
const { quizId } = req.params;
|
||||||
|
|
||||||
|
|
@ -77,13 +77,13 @@ class QuizController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is this quiz mine
|
// Is this quiz mine
|
||||||
const owner = await model.getOwner(quizId);
|
const owner = await this.quizzes.getOwner(quizId);
|
||||||
|
|
||||||
if (owner != req.user.userId) {
|
if (owner != req.user.userId) {
|
||||||
throw new AppError(QUIZ_NOT_FOUND);
|
throw new AppError(QUIZ_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await model.delete(quizId);
|
const result = await this.quizzes.delete(quizId);
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
throw new AppError(DELETE_QUIZ_ERROR);
|
throw new AppError(DELETE_QUIZ_ERROR);
|
||||||
|
|
@ -93,13 +93,12 @@ class QuizController {
|
||||||
message: 'Quiz supprimé avec succès.'
|
message: 'Quiz supprimé avec succès.'
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
return next(error);
|
return next(error);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
async update(req, res, next) {
|
update = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { quizId, newTitle, newContent } = req.body;
|
const { quizId, newTitle, newContent } = req.body;
|
||||||
|
|
||||||
|
|
@ -108,13 +107,13 @@ class QuizController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is this quiz mine
|
// Is this quiz mine
|
||||||
const owner = await model.getOwner(quizId);
|
const owner = await this.quizzes.getOwner(quizId);
|
||||||
|
|
||||||
if (owner != req.user.userId) {
|
if (owner != req.user.userId) {
|
||||||
throw new AppError(QUIZ_NOT_FOUND);
|
throw new AppError(QUIZ_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await model.update(quizId, newTitle, newContent);
|
const result = await this.quizzes.update(quizId, newTitle, newContent);
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
throw new AppError(UPDATE_QUIZ_ERROR);
|
throw new AppError(UPDATE_QUIZ_ERROR);
|
||||||
|
|
@ -124,13 +123,12 @@ class QuizController {
|
||||||
message: 'Quiz mis à jours avec succès.'
|
message: 'Quiz mis à jours avec succès.'
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
return next(error);
|
return next(error);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
async move(req, res, next) {
|
move = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { quizId, newFolderId } = req.body;
|
const { quizId, newFolderId } = req.body;
|
||||||
|
|
||||||
|
|
@ -139,20 +137,20 @@ class QuizController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is this quiz mine
|
// Is this quiz mine
|
||||||
const quizOwner = await model.getOwner(quizId);
|
const quizOwner = await this.quizzes.getOwner(quizId);
|
||||||
|
|
||||||
if (quizOwner != req.user.userId) {
|
if (quizOwner != req.user.userId) {
|
||||||
throw new AppError(QUIZ_NOT_FOUND);
|
throw new AppError(QUIZ_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is this folder mine
|
// Is this folder mine
|
||||||
const folderOwner = await folderModel.getOwner(newFolderId);
|
const folderOwner = await this.folders.getOwner(newFolderId);
|
||||||
|
|
||||||
if (folderOwner != req.user.userId) {
|
if (folderOwner != req.user.userId) {
|
||||||
throw new AppError(FOLDER_NOT_FOUND);
|
throw new AppError(FOLDER_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await model.move(quizId, newFolderId);
|
const result = await this.quizzes.move(quizId, newFolderId);
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
throw new AppError(MOVING_QUIZ_ERROR);
|
throw new AppError(MOVING_QUIZ_ERROR);
|
||||||
|
|
@ -162,15 +160,12 @@ class QuizController {
|
||||||
message: 'Utilisateur déplacé avec succès.'
|
message: 'Utilisateur déplacé avec succès.'
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
return next(error);
|
return next(error);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
copy = async (req, res, next) => {
|
||||||
|
|
||||||
|
|
||||||
async copy(req, res, next) {
|
|
||||||
const { quizId, newTitle, folderId } = req.body;
|
const { quizId, newTitle, folderId } = req.body;
|
||||||
|
|
||||||
if (!quizId || !newTitle || !folderId) {
|
if (!quizId || !newTitle || !folderId) {
|
||||||
|
|
@ -184,7 +179,7 @@ class QuizController {
|
||||||
// try {
|
// try {
|
||||||
// //Trouver le quiz a dupliquer
|
// //Trouver le quiz a dupliquer
|
||||||
// const conn = db.getConnection();
|
// const conn = db.getConnection();
|
||||||
// const quiztoduplicate = await conn.collection('quiz').findOne({ _id: new ObjectId(quizId) });
|
// const quiztoduplicate = await conn.collection('quiz').findOne({ _id: ObjectId.createFromHexString(quizId) });
|
||||||
// if (!quiztoduplicate) {
|
// if (!quiztoduplicate) {
|
||||||
// throw new Error("Quiz non trouvé");
|
// throw new Error("Quiz non trouvé");
|
||||||
// }
|
// }
|
||||||
|
|
@ -192,7 +187,7 @@ class QuizController {
|
||||||
// //Suppression du id du quiz pour ne pas le répliquer
|
// //Suppression du id du quiz pour ne pas le répliquer
|
||||||
// delete quiztoduplicate._id;
|
// delete quiztoduplicate._id;
|
||||||
// //Ajout du duplicata
|
// //Ajout du duplicata
|
||||||
// await conn.collection('quiz').insertOne({ ...quiztoduplicate, userId: new ObjectId(newUserId) });
|
// await conn.collection('quiz').insertOne({ ...quiztoduplicate, userId: ObjectId.createFromHexString(newUserId) });
|
||||||
// res.json(Response.ok("Dossier dupliqué avec succès pour un autre utilisateur"));
|
// res.json(Response.ok("Dossier dupliqué avec succès pour un autre utilisateur"));
|
||||||
|
|
||||||
// } catch (error) {
|
// } catch (error) {
|
||||||
|
|
@ -201,9 +196,9 @@ class QuizController {
|
||||||
// }
|
// }
|
||||||
// res.status(500).json(Response.serverError(error.message));
|
// res.status(500).json(Response.serverError(error.message));
|
||||||
// }
|
// }
|
||||||
}
|
};
|
||||||
|
|
||||||
async deleteQuizzesByFolderId(req, res, next) {
|
deleteQuizzesByFolderId = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { folderId } = req.body;
|
const { folderId } = req.body;
|
||||||
|
|
||||||
|
|
@ -220,29 +215,29 @@ class QuizController {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return next(error);
|
return next(error);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
async duplicate(req, res, next) {
|
duplicate = async (req, res, next) => {
|
||||||
const { quizId } = req.body;
|
const { quizId } = req.body;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const newQuizId = await model.duplicate(quizId,req.user.userId);
|
const newQuizId = await this.quizzes.duplicate(quizId, req.user.userId);
|
||||||
res.status(200).json({ success: true, newQuizId });
|
res.status(200).json({ success: true, newQuizId });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return next(error);
|
return next(error);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
async quizExists(title, userId) {
|
quizExists = async (title, userId) => {
|
||||||
try {
|
try {
|
||||||
const existingFile = await model.quizExists(title, userId);
|
const existingFile = await this.quizzes.quizExists(title, userId);
|
||||||
return existingFile !== null;
|
return existingFile !== null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new AppError(GETTING_QUIZ_ERROR);
|
throw new AppError(GETTING_QUIZ_ERROR);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
async Share(req, res, next) {
|
share = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { quizId, email } = req.body;
|
const { quizId, email } = req.body;
|
||||||
|
|
||||||
|
|
@ -258,13 +253,12 @@ class QuizController {
|
||||||
message: 'Quiz partagé avec succès.'
|
message: 'Quiz partagé avec succès.'
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
return next(error);
|
return next(error);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
async getShare(req, res, next) {
|
getShare = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { quizId } = req.params;
|
const { quizId } = req.params;
|
||||||
|
|
||||||
|
|
@ -272,7 +266,7 @@ class QuizController {
|
||||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = await model.getContent(quizId);
|
const content = await this.quizzes.getContent(quizId);
|
||||||
|
|
||||||
if (!content) {
|
if (!content) {
|
||||||
throw new AppError(GETTING_QUIZ_ERROR);
|
throw new AppError(GETTING_QUIZ_ERROR);
|
||||||
|
|
@ -282,13 +276,12 @@ class QuizController {
|
||||||
data: content.title
|
data: content.title
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
return next(error);
|
return next(error);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
async receiveShare(req, res, next) {
|
receiveShare = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { quizId, folderId } = req.body;
|
const { quizId, folderId } = req.body;
|
||||||
|
|
||||||
|
|
@ -296,17 +289,17 @@ class QuizController {
|
||||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||||
}
|
}
|
||||||
|
|
||||||
const folderOwner = await folderModel.getOwner(folderId);
|
const folderOwner = await this.folders.getOwner(folderId);
|
||||||
if (folderOwner != req.user.userId) {
|
if (folderOwner != req.user.userId) {
|
||||||
throw new AppError(FOLDER_NOT_FOUND);
|
throw new AppError(FOLDER_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = await model.getContent(quizId);
|
const content = await this.quizzes.getContent(quizId);
|
||||||
if (!content) {
|
if (!content) {
|
||||||
throw new AppError(GETTING_QUIZ_ERROR);
|
throw new AppError(GETTING_QUIZ_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await model.create(content.title, content.content, folderId, req.user.userId);
|
const result = await this.quizzes.create(content.title, content.content, folderId, req.user.userId);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
throw new AppError(QUIZ_ALREADY_EXISTS);
|
throw new AppError(QUIZ_ALREADY_EXISTS);
|
||||||
}
|
}
|
||||||
|
|
@ -314,13 +307,11 @@ class QuizController {
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
message: 'Quiz partagé reçu.'
|
message: 'Quiz partagé reçu.'
|
||||||
});
|
});
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
return next(error);
|
return next(error);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = new QuizController;
|
module.exports = QuizController;
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
const emailer = require('../config/email.js');
|
const emailer = require('../config/email.js');
|
||||||
const model = require('../models/users.js');
|
|
||||||
const jwt = require('../middleware/jwtToken.js');
|
const jwt = require('../middleware/jwtToken.js');
|
||||||
|
|
||||||
const AppError = require('../middleware/AppError.js');
|
const AppError = require('../middleware/AppError.js');
|
||||||
const { MISSING_REQUIRED_PARAMETER, LOGIN_CREDENTIALS_ERROR, GENERATE_PASSWORD_ERROR, UPDATE_PASSWORD_ERROR, DELETE_USER_ERROR } = require('../constants/errorCodes');
|
const { MISSING_REQUIRED_PARAMETER, LOGIN_CREDENTIALS_ERROR, GENERATE_PASSWORD_ERROR, UPDATE_PASSWORD_ERROR, DELETE_USER_ERROR } = require('../constants/errorCodes');
|
||||||
|
|
||||||
|
// controllers must use arrow functions to bind 'this' to the class instance in order to access class properties as callbacks in Express
|
||||||
class UsersController {
|
class UsersController {
|
||||||
|
|
||||||
async register(req, res, next) {
|
constructor(userModel) {
|
||||||
|
this.users = userModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
register = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { email, password } = req.body;
|
const { email, password } = req.body;
|
||||||
|
|
||||||
|
|
@ -15,21 +19,22 @@ class UsersController {
|
||||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||||
}
|
}
|
||||||
|
|
||||||
await model.register(email, password);
|
if (!this.users) {
|
||||||
|
throw new AppError('Users model not found');
|
||||||
|
}
|
||||||
|
await this.users.register(email, password);
|
||||||
|
|
||||||
emailer.registerConfirmation(email)
|
emailer.registerConfirmation(email);
|
||||||
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
message: 'Utilisateur créé avec succès.'
|
message: 'Utilisateur créé avec succès.'
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
return next(error);
|
return next(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
login = async (req, res, next) => {
|
||||||
async login(req, res, next) {
|
|
||||||
try {
|
try {
|
||||||
const { email, password } = req.body;
|
const { email, password } = req.body;
|
||||||
|
|
||||||
|
|
@ -37,7 +42,11 @@ class UsersController {
|
||||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await model.login(email, password);
|
if (!this) {
|
||||||
|
throw new AppError('UsersController not initialized');
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await this.users.login(email, password);
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new AppError(LOGIN_CREDENTIALS_ERROR);
|
throw new AppError(LOGIN_CREDENTIALS_ERROR);
|
||||||
|
|
@ -45,18 +54,13 @@ class UsersController {
|
||||||
|
|
||||||
const token = jwt.create(user.email, user._id);
|
const token = jwt.create(user.email, user._id);
|
||||||
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({ token });
|
||||||
token: token,
|
} catch (error) {
|
||||||
id: user.email
|
next(error);
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
return next(error);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async resetPassword(req, res, next) {
|
resetPassword = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { email } = req.body;
|
const { email } = req.body;
|
||||||
|
|
||||||
|
|
@ -64,7 +68,7 @@ class UsersController {
|
||||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||||
}
|
}
|
||||||
|
|
||||||
const newPassword = await model.resetPassword(email);
|
const newPassword = await this.users.resetPassword(email);
|
||||||
|
|
||||||
if (!newPassword) {
|
if (!newPassword) {
|
||||||
throw new AppError(GENERATE_PASSWORD_ERROR);
|
throw new AppError(GENERATE_PASSWORD_ERROR);
|
||||||
|
|
@ -75,13 +79,12 @@ class UsersController {
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
message: 'Nouveau mot de passe envoyé par courriel.'
|
message: 'Nouveau mot de passe envoyé par courriel.'
|
||||||
});
|
});
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
return next(error);
|
return next(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async changePassword(req, res, next) {
|
changePassword = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { email, oldPassword, newPassword } = req.body;
|
const { email, oldPassword, newPassword } = req.body;
|
||||||
|
|
||||||
|
|
@ -90,13 +93,13 @@ class UsersController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify creds first
|
// verify creds first
|
||||||
const user = await model.login(email, oldPassword);
|
const user = await this.users.login(email, oldPassword);
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new AppError(LOGIN_CREDENTIALS_ERROR);
|
throw new AppError(LOGIN_CREDENTIALS_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
const password = await model.changePassword(email, newPassword)
|
const password = await this.users.changePassword(email, newPassword);
|
||||||
|
|
||||||
if (!password) {
|
if (!password) {
|
||||||
throw new AppError(UPDATE_PASSWORD_ERROR);
|
throw new AppError(UPDATE_PASSWORD_ERROR);
|
||||||
|
|
@ -105,13 +108,12 @@ class UsersController {
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
message: 'Mot de passe changé avec succès.'
|
message: 'Mot de passe changé avec succès.'
|
||||||
});
|
});
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
return next(error);
|
return next(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(req, res, next) {
|
delete = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { email, password } = req.body;
|
const { email, password } = req.body;
|
||||||
|
|
||||||
|
|
@ -120,27 +122,25 @@ class UsersController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify creds first
|
// verify creds first
|
||||||
const user = await model.login(email, password);
|
const user = await this.users.login(email, password);
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new AppError(LOGIN_CREDENTIALS_ERROR);
|
throw new AppError(LOGIN_CREDENTIALS_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await model.delete(email)
|
const result = await this.users.delete(email);
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
throw new AppError(DELETE_USER_ERROR)
|
throw new AppError(DELETE_USER_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
message: 'Utilisateur supprimé avec succès'
|
message: 'Utilisateur supprimé avec succès'
|
||||||
});
|
});
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
|
||||||
return next(error);
|
return next(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = new UsersController;
|
module.exports = UsersController;
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,31 @@
|
||||||
//model
|
//model
|
||||||
const db = require('../config/db.js')
|
const ObjectId = require('mongodb').ObjectId;
|
||||||
const { ObjectId } = require('mongodb');
|
const { generateUniqueTitle } = require('./utils');
|
||||||
const Quiz = require('./quiz.js');
|
|
||||||
|
|
||||||
class Folders {
|
class Folders {
|
||||||
|
constructor(db, quizModel) {
|
||||||
|
this.db = db;
|
||||||
|
this.quizModel = quizModel;
|
||||||
|
}
|
||||||
|
|
||||||
async create(title, userId) {
|
async create(title, userId) {
|
||||||
await db.connect()
|
|
||||||
const conn = db.getConnection();
|
console.log("LOG: create", title, userId);
|
||||||
|
|
||||||
|
if (!title || !userId) {
|
||||||
|
throw new Error('Missing required parameter(s)');
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.db.connect()
|
||||||
|
const conn = this.db.getConnection();
|
||||||
|
|
||||||
const foldersCollection = conn.collection('folders');
|
const foldersCollection = conn.collection('folders');
|
||||||
|
|
||||||
const existingFolder = await foldersCollection.findOne({ title: title, userId: userId });
|
const existingFolder = await foldersCollection.findOne({ title: title, userId: userId });
|
||||||
|
|
||||||
if (existingFolder) return new Error('Folder already exists');
|
if (existingFolder) {
|
||||||
|
throw new Error('Folder already exists');
|
||||||
|
}
|
||||||
|
|
||||||
const newFolder = {
|
const newFolder = {
|
||||||
userId: userId,
|
userId: userId,
|
||||||
|
|
@ -27,8 +39,8 @@ class Folders {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getUserFolders(userId) {
|
async getUserFolders(userId) {
|
||||||
await db.connect()
|
await this.db.connect()
|
||||||
const conn = db.getConnection();
|
const conn = this.db.getConnection();
|
||||||
|
|
||||||
const foldersCollection = conn.collection('folders');
|
const foldersCollection = conn.collection('folders');
|
||||||
|
|
||||||
|
|
@ -38,19 +50,20 @@ class Folders {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOwner(folderId) {
|
async getOwner(folderId) {
|
||||||
await db.connect()
|
await this.db.connect()
|
||||||
const conn = db.getConnection();
|
const conn = this.db.getConnection();
|
||||||
|
|
||||||
const foldersCollection = conn.collection('folders');
|
const foldersCollection = conn.collection('folders');
|
||||||
|
|
||||||
const folder = await foldersCollection.findOne({ _id: new ObjectId(folderId) });
|
const folder = await foldersCollection.findOne({ _id: ObjectId.createFromHexString(folderId) });
|
||||||
|
|
||||||
return folder.userId;
|
return folder.userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// finds all quizzes in a folder
|
||||||
async getContent(folderId) {
|
async getContent(folderId) {
|
||||||
await db.connect()
|
await this.db.connect()
|
||||||
const conn = db.getConnection();
|
const conn = this.db.getConnection();
|
||||||
|
|
||||||
const filesCollection = conn.collection('files');
|
const filesCollection = conn.collection('files');
|
||||||
|
|
||||||
|
|
@ -60,26 +73,26 @@ class Folders {
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(folderId) {
|
async delete(folderId) {
|
||||||
await db.connect()
|
await this.db.connect()
|
||||||
const conn = db.getConnection();
|
const conn = this.db.getConnection();
|
||||||
|
|
||||||
const foldersCollection = conn.collection('folders');
|
const foldersCollection = conn.collection('folders');
|
||||||
|
|
||||||
const folderResult = await foldersCollection.deleteOne({ _id: new ObjectId(folderId) });
|
const folderResult = await foldersCollection.deleteOne({ _id: ObjectId.createFromHexString(folderId) });
|
||||||
|
|
||||||
if (folderResult.deletedCount != 1) return false;
|
if (folderResult.deletedCount != 1) return false;
|
||||||
await Quiz.deleteQuizzesByFolderId(folderId);
|
await this.quizModel.deleteQuizzesByFolderId(folderId);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async rename(folderId, newTitle) {
|
async rename(folderId, newTitle) {
|
||||||
await db.connect()
|
await this.db.connect()
|
||||||
const conn = db.getConnection();
|
const conn = this.db.getConnection();
|
||||||
|
|
||||||
const foldersCollection = conn.collection('folders');
|
const foldersCollection = conn.collection('folders');
|
||||||
|
|
||||||
const result = await foldersCollection.updateOne({ _id: new ObjectId(folderId) }, { $set: { title: newTitle } })
|
const result = await foldersCollection.updateOne({ _id: ObjectId.createFromHexString(folderId) }, { $set: { title: newTitle } })
|
||||||
|
|
||||||
if (result.modifiedCount != 1) return false;
|
if (result.modifiedCount != 1) return false;
|
||||||
|
|
||||||
|
|
@ -87,69 +100,77 @@ class Folders {
|
||||||
}
|
}
|
||||||
|
|
||||||
async duplicate(folderId, userId) {
|
async duplicate(folderId, userId) {
|
||||||
|
console.log("LOG: duplicate", folderId, userId);
|
||||||
|
const conn = this.db.getConnection();
|
||||||
|
const foldersCollection = conn.collection('folders');
|
||||||
|
|
||||||
const sourceFolder = await this.getFolderWithContent(folderId);
|
const sourceFolder = await foldersCollection.findOne({ _id: ObjectId.createFromHexString(folderId), userId: userId });
|
||||||
|
if (!sourceFolder) {
|
||||||
// Check if the new title already exists
|
throw new Error(`Folder ${folderId} not found`);
|
||||||
let newFolderTitle = sourceFolder.title + "-copie";
|
|
||||||
let counter = 1;
|
|
||||||
|
|
||||||
while (await this.folderExists(newFolderTitle, userId)) {
|
|
||||||
newFolderTitle = `${sourceFolder.title}-copie(${counter})`;
|
|
||||||
counter++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const theUserId = userId;
|
||||||
|
// Use the utility function to generate a unique title
|
||||||
|
const newFolderTitle = await generateUniqueTitle(sourceFolder.title, async (title) => {
|
||||||
|
console.log(`generateUniqueTitle(${title}): userId`, theUserId);
|
||||||
|
return await foldersCollection.findOne({ title: title, userId: theUserId });
|
||||||
|
});
|
||||||
|
|
||||||
const newFolderId = await this.create(newFolderTitle, userId);
|
const newFolderId = await this.create(newFolderTitle, userId);
|
||||||
|
|
||||||
if (!newFolderId) {
|
if (!newFolderId) {
|
||||||
throw new Error('Failed to create a duplicate folder.');
|
throw new Error('Failed to create duplicate folder');
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const quiz of sourceFolder.content) {
|
// copy the quizzes from source folder to destination folder
|
||||||
const { title, content } = quiz;
|
const content = await this.getContent(folderId);
|
||||||
//console.log(title);
|
console.log("folders.duplicate: found content", content);
|
||||||
//console.log(content);
|
for (const quiz of content) {
|
||||||
await Quiz.create(title, content, newFolderId.toString(), userId);
|
console.log("folders.duplicate: creating quiz (copy)", quiz);
|
||||||
|
const result = await this.quizModel.create(quiz.title, quiz.content, newFolderId.toString(), userId);
|
||||||
|
if (!result) {
|
||||||
|
throw new Error('Failed to create duplicate quiz');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return newFolderId;
|
return newFolderId;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async folderExists(title, userId) {
|
async folderExists(title, userId) {
|
||||||
await db.connect();
|
console.log("LOG: folderExists", title, userId);
|
||||||
const conn = db.getConnection();
|
await this.db.connect();
|
||||||
|
const conn = this.db.getConnection();
|
||||||
|
|
||||||
const foldersCollection = conn.collection('folders');
|
const foldersCollection = conn.collection('folders');
|
||||||
const existingFolder = await foldersCollection.findOne({ title: title, userId: userId });
|
const existingFolder = await foldersCollection.findOne({ title: title, userId: userId });
|
||||||
|
|
||||||
return existingFolder !== null;
|
return !!existingFolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async copy(folderId, userId) {
|
async copy(folderId, userId) {
|
||||||
|
|
||||||
|
|
||||||
const sourceFolder = await this.getFolderWithContent(folderId);
|
const sourceFolder = await this.getFolderWithContent(folderId);
|
||||||
const newFolderId = await this.create(sourceFolder.title, userId);
|
const newFolderId = await this.create(sourceFolder.title, userId);
|
||||||
if (!newFolderId) {
|
if (!newFolderId) {
|
||||||
throw new Error('Failed to create a new folder.');
|
throw new Error('Failed to create a new folder.');
|
||||||
}
|
}
|
||||||
for (const quiz of sourceFolder.content) {
|
for (const quiz of sourceFolder.content) {
|
||||||
await this.createQuiz(quiz.title, quiz.content, newFolderId, userId);
|
await this.quizModel.create(quiz.title, quiz.content, newFolderId, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return newFolderId;
|
return newFolderId;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getFolderById(folderId) {
|
async getFolderById(folderId) {
|
||||||
await db.connect();
|
await this.db.connect();
|
||||||
const conn = db.getConnection();
|
const conn = this.db.getConnection();
|
||||||
|
|
||||||
const foldersCollection = conn.collection('folders');
|
const foldersCollection = conn.collection('folders');
|
||||||
|
|
||||||
const folder = await foldersCollection.findOne({ _id: new ObjectId(folderId) });
|
const folder = await foldersCollection.findOne({ _id: ObjectId.createFromHexString(folderId) });
|
||||||
|
|
||||||
|
if (!folder) return new Error(`Folder ${folderId} not found`);
|
||||||
|
|
||||||
return folder;
|
return folder;
|
||||||
}
|
}
|
||||||
|
|
@ -171,4 +192,4 @@ class Folders {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = new Folders;
|
module.exports = Folders;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
const db = require('../config/db.js')
|
//const db = require('../config/db.js')
|
||||||
const { ObjectId } = require('mongodb');
|
const { ObjectId } = require('mongodb');
|
||||||
|
|
||||||
class Images {
|
class Images {
|
||||||
|
|
||||||
|
constructor(db) {
|
||||||
|
this.db = db;
|
||||||
|
}
|
||||||
|
|
||||||
async upload(file, userId) {
|
async upload(file, userId) {
|
||||||
await db.connect()
|
await db.connect()
|
||||||
const conn = db.getConnection();
|
const conn = db.getConnection();
|
||||||
|
|
@ -28,7 +32,7 @@ class Images {
|
||||||
|
|
||||||
const imagesCollection = conn.collection('images');
|
const imagesCollection = conn.collection('images');
|
||||||
|
|
||||||
const result = await imagesCollection.findOne({ _id: new ObjectId(id) });
|
const result = await imagesCollection.findOne({ _id: ObjectId.createFromHexString(id) });
|
||||||
|
|
||||||
if (!result) return null;
|
if (!result) return null;
|
||||||
|
|
||||||
|
|
@ -41,4 +45,4 @@ class Images {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = new Images;
|
module.exports = Images;
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,25 @@
|
||||||
const db = require('../config/db.js')
|
|
||||||
const { ObjectId } = require('mongodb');
|
const { ObjectId } = require('mongodb');
|
||||||
|
const { generateUniqueTitle } = require('./utils');
|
||||||
|
|
||||||
class Quiz {
|
class Quiz {
|
||||||
|
|
||||||
|
constructor(db) {
|
||||||
|
// console.log("Quiz constructor: db", db)
|
||||||
|
this.db = db;
|
||||||
|
}
|
||||||
|
|
||||||
async create(title, content, folderId, userId) {
|
async create(title, content, folderId, userId) {
|
||||||
await db.connect()
|
console.log(`quizzes: create title: ${title}, folderId: ${folderId}, userId: ${userId}`);
|
||||||
const conn = db.getConnection();
|
await this.db.connect()
|
||||||
|
const conn = this.db.getConnection();
|
||||||
|
|
||||||
const quizCollection = conn.collection('files');
|
const quizCollection = conn.collection('files');
|
||||||
|
|
||||||
const existingQuiz = await quizCollection.findOne({ title: title, folderId: folderId, userId: userId })
|
const existingQuiz = await quizCollection.findOne({ title: title, folderId: folderId, userId: userId })
|
||||||
|
|
||||||
if (existingQuiz) return null;
|
if (existingQuiz) {
|
||||||
|
throw new Error(`Quiz already exists with title: ${title}, folderId: ${folderId}, userId: ${userId}`);
|
||||||
|
}
|
||||||
|
|
||||||
const newQuiz = {
|
const newQuiz = {
|
||||||
folderId: folderId,
|
folderId: folderId,
|
||||||
|
|
@ -23,74 +31,86 @@ class Quiz {
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await quizCollection.insertOne(newQuiz);
|
const result = await quizCollection.insertOne(newQuiz);
|
||||||
|
console.log("quizzes: create insertOne result", result);
|
||||||
|
|
||||||
return result.insertedId;
|
return result.insertedId;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOwner(quizId) {
|
async getOwner(quizId) {
|
||||||
await db.connect()
|
await this.db.connect()
|
||||||
const conn = db.getConnection();
|
const conn = this.db.getConnection();
|
||||||
|
|
||||||
const quizCollection = conn.collection('files');
|
const quizCollection = conn.collection('files');
|
||||||
|
|
||||||
const quiz = await quizCollection.findOne({ _id: new ObjectId(quizId) });
|
const quiz = await quizCollection.findOne({ _id: ObjectId.createFromHexString(quizId) });
|
||||||
|
|
||||||
return quiz.userId;
|
return quiz.userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getContent(quizId) {
|
async getContent(quizId) {
|
||||||
await db.connect()
|
await this.db.connect()
|
||||||
const conn = db.getConnection();
|
const conn = this.db.getConnection();
|
||||||
|
|
||||||
const quizCollection = conn.collection('files');
|
const quizCollection = conn.collection('files');
|
||||||
|
|
||||||
const quiz = await quizCollection.findOne({ _id: new ObjectId(quizId) });
|
const quiz = await quizCollection.findOne({ _id: ObjectId.createFromHexString(quizId) });
|
||||||
|
|
||||||
return quiz;
|
return quiz;
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(quizId) {
|
async delete(quizId) {
|
||||||
await db.connect()
|
await this.db.connect()
|
||||||
const conn = db.getConnection();
|
const conn = this.db.getConnection();
|
||||||
|
|
||||||
const quizCollection = conn.collection('files');
|
const quizCollection = conn.collection('files');
|
||||||
|
|
||||||
const result = await quizCollection.deleteOne({ _id: new ObjectId(quizId) });
|
const result = await quizCollection.deleteOne({ _id: ObjectId.createFromHexString(quizId) });
|
||||||
|
|
||||||
if (result.deletedCount != 1) return false;
|
if (result.deletedCount != 1) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
async deleteQuizzesByFolderId(folderId) {
|
async deleteQuizzesByFolderId(folderId) {
|
||||||
await db.connect();
|
await this.db.connect();
|
||||||
const conn = db.getConnection();
|
const conn = this.db.getConnection();
|
||||||
|
|
||||||
const quizzesCollection = conn.collection('files');
|
const quizzesCollection = conn.collection('files');
|
||||||
|
|
||||||
// Delete all quizzes with the specified folderId
|
// Delete all quizzes with the specified folderId
|
||||||
await quizzesCollection.deleteMany({ folderId: folderId });
|
const result = await quizzesCollection.deleteMany({ folderId: folderId });
|
||||||
|
return result.deletedCount > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(quizId, newTitle, newContent) {
|
async update(quizId, newTitle, newContent) {
|
||||||
await db.connect()
|
await this.db.connect()
|
||||||
const conn = db.getConnection();
|
const conn = this.db.getConnection();
|
||||||
|
|
||||||
const quizCollection = conn.collection('files');
|
const quizCollection = conn.collection('files');
|
||||||
|
|
||||||
const result = await quizCollection.updateOne({ _id: new ObjectId(quizId) }, { $set: { title: newTitle, content: newContent } });
|
const result = await quizCollection.updateOne(
|
||||||
//Ne fonctionne pas si rien n'est chngé dans le quiz
|
{ _id: ObjectId.createFromHexString(quizId) },
|
||||||
//if (result.modifiedCount != 1) return false;
|
{
|
||||||
|
$set: {
|
||||||
|
title: newTitle,
|
||||||
|
content: newContent,
|
||||||
|
updated_at: new Date()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return true
|
return result.modifiedCount === 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
async move(quizId, newFolderId) {
|
async move(quizId, newFolderId) {
|
||||||
await db.connect()
|
await this.db.connect()
|
||||||
const conn = db.getConnection();
|
const conn = this.db.getConnection();
|
||||||
|
|
||||||
const quizCollection = conn.collection('files');
|
const quizCollection = conn.collection('files');
|
||||||
|
|
||||||
const result = await quizCollection.updateOne({ _id: new ObjectId(quizId) }, { $set: { folderId: newFolderId } });
|
const result = await quizCollection.updateOne(
|
||||||
|
{ _id: ObjectId.createFromHexString(quizId) },
|
||||||
|
{ $set: { folderId: newFolderId } }
|
||||||
|
);
|
||||||
|
|
||||||
if (result.modifiedCount != 1) return false;
|
if (result.modifiedCount != 1) return false;
|
||||||
|
|
||||||
|
|
@ -98,29 +118,31 @@ class Quiz {
|
||||||
}
|
}
|
||||||
|
|
||||||
async duplicate(quizId, userId) {
|
async duplicate(quizId, userId) {
|
||||||
|
const conn = this.db.getConnection();
|
||||||
|
const quizCollection = conn.collection('files');
|
||||||
|
|
||||||
const sourceQuiz = await this.getContent(quizId);
|
const sourceQuiz = await quizCollection.findOne({ _id: ObjectId.createFromHexString(quizId), userId: userId });
|
||||||
|
if (!sourceQuiz) {
|
||||||
let newQuizTitle = `${sourceQuiz.title}-copy`;
|
throw new Error('Quiz not found for quizId: ' + quizId);
|
||||||
let counter = 1;
|
|
||||||
while (await this.quizExists(newQuizTitle, userId)) {
|
|
||||||
newQuizTitle = `${sourceQuiz.title}-copy(${counter})`;
|
|
||||||
counter++;
|
|
||||||
}
|
}
|
||||||
//console.log(newQuizTitle);
|
|
||||||
|
// Use the utility function to generate a unique title
|
||||||
|
const newQuizTitle = await generateUniqueTitle(sourceQuiz.title, async (title) => {
|
||||||
|
return await quizCollection.findOne({ title: title, folderId: sourceQuiz.folderId, userId: userId });
|
||||||
|
});
|
||||||
|
|
||||||
const newQuizId = await this.create(newQuizTitle, sourceQuiz.content, sourceQuiz.folderId, userId);
|
const newQuizId = await this.create(newQuizTitle, sourceQuiz.content, sourceQuiz.folderId, userId);
|
||||||
|
|
||||||
if (!newQuizId) {
|
if (!newQuizId) {
|
||||||
throw new Error('Failed to create a duplicate quiz.');
|
throw new Error('Failed to create duplicate quiz');
|
||||||
}
|
}
|
||||||
|
|
||||||
return newQuizId;
|
return newQuizId;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async quizExists(title, userId) {
|
async quizExists(title, userId) {
|
||||||
await db.connect();
|
await this.db.connect();
|
||||||
const conn = db.getConnection();
|
const conn = this.db.getConnection();
|
||||||
|
|
||||||
const filesCollection = conn.collection('files');
|
const filesCollection = conn.collection('files');
|
||||||
const existingFolder = await filesCollection.findOne({ title: title, userId: userId });
|
const existingFolder = await filesCollection.findOne({ title: title, userId: userId });
|
||||||
|
|
@ -130,4 +152,4 @@ class Quiz {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = new Quiz;
|
module.exports = Quiz;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
//user
|
//user
|
||||||
const db = require('../config/db.js');
|
|
||||||
const bcrypt = require('bcrypt');
|
const bcrypt = require('bcrypt');
|
||||||
const AppError = require('../middleware/AppError.js');
|
const AppError = require('../middleware/AppError.js');
|
||||||
const { USER_ALREADY_EXISTS } = require('../constants/errorCodes');
|
const { USER_ALREADY_EXISTS } = require('../constants/errorCodes');
|
||||||
const Folders = require('../models/folders.js');
|
|
||||||
|
|
||||||
class Users {
|
class Users {
|
||||||
|
constructor(db, foldersModel) {
|
||||||
|
// console.log("Users constructor: db", db)
|
||||||
|
this.db = db;
|
||||||
|
this.folders = foldersModel;
|
||||||
|
}
|
||||||
|
|
||||||
async hashPassword(password) {
|
async hashPassword(password) {
|
||||||
return await bcrypt.hash(password, 10)
|
return await bcrypt.hash(password, 10)
|
||||||
|
|
@ -20,8 +23,8 @@ class Users {
|
||||||
}
|
}
|
||||||
|
|
||||||
async register(email, password) {
|
async register(email, password) {
|
||||||
await db.connect()
|
await this.db.connect()
|
||||||
const conn = db.getConnection();
|
const conn = this.db.getConnection();
|
||||||
|
|
||||||
const userCollection = conn.collection('users');
|
const userCollection = conn.collection('users');
|
||||||
|
|
||||||
|
|
@ -37,18 +40,19 @@ class Users {
|
||||||
created_at: new Date()
|
created_at: new Date()
|
||||||
};
|
};
|
||||||
|
|
||||||
await userCollection.insertOne(newUser);
|
const result = await userCollection.insertOne(newUser);
|
||||||
|
// console.log("userCollection.insertOne() result", result);
|
||||||
|
const userId = result.insertedId.toString();
|
||||||
|
|
||||||
const folderTitle = 'Dossier par Défaut';
|
const folderTitle = 'Dossier par Défaut';
|
||||||
const userId = newUser._id.toString();
|
await this.folders.create(folderTitle, userId);
|
||||||
await Folders.create(folderTitle, userId);
|
|
||||||
|
|
||||||
// TODO: verif if inserted properly...
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async login(email, password) {
|
async login(email, password) {
|
||||||
await db.connect()
|
await this.db.connect()
|
||||||
const conn = db.getConnection();
|
const conn = this.db.getConnection();
|
||||||
|
|
||||||
const userCollection = conn.collection('users');
|
const userCollection = conn.collection('users');
|
||||||
|
|
||||||
|
|
@ -74,8 +78,8 @@ class Users {
|
||||||
}
|
}
|
||||||
|
|
||||||
async changePassword(email, newPassword) {
|
async changePassword(email, newPassword) {
|
||||||
await db.connect()
|
await this.db.connect()
|
||||||
const conn = db.getConnection();
|
const conn = this.db.getConnection();
|
||||||
|
|
||||||
const userCollection = conn.collection('users');
|
const userCollection = conn.collection('users');
|
||||||
|
|
||||||
|
|
@ -89,8 +93,8 @@ class Users {
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(email) {
|
async delete(email) {
|
||||||
await db.connect()
|
await this.db.connect()
|
||||||
const conn = db.getConnection();
|
const conn = this.db.getConnection();
|
||||||
|
|
||||||
const userCollection = conn.collection('users');
|
const userCollection = conn.collection('users');
|
||||||
|
|
||||||
|
|
@ -102,8 +106,8 @@ class Users {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getId(email) {
|
async getId(email) {
|
||||||
await db.connect()
|
await this.db.connect()
|
||||||
const conn = db.getConnection();
|
const conn = this.db.getConnection();
|
||||||
|
|
||||||
const userCollection = conn.collection('users');
|
const userCollection = conn.collection('users');
|
||||||
|
|
||||||
|
|
@ -118,4 +122,4 @@ class Users {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = new Users;
|
module.exports = Users;
|
||||||
|
|
|
||||||
35
server/models/utils.js
Normal file
35
server/models/utils.js
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
// utils.js
|
||||||
|
async function generateUniqueTitle(baseTitle, existsCallback) {
|
||||||
|
console.log(`generateUniqueTitle(${baseTitle})`);
|
||||||
|
let newTitle = baseTitle;
|
||||||
|
let counter = 1;
|
||||||
|
|
||||||
|
const titleRegex = /(.*?)(\((\d+)\))?$/;
|
||||||
|
const match = baseTitle.match(titleRegex);
|
||||||
|
if (match) {
|
||||||
|
baseTitle = match[1].trim();
|
||||||
|
counter = match[3] ? parseInt(match[3], 10) + 1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the base title does not end with a parentheses expression, start with "(1)"
|
||||||
|
if (!match[2]) {
|
||||||
|
newTitle = `${baseTitle} (${counter})`;
|
||||||
|
} else {
|
||||||
|
// else increment the counter in the parentheses expression as a first try
|
||||||
|
newTitle = `${baseTitle} (${counter})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`first check of newTitle: ${newTitle}`);
|
||||||
|
|
||||||
|
while (await existsCallback(newTitle)) {
|
||||||
|
counter++;
|
||||||
|
newTitle = `${baseTitle} (${counter})`;
|
||||||
|
console.log(`trying newTitle: ${newTitle}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
generateUniqueTitle
|
||||||
|
};
|
||||||
1
server/package-lock.json
generated
1
server/package-lock.json
generated
|
|
@ -22,6 +22,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
|
"jest-mock": "^29.7.0",
|
||||||
"nodemon": "^3.0.1",
|
"nodemon": "^3.0.1",
|
||||||
"supertest": "^6.3.4"
|
"supertest": "^6.3.4"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
"build": "webpack --config webpack.config.js",
|
"build": "webpack --config webpack.config.js",
|
||||||
"start": "node app.js",
|
"start": "node app.js",
|
||||||
"dev": "nodemon app.js",
|
"dev": "nodemon app.js",
|
||||||
"test": "jest"
|
"test": "jest --colors"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
|
|
@ -26,6 +26,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
|
"jest-mock": "^29.7.0",
|
||||||
"nodemon": "^3.0.1",
|
"nodemon": "^3.0.1",
|
||||||
"supertest": "^6.3.4"
|
"supertest": "^6.3.4"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,22 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const jwt = require('../middleware/jwtToken.js');
|
const jwt = require('../middleware/jwtToken.js');
|
||||||
|
const folders = require('../app.js').folders;
|
||||||
|
|
||||||
const foldersController = require('../controllers/folders.js')
|
router.post("/create", jwt.authenticate, folders.create);
|
||||||
|
router.get("/getUserFolders", jwt.authenticate, folders.getUserFolders);
|
||||||
router.post("/create", jwt.authenticate, foldersController.create);
|
router.get("/getFolderContent/:folderId", jwt.authenticate, folders.getFolderContent);
|
||||||
router.get("/getUserFolders", jwt.authenticate, foldersController.getUserFolders);
|
router.delete("/delete/:folderId", jwt.authenticate, folders.delete);
|
||||||
router.get("/getFolderContent/:folderId", jwt.authenticate, foldersController.getFolderContent);
|
router.put("/rename", jwt.authenticate, folders.rename);
|
||||||
router.delete("/delete/:folderId", jwt.authenticate, foldersController.delete);
|
|
||||||
router.put("/rename", jwt.authenticate, foldersController.rename);
|
|
||||||
|
|
||||||
//router.post("/duplicate", jwt.authenticate, foldersController.duplicate);
|
//router.post("/duplicate", jwt.authenticate, foldersController.duplicate);
|
||||||
router.post("/duplicate", jwt.authenticate, foldersController.duplicate);
|
router.post("/duplicate", jwt.authenticate, folders.duplicate);
|
||||||
|
|
||||||
router.post("/copy/:folderId", jwt.authenticate, foldersController.copy);
|
router.post("/copy/:folderId", jwt.authenticate, folders.copy);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
||||||
|
// export also folders (the controller)
|
||||||
|
module.exports.folders = folders;
|
||||||
|
|
||||||
|
// Refer to folders using: const folders = require('../controllers/folders.js').folders;
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
const images = require('../app.js').images;
|
||||||
|
|
||||||
const jwt = require('../middleware/jwtToken.js');
|
const jwt = require('../middleware/jwtToken.js');
|
||||||
const imagesController = require('../controllers/images.js')
|
|
||||||
|
|
||||||
// For getting the image out of the form data
|
// For getting the image out of the form data
|
||||||
const multer = require('multer');
|
const multer = require('multer');
|
||||||
const storage = multer.memoryStorage();
|
const storage = multer.memoryStorage();
|
||||||
const upload = multer({ storage: storage });
|
const upload = multer({ storage: storage });
|
||||||
|
|
||||||
router.post("/upload", jwt.authenticate, upload.single('image'), imagesController.upload);
|
router.post("/upload", jwt.authenticate, upload.single('image'), images.upload);
|
||||||
router.get("/get/:id", imagesController.get);
|
router.get("/get/:id", images.get);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
@ -1,19 +1,22 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
const quizzes = require('../app.js').quizzes;
|
||||||
const jwt = require('../middleware/jwtToken.js');
|
const jwt = require('../middleware/jwtToken.js');
|
||||||
const quizController = require('../controllers/quiz.js')
|
|
||||||
|
|
||||||
router.post("/create", jwt.authenticate, quizController.create);
|
if (!quizzes) {
|
||||||
router.get("/get/:quizId", jwt.authenticate, quizController.get);
|
console.error("quizzes is not defined");
|
||||||
router.delete("/delete/:quizId", jwt.authenticate, quizController.delete);
|
}
|
||||||
router.put("/update", jwt.authenticate, quizController.update);
|
|
||||||
router.put("/move", jwt.authenticate, quizController.move);
|
|
||||||
|
|
||||||
router.post("/duplicate", jwt.authenticate, quizController.duplicate);
|
router.post("/create", jwt.authenticate, quizzes.create);
|
||||||
router.post("/copy/:quizId", jwt.authenticate, quizController.copy);
|
router.get("/get/:quizId", jwt.authenticate, quizzes.get);
|
||||||
router.put("/Share", jwt.authenticate, quizController.Share);
|
router.delete("/delete/:quizId", jwt.authenticate, quizzes.delete);
|
||||||
router.get("/getShare/:quizId", jwt.authenticate, quizController.getShare);
|
router.put("/update", jwt.authenticate, quizzes.update);
|
||||||
router.post("/receiveShare", jwt.authenticate, quizController.receiveShare);
|
router.put("/move", jwt.authenticate, quizzes.move);
|
||||||
|
|
||||||
|
router.post("/duplicate", jwt.authenticate, quizzes.duplicate);
|
||||||
|
router.post("/copy/:quizId", jwt.authenticate, quizzes.copy);
|
||||||
|
router.put("/Share", jwt.authenticate, quizzes.share);
|
||||||
|
router.get("/getShare/:quizId", jwt.authenticate, quizzes.getShare);
|
||||||
|
router.post("/receiveShare", jwt.authenticate, quizzes.receiveShare);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
const users = require('../app.js').users;
|
||||||
const jwt = require('../middleware/jwtToken.js');
|
const jwt = require('../middleware/jwtToken.js');
|
||||||
const usersController = require('../controllers/users.js')
|
|
||||||
|
|
||||||
router.post("/register", usersController.register);
|
router.post("/register", users.register);
|
||||||
router.post("/login", usersController.login);
|
router.post("/login", users.login);
|
||||||
router.post("/reset-password", usersController.resetPassword);
|
router.post("/reset-password", users.resetPassword);
|
||||||
router.post("/change-password", jwt.authenticate, usersController.changePassword);
|
router.post("/change-password", jwt.authenticate, users.changePassword);
|
||||||
router.post("/delete-user", jwt.authenticate, usersController.delete);
|
router.post("/delete-user", jwt.authenticate, users.delete);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
@ -9,7 +9,7 @@ const setupWebsocket = (io) => {
|
||||||
console.log("Connection limit reached. Disconnecting client.");
|
console.log("Connection limit reached. Disconnecting client.");
|
||||||
socket.emit(
|
socket.emit(
|
||||||
"join-failure",
|
"join-failure",
|
||||||
"Le nombre maximum de connexion a été atteint"
|
"Le nombre maximum de connexions a été atteint"
|
||||||
);
|
);
|
||||||
socket.disconnect(true);
|
socket.disconnect(true);
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue