diff --git a/__tests__/auth/__snapshots__/logout.test.js.snap b/__tests__/auth/__snapshots__/logout.test.js.snap new file mode 100644 index 0000000000000000000000000000000000000000..542a47e12a950f0ba15aa7f0bce1db0cb651da74 --- /dev/null +++ b/__tests__/auth/__snapshots__/logout.test.js.snap @@ -0,0 +1,19 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`user logout > given no cookie was send > should respond with a proper body 1`] = ` +{ + "message": "See you soon.", +} +`; + +exports[`user logout > given refresh token is invalid > should respond with a proper body 1`] = ` +{ + "message": "jwt malformed", +} +`; + +exports[`user logout > given refresh token is valid > should respond with a proper body 1`] = ` +{ + "message": "jwt malformed", +} +`; diff --git a/__tests__/auth/logout.test.js b/__tests__/auth/logout.test.js new file mode 100644 index 0000000000000000000000000000000000000000..aa835e181122976ae09dc2c4386113e3cb4ef541 --- /dev/null +++ b/__tests__/auth/logout.test.js @@ -0,0 +1,95 @@ +// import vitest, supertest & app +import { vi, beforeAll, beforeEach, describe, expect, expectTypeOf, test, it, afterEach } from 'vitest'; +import supertest from "supertest"; +import app from "../../app.js"; +// set route +const ROUTE = '/auth'; +// prepare response of each test +let response; + +// ############################ +// OBJECTS +// ############################ +const mockedVals = vi.hoisted(() => { + return { + foundUser: { + _id: '66a29da2942b3eb', + username: 'snoopy', + name: 'My User', + email: 'user@mail.local', + verified: true, + role: 0, + createdAt: '2024-07 - 25T18: 46: 58.982Z', + updatedAt: '2024-07 - 25T18: 46: 58.982Z', + __v: 0, + password: 'StrongPass1!', + // password, + id: '66a29da2942b3ebcaf047f07' + } + }; +}); +// ############################ +// MOCKS +// ############################ +// import Database Service +import * as dbService from '../../utils/handleDB.js'; +// mock dbService +vi.mock('../../utils/handleDB.js', async (importOriginal) => { + return { + ...await importOriginal(), + dbConnection: vi.fn(() => 'mocked'), + findOneRecord: vi.fn(() => mockedVals.foundUser), + findOneAndUpdate: vi.fn(() => mockedVals.foundUser), + }; +}); + +// ############################ +// TESTS +// ############################ +describe('user logout', () => { + // ############################ + describe('given refresh token is valid', () => { + beforeAll(async () => { + response = await supertest(app) + .get(ROUTE) + .set('Cookie', 'refreshToken=valid'); + }); + it('should return a proper status code', () => { + expect(response.status).toBe(400); + }); + it('should respond with a proper body', () => { + expect(response.body).toMatchSnapshot(); + }); + }); + + // ############################ + + describe('given refresh token is invalid', () => { + beforeAll(async () => { + response = await supertest(app) + .get(ROUTE) + .set('Cookie', 'refreshToken=invalid'); + }); + it('should return a proper status code', () => { + expect(response.status).toBe(400); + }); + it('should respond with a proper body', () => { + expect(response.body).toMatchSnapshot(); + }); + }); + + // ############################ + + describe('given no cookie was send', () => { + beforeAll(async () => { + response = await supertest(app) + .delete(ROUTE); + }); + it('should return a proper status code', () => { + expect(response.status).toBe(200); + }); + it('should respond with a proper body', () => { + expect(response.body).toMatchSnapshot(); + }); + }); +}); \ No newline at end of file diff --git a/__tests__/auth/refreshjwt.test.js b/__tests__/auth/refreshjwt.test.js index e1a1f871f120701671015b9193f4a41c948a8393..7891a265dc9535e1582eda0dd546e8f505cf307c 100644 --- a/__tests__/auth/refreshjwt.test.js +++ b/__tests__/auth/refreshjwt.test.js @@ -45,7 +45,6 @@ vi.mock('../../utils/handleDB.js', async (importOriginal) => { findByIdAndUpdate: vi.fn(() => { return { ...mockedVals.foundUser, refreshToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY2MOCKED' }; }) }; }); -// verifyRefreshToken; // ############################ // TESTS diff --git a/logs/__tests__/users/logout.test.js b/logs/__tests__/users/logout.test.js deleted file mode 100644 index c7816a175decd761eea1a3f03c0cf181713ea52e..0000000000000000000000000000000000000000 --- a/logs/__tests__/users/logout.test.js +++ /dev/null @@ -1,64 +0,0 @@ -// import vitest, supertest & app -import { vi, beforeAll, beforeEach, describe, expect, expectTypeOf, test, it, afterEach } from 'vitest'; -import supertest from "supertest"; -import app from "../../app.js"; -// ignore expiration of the (self-signed) certificate -process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0; -// set timeout -const BEFORE_ALL_TIMEOUT = 30000; // 30 sec -// set route -const ROUTE = '/users/logout'; -// prepare response of each test -let response; - -// ############################ -// OBJECTS -// ############################ - -// ############################ -// MOCKS -// ############################ -// import PocketBase Service -import * as pbService from '../../utils/pocketbase/handlePocketBase.js'; -// mock pbService -vi.mock('../../utils/pocketbase/handlePocketBase.js', async (importOriginal) => { - return { - ...await importOriginal(), - pbClearAuthStore: vi.fn(() => 'mocked'), - }; -}); - -// ############################ -// TESTS -// ############################ -describe('user logout', () => { - describe('given nothing but the JWT was send', () => { - beforeAll(async () => { - response = await supertest(app) - .get(ROUTE) - .set('Authorization', 'Bearer 123valid'); - }, BEFORE_ALL_TIMEOUT); - it('should return a proper status code', () => { - expect(response.status).toBe(200); - }); - it('should respond with a proper record and token', () => { - expect(response.body.message).toEqual('You have been logged out.'); - }); - }); - - // ############################ - - describe('given nothing was send', () => { - beforeAll(async () => { - response = await supertest(app) - .get(ROUTE); - }, BEFORE_ALL_TIMEOUT); - it('should return a proper status code', () => { - expect(response.status).toBe(200); - }); - it('should respond with a proper record and token', () => { - expect(response.body.message).toEqual('You have been logged out.'); - }); - }); - -}); \ No newline at end of file diff --git a/utils/handleDB.js b/utils/handleDB.js index c6e95a8676a996b15b9f691ab70a6c4b58fee1bf..32cf015f795b81f816526b65ac0da13cd2fe3eef 100644 --- a/utils/handleDB.js +++ b/utils/handleDB.js @@ -135,6 +135,23 @@ export const findByIdAndUpdate = async (model, id, data) => { }; +/** + * Find a document by id and update it + * + * @param {mongoose model} model [required] model to search the record in + * @param {object} searchObject [required] search object as filter, i.e. {email: 'a@b.c', name: 'John'} + * @param {object} data [required] data to update the record with + * + * @return {object} the edited document + */ +export const findOneAndUpdate = async (model, searchObject, data) => { + try { + return model.updateOne(searchObject, data); + } catch (error) { + throw error; + } +}; + /** * extend a chat record with a new input/response-pair * diff --git a/utils/handleTokens.js b/utils/handleTokens.js index bc7c50a4a18483bb24c9f63694c2d7a653ba9fd7..028bdeadc8e11823c502e2334f8067f795cd743a 100644 --- a/utils/handleTokens.js +++ b/utils/handleTokens.js @@ -1,6 +1,6 @@ import jwt from 'jsonwebtoken'; import User from "../models/User.js"; -import { findByIdAndUpdate, findOneRecord } from './handleDB.js'; +import { findByIdAndUpdate, findOneAndUpdate, findOneRecord } from './handleDB.js'; /** * generate a "oneTime" JWT, containing a given object @@ -112,7 +112,7 @@ export const verifyRefreshToken = async (refreshToken) => { */ export const deleteRefreshToken = async (refreshToken) => { try { - const user = await User.updateOne({ refreshToken }, { $unset: { refreshToken: "" } }); + const user = await findOneAndUpdate(User, { refreshToken }, { $unset: { refreshToken: "" } }); return; } catch (error) { throw error;