From a4a30cb8f436a3585a5d933d8444bea5d2961b33 Mon Sep 17 00:00:00 2001 From: DonVietnam Date: Sun, 11 Sep 2022 15:33:02 +0300 Subject: [PATCH] test: add integration tests for Auth module. test: split tests into Unit and Integration tests. auth: fix bug when Auth.login method crashed when no user found in database. auth: fix bug when user could change password for every another user with Auth.changePassword method. auth: set minimum username length to 2 and minimum password length to 8 in Auth module. --- .github/workflows/ci.yml | 14 + CHANGELOG.md | 16 +- lib/auth-module.js | 11 +- lib/auth.js | 8 +- package-lock.json | 60 +- package.json | 7 +- src/index.js | 6 +- test/integration/auth.test.js | 166 +++++ test/integration/helpers.js | 131 ++++ test/{ => unit}/auth/auth.test.js | 106 ++-- test/{ => unit}/auth/prepare.js | 10 +- test/{ => unit}/connection/connection.test.js | 580 +++++++++--------- test/{ => unit}/connection/prepare.js | 6 +- .../console-transport.test.js | 28 +- test/{ => unit}/console-transport/prepare.js | 2 +- test/{ => unit}/db/db.test.js | 338 +++++----- test/{ => unit}/db/prepare.js | 4 +- test/{ => unit}/error/error.test.js | 2 +- .../http-connection/http-connection.test.js | 224 +++---- test/{ => unit}/http-connection/prepare.js | 2 +- .../instrospection/instrospection.test.js | 2 +- test/{ => unit}/logger/logger.test.js | 106 ++-- test/{ => unit}/logger/prepare.js | 2 +- .../modules-factory/modules-factory.test.js | 4 +- .../security-util/security-util.test.js | 2 +- test/{ => unit}/server/prepare.js | 18 +- test/{ => unit}/server/server.test.js | 274 ++++----- test/{ => unit}/session/prepare.js | 4 +- test/{ => unit}/session/session.test.js | 64 +- test/{ => unit}/utils/utils.test.js | 2 +- test/{ => unit}/ws-connection/prepare.js | 4 +- .../ws-connection/ws-connection.test.js | 58 +- 32 files changed, 1309 insertions(+), 952 deletions(-) create mode 100644 test/integration/auth.test.js create mode 100644 test/integration/helpers.js rename test/{ => unit}/auth/auth.test.js (97%) rename test/{ => unit}/auth/prepare.js (77%) rename test/{ => unit}/connection/connection.test.js (97%) rename test/{ => unit}/connection/prepare.js (86%) rename test/{ => unit}/console-transport/console-transport.test.js (97%) rename test/{ => unit}/console-transport/prepare.js (74%) rename test/{ => unit}/db/db.test.js (97%) rename test/{ => unit}/db/prepare.js (89%) rename test/{ => unit}/error/error.test.js (89%) rename test/{ => unit}/http-connection/http-connection.test.js (97%) rename test/{ => unit}/http-connection/prepare.js (93%) rename test/{ => unit}/instrospection/instrospection.test.js (92%) rename test/{ => unit}/logger/logger.test.js (97%) rename test/{ => unit}/logger/prepare.js (87%) rename test/{ => unit}/modules-factory/modules-factory.test.js (96%) rename test/{ => unit}/security-util/security-util.test.js (95%) rename test/{ => unit}/server/prepare.js (81%) rename test/{ => unit}/server/server.test.js (97%) rename test/{ => unit}/session/prepare.js (71%) rename test/{ => unit}/session/session.test.js (98%) rename test/{ => unit}/utils/utils.test.js (95%) rename test/{ => unit}/ws-connection/prepare.js (84%) rename test/{ => unit}/ws-connection/ws-connection.test.js (97%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1dd425e..4ce251c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,14 @@ on: - opened - edited - reopened + - closed + - synchronize + - converted_to_draft + - ready_for_review + - locked + - unlocked + - review_requested + - review_request branches: [ master ] workflow_dispatch: jobs: @@ -20,3 +28,9 @@ jobs: run: npm i - run: npm run lint - run: npm run test + - run: npm run docker + - run: npm install -g forever + - run: PGHOST=localhost PGUSER=test_user PGDATABASE=test_db PGPASSWORD=test_password PGPORT=5432 forever start ./src/index.js + - run: docker ps -a + - run: npm run integration + - run: forever stopall diff --git a/CHANGELOG.md b/CHANGELOG.md index 27faa84..934775d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [2.0.1] - 2022-09-08 +### Added + +- Integration tests for Auth module. + +### Changed + +- Fix bug when Auth.login method crashed when no user found in database. +- Fix bug when user could change password for every another user with Auth.changePassword method. +- Set minimum username length to 2 and minimum password length to 8 in Auth module. +- Split tests into Unit and Integration tests. + +## [2.0.1] - 2022-09-08 + ### Changed - Fix bug when SessionService.restoreSession method pass empty token to database. @@ -68,7 +81,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Logger service. - Introspection module. -[unreleased]: https://github.com/web-soft-llc/web-soft-server/compare/v2.0.1...master +[unreleased]: https://github.com/web-soft-llc/web-soft-server/compare/v2.0.2...master +[2.0.2]: https://github.com/web-soft-llc/web-soft-server/compare/v2.0.1...v2.0.2 [2.0.1]: https://github.com/web-soft-llc/web-soft-server/compare/v2.0.0...v2.0.1 [2.0.0]: https://github.com/web-soft-llc/web-soft-server/compare/v.1.0.2...v2.0.0 [1.0.2]: https://github.com/web-soft-llc/web-soft-server/compare/v.1.0.1...v.1.0.2 diff --git a/lib/auth-module.js b/lib/auth-module.js index 953ab1d..434c7ce 100644 --- a/lib/auth-module.js +++ b/lib/auth-module.js @@ -11,10 +11,12 @@ module.exports = { properties: { username: { type: 'string', + minLength: 2, description: 'Имя пользователя.' }, password: { description: 'Пароль.', + minLength: 8, type: 'string' } } @@ -103,19 +105,16 @@ module.exports = { public: false, description: 'Смена пароля текущего пользователя.', params: { - required: ['username', 'oldPassword', 'newPassword'], + required: ['oldPassword', 'newPassword'], properties: { - username: { - description: 'Имя пользователя.', - type: 'string' - }, oldPassword: { description: 'Старый пароль.', type: 'string' }, newPassword: { description: 'Новый пароль.', - type: 'string' + type: 'string', + minLength: 8 } } }, diff --git a/lib/auth.js b/lib/auth.js index 6fab8a7..4d08628 100644 --- a/lib/auth.js +++ b/lib/auth.js @@ -15,7 +15,7 @@ class Auth { const session = context.session; if (session.username !== username) { const user = await userService.getByUsername(username); - if (await security.validatePassword(password, user.password)) { + if (user.password && (await security.validatePassword(password, user.password))) { await context.startSession(user); } else { throw new ConnectionError(ERRORS.AUTHENTICATION_FAILED); @@ -33,12 +33,12 @@ class Auth { return { username, role, createdTime }; } - async changePassword({ username, oldPassword, newPassword }, context) { + async changePassword({ oldPassword, newPassword }, context) { const user = await context.user; if (await security.validatePassword(oldPassword, user.password)) { const newHashPassword = await security.hashPassword(newPassword); - await userService.updatePassword(username, newHashPassword); - return { username, role: user.role, createdTime: user.createdTime }; + await userService.updatePassword(user.username, newHashPassword); + return { username: user.username, role: user.role, createdTime: user.createdTime }; } else { throw new ConnectionError(ERRORS.AUTHENTICATION_FAILED); } diff --git a/package-lock.json b/package-lock.json index c3719d6..3d57853 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "web-soft-server", - "version": "1.0.2", + "version": "2.0.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "web-soft-server", - "version": "1.0.2", + "version": "2.0.1", "license": "MIT", "dependencies": { "ajv": "^8.10.0", @@ -15,6 +15,7 @@ }, "devDependencies": { "@types/node": "^16.11.7", + "cross-env": "^7.0.3", "eslint": "^7.8.1", "eslint-config-prettier": "^8.3.0", "jest": "^27.3.1", @@ -1533,14 +1534,20 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001299", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001299.tgz", - "integrity": "sha512-iujN4+x7QzqA2NCSrS5VUy+4gLmRd4xv6vbBBsmfVqTx8bLAD8097euLqQgKxSVLvxjSDcvF1T/i9ocgnUFexw==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } + "version": "1.0.30001393", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001393.tgz", + "integrity": "sha512-N/od11RX+Gsk+1qY/jbPa0R6zJupEa0lxeBG598EbrtblxVCTJsQwbRBm6+V+rxpc5lHKdsXb9RY83cZIPLseA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] }, "node_modules/chalk": { "version": "4.1.2", @@ -1672,6 +1679,24 @@ "safe-buffer": "~5.1.1" } }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -6303,9 +6328,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001299", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001299.tgz", - "integrity": "sha512-iujN4+x7QzqA2NCSrS5VUy+4gLmRd4xv6vbBBsmfVqTx8bLAD8097euLqQgKxSVLvxjSDcvF1T/i9ocgnUFexw==", + "version": "1.0.30001393", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001393.tgz", + "integrity": "sha512-N/od11RX+Gsk+1qY/jbPa0R6zJupEa0lxeBG598EbrtblxVCTJsQwbRBm6+V+rxpc5lHKdsXb9RY83cZIPLseA==", "dev": true }, "chalk": { @@ -6414,6 +6439,15 @@ "safe-buffer": "~5.1.1" } }, + "cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.1" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", diff --git a/package.json b/package.json index b470289..d50080a 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "name": "DonVietnam", "email": "don.vietnam.js@gmail.com" }, - "version": "2.0.1", + "version": "2.0.2", "description": "Server for web-soft-projects.", "license": "MIT", "keywords": [ @@ -37,16 +37,19 @@ "start": "node index.js", "dev": "nodemon --signal SIGTERM --exec \"node src/index.js\"", "dev-docker": "docker volume create pgdata && docker compose up -d && npm run dev", + "docker": "docker volume create pgdata && docker compose up -d", "certs": "bash ./certs.sh", "lint": "eslint --ignore-path .eslintignore .", "fix": "eslint --fix --ignore-path .eslintignore .", - "test": "jest test" + "test": "jest test/unit", + "integration": "cross-env PGHOST=localhost PGUSER=test_user PGDATABASE=test_db PGPASSWORD=test_password PGPORT=5432 HOST=localhost PORT=8000 jest test/integration" }, "engines": { "node": ">=16" }, "devDependencies": { "@types/node": "^16.11.7", + "cross-env": "^7.0.3", "eslint": "^7.8.1", "eslint-config-prettier": "^8.3.0", "jest": "^27.3.1", diff --git a/src/index.js b/src/index.js index 0e44f58..a73ee3e 100644 --- a/src/index.js +++ b/src/index.js @@ -1,13 +1,9 @@ -const fs = require('fs'); const { Server, logger, AuthModule } = require('../index'); const modules = require('./modules'); -const key = fs.readFileSync('./certs/localhost.key'); -const cert = fs.readFileSync('./certs/localhost.crt'); - const start = async () => { try { - const server = new Server({ host: '0.0.0.0', port: 443, cors: false, key, cert, secure: true }); + const server = new Server({ host: '0.0.0.0', port: 8000, cors: false }); server.start({ ...modules, auth: AuthModule }); } catch (error) { logger.fatal(error); diff --git a/test/integration/auth.test.js b/test/integration/auth.test.js new file mode 100644 index 0000000..2669c49 --- /dev/null +++ b/test/integration/auth.test.js @@ -0,0 +1,166 @@ +const { test, expect, describe, beforeEach, afterAll } = require('@jest/globals'); +const { call, clearTables, closeDatabaseConnection, addTestData, clearCookies, setCookie, auth } = require('./helpers'); + +const testUserData = { + username: 'test_user', + password: + '$scrypt$N=32768,r=8,p=1,maxmem=67108864$WDvlWv547xK6YjokhmlArebEs92/Ug+a8GtU2b+ER84$11RTxyQMbuyft3XJ7nethkvNALfSREfemmr0phYAvam8MC4qp0lSAe91DDmZC2FufT0RKTo18p8do+jj+M8oMw', + role: 'user' +}; + +describe('Tests for auth module.', () => { + beforeEach(async () => { + await clearTables('SystemUser', 'Session'); + }); + + test('AuthLogin_SuccessfulRequest', async () => { + await addTestData('SystemUser', testUserData); + await expect(call('auth/login', { username: 'test_user', password: 'test_password' })).resolves.toEqual( + expect.objectContaining({ + username: 'test_user', + role: 'user', + createdTime: expect.any(String) + }) + ); + }); + + test('AuthLogin_InvalidParams_Password', async () => { + await addTestData('SystemUser', testUserData); + await expect(call('auth/login', { username: 'test_user', password: 'incorrect_password' })).resolves.toEqual( + expect.objectContaining({ + code: expect.any(Number), + message: expect.stringContaining('Authentication') + }) + ); + }); + + test('AuthLogin_NotFound_User', async () => { + await expect(call('auth/login', { username: 'test_user', password: 'test_password' })).resolves.toEqual( + expect.objectContaining({ + code: expect.any(Number), + message: expect.stringContaining('Authentication') + }) + ); + }); + + test('AuthRegister_SuccessfulRequest', async () => { + await expect(call('auth/register', { username: 'test_user', password: 'test_password' })).resolves.toEqual( + expect.objectContaining({ + username: 'test_user', + role: 'user', + createdTime: expect.any(String) + }) + ); + }); + + test('AuthRegister_InvalidParams_EmptyUsername', async () => { + await expect(call('auth/register', { username: '', password: 'test_password' })).resolves.toEqual( + expect.objectContaining({ + code: expect.any(Number), + message: expect.stringContaining('Invalid') + }) + ); + }); + + test('AuthRegister_InvalidParams_EmptyPassword', async () => { + await expect(call('auth/register', { username: 'test_user', password: '' })).resolves.toEqual( + expect.objectContaining({ + code: expect.any(Number), + message: expect.stringContaining('Invalid') + }) + ); + }); + + test('AuthRegister_DatabaseConflict_SameUsername', async () => { + await addTestData('SystemUser', testUserData); + await expect(call('auth/register', { username: 'test_user', password: 'test_password' })).resolves.toEqual( + expect.objectContaining({ + code: expect.any(Number), + message: expect.stringContaining('Conflict') + }) + ); + }); + + test('AuthLogout_SuccessfulRequest', async () => { + await auth('user'); + await expect(call('auth/logout', {})).resolves.toEqual({}); + }); + + test('AuthLogout_InvalidParams_NotAuth', async () => { + clearCookies(); + await expect(call('auth/logout', {})).resolves.toEqual( + expect.objectContaining({ + code: expect.any(Number), + message: expect.stringContaining('Authentication') + }) + ); + }); + + test('AuthMe_SuccessfulRequest', async () => { + const user = await auth('user'); + await expect(call('auth/me', {})).resolves.toEqual( + expect.objectContaining({ + username: user.username, + role: 'user', + createdTime: expect.any(String) + }) + ); + }); + + test('AuthMe_InvalidParams_NotAuth', async () => { + await expect(call('auth/me', {})).resolves.toEqual( + expect.objectContaining({ + code: expect.any(Number), + message: expect.stringContaining('Authentication') + }) + ); + }); + + test('AuthChangePassword_SuccessfulRequest', async () => { + const user = await auth('user'); + await expect( + call('auth/changePassword', { oldPassword: user.password, newPassword: 'new_password' }) + ).resolves.toEqual( + expect.objectContaining({ + username: user.username, + role: 'user', + createdTime: expect.any(String) + }) + ); + }); + + test('AuthChangePassword_InvalidParams_OldPassword', async () => { + await auth('user'); + await expect( + call('auth/changePassword', { + oldPassword: 'invalid_password', + newPassword: 'new_password' + }) + ).resolves.toEqual( + expect.objectContaining({ + code: expect.any(Number), + message: expect.stringContaining('Authentication') + }) + ); + }); + + test('AuthChangePassword_InvalidParams_NewPassword', async () => { + const user = await auth('user'); + await expect( + call('auth/changePassword', { + oldPassword: user.password, + newPassword: '' + }) + ).resolves.toEqual( + expect.objectContaining({ + code: expect.any(Number), + message: expect.stringContaining('Invalid') + }) + ); + }); + + afterAll(async () => { + await clearTables('SystemUser', 'Session'); + await closeDatabaseConnection(); + }); +}); diff --git a/test/integration/helpers.js b/test/integration/helpers.js new file mode 100644 index 0000000..26e3a2d --- /dev/null +++ b/test/integration/helpers.js @@ -0,0 +1,131 @@ +// COMMON TESTING CASES: +// - SuccessfulRequest +// - InvalidParams +// - DatabaseConflict +// - NotFound + +const transport = require('http'); +const { Pool } = require('pg'); + +const pool = new Pool(); +let cookies = ''; + +const setCookie = (key, value) => { + cookies += `${key}=${value};`; +}; + +const clearCookies = () => { + cookies = ''; +}; + +const closeDatabaseConnection = async () => { + return await pool.end(); +}; + +const inserts = (entry) => { + const numbers = []; + const keys = Object.keys(entry); + if (keys.length) { + for (let i = 1; i <= keys.length; i++) { + numbers.push(`$${i}`); + } + return [`"${keys.join('", "')}"`, numbers.join(', '), Object.values(entry)]; + } + return ['', '', []]; +}; + +const addTestData = async (table, ...entries) => { + const promises = []; + for (const entry of entries) { + const [keys, numbers, values] = inserts(entry); + promises.push(pool.query(`INSERT INTO "${table}" (${keys}) VALUES (${numbers})`, values)); + } + + return await Promise.allSettled(promises); +}; + +const clearTables = async (...names) => { + const promises = []; + for (const name of names) { + promises.push(pool.query(`DELETE FROM "${name}";`)); + } + return Promise.allSettled(promises); +}; + +const parseCookies = (setCookies) => { + let cookies = ''; + for (const setCookie of setCookies) { + cookies += setCookie.split(';')[0]; + } + return cookies; +}; + +const request = (data, hostname, port) => + new Promise((resolve, reject) => { + const request = transport.request( + { + hostname, + port, + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Content-Length': data.length, + Cookie: cookies + }, + rejectUnauthorized: false + }, + (response) => { + let data = ''; + cookies = parseCookies(response.headers['set-cookie'] || []); + response.on('data', (chunk) => { + data += chunk; + }); + + response.on('end', () => { + resolve(JSON.parse(data)); + }); + } + ); + request.on('error', (error) => { + reject(error); + }); + request.write(data); + request.end(); + }); + +const call = async (method, params) => { + const data = { jsonrpc: '2.0', method, params }; + const { result, error } = await request(JSON.stringify(data), process.env.HOST, process.env.PORT); + return result ? result : error ? error : {}; +}; + +const auth = async (role) => { + const user = { + username: 'test_user', + password: + '$scrypt$N=32768,r=8,p=1,maxmem=67108864$WDvlWv547xK6YjokhmlArebEs92/Ug+a8GtU2b+ER84$11RTxyQMbuyft3XJ7nethkvNALfSREfemmr0phYAvam8MC4qp0lSAe91DDmZC2FufT0RKTo18p8do+jj+M8oMw', + role + }; + + const session = { + token: '409715e3-9c5e-474a-8070-884b23ac3a6a', + username: 'test_user' + }; + + clearCookies(); + setCookie('token', session.token); + await addTestData('SystemUser', user); + await addTestData('Session', session); + + return { ...user, password: 'test_password' }; +}; + +module.exports = { + call, + clearTables, + closeDatabaseConnection, + addTestData, + clearCookies, + setCookie, + auth +}; diff --git a/test/auth/auth.test.js b/test/unit/auth/auth.test.js similarity index 97% rename from test/auth/auth.test.js rename to test/unit/auth/auth.test.js index 7dcb775..492a6e2 100644 --- a/test/auth/auth.test.js +++ b/test/unit/auth/auth.test.js @@ -1,53 +1,53 @@ -const { test, expect } = require('@jest/globals'); -const { createAuth, getContext, userService } = require('./prepare'); - -test('AuthLogin_CallUserServiceGetByUsername_UserHasNoSession', async () => { - const auth = createAuth(); - await auth.login({ username: 'username', password: 'testPassword' }, getContext()); - expect(userService.getByUsername.mock.calls.length).toEqual(1); -}); - -test('AuthLogin_NotCallUserServiceGetByUsername_UserHasSession', async () => { - const auth = createAuth(); - await auth.login({ username: 'username', password: 'testPassword' }, getContext({ username: 'username' })); - expect(userService.getByUsername.mock.calls.length).toEqual(0); -}); - -test('AuthLogin_CallStartSession_ValidPassword', async () => { - const auth = createAuth(); - const context = getContext(); - await auth.login({ username: 'username', password: 'testPassword' }, context); - expect(context.startSession.mock.calls.length).toEqual(1); -}); - -test('AuthLogin_ThrowError_InvalidPassword', async () => { - const auth = createAuth(); - const promise = auth.login({ username: 'username', password: 'invalidPassword' }, getContext()); - await expect(promise).rejects.toThrowError('Authentication failed.'); -}); - -test('AuthChangePassword_ThrowError_InvalidPassword', async () => { - const auth = createAuth(); - const promise = auth.changePassword( - { - username: 'username', - oldPassword: 'invalidPassword', - newPassword: 'newPassword' - }, - getContext() - ); - await expect(promise).rejects.toThrowError('Authentication failed.'); -}); - -test('AuthChangePassword_CallUserServiceUpdatePassword_ValidPassword', async () => { - const auth = createAuth(); - await auth.changePassword( - { - username: 'username', - oldPassword: 'testPassword', - newPassword: 'newPassword' - }, - getContext() - ); - expect(userService.updatePassword.mock.calls.length).toEqual(1); -}); +const { test, expect } = require('@jest/globals'); +const { createAuth, getContext, userService } = require('./prepare'); + +test('AuthLogin_CallUserServiceGetByUsername_UserHasNoSession', async () => { + const auth = createAuth(); + await auth.login({ username: 'username', password: 'testPassword' }, getContext()); + expect(userService.getByUsername.mock.calls.length).toEqual(1); +}); + +test('AuthLogin_NotCallUserServiceGetByUsername_UserHasSession', async () => { + const auth = createAuth(); + await auth.login({ username: 'username', password: 'testPassword' }, getContext({ username: 'username' })); + expect(userService.getByUsername.mock.calls.length).toEqual(0); +}); + +test('AuthLogin_CallStartSession_ValidPassword', async () => { + const auth = createAuth(); + const context = getContext(); + await auth.login({ username: 'username', password: 'testPassword' }, context); + expect(context.startSession.mock.calls.length).toEqual(1); +}); + +test('AuthLogin_ThrowError_InvalidPassword', async () => { + const auth = createAuth(); + const promise = auth.login({ username: 'username', password: 'invalidPassword' }, getContext()); + await expect(promise).rejects.toThrowError('Authentication failed.'); +}); + +test('AuthChangePassword_ThrowError_InvalidPassword', async () => { + const auth = createAuth(); + const promise = auth.changePassword( + { + username: 'username', + oldPassword: 'invalidPassword', + newPassword: 'newPassword' + }, + getContext() + ); + await expect(promise).rejects.toThrowError('Authentication failed.'); +}); + +test('AuthChangePassword_CallUserServiceUpdatePassword_ValidPassword', async () => { + const auth = createAuth(); + await auth.changePassword( + { + username: 'username', + oldPassword: 'testPassword', + newPassword: 'newPassword' + }, + getContext() + ); + expect(userService.updatePassword.mock.calls.length).toEqual(1); +}); diff --git a/test/auth/prepare.js b/test/unit/auth/prepare.js similarity index 77% rename from test/auth/prepare.js rename to test/unit/auth/prepare.js index 192333b..ccf545a 100644 --- a/test/auth/prepare.js +++ b/test/unit/auth/prepare.js @@ -1,8 +1,8 @@ -const { security } = require('../../lib/security-util'); -const { userService } = require('../../lib/user'); -const { Auth } = require('../../lib/auth'); +const { security } = require('../../../lib/security-util'); +const { userService } = require('../../../lib/user'); +const { Auth } = require('../../../lib/auth'); -jest.mock('../../lib/security-util', () => { +jest.mock('../../../lib/security-util', () => { return { security: { validatePassword: jest.fn((password) => { @@ -13,7 +13,7 @@ jest.mock('../../lib/security-util', () => { }; }); -jest.mock('../../lib/user', () => { +jest.mock('../../../lib/user', () => { return { userService: { getByUsername: jest.fn((username) => { diff --git a/test/connection/connection.test.js b/test/unit/connection/connection.test.js similarity index 97% rename from test/connection/connection.test.js rename to test/unit/connection/connection.test.js index dc4a2bf..ef1a5dd 100644 --- a/test/connection/connection.test.js +++ b/test/unit/connection/connection.test.js @@ -1,290 +1,290 @@ -const { test, expect } = require('@jest/globals'); -const { - createClient, - CORRECT_EVENT_DATA, - createConnection, - CORRECT_JSON, - CORRECT_JSON_STRING, - TEST_ERROR -} = require('./prepare'); - -test('ClientEmit_ThrowError_NoConnection', async () => { - const client = createClient(); - const promise = client.emit('test-event', { test: 'test' }); - await expect(promise).rejects.toThrowError('Inappropriate transport protocol.'); -}); - -test('ClientEmit_ThrowError_ConnectionHasNoConnectionProperty', async () => { - const client = createClient({}); - const promise = client.emit('test-event', { test: 'test' }); - await expect(promise).rejects.toThrowError('Inappropriate transport protocol.'); -}); - -test('ClientEmit_CallConnectionSendProperData_ConnectionsHasProperConncection', async () => { - const mockSend = jest.fn(() => {}); - const client = createClient({ connection: {}, send: mockSend }); - await client.emit('test-event', { test: 'test' }); - expect(mockSend.mock.calls[0][0]).toEqual(CORRECT_EVENT_DATA); -}); - -test('ClientСheckConnection_ReturnTrue_ConnectionsHasProperClientsConnection', async () => { - const client = createClient({ connection: {} }); - expect(client.checkConnection()).toEqual(true); -}); - -test('ClientСheckConnection_ReturnFalse_ConnectionsHasNoClientsConnection', async () => { - const client = createClient(); - expect(client.checkConnection()).toEqual(false); -}); - -test('ClientСheckConnection_ReturnFalse_ConnectionsHasNotProperClientsConnection', async () => { - const client = createClient({}); - expect(client.checkConnection()).toEqual(false); -}); - -test('ClientStartSession_ThrowError_ConnectionsHasNoClientsConnection', async () => { - const client = createClient(); - const promise = client.startSession({}); - await expect(promise).rejects.toThrowError('Inappropriate transport protocol.'); -}); - -test('ClientStartSession_ThrowError_ConnectionsHasNoStartUserSessionMethod', async () => { - const client = createClient({}); - const promise = client.startSession({}); - await expect(promise).rejects.toThrowError('Inappropriate transport protocol.'); -}); - -test('ClientStartSession_CallConnectionStartUserSession_ProperConnectionExists', async () => { - const mockStartUserSession = jest.fn(() => {}); - const client = createClient({ startUserSession: mockStartUserSession }); - await client.startSession({}); - expect(mockStartUserSession.mock.calls.length).toEqual(1); -}); - -test('ClientDeleteSession_ThrowError_ConnectionsHasNoClientsConnection', async () => { - const client = createClient(); - const promise = client.deleteSession({}); - await expect(promise).rejects.toThrowError('Inappropriate transport protocol.'); -}); - -test('ClientDeleteSession_ThrowError_ConnectionsHasNoDeleteUserSessionMethod', async () => { - const client = createClient({}); - const promise = client.deleteSession({}); - await expect(promise).rejects.toThrowError('Inappropriate transport protocol.'); -}); - -test('ClientDeleteSession_CallConnectionDeleteUserSession_ProperConnectionExists', async () => { - const mockDeleteUserSession = jest.fn(() => {}); - const client = createClient({ deleteUserSession: mockDeleteUserSession }); - await client.deleteSession({}); - expect(mockDeleteUserSession.mock.calls.length).toEqual(1); -}); - -test('ConnectionInitialise_CallClientSetUser_SessionExists', async () => { - const connection = createConnection({ username: 'username' }, {}); - await connection.initialise(); - expect(connection.client.setUser.mock.calls.length).toEqual(1); -}); - -test('ConnectionInitialise_NotCallClientSetUser_SessionNotExists', async () => { - const connection = createConnection({}, {}); - await connection.initialise(); - expect(connection.client.setUser.mock.calls.length).toEqual(0); -}); - -test('ConnectionValidateStructure_ThrowError_InvalidJSON', async () => { - const connection = createConnection({}, {}); - const invalidJSON = { some: 'json' }; - expect(() => connection.validateStructure(invalidJSON)).toThrowError('The JSON sent is not a valid Request object.'); -}); - -test('ConnectionValidateStructure_ReturnUndefined_ValidJSON', async () => { - const connection = createConnection({}, {}); - expect(connection.validateStructure(CORRECT_JSON)).toBeUndefined(); -}); - -test('ConnectionParseJSON_ReturnObject_ValidJSON', async () => { - const connection = createConnection({}, {}); - expect(connection.parseJSON(CORRECT_JSON_STRING)).toEqual(CORRECT_JSON); -}); - -test('ConnectionParseJSON_ThrowError_InvalidJSON', async () => { - const connection = createConnection({}, {}); - expect(() => connection.parseJSON('{invalid: json')).toThrowError('Invalid JSON was received by the server.'); -}); - -test('ConnectionError_NoDataAdded_ErrorHasNoDataProperty', async () => { - const connection = createConnection({}, {}); - await connection.error(TEST_ERROR, 2); - expect(connection.send.mock.calls[0][0]).toEqual({ jsonrpc: '2.0', id: 2, error: TEST_ERROR }); -}); - -test('ConnectionError_IdNull_NoIdProvided', async () => { - const connection = createConnection({}, {}); - await connection.error(TEST_ERROR); - expect(connection.send.mock.calls[0][0]).toEqual({ jsonrpc: '2.0', id: null, error: TEST_ERROR }); -}); - -test('ConnectionMessage_CallConnectionErrorWithThrowedError_OnErrorAndErrorPassIsTrue', async () => { - const connection = createConnection({}, {}); - connection.parseJSON = jest.fn(() => { - throw { TEST_ERROR, pass: true }; - }); - connection.error = jest.fn(); - await connection.message(); - expect(connection.error.mock.calls[0][0]).toEqual({ TEST_ERROR, pass: true }); -}); - -test('ConnectionMessage_ThrowError_OnErrorAndErrorPassIsNotTrue', async () => { - const connection = createConnection({}, {}); - connection.parseJSON = jest.fn(() => { - throw new Error('test-message'); - }); - connection.error = jest.fn(() => {}); - const promise = connection.message(); - await expect(promise).rejects.toThrowError('test-message'); -}); - -test('ConnectionCallProcedure_ThrowError_ConnectionModulesHasNoModuleWithModuleName', async () => { - const connection = createConnection({}, {}); - const promise = connection.callProcedure('some', 'method', {}); - await expect(promise).rejects.toThrowError('The method does not exist / is not available.'); -}); - -test('ConnectionCallProcedure_ThrowError_NoInstanceFoundInModule', async () => { - const connection = createConnection({}, { some: {} }); - const promise = connection.callProcedure('some', 'method', {}); - await expect(promise).rejects.toThrowError('The method does not exist / is not available.'); -}); - -test('ConnectionCallProcedure_ThrowError_InstanceHasNoMethod', async () => { - const connection = createConnection({}, { some: { instance: {} } }); - const promise = connection.callProcedure('some', 'method', {}); - await expect(promise).rejects.toThrowError('The method does not exist / is not available.'); -}); - -test('ConnectionCallProcedure_ThrowError_ParamsForMethodIsNotValid', async () => { - const modules = { - some: { - instance: { method: () => {} }, - validators: { - method: { - params: () => { - return false; - } - } - } - } - }; - const connection = createConnection({}, modules); - const promise = connection.callProcedure('some', 'method', {}); - await expect(promise).rejects.toThrowError('Invalid method parameter(s).'); -}); - -test('ConnectionCallProcedure_ThrowError_MethodIsNotPublicCientHasNoUsername', async () => { - const modules = { - some: { - instance: { method: () => {} }, - validators: { - method: { - params: () => { - return true; - } - } - }, - schema: { method: { public: false } } - } - }; - const connection = createConnection({}, modules); - const promise = connection.callProcedure('some', 'method', {}); - await expect(promise).rejects.toThrowError('Authentication credentials required.'); -}); - -test('ConnectionCallProcedure_ThrowError_UserHasNoProperRole', async () => { - const modules = { - some: { - instance: { method: () => {} }, - validators: { - method: { - params: () => { - return true; - } - } - }, - schema: { method: { public: true, roles: ['needRole'] } } - } - }; - const connection = createConnection({}, modules); - connection.client.user = { username: 'username', role: 'role' }; - const promise = connection.callProcedure('some', 'method', {}); - await expect(promise).rejects.toThrowError('Permission denied.'); -}); - -test('ConnectionCallProcedure_ThrowError_InvalidResult', async () => { - const modules = { - some: { - instance: { method: () => {} }, - validators: { - method: { - params: () => { - return true; - }, - result: () => { - return false; - } - } - }, - schema: { method: { public: true, roles: [] } } - } - }; - const connection = createConnection({}, modules); - connection.client.user = { username: 'username', role: 'role' }; - const promise = connection.callProcedure('some', 'method', {}); - await expect(promise).rejects.toThrowError(`Invalid response. From some/method. Recieve {}.`); -}); - -test('ConnectionCallProcedure_ReturnResult_MethodIsNotPublicCientHasUsername', async () => { - const modules = { - some: { - instance: { method: () => {} }, - validators: { - method: { - params: () => { - return true; - }, - result: () => { - return true; - } - } - }, - schema: { method: { public: false, roles: [] } } - } - }; - const connection = createConnection({}, modules); - connection.client.user = { username: 'username', role: 'role' }; - const promise = connection.callProcedure('some', 'method', {}); - await expect(promise).resolves.toEqual({}); -}); - -test('ConnectionCallProcedure_ReturnResult_UserHasProperRole', async () => { - const modules = { - some: { - instance: { method: () => {} }, - validators: { - method: { - params: () => { - return true; - }, - result: () => { - return true; - } - } - }, - schema: { method: { public: true, roles: ['role'] } } - } - }; - const connection = createConnection({}, modules); - connection.client.user = { username: 'username', role: 'role' }; - const promise = connection.callProcedure('some', 'method', {}); - await expect(promise).resolves.toEqual({}); -}); +const { test, expect } = require('@jest/globals'); +const { + createClient, + CORRECT_EVENT_DATA, + createConnection, + CORRECT_JSON, + CORRECT_JSON_STRING, + TEST_ERROR +} = require('./prepare'); + +test('ClientEmit_ThrowError_NoConnection', async () => { + const client = createClient(); + const promise = client.emit('test-event', { test: 'test' }); + await expect(promise).rejects.toThrowError('Inappropriate transport protocol.'); +}); + +test('ClientEmit_ThrowError_ConnectionHasNoConnectionProperty', async () => { + const client = createClient({}); + const promise = client.emit('test-event', { test: 'test' }); + await expect(promise).rejects.toThrowError('Inappropriate transport protocol.'); +}); + +test('ClientEmit_CallConnectionSendProperData_ConnectionsHasProperConncection', async () => { + const mockSend = jest.fn(() => {}); + const client = createClient({ connection: {}, send: mockSend }); + await client.emit('test-event', { test: 'test' }); + expect(mockSend.mock.calls[0][0]).toEqual(CORRECT_EVENT_DATA); +}); + +test('ClientСheckConnection_ReturnTrue_ConnectionsHasProperClientsConnection', async () => { + const client = createClient({ connection: {} }); + expect(client.checkConnection()).toEqual(true); +}); + +test('ClientСheckConnection_ReturnFalse_ConnectionsHasNoClientsConnection', async () => { + const client = createClient(); + expect(client.checkConnection()).toEqual(false); +}); + +test('ClientСheckConnection_ReturnFalse_ConnectionsHasNotProperClientsConnection', async () => { + const client = createClient({}); + expect(client.checkConnection()).toEqual(false); +}); + +test('ClientStartSession_ThrowError_ConnectionsHasNoClientsConnection', async () => { + const client = createClient(); + const promise = client.startSession({}); + await expect(promise).rejects.toThrowError('Inappropriate transport protocol.'); +}); + +test('ClientStartSession_ThrowError_ConnectionsHasNoStartUserSessionMethod', async () => { + const client = createClient({}); + const promise = client.startSession({}); + await expect(promise).rejects.toThrowError('Inappropriate transport protocol.'); +}); + +test('ClientStartSession_CallConnectionStartUserSession_ProperConnectionExists', async () => { + const mockStartUserSession = jest.fn(() => {}); + const client = createClient({ startUserSession: mockStartUserSession }); + await client.startSession({}); + expect(mockStartUserSession.mock.calls.length).toEqual(1); +}); + +test('ClientDeleteSession_ThrowError_ConnectionsHasNoClientsConnection', async () => { + const client = createClient(); + const promise = client.deleteSession({}); + await expect(promise).rejects.toThrowError('Inappropriate transport protocol.'); +}); + +test('ClientDeleteSession_ThrowError_ConnectionsHasNoDeleteUserSessionMethod', async () => { + const client = createClient({}); + const promise = client.deleteSession({}); + await expect(promise).rejects.toThrowError('Inappropriate transport protocol.'); +}); + +test('ClientDeleteSession_CallConnectionDeleteUserSession_ProperConnectionExists', async () => { + const mockDeleteUserSession = jest.fn(() => {}); + const client = createClient({ deleteUserSession: mockDeleteUserSession }); + await client.deleteSession({}); + expect(mockDeleteUserSession.mock.calls.length).toEqual(1); +}); + +test('ConnectionInitialise_CallClientSetUser_SessionExists', async () => { + const connection = createConnection({ username: 'username' }, {}); + await connection.initialise(); + expect(connection.client.setUser.mock.calls.length).toEqual(1); +}); + +test('ConnectionInitialise_NotCallClientSetUser_SessionNotExists', async () => { + const connection = createConnection({}, {}); + await connection.initialise(); + expect(connection.client.setUser.mock.calls.length).toEqual(0); +}); + +test('ConnectionValidateStructure_ThrowError_InvalidJSON', async () => { + const connection = createConnection({}, {}); + const invalidJSON = { some: 'json' }; + expect(() => connection.validateStructure(invalidJSON)).toThrowError('The JSON sent is not a valid Request object.'); +}); + +test('ConnectionValidateStructure_ReturnUndefined_ValidJSON', async () => { + const connection = createConnection({}, {}); + expect(connection.validateStructure(CORRECT_JSON)).toBeUndefined(); +}); + +test('ConnectionParseJSON_ReturnObject_ValidJSON', async () => { + const connection = createConnection({}, {}); + expect(connection.parseJSON(CORRECT_JSON_STRING)).toEqual(CORRECT_JSON); +}); + +test('ConnectionParseJSON_ThrowError_InvalidJSON', async () => { + const connection = createConnection({}, {}); + expect(() => connection.parseJSON('{invalid: json')).toThrowError('Invalid JSON was received by the server.'); +}); + +test('ConnectionError_NoDataAdded_ErrorHasNoDataProperty', async () => { + const connection = createConnection({}, {}); + await connection.error(TEST_ERROR, 2); + expect(connection.send.mock.calls[0][0]).toEqual({ jsonrpc: '2.0', id: 2, error: TEST_ERROR }); +}); + +test('ConnectionError_IdNull_NoIdProvided', async () => { + const connection = createConnection({}, {}); + await connection.error(TEST_ERROR); + expect(connection.send.mock.calls[0][0]).toEqual({ jsonrpc: '2.0', id: null, error: TEST_ERROR }); +}); + +test('ConnectionMessage_CallConnectionErrorWithThrowedError_OnErrorAndErrorPassIsTrue', async () => { + const connection = createConnection({}, {}); + connection.parseJSON = jest.fn(() => { + throw { TEST_ERROR, pass: true }; + }); + connection.error = jest.fn(); + await connection.message(); + expect(connection.error.mock.calls[0][0]).toEqual({ TEST_ERROR, pass: true }); +}); + +test('ConnectionMessage_ThrowError_OnErrorAndErrorPassIsNotTrue', async () => { + const connection = createConnection({}, {}); + connection.parseJSON = jest.fn(() => { + throw new Error('test-message'); + }); + connection.error = jest.fn(() => {}); + const promise = connection.message(); + await expect(promise).rejects.toThrowError('test-message'); +}); + +test('ConnectionCallProcedure_ThrowError_ConnectionModulesHasNoModuleWithModuleName', async () => { + const connection = createConnection({}, {}); + const promise = connection.callProcedure('some', 'method', {}); + await expect(promise).rejects.toThrowError('The method does not exist / is not available.'); +}); + +test('ConnectionCallProcedure_ThrowError_NoInstanceFoundInModule', async () => { + const connection = createConnection({}, { some: {} }); + const promise = connection.callProcedure('some', 'method', {}); + await expect(promise).rejects.toThrowError('The method does not exist / is not available.'); +}); + +test('ConnectionCallProcedure_ThrowError_InstanceHasNoMethod', async () => { + const connection = createConnection({}, { some: { instance: {} } }); + const promise = connection.callProcedure('some', 'method', {}); + await expect(promise).rejects.toThrowError('The method does not exist / is not available.'); +}); + +test('ConnectionCallProcedure_ThrowError_ParamsForMethodIsNotValid', async () => { + const modules = { + some: { + instance: { method: () => {} }, + validators: { + method: { + params: () => { + return false; + } + } + } + } + }; + const connection = createConnection({}, modules); + const promise = connection.callProcedure('some', 'method', {}); + await expect(promise).rejects.toThrowError('Invalid method parameter(s).'); +}); + +test('ConnectionCallProcedure_ThrowError_MethodIsNotPublicCientHasNoUsername', async () => { + const modules = { + some: { + instance: { method: () => {} }, + validators: { + method: { + params: () => { + return true; + } + } + }, + schema: { method: { public: false } } + } + }; + const connection = createConnection({}, modules); + const promise = connection.callProcedure('some', 'method', {}); + await expect(promise).rejects.toThrowError('Authentication credentials required.'); +}); + +test('ConnectionCallProcedure_ThrowError_UserHasNoProperRole', async () => { + const modules = { + some: { + instance: { method: () => {} }, + validators: { + method: { + params: () => { + return true; + } + } + }, + schema: { method: { public: true, roles: ['needRole'] } } + } + }; + const connection = createConnection({}, modules); + connection.client.user = { username: 'username', role: 'role' }; + const promise = connection.callProcedure('some', 'method', {}); + await expect(promise).rejects.toThrowError('Permission denied.'); +}); + +test('ConnectionCallProcedure_ThrowError_InvalidResult', async () => { + const modules = { + some: { + instance: { method: () => {} }, + validators: { + method: { + params: () => { + return true; + }, + result: () => { + return false; + } + } + }, + schema: { method: { public: true, roles: [] } } + } + }; + const connection = createConnection({}, modules); + connection.client.user = { username: 'username', role: 'role' }; + const promise = connection.callProcedure('some', 'method', {}); + await expect(promise).rejects.toThrowError(`Invalid response. From some/method. Recieve {}.`); +}); + +test('ConnectionCallProcedure_ReturnResult_MethodIsNotPublicCientHasUsername', async () => { + const modules = { + some: { + instance: { method: () => {} }, + validators: { + method: { + params: () => { + return true; + }, + result: () => { + return true; + } + } + }, + schema: { method: { public: false, roles: [] } } + } + }; + const connection = createConnection({}, modules); + connection.client.user = { username: 'username', role: 'role' }; + const promise = connection.callProcedure('some', 'method', {}); + await expect(promise).resolves.toEqual({}); +}); + +test('ConnectionCallProcedure_ReturnResult_UserHasProperRole', async () => { + const modules = { + some: { + instance: { method: () => {} }, + validators: { + method: { + params: () => { + return true; + }, + result: () => { + return true; + } + } + }, + schema: { method: { public: true, roles: ['role'] } } + } + }; + const connection = createConnection({}, modules); + connection.client.user = { username: 'username', role: 'role' }; + const promise = connection.callProcedure('some', 'method', {}); + await expect(promise).resolves.toEqual({}); +}); diff --git a/test/connection/prepare.js b/test/unit/connection/prepare.js similarity index 86% rename from test/connection/prepare.js rename to test/unit/connection/prepare.js index 42136a5..75776e7 100644 --- a/test/connection/prepare.js +++ b/test/unit/connection/prepare.js @@ -1,4 +1,4 @@ -const { Client, connections, Connection } = require('../../lib/connection'); +const { Client, connections, Connection } = require('../../../lib/connection'); const CORRECT_EVENT_DATA = { jsonrpc: '2.0', @@ -9,7 +9,7 @@ const CORRECT_JSON = { jsonrpc: '2.0', method: '', id: 0, params: {} }; const CORRECT_JSON_STRING = JSON.stringify(CORRECT_JSON); const TEST_ERROR = { message: 'test-message', code: 'test-code' }; -jest.mock('../../lib/session', () => { +jest.mock('../../../lib/session', () => { return { sessionService: { restoreSession: jest.fn(({ username }) => { @@ -19,7 +19,7 @@ jest.mock('../../lib/session', () => { }; }); -jest.mock('../../lib/logger', () => { +jest.mock('../../../lib/logger', () => { return { logger: { error: jest.fn() diff --git a/test/console-transport/console-transport.test.js b/test/unit/console-transport/console-transport.test.js similarity index 97% rename from test/console-transport/console-transport.test.js rename to test/unit/console-transport/console-transport.test.js index 8832f2c..5b7b907 100644 --- a/test/console-transport/console-transport.test.js +++ b/test/unit/console-transport/console-transport.test.js @@ -1,14 +1,14 @@ -const { test, expect } = require('@jest/globals'); -const { createTransport } = require('./prepare'); - -test('ConsoleTransportLog_LogStack_StackProvided', async () => { - const transport = createTransport(); - transport.log({ stack: 'stack', message: 'message' }); - expect(console.log.mock.calls[0][1]).toEqual('stack'); -}); - -test('ConsoleTransportLog_LogMessage_StackNotProvided', async () => { - const transport = createTransport(); - transport.log({ message: 'message' }); - expect(console.log.mock.calls[0][1]).toEqual('message'); -}); +const { test, expect } = require('@jest/globals'); +const { createTransport } = require('./prepare'); + +test('ConsoleTransportLog_LogStack_StackProvided', async () => { + const transport = createTransport(); + transport.log({ stack: 'stack', message: 'message' }); + expect(console.log.mock.calls[0][1]).toEqual('stack'); +}); + +test('ConsoleTransportLog_LogMessage_StackNotProvided', async () => { + const transport = createTransport(); + transport.log({ message: 'message' }); + expect(console.log.mock.calls[0][1]).toEqual('message'); +}); diff --git a/test/console-transport/prepare.js b/test/unit/console-transport/prepare.js similarity index 74% rename from test/console-transport/prepare.js rename to test/unit/console-transport/prepare.js index 3aaff6e..79dd0b4 100644 --- a/test/console-transport/prepare.js +++ b/test/unit/console-transport/prepare.js @@ -1,4 +1,4 @@ -const { ConsoleTransport } = require('../../lib/console-transport'); +const { ConsoleTransport } = require('../../../lib/console-transport'); console.log = jest.fn(); diff --git a/test/db/db.test.js b/test/unit/db/db.test.js similarity index 97% rename from test/db/db.test.js rename to test/unit/db/db.test.js index 3002e89..9b153ae 100644 --- a/test/db/db.test.js +++ b/test/unit/db/db.test.js @@ -1,169 +1,169 @@ -const { test, expect } = require('@jest/globals'); -const { database } = require('./prepare'); - -test('DatabaseQuery_ThrowDatabaseError_PoolQueryThrowError', async () => { - await expect(database.query('errorSql')).rejects.toThrow(new Error('Data error.')); -}); - -test('DatabaseQuery_ThrowConflictError_PoolQueryThrowConflictError', async () => { - await expect(database.query('conflictErrorSql')).rejects.toThrow(new Error('Conflict error.')); -}); - -test('DatabaseParseOperatorAndValue_ReturnLikeTemplateExpression_ValueHas*Or?', () => { - expect(database.parseOperatorAndValue('some*template?inquery')).toEqual(['LIKE', 'some%template_inquery']); -}); - -test('DatabaseParseOperatorAndValue_ParseLessOperator_OperatorIsLess', () => { - expect(database.parseOperatorAndValue(' { - expect(database.parseOperatorAndValue('<=value')).toEqual(['<=', 'value']); -}); - -test('DatabaseParseOperatorAndValue_ParseGreaterOperator_OperatorIsGreater', () => { - expect(database.parseOperatorAndValue('>value')).toEqual(['>', 'value']); -}); - -test('DatabaseParseOperatorAndValue_ParseGreaterOrEqualOperator_OperatorIsGreaterOrEqual', () => { - expect(database.parseOperatorAndValue('>=value')).toEqual(['>=', 'value']); -}); - -test('DatabaseParseOperatorAndValue_ParseNotEqualOperator_OperatorIsNotEqual', () => { - expect(database.parseOperatorAndValue('<>value')).toEqual(['<>', 'value']); -}); - -test('DatabaseParseOperatorAndValue_ParseEqualOperator_NoOperatorInValue', () => { - expect(database.parseOperatorAndValue('value')).toEqual(['=', 'value']); -}); - -test('DatabaseParseClauseAndArgs_ReturnEmptyArrays_NoPairsPassed', () => { - expect(database.parseClauseAndArgs()).toEqual([[], []]); -}); - -test('DatabaseParseClauseAndArgs_StartArgsWithOne_NoStartArgPassed', () => { - const pairs = { prop1: 'val1', prop2: 'val2', prop3: 'val3' }; - const result = [ - ['"prop1" = $1', '"prop2" = $2', '"prop3" = $3'], - ['val1', 'val2', 'val3'] - ]; - expect(database.parseClauseAndArgs(pairs)).toEqual(result); -}); - -test('DatabaseParseClauseAndArgs_StartArgsPassedParam_StartArgPassed', () => { - const pairs = { prop1: 'val1', prop2: 'val2', prop3: 'val3' }; - const result = [ - ['"prop1" = $3', '"prop2" = $4', '"prop3" = $5'], - ['val1', 'val2', 'val3'] - ]; - expect(database.parseClauseAndArgs(pairs, 3)).toEqual(result); -}); - -test('DatabaseWhere_ReturnEmptyClause_NoConditionsPassed', () => { - expect(database.where({})).toEqual(['', []]); -}); - -test('DatabaseWhere_ReturnWhereClause_ConditionsPassed', () => { - const conditions = { prop1: 'val1', prop2: 23, prop3: 'val3' }; - const result = ['WHERE "prop1" = $1 AND "prop2" = $2 AND "prop3" = $3', ['val1', 23, 'val3']]; - expect(database.where(conditions)).toEqual(result); -}); - -test('DatabaseUpdates_ReturnEmptyClause_NoDeltaPassed', () => { - expect(database.updates({})).toEqual(['', []]); -}); - -test('DatabaseUpdates_ReturnUpdateClause_DeltaPassed', () => { - const delta = { prop1: 'val1', prop2: 'val2', prop3: 'val3' }; - const result = ['SET "prop1" = $1, "prop2" = $2, "prop3" = $3', ['val1', 'val2', 'val3']]; - expect(database.updates(delta)).toEqual(result); -}); - -test('DatabaseInserts_ReturnEmptyValues_NoDataPassed', () => { - const data = {}; - expect(database.inserts(data)).toEqual(['', '', []]); -}); - -test('DatabaseInserts_ReturnKeysNumbersValues_DataPassed', () => { - const data = { prop1: 'val1', prop2: 'val2', prop3: 'val3' }; - expect(database.inserts(data)).toEqual(['"prop1", "prop2", "prop3"', '$1, $2, $3', ['val1', 'val2', 'val3']]); -}); - -test('DatabaseOrder_ReturnEmptyClause_NoFieldsPassed', () => { - expect(database.order()).toEqual(''); -}); - -test('DatabaseOrder_ReturnOrderClause_FieldsPassed', () => { - const fields = ['field1', 'field2', 'field3']; - expect(database.order(fields)).toEqual('ORDER BY "field1", "field2", "field3"'); -}); - -test('DatabaseGetPageClause_ReturnEmptyClause_NoItemsOnPagePassed', () => { - expect(database.getPageClause()).toEqual(''); -}); - -test('DatabaseGetPageClause_ReturnEmptyClause_ItemsOnPageNotANumber', () => { - expect(database.getPageClause('test')).toEqual(''); -}); - -test('DatabaseGetPageClause_ReturnPageClauseWithOffsetZero_ItemsOnPagePassedPageNotPassed', () => { - expect(database.getPageClause(30)).toEqual('LIMIT 30 OFFSET 0'); -}); - -test('DatabaseGetPageClause_ReturnPageClauseWithProperOffset_ItemsOnPagePassedPagePassed', () => { - expect(database.getPageClause(30, 2)).toEqual('LIMIT 30 OFFSET 30'); -}); - -test('DatabaseReturning_ReturnEmptyClause_NoFieldsPassed', () => { - expect(database.returning()).toEqual(''); -}); - -test('DatabaseReturning_ReturnReturnClause_FieldsPassed', () => { - const fields = ['field1', 'field2', 'field3']; - expect(database.returning(fields)).toEqual('RETURNING "field1", "field2", "field3"'); -}); - -test('DatabaseReturning_ReturnReturnClauseWith*_FirstFieldIs*', () => { - const fields = ['*']; - expect(database.returning(fields)).toEqual('RETURNING *'); -}); - -test('DatabaseDelete_PassToQueryCorrectSql_AllArgsPassed', async () => { - await database.delete('TestTable', { id: 40, username: 'admin' }, ['field1']); - expect(database.pool.query.mock.calls[0][0]).toEqual( - 'DELETE FROM "TestTable" WHERE "id" = $1 AND "username" = $2 RETURNING "field1"' - ); - expect(database.pool.query.mock.calls[0][1]).toEqual([40, 'admin']); -}); - -test('DatabaseUpdate_PassToQueryCorrectSql_AllArgsPassed', async () => { - await database.update('TestTable', { field3: 50, field2: 'text' }, { id: 40, username: 'admin' }, ['field1']); - expect(database.pool.query.mock.calls[0][0]).toEqual( - 'UPDATE "TestTable" SET "field3" = $1, "field2" = $2 WHERE "id" = $3 AND "username" = $4 RETURNING "field1"' - ); - expect(database.pool.query.mock.calls[0][1]).toEqual([50, 'text', 40, 'admin']); -}); - -test('DatabaseSelect_PassToQueryCorrectSql_AllArgsPassed', async () => { - await database.select('TestTable', ['field1', 'field2'], { id: 40, username: 'admin' }, ['field1'], 30, 2); - expect(database.pool.query.mock.calls[0][0]).toEqual( - 'SELECT "field1", "field2" FROM "TestTable" WHERE "id" = $1 AND "username" = $2 ORDER BY "field1" LIMIT 30 OFFSET 30' - ); - expect(database.pool.query.mock.calls[0][1]).toEqual([40, 'admin']); -}); - -test('DatabaseSelect_PassToQueryCorrectSqlWith*_NoFieldsPassed', async () => { - await database.select('TestTable', [], { id: 40, username: 'admin' }, ['field1'], 30, 2); - expect(database.pool.query.mock.calls[0][0]).toEqual( - 'SELECT * FROM "TestTable" WHERE "id" = $1 AND "username" = $2 ORDER BY "field1" LIMIT 30 OFFSET 30' - ); - expect(database.pool.query.mock.calls[0][1]).toEqual([40, 'admin']); -}); - -test('DatabaseInsert_PassToQueryCorrectSql_AllArgsPassed', async () => { - await database.insert('TestTable', { id: 40, username: 'admin' }, ['field1']); - expect(database.pool.query.mock.calls[0][0]).toEqual( - 'INSERT INTO "TestTable" ("id", "username") VALUES ($1, $2) RETURNING "field1"' - ); - expect(database.pool.query.mock.calls[0][1]).toEqual([40, 'admin']); -}); +const { test, expect } = require('@jest/globals'); +const { database } = require('./prepare'); + +test('DatabaseQuery_ThrowDatabaseError_PoolQueryThrowError', async () => { + await expect(database.query('errorSql')).rejects.toThrow(new Error('Data error.')); +}); + +test('DatabaseQuery_ThrowConflictError_PoolQueryThrowConflictError', async () => { + await expect(database.query('conflictErrorSql')).rejects.toThrow(new Error('Conflict error.')); +}); + +test('DatabaseParseOperatorAndValue_ReturnLikeTemplateExpression_ValueHas*Or?', () => { + expect(database.parseOperatorAndValue('some*template?inquery')).toEqual(['LIKE', 'some%template_inquery']); +}); + +test('DatabaseParseOperatorAndValue_ParseLessOperator_OperatorIsLess', () => { + expect(database.parseOperatorAndValue(' { + expect(database.parseOperatorAndValue('<=value')).toEqual(['<=', 'value']); +}); + +test('DatabaseParseOperatorAndValue_ParseGreaterOperator_OperatorIsGreater', () => { + expect(database.parseOperatorAndValue('>value')).toEqual(['>', 'value']); +}); + +test('DatabaseParseOperatorAndValue_ParseGreaterOrEqualOperator_OperatorIsGreaterOrEqual', () => { + expect(database.parseOperatorAndValue('>=value')).toEqual(['>=', 'value']); +}); + +test('DatabaseParseOperatorAndValue_ParseNotEqualOperator_OperatorIsNotEqual', () => { + expect(database.parseOperatorAndValue('<>value')).toEqual(['<>', 'value']); +}); + +test('DatabaseParseOperatorAndValue_ParseEqualOperator_NoOperatorInValue', () => { + expect(database.parseOperatorAndValue('value')).toEqual(['=', 'value']); +}); + +test('DatabaseParseClauseAndArgs_ReturnEmptyArrays_NoPairsPassed', () => { + expect(database.parseClauseAndArgs()).toEqual([[], []]); +}); + +test('DatabaseParseClauseAndArgs_StartArgsWithOne_NoStartArgPassed', () => { + const pairs = { prop1: 'val1', prop2: 'val2', prop3: 'val3' }; + const result = [ + ['"prop1" = $1', '"prop2" = $2', '"prop3" = $3'], + ['val1', 'val2', 'val3'] + ]; + expect(database.parseClauseAndArgs(pairs)).toEqual(result); +}); + +test('DatabaseParseClauseAndArgs_StartArgsPassedParam_StartArgPassed', () => { + const pairs = { prop1: 'val1', prop2: 'val2', prop3: 'val3' }; + const result = [ + ['"prop1" = $3', '"prop2" = $4', '"prop3" = $5'], + ['val1', 'val2', 'val3'] + ]; + expect(database.parseClauseAndArgs(pairs, 3)).toEqual(result); +}); + +test('DatabaseWhere_ReturnEmptyClause_NoConditionsPassed', () => { + expect(database.where({})).toEqual(['', []]); +}); + +test('DatabaseWhere_ReturnWhereClause_ConditionsPassed', () => { + const conditions = { prop1: 'val1', prop2: 23, prop3: 'val3' }; + const result = ['WHERE "prop1" = $1 AND "prop2" = $2 AND "prop3" = $3', ['val1', 23, 'val3']]; + expect(database.where(conditions)).toEqual(result); +}); + +test('DatabaseUpdates_ReturnEmptyClause_NoDeltaPassed', () => { + expect(database.updates({})).toEqual(['', []]); +}); + +test('DatabaseUpdates_ReturnUpdateClause_DeltaPassed', () => { + const delta = { prop1: 'val1', prop2: 'val2', prop3: 'val3' }; + const result = ['SET "prop1" = $1, "prop2" = $2, "prop3" = $3', ['val1', 'val2', 'val3']]; + expect(database.updates(delta)).toEqual(result); +}); + +test('DatabaseInserts_ReturnEmptyValues_NoDataPassed', () => { + const data = {}; + expect(database.inserts(data)).toEqual(['', '', []]); +}); + +test('DatabaseInserts_ReturnKeysNumbersValues_DataPassed', () => { + const data = { prop1: 'val1', prop2: 'val2', prop3: 'val3' }; + expect(database.inserts(data)).toEqual(['"prop1", "prop2", "prop3"', '$1, $2, $3', ['val1', 'val2', 'val3']]); +}); + +test('DatabaseOrder_ReturnEmptyClause_NoFieldsPassed', () => { + expect(database.order()).toEqual(''); +}); + +test('DatabaseOrder_ReturnOrderClause_FieldsPassed', () => { + const fields = ['field1', 'field2', 'field3']; + expect(database.order(fields)).toEqual('ORDER BY "field1", "field2", "field3"'); +}); + +test('DatabaseGetPageClause_ReturnEmptyClause_NoItemsOnPagePassed', () => { + expect(database.getPageClause()).toEqual(''); +}); + +test('DatabaseGetPageClause_ReturnEmptyClause_ItemsOnPageNotANumber', () => { + expect(database.getPageClause('test')).toEqual(''); +}); + +test('DatabaseGetPageClause_ReturnPageClauseWithOffsetZero_ItemsOnPagePassedPageNotPassed', () => { + expect(database.getPageClause(30)).toEqual('LIMIT 30 OFFSET 0'); +}); + +test('DatabaseGetPageClause_ReturnPageClauseWithProperOffset_ItemsOnPagePassedPagePassed', () => { + expect(database.getPageClause(30, 2)).toEqual('LIMIT 30 OFFSET 30'); +}); + +test('DatabaseReturning_ReturnEmptyClause_NoFieldsPassed', () => { + expect(database.returning()).toEqual(''); +}); + +test('DatabaseReturning_ReturnReturnClause_FieldsPassed', () => { + const fields = ['field1', 'field2', 'field3']; + expect(database.returning(fields)).toEqual('RETURNING "field1", "field2", "field3"'); +}); + +test('DatabaseReturning_ReturnReturnClauseWith*_FirstFieldIs*', () => { + const fields = ['*']; + expect(database.returning(fields)).toEqual('RETURNING *'); +}); + +test('DatabaseDelete_PassToQueryCorrectSql_AllArgsPassed', async () => { + await database.delete('TestTable', { id: 40, username: 'admin' }, ['field1']); + expect(database.pool.query.mock.calls[0][0]).toEqual( + 'DELETE FROM "TestTable" WHERE "id" = $1 AND "username" = $2 RETURNING "field1"' + ); + expect(database.pool.query.mock.calls[0][1]).toEqual([40, 'admin']); +}); + +test('DatabaseUpdate_PassToQueryCorrectSql_AllArgsPassed', async () => { + await database.update('TestTable', { field3: 50, field2: 'text' }, { id: 40, username: 'admin' }, ['field1']); + expect(database.pool.query.mock.calls[0][0]).toEqual( + 'UPDATE "TestTable" SET "field3" = $1, "field2" = $2 WHERE "id" = $3 AND "username" = $4 RETURNING "field1"' + ); + expect(database.pool.query.mock.calls[0][1]).toEqual([50, 'text', 40, 'admin']); +}); + +test('DatabaseSelect_PassToQueryCorrectSql_AllArgsPassed', async () => { + await database.select('TestTable', ['field1', 'field2'], { id: 40, username: 'admin' }, ['field1'], 30, 2); + expect(database.pool.query.mock.calls[0][0]).toEqual( + 'SELECT "field1", "field2" FROM "TestTable" WHERE "id" = $1 AND "username" = $2 ORDER BY "field1" LIMIT 30 OFFSET 30' + ); + expect(database.pool.query.mock.calls[0][1]).toEqual([40, 'admin']); +}); + +test('DatabaseSelect_PassToQueryCorrectSqlWith*_NoFieldsPassed', async () => { + await database.select('TestTable', [], { id: 40, username: 'admin' }, ['field1'], 30, 2); + expect(database.pool.query.mock.calls[0][0]).toEqual( + 'SELECT * FROM "TestTable" WHERE "id" = $1 AND "username" = $2 ORDER BY "field1" LIMIT 30 OFFSET 30' + ); + expect(database.pool.query.mock.calls[0][1]).toEqual([40, 'admin']); +}); + +test('DatabaseInsert_PassToQueryCorrectSql_AllArgsPassed', async () => { + await database.insert('TestTable', { id: 40, username: 'admin' }, ['field1']); + expect(database.pool.query.mock.calls[0][0]).toEqual( + 'INSERT INTO "TestTable" ("id", "username") VALUES ($1, $2) RETURNING "field1"' + ); + expect(database.pool.query.mock.calls[0][1]).toEqual([40, 'admin']); +}); diff --git a/test/db/prepare.js b/test/unit/db/prepare.js similarity index 89% rename from test/db/prepare.js rename to test/unit/db/prepare.js index 27d4f5e..599b09c 100644 --- a/test/db/prepare.js +++ b/test/unit/db/prepare.js @@ -21,7 +21,7 @@ Pool.mockImplementation(() => { }; }); -jest.mock('../../lib/logger', () => { +jest.mock('../../../lib/logger', () => { return { logger: { sql: () => {} @@ -30,7 +30,7 @@ jest.mock('../../lib/logger', () => { }); //Require database after Pool is mocked. Database is singlton service and using Pool inside constructor. -const { database } = require('../../lib/db'); +const { database } = require('../../../lib/db'); beforeEach(() => { database.pool.query.mockClear(); diff --git a/test/error/error.test.js b/test/unit/error/error.test.js similarity index 89% rename from test/error/error.test.js rename to test/unit/error/error.test.js index cca8bf2..d2926e4 100644 --- a/test/error/error.test.js +++ b/test/unit/error/error.test.js @@ -1,5 +1,5 @@ const { test, expect } = require('@jest/globals'); -const { registerError, ERRORS } = require('../../lib/error'); +const { registerError, ERRORS } = require('../../../lib/error'); test('RegisterError_ThrowError_LabelIsNotProvided', async () => { expect(() => { diff --git a/test/http-connection/http-connection.test.js b/test/unit/http-connection/http-connection.test.js similarity index 97% rename from test/http-connection/http-connection.test.js rename to test/unit/http-connection/http-connection.test.js index d10c6ac..1a6dd6a 100644 --- a/test/http-connection/http-connection.test.js +++ b/test/unit/http-connection/http-connection.test.js @@ -1,112 +1,112 @@ -const { test, expect } = require('@jest/globals'); -const { createConnection, HEADERS, createFakeRequest, createFakeResponse } = require('./prepare'); - -test('HTTPConnection_HangListener_OnInitialisation', async () => { - const connection = createConnection(); - expect(connection.response.on.mock.calls[0][0]).toEqual('close'); - expect(typeof connection.response.on.mock.calls[0][1]).toEqual('function'); -}); - -test('HTTPConnectionSetCors_ChangeCorsValue_ValueIsBoolean', async () => { - const connection = createConnection(); - connection.setCors(false); - expect(connection.cors).toEqual(false); -}); - -test('HTTPConnectionSetCors_NotChangeCorsValue_ValueIsNotBoolean', async () => { - const connection = createConnection(); - connection.setCors(null); - expect(connection.cors).toEqual(true); -}); - -test('HTTPConnectionGetHeaders_ChangeAccessControlAllowOriginHeader_CorsIsDisableAndOriginProvided', async () => { - const request = createFakeRequest('test-origin'); - const connection = createConnection(request); - connection.setCors(false); - expect(connection.getHeaders()).toEqual({ - ...HEADERS, - 'Access-Control-Allow-Origin': 'test-origin' - }); -}); - -test('HTTPConnectionGetHeaders_NotChangeAccessControlAllowOriginHeader_CorsIsDisableAndOriginNotProvided', async () => { - const connection = createConnection(); - connection.setCors(false); - expect(connection.getHeaders()).toEqual(HEADERS); -}); - -test('HTTPConnectionGetHeaders_NotChangeAccessControlAllowOriginHeader_CorsIsEnable', async () => { - const connection = createConnection(); - expect(connection.getHeaders()).toEqual(HEADERS); -}); - -test('HTTPConnectionGetHeaders_AddContentTypeHeader_ContentTypeProvided', async () => { - const connection = createConnection(); - expect(connection.getHeaders('type')).toEqual({ - ...HEADERS, - 'Content-Type': 'type' - }); -}); - -test('HTTPConnectionGetHeaders_NotAddContentTypeHeader_ContentTypeIsNotProvided', async () => { - const connection = createConnection(); - expect(connection.getHeaders()).toEqual(HEADERS); -}); - -test('HTTPConnectionGetHeaders_NotAddContentTypeHeader_ContentTypeIsEmptyString', async () => { - const connection = createConnection(); - expect(connection.getHeaders('')).toEqual(HEADERS); -}); - -test('HTTPConnectionWrite_NotCallResponseWriteHead_ResponseWritableEndedIsTrue', async () => { - const response = createFakeResponse({ writableEnded: true }); - const connection = createConnection({}, {}, response); - connection.write('data', 'json'); - expect(response.writeHead.mock.calls.length).toEqual(0); -}); - -test('HTTPConnectionWrite_CallHTTPConnectionGetHeaderWithProperMimyType_ResponseWritableEndedIsFalse', async () => { - const connection = createConnection(); - connection.getHeaders = jest.fn(); - connection.write('data', 'json'); - expect(connection.getHeaders.mock.calls[0][0]).toEqual('application/json'); -}); - -test('HTTPConnectionWrite_CallResponseWriteHead_ResponseWritableEndedIsFalse', async () => { - const connection = createConnection(); - connection.getHeaders = jest.fn(() => { - return 'headers'; - }); - connection.write('data', 'json'); - expect(connection.response.writeHead.mock.calls[0][0]).toEqual(200); - expect(connection.response.writeHead.mock.calls[0][1]).toEqual('headers'); -}); - -test('HTTPConnectionWrite_CallResponseEnd_ResponseWritableEndedIsFalse', async () => { - const connection = createConnection(); - connection.write('data', 'json'); - expect(connection.response.end.mock.calls[0][0]).toEqual('data'); -}); - -test('HTTPConnectionOptions_NotCallResponseEnd_ResponseHeadersSentIsTrue', async () => { - const response = createFakeResponse({ headersSent: true }); - const connection = createConnection({}, {}, response); - connection.options(); - expect(response.end.mock.calls.length).toEqual(0); -}); - -test('HTTPConnectionOptions_CallResponseEnd_ResponseHeadersSentIsFalse', async () => { - const connection = createConnection(); - connection.options(); - expect(connection.response.end.mock.calls.length).toEqual(1); -}); - -test('HTTPConnectionOptions_CallResponseWriteHead_ResponseHeadersSentIsFalse', async () => { - const connection = createConnection(); - connection.getHeaders = jest.fn(() => { - return 'headers'; - }); - connection.options(); - expect(connection.response.writeHead.mock.calls[0][0]).toEqual(200); - expect(connection.response.writeHead.mock.calls[0][1]).toEqual('headers'); -}); +const { test, expect } = require('@jest/globals'); +const { createConnection, HEADERS, createFakeRequest, createFakeResponse } = require('./prepare'); + +test('HTTPConnection_HangListener_OnInitialisation', async () => { + const connection = createConnection(); + expect(connection.response.on.mock.calls[0][0]).toEqual('close'); + expect(typeof connection.response.on.mock.calls[0][1]).toEqual('function'); +}); + +test('HTTPConnectionSetCors_ChangeCorsValue_ValueIsBoolean', async () => { + const connection = createConnection(); + connection.setCors(false); + expect(connection.cors).toEqual(false); +}); + +test('HTTPConnectionSetCors_NotChangeCorsValue_ValueIsNotBoolean', async () => { + const connection = createConnection(); + connection.setCors(null); + expect(connection.cors).toEqual(true); +}); + +test('HTTPConnectionGetHeaders_ChangeAccessControlAllowOriginHeader_CorsIsDisableAndOriginProvided', async () => { + const request = createFakeRequest('test-origin'); + const connection = createConnection(request); + connection.setCors(false); + expect(connection.getHeaders()).toEqual({ + ...HEADERS, + 'Access-Control-Allow-Origin': 'test-origin' + }); +}); + +test('HTTPConnectionGetHeaders_NotChangeAccessControlAllowOriginHeader_CorsIsDisableAndOriginNotProvided', async () => { + const connection = createConnection(); + connection.setCors(false); + expect(connection.getHeaders()).toEqual(HEADERS); +}); + +test('HTTPConnectionGetHeaders_NotChangeAccessControlAllowOriginHeader_CorsIsEnable', async () => { + const connection = createConnection(); + expect(connection.getHeaders()).toEqual(HEADERS); +}); + +test('HTTPConnectionGetHeaders_AddContentTypeHeader_ContentTypeProvided', async () => { + const connection = createConnection(); + expect(connection.getHeaders('type')).toEqual({ + ...HEADERS, + 'Content-Type': 'type' + }); +}); + +test('HTTPConnectionGetHeaders_NotAddContentTypeHeader_ContentTypeIsNotProvided', async () => { + const connection = createConnection(); + expect(connection.getHeaders()).toEqual(HEADERS); +}); + +test('HTTPConnectionGetHeaders_NotAddContentTypeHeader_ContentTypeIsEmptyString', async () => { + const connection = createConnection(); + expect(connection.getHeaders('')).toEqual(HEADERS); +}); + +test('HTTPConnectionWrite_NotCallResponseWriteHead_ResponseWritableEndedIsTrue', async () => { + const response = createFakeResponse({ writableEnded: true }); + const connection = createConnection({}, {}, response); + connection.write('data', 'json'); + expect(response.writeHead.mock.calls.length).toEqual(0); +}); + +test('HTTPConnectionWrite_CallHTTPConnectionGetHeaderWithProperMimyType_ResponseWritableEndedIsFalse', async () => { + const connection = createConnection(); + connection.getHeaders = jest.fn(); + connection.write('data', 'json'); + expect(connection.getHeaders.mock.calls[0][0]).toEqual('application/json'); +}); + +test('HTTPConnectionWrite_CallResponseWriteHead_ResponseWritableEndedIsFalse', async () => { + const connection = createConnection(); + connection.getHeaders = jest.fn(() => { + return 'headers'; + }); + connection.write('data', 'json'); + expect(connection.response.writeHead.mock.calls[0][0]).toEqual(200); + expect(connection.response.writeHead.mock.calls[0][1]).toEqual('headers'); +}); + +test('HTTPConnectionWrite_CallResponseEnd_ResponseWritableEndedIsFalse', async () => { + const connection = createConnection(); + connection.write('data', 'json'); + expect(connection.response.end.mock.calls[0][0]).toEqual('data'); +}); + +test('HTTPConnectionOptions_NotCallResponseEnd_ResponseHeadersSentIsTrue', async () => { + const response = createFakeResponse({ headersSent: true }); + const connection = createConnection({}, {}, response); + connection.options(); + expect(response.end.mock.calls.length).toEqual(0); +}); + +test('HTTPConnectionOptions_CallResponseEnd_ResponseHeadersSentIsFalse', async () => { + const connection = createConnection(); + connection.options(); + expect(connection.response.end.mock.calls.length).toEqual(1); +}); + +test('HTTPConnectionOptions_CallResponseWriteHead_ResponseHeadersSentIsFalse', async () => { + const connection = createConnection(); + connection.getHeaders = jest.fn(() => { + return 'headers'; + }); + connection.options(); + expect(connection.response.writeHead.mock.calls[0][0]).toEqual(200); + expect(connection.response.writeHead.mock.calls[0][1]).toEqual('headers'); +}); diff --git a/test/http-connection/prepare.js b/test/unit/http-connection/prepare.js similarity index 93% rename from test/http-connection/prepare.js rename to test/unit/http-connection/prepare.js index 9edc054..d470e3b 100644 --- a/test/http-connection/prepare.js +++ b/test/unit/http-connection/prepare.js @@ -1,4 +1,4 @@ -const { HTTPConnectionFactory } = require('../../lib/http-connection'); +const { HTTPConnectionFactory } = require('../../../lib/http-connection'); const HEADERS = { 'X-XSS-Protection': '1; mode=block', diff --git a/test/instrospection/instrospection.test.js b/test/unit/instrospection/instrospection.test.js similarity index 92% rename from test/instrospection/instrospection.test.js rename to test/unit/instrospection/instrospection.test.js index 82e3259..2e02aee 100644 --- a/test/instrospection/instrospection.test.js +++ b/test/unit/instrospection/instrospection.test.js @@ -1,5 +1,5 @@ const { test, expect } = require('@jest/globals'); -const { getIntrospectionModule } = require('../../lib/instrospection'); +const { getIntrospectionModule } = require('../../../lib/instrospection'); test('IntrospectionGetModules_ReturnObjectWithSchemas_ModulesPassed', async () => { const { Module } = getIntrospectionModule({ testModule: { schema: { testMethod: {} } } }).introspection; diff --git a/test/logger/logger.test.js b/test/unit/logger/logger.test.js similarity index 97% rename from test/logger/logger.test.js rename to test/unit/logger/logger.test.js index 7c90118..8ce65e6 100644 --- a/test/logger/logger.test.js +++ b/test/unit/logger/logger.test.js @@ -1,53 +1,53 @@ -const { test, expect } = require('@jest/globals'); -const { logger, createFakeTransport, DEFAULT_SETTINGS } = require('./prepare'); - -test('LoggerSetSettings_OverrideSettings_SettingsObjectPass', async () => { - logger.setSettings({ sql: false }); - expect(logger.settings).toEqual({ - ...DEFAULT_SETTINGS, - sql: false - }); -}); - -test('LoggerSetTransport_ThrowError_TransportHasNoLogMethod', async () => { - expect(() => { - logger.setTransport({}); - }).toThrowError('Transport for Logger must have log method.'); -}); - -test('LoggerSetTransport_ChangeTransport_TransportHasLogMethod', async () => { - const transport = createFakeTransport(); - logger.setTransport(transport); - expect(logger.transport).toEqual(transport); -}); - -test('LoggerLogMessage_NotCallTransportLog_LogTypeIsFalse', async () => { - logger.setSettings({ info: false }); - logger.logMessage('info', 'test', ''); - expect(logger.transport.log.mock.calls.length).toEqual(0); -}); - -test('LoggerLogMessage_CallTransportLogWithStack_LogTypeIsTrueStackProvided', async () => { - logger.logMessage('info', 'test', 'test-stack'); - expect(logger.transport.log.mock.calls[0][0]).toEqual({ type: 'info', message: 'test', stack: 'test-stack' }); -}); - -test('LoggerLogMessage_CallTransportLogWithoutStack_LogTypeIsTrueStackNotProvided', async () => { - logger.logMessage('info', 'test'); - expect(logger.transport.log.mock.calls[0][0]).toEqual({ type: 'info', message: 'test' }); -}); - -test('LoggerDataToString_ConvertArgumentToString_ArgumentIsNotAnObjectOrArray', async () => { - const types = [1, null, undefined, () => {}, Symbol('sym'), true, 'string']; - expect(logger.dataToString(types)).toEqual('\n1\nnull\nundefined\n() => {}\nSymbol(sym)\ntrue\nstring'); -}); - -test('LoggerDataToString_ConvertArrayWithoutSpacing_ArgumentIsAnArray', async () => { - const types = [[1, 2, 3, 4, 5]]; - expect(logger.dataToString(types)).toEqual('\n[1,2,3,4,5]'); -}); - -test('LoggerDataToString_ConvertObjectWithSpacing_ArgumentIsAnObject', async () => { - const types = [{ prop1: 1, prop2: 2 }]; - expect(logger.dataToString(types)).toEqual(`\n{\n "prop1": 1,\n "prop2": 2\n}`); -}); +const { test, expect } = require('@jest/globals'); +const { logger, createFakeTransport, DEFAULT_SETTINGS } = require('./prepare'); + +test('LoggerSetSettings_OverrideSettings_SettingsObjectPass', async () => { + logger.setSettings({ sql: false }); + expect(logger.settings).toEqual({ + ...DEFAULT_SETTINGS, + sql: false + }); +}); + +test('LoggerSetTransport_ThrowError_TransportHasNoLogMethod', async () => { + expect(() => { + logger.setTransport({}); + }).toThrowError('Transport for Logger must have log method.'); +}); + +test('LoggerSetTransport_ChangeTransport_TransportHasLogMethod', async () => { + const transport = createFakeTransport(); + logger.setTransport(transport); + expect(logger.transport).toEqual(transport); +}); + +test('LoggerLogMessage_NotCallTransportLog_LogTypeIsFalse', async () => { + logger.setSettings({ info: false }); + logger.logMessage('info', 'test', ''); + expect(logger.transport.log.mock.calls.length).toEqual(0); +}); + +test('LoggerLogMessage_CallTransportLogWithStack_LogTypeIsTrueStackProvided', async () => { + logger.logMessage('info', 'test', 'test-stack'); + expect(logger.transport.log.mock.calls[0][0]).toEqual({ type: 'info', message: 'test', stack: 'test-stack' }); +}); + +test('LoggerLogMessage_CallTransportLogWithoutStack_LogTypeIsTrueStackNotProvided', async () => { + logger.logMessage('info', 'test'); + expect(logger.transport.log.mock.calls[0][0]).toEqual({ type: 'info', message: 'test' }); +}); + +test('LoggerDataToString_ConvertArgumentToString_ArgumentIsNotAnObjectOrArray', async () => { + const types = [1, null, undefined, () => {}, Symbol('sym'), true, 'string']; + expect(logger.dataToString(types)).toEqual('\n1\nnull\nundefined\n() => {}\nSymbol(sym)\ntrue\nstring'); +}); + +test('LoggerDataToString_ConvertArrayWithoutSpacing_ArgumentIsAnArray', async () => { + const types = [[1, 2, 3, 4, 5]]; + expect(logger.dataToString(types)).toEqual('\n[1,2,3,4,5]'); +}); + +test('LoggerDataToString_ConvertObjectWithSpacing_ArgumentIsAnObject', async () => { + const types = [{ prop1: 1, prop2: 2 }]; + expect(logger.dataToString(types)).toEqual(`\n{\n "prop1": 1,\n "prop2": 2\n}`); +}); diff --git a/test/logger/prepare.js b/test/unit/logger/prepare.js similarity index 87% rename from test/logger/prepare.js rename to test/unit/logger/prepare.js index 35b3551..111cfe6 100644 --- a/test/logger/prepare.js +++ b/test/unit/logger/prepare.js @@ -1,4 +1,4 @@ -const { logger } = require('../../lib/logger'); +const { logger } = require('../../../lib/logger'); const DEFAULT_SETTINGS = { info: true, diff --git a/test/modules-factory/modules-factory.test.js b/test/unit/modules-factory/modules-factory.test.js similarity index 96% rename from test/modules-factory/modules-factory.test.js rename to test/unit/modules-factory/modules-factory.test.js index b3abd7b..1f24252 100644 --- a/test/modules-factory/modules-factory.test.js +++ b/test/unit/modules-factory/modules-factory.test.js @@ -1,7 +1,7 @@ const { test, expect } = require('@jest/globals'); jest.enableAutomock(); -const { ModulesFactory } = require('../../lib/modules-factory'); -jest.unmock('../../lib/modules-factory'); +const { ModulesFactory } = require('../../../lib/modules-factory'); +jest.unmock('../../../lib/modules-factory'); test('ModulesFactoryCreate_ReturnEmptyObject_EmprySchemaPassed', () => { const factory = new ModulesFactory(); diff --git a/test/security-util/security-util.test.js b/test/unit/security-util/security-util.test.js similarity index 95% rename from test/security-util/security-util.test.js rename to test/unit/security-util/security-util.test.js index 930e2e8..49f77d0 100644 --- a/test/security-util/security-util.test.js +++ b/test/unit/security-util/security-util.test.js @@ -1,5 +1,5 @@ const { test, expect } = require('@jest/globals'); -const { security } = require('../../lib/security-util'); +const { security } = require('../../../lib/security-util'); test('SecurityUtilValidatePassword_ReturnTrue_PassingCorrectPasswordAndHash', async () => { const password = 'testpassword'; diff --git a/test/server/prepare.js b/test/unit/server/prepare.js similarity index 81% rename from test/server/prepare.js rename to test/unit/server/prepare.js index a44266a..ce0d908 100644 --- a/test/server/prepare.js +++ b/test/unit/server/prepare.js @@ -1,26 +1,26 @@ jest.enableAutomock(); -const { Server } = require('../../lib/server'); -const { getIntrospectionModule } = require('../../lib/instrospection'); -const { connections } = require('../../lib/connection'); -const { logger } = require('../../lib/logger'); -jest.unmock('../../lib/server'); -jest.unmock('../../lib/error'); +const { Server } = require('../../../lib/server'); +const { getIntrospectionModule } = require('../../../lib/instrospection'); +const { connections } = require('../../../lib/connection'); +const { logger } = require('../../../lib/logger'); +jest.unmock('../../../lib/server'); +jest.unmock('../../../lib/error'); -jest.mock('../../lib/http-connection', () => { +jest.mock('../../../lib/http-connection', () => { class HTTPConnectionFactory {} return { HTTPConnectionFactory }; }); -jest.mock('../../lib/ws-connection', () => { +jest.mock('../../../lib/ws-connection', () => { class WSConnectionFactory {} return { WSConnectionFactory }; }); -jest.mock('../../lib/connection', () => { +jest.mock('../../../lib/connection', () => { return { connections: new Map() }; diff --git a/test/server/server.test.js b/test/unit/server/server.test.js similarity index 97% rename from test/server/server.test.js rename to test/unit/server/server.test.js index 324a09e..b770447 100644 --- a/test/server/server.test.js +++ b/test/unit/server/server.test.js @@ -1,137 +1,137 @@ -const { test, expect } = require('@jest/globals'); -const { createFakeConnection, createServer, getIntrospectionModule, logger } = require('./prepare'); - -test('Server_RewriteDefaultConfig_ConfigPassed', async () => { - const server = createServer({ port: 4000 }); - expect(server.config.port).toEqual(4000); -}); - -test('ServerStart_CallModuleFactoryCreateWithModulesPassed_MethodCalled', async () => { - const server = createServer(); - server.start({ test: 'test' }); - expect(server.modulesFactory.create.mock.calls[0][0]).toEqual({ test: 'test' }); -}); - -test('ServerStart_CallGetIntrospectionModuleWithModulesPassed_MethodCalled', async () => { - const server = createServer(); - server.start({ test: 'test' }); - expect(getIntrospectionModule.mock.calls[0][0]).toEqual({ test: 'test' }); -}); - -test('ServerStart_LogInformation_HTTPServerEmitsListening', async () => { - const server = createServer(); - logger.info.mockClear(); - server.start({ test: 'test' }); - server.server.emit('listening'); - expect(logger.info.mock.calls.length).toEqual(2); -}); - -test('ServerStart_CallHttpFactoryCreate_OnHttpRequest', async () => { - const connection = createFakeConnection(); - const server = createServer({}, connection); - server.start(); - await server.server.emitRequest(); - expect(server.httpFactory.create.mock.calls.length).toEqual(1); -}); - -test('ServerStart_CallConnectionSetCors_ServerCorsIsFalse', async () => { - const connection = createFakeConnection(); - const server = createServer({ cors: false }, connection); - server.start(); - await server.server.emitRequest(); - expect(connection.setCors.mock.calls.length).toEqual(1); - expect(connection.setCors.mock.calls[0][0]).toEqual(false); -}); - -test('ServerStart_CallConnectionOptions_RequestMethodIsOPTIONS', async () => { - const connection = createFakeConnection('OPTIONS'); - const server = createServer({}, connection); - server.start(); - await server.server.emitRequest(); - expect(connection.options.mock.calls.length).toEqual(1); -}); - -test('ServerStart_CallConnectionMessage_RequestMethodIsPOST', async () => { - const connection = createFakeConnection(); - const server = createServer({}, connection); - server.start(); - await server.server.emitRequest(); - expect(connection.message.mock.calls.length).toEqual(1); -}); - -test('ServerStart_CallConnectionInitialise_RequestMethodIsPOST', async () => { - const connection = createFakeConnection(); - const server = createServer({}, connection); - server.start(); - await server.server.emitRequest(); - expect(connection.initialise.mock.calls.length).toEqual(1); -}); - -test('ServerStart_CallConnectionError_InvalidHttpMethod', async () => { - const connection = createFakeConnection('GET'); - const server = createServer({}, connection); - server.start(); - await server.server.emitRequest(); - expect(connection.error.mock.calls.length).toEqual(1); - expect(connection.error.mock.calls[0][0]).toBeInstanceOf(Error); -}); - -test('ServerStart_ThrowError_ErrorPassIsNotTrue', async () => { - const connection = createFakeConnection(); - connection.message = () => { - throw new Error('test-error'); - }; - const server = createServer({}, connection); - server.start(); - const promise = server.server.emitRequest(); - await expect(promise).rejects.toThrowError('test-error'); -}); - -test('ServerStart_CallWSFactoryCreate_OnWSConnection', async () => { - const connection = createFakeConnection(); - const server = createServer({}, connection); - server.start(); - await server.ws.emit('connection', connection); - expect(server.wsFactory.create.mock.calls.length).toEqual(1); -}); - -test('ServerStart_CallConnectionInitialise_OnWSConnection', async () => { - const connection = createFakeConnection(); - const server = createServer({}, connection); - server.start(); - await server.ws.emit('connection', connection); - expect(connection.initialise.mock.calls.length).toEqual(1); -}); - -test('ServerClose_LoggerError_NodeServerSendError', async () => { - const server = createServer({}); - server.server.close = (callback) => { - callback(new Error()); - }; - logger.error.mockClear(); - await server.close(); - expect(logger.error.mock.calls.length).toEqual(1); - expect(logger.error.mock.calls[0][0]).toBeInstanceOf(Error); -}); - -test('ServerClose_CallConnectionTerminate_ConnectionsHasWSConnection', async () => { - const server = createServer({}); - const connection = createFakeConnection(); - connection.connection = { terminate: jest.fn() }; - await server.close(); - expect(connection.connection.terminate.mock.calls.length).toEqual(1); -}); - -test('ServerClose_CallConnectionError_ConnectionsHasHTTPConnection', async () => { - const server = createServer({}); - const connection = createFakeConnection(); - await server.close(); - expect(connection.error.mock.calls.length).toEqual(1); -}); - -test('ServerClose_CallNodeConnectionDestroy_ConnectionsHasHTTPConnection', async () => { - const server = createServer({}); - const connection = createFakeConnection(); - await server.close(); - expect(connection.request.connection.destroy.mock.calls.length).toEqual(1); -}); +const { test, expect } = require('@jest/globals'); +const { createFakeConnection, createServer, getIntrospectionModule, logger } = require('./prepare'); + +test('Server_RewriteDefaultConfig_ConfigPassed', async () => { + const server = createServer({ port: 4000 }); + expect(server.config.port).toEqual(4000); +}); + +test('ServerStart_CallModuleFactoryCreateWithModulesPassed_MethodCalled', async () => { + const server = createServer(); + server.start({ test: 'test' }); + expect(server.modulesFactory.create.mock.calls[0][0]).toEqual({ test: 'test' }); +}); + +test('ServerStart_CallGetIntrospectionModuleWithModulesPassed_MethodCalled', async () => { + const server = createServer(); + server.start({ test: 'test' }); + expect(getIntrospectionModule.mock.calls[0][0]).toEqual({ test: 'test' }); +}); + +test('ServerStart_LogInformation_HTTPServerEmitsListening', async () => { + const server = createServer(); + logger.info.mockClear(); + server.start({ test: 'test' }); + server.server.emit('listening'); + expect(logger.info.mock.calls.length).toEqual(2); +}); + +test('ServerStart_CallHttpFactoryCreate_OnHttpRequest', async () => { + const connection = createFakeConnection(); + const server = createServer({}, connection); + server.start(); + await server.server.emitRequest(); + expect(server.httpFactory.create.mock.calls.length).toEqual(1); +}); + +test('ServerStart_CallConnectionSetCors_ServerCorsIsFalse', async () => { + const connection = createFakeConnection(); + const server = createServer({ cors: false }, connection); + server.start(); + await server.server.emitRequest(); + expect(connection.setCors.mock.calls.length).toEqual(1); + expect(connection.setCors.mock.calls[0][0]).toEqual(false); +}); + +test('ServerStart_CallConnectionOptions_RequestMethodIsOPTIONS', async () => { + const connection = createFakeConnection('OPTIONS'); + const server = createServer({}, connection); + server.start(); + await server.server.emitRequest(); + expect(connection.options.mock.calls.length).toEqual(1); +}); + +test('ServerStart_CallConnectionMessage_RequestMethodIsPOST', async () => { + const connection = createFakeConnection(); + const server = createServer({}, connection); + server.start(); + await server.server.emitRequest(); + expect(connection.message.mock.calls.length).toEqual(1); +}); + +test('ServerStart_CallConnectionInitialise_RequestMethodIsPOST', async () => { + const connection = createFakeConnection(); + const server = createServer({}, connection); + server.start(); + await server.server.emitRequest(); + expect(connection.initialise.mock.calls.length).toEqual(1); +}); + +test('ServerStart_CallConnectionError_InvalidHttpMethod', async () => { + const connection = createFakeConnection('GET'); + const server = createServer({}, connection); + server.start(); + await server.server.emitRequest(); + expect(connection.error.mock.calls.length).toEqual(1); + expect(connection.error.mock.calls[0][0]).toBeInstanceOf(Error); +}); + +test('ServerStart_ThrowError_ErrorPassIsNotTrue', async () => { + const connection = createFakeConnection(); + connection.message = () => { + throw new Error('test-error'); + }; + const server = createServer({}, connection); + server.start(); + const promise = server.server.emitRequest(); + await expect(promise).rejects.toThrowError('test-error'); +}); + +test('ServerStart_CallWSFactoryCreate_OnWSConnection', async () => { + const connection = createFakeConnection(); + const server = createServer({}, connection); + server.start(); + await server.ws.emit('connection', connection); + expect(server.wsFactory.create.mock.calls.length).toEqual(1); +}); + +test('ServerStart_CallConnectionInitialise_OnWSConnection', async () => { + const connection = createFakeConnection(); + const server = createServer({}, connection); + server.start(); + await server.ws.emit('connection', connection); + expect(connection.initialise.mock.calls.length).toEqual(1); +}); + +test('ServerClose_LoggerError_NodeServerSendError', async () => { + const server = createServer({}); + server.server.close = (callback) => { + callback(new Error()); + }; + logger.error.mockClear(); + await server.close(); + expect(logger.error.mock.calls.length).toEqual(1); + expect(logger.error.mock.calls[0][0]).toBeInstanceOf(Error); +}); + +test('ServerClose_CallConnectionTerminate_ConnectionsHasWSConnection', async () => { + const server = createServer({}); + const connection = createFakeConnection(); + connection.connection = { terminate: jest.fn() }; + await server.close(); + expect(connection.connection.terminate.mock.calls.length).toEqual(1); +}); + +test('ServerClose_CallConnectionError_ConnectionsHasHTTPConnection', async () => { + const server = createServer({}); + const connection = createFakeConnection(); + await server.close(); + expect(connection.error.mock.calls.length).toEqual(1); +}); + +test('ServerClose_CallNodeConnectionDestroy_ConnectionsHasHTTPConnection', async () => { + const server = createServer({}); + const connection = createFakeConnection(); + await server.close(); + expect(connection.request.connection.destroy.mock.calls.length).toEqual(1); +}); diff --git a/test/session/prepare.js b/test/unit/session/prepare.js similarity index 71% rename from test/session/prepare.js rename to test/unit/session/prepare.js index 080050c..10400a0 100644 --- a/test/session/prepare.js +++ b/test/unit/session/prepare.js @@ -1,6 +1,6 @@ -const { sessionService } = require('../../lib/session'); +const { sessionService } = require('../../../lib/session'); -jest.mock('../../lib/db', () => { +jest.mock('../../../lib/db', () => { return { database: { insert: () => {}, diff --git a/test/session/session.test.js b/test/unit/session/session.test.js similarity index 98% rename from test/session/session.test.js rename to test/unit/session/session.test.js index 1052be8..3e52121 100644 --- a/test/session/session.test.js +++ b/test/unit/session/session.test.js @@ -1,32 +1,32 @@ -const { test, expect } = require('@jest/globals'); -const { sessionService } = require('./prepare'); - -test('SessionServiceGetTokenFromRequest_ReturnEmptyString_NoCookieHeaderPassed', () => { - expect(sessionService.getTokenFromRequest({ headers: {} })).toEqual(''); -}); - -test('SessionServiceGetTokenFromRequest_ReturnEmptyString_NoTokenCookieFind', () => { - expect(sessionService.getTokenFromRequest({ headers: { cookie: '' } })).toEqual(''); -}); - -test('SessionServiceGetTokenFromRequest_ReturnToken_CorrectCookiePassed', () => { - expect(sessionService.getTokenFromRequest({ headers: { cookie: 'token=testtoken' } })).toEqual('testtoken'); -}); - -test('SessionServiceStartSession_SetCorrectCookie_ResponsePassed', async () => { - const response = { setHeader: jest.fn() }; - await sessionService.startSession({ headers: { host: 'www.example.com' } }, response, 'testusername'); - expect(response.setHeader.mock.calls[0][0]).toEqual('Set-Cookie'); - expect(response.setHeader.mock.calls[0][1]).toEqual( - `token=randomuuid; Expires=Fri, 01 Jan 2100 00:00:00 GMT; Path=/; Domain=www.example.com; SameSite=None; Secure; HttpOnly` - ); -}); - -test('SessionServiceEndSession_SetCorrectCookie_ResponsePassed', async () => { - const response = { setHeader: jest.fn() }; - await sessionService.endSession({ headers: { host: 'www.example.com' } }, response, 'testusername'); - expect(response.setHeader.mock.calls[0][0]).toEqual('Set-Cookie'); - expect(response.setHeader.mock.calls[0][1]).toEqual( - `token=deleted; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=/; Domain=www.example.com; SameSite=None; Secure; HttpOnly` - ); -}); +const { test, expect } = require('@jest/globals'); +const { sessionService } = require('./prepare'); + +test('SessionServiceGetTokenFromRequest_ReturnEmptyString_NoCookieHeaderPassed', () => { + expect(sessionService.getTokenFromRequest({ headers: {} })).toEqual(''); +}); + +test('SessionServiceGetTokenFromRequest_ReturnEmptyString_NoTokenCookieFind', () => { + expect(sessionService.getTokenFromRequest({ headers: { cookie: '' } })).toEqual(''); +}); + +test('SessionServiceGetTokenFromRequest_ReturnToken_CorrectCookiePassed', () => { + expect(sessionService.getTokenFromRequest({ headers: { cookie: 'token=testtoken' } })).toEqual('testtoken'); +}); + +test('SessionServiceStartSession_SetCorrectCookie_ResponsePassed', async () => { + const response = { setHeader: jest.fn() }; + await sessionService.startSession({ headers: { host: 'www.example.com' } }, response, 'testusername'); + expect(response.setHeader.mock.calls[0][0]).toEqual('Set-Cookie'); + expect(response.setHeader.mock.calls[0][1]).toEqual( + `token=randomuuid; Expires=Fri, 01 Jan 2100 00:00:00 GMT; Path=/; Domain=www.example.com; SameSite=None; Secure; HttpOnly` + ); +}); + +test('SessionServiceEndSession_SetCorrectCookie_ResponsePassed', async () => { + const response = { setHeader: jest.fn() }; + await sessionService.endSession({ headers: { host: 'www.example.com' } }, response, 'testusername'); + expect(response.setHeader.mock.calls[0][0]).toEqual('Set-Cookie'); + expect(response.setHeader.mock.calls[0][1]).toEqual( + `token=deleted; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=/; Domain=www.example.com; SameSite=None; Secure; HttpOnly` + ); +}); diff --git a/test/utils/utils.test.js b/test/unit/utils/utils.test.js similarity index 95% rename from test/utils/utils.test.js rename to test/unit/utils/utils.test.js index d090854..213895c 100644 --- a/test/utils/utils.test.js +++ b/test/unit/utils/utils.test.js @@ -1,5 +1,5 @@ const { test, expect } = require('@jest/globals'); -const { parseHost, parseCookies } = require('../../lib/utils'); +const { parseHost, parseCookies } = require('../../../lib/utils'); test('ParseHost_ThrowsError_HostIsFalsy', () => { expect(() => parseHost('')).toThrowError('No host name in HTTP headers!'); diff --git a/test/ws-connection/prepare.js b/test/unit/ws-connection/prepare.js similarity index 84% rename from test/ws-connection/prepare.js rename to test/unit/ws-connection/prepare.js index ed7c647..eeb86b5 100644 --- a/test/ws-connection/prepare.js +++ b/test/unit/ws-connection/prepare.js @@ -1,7 +1,7 @@ const { EventEmitter } = require('events'); -const { WSConnectionFactory } = require('../../lib/ws-connection'); +const { WSConnectionFactory } = require('../../../lib/ws-connection'); -jest.mock('../../lib/connection', () => { +jest.mock('../../../lib/connection', () => { const { EventEmitter } = require('events'); class Connection extends EventEmitter {} return { diff --git a/test/ws-connection/ws-connection.test.js b/test/unit/ws-connection/ws-connection.test.js similarity index 97% rename from test/ws-connection/ws-connection.test.js rename to test/unit/ws-connection/ws-connection.test.js index b49bb1f..fa2f454 100644 --- a/test/ws-connection/ws-connection.test.js +++ b/test/unit/ws-connection/ws-connection.test.js @@ -1,29 +1,29 @@ -const { test, expect } = require('@jest/globals'); -const { getWSConnection } = require('./prepare'); - -test('WSConnection_CallDestroy_OnConnectionCloseEvent', () => { - const connection = getWSConnection(); - connection.connection.emit('close'); - expect(connection.destroy.mock.calls.length).toEqual(1); -}); - -test('WSConnection_CallMessage_OnConnectionMessageEventAndInitialisedTrue', () => { - const connection = getWSConnection(); - connection.initialised = true; - connection.connection.emit('message'); - expect(connection.message.mock.calls.length).toEqual(1); -}); - -test('WSConnection_SubscribeOnInitialisedEvent_OnConnectionMessageEventAndInitialisedFalse', () => { - const connection = getWSConnection(); - connection.on = jest.fn(); - connection.connection.emit('message'); - expect(connection.on.mock.calls[0][0]).toContain('initialised'); -}); - -test('WSConnection_CallMessage_OnMessageEventAndInitialiseEvent', () => { - const connection = getWSConnection(); - connection.connection.emit('message'); - connection.emit('initialised'); - expect(connection.message.mock.calls.length).toEqual(1); -}); +const { test, expect } = require('@jest/globals'); +const { getWSConnection } = require('./prepare'); + +test('WSConnection_CallDestroy_OnConnectionCloseEvent', () => { + const connection = getWSConnection(); + connection.connection.emit('close'); + expect(connection.destroy.mock.calls.length).toEqual(1); +}); + +test('WSConnection_CallMessage_OnConnectionMessageEventAndInitialisedTrue', () => { + const connection = getWSConnection(); + connection.initialised = true; + connection.connection.emit('message'); + expect(connection.message.mock.calls.length).toEqual(1); +}); + +test('WSConnection_SubscribeOnInitialisedEvent_OnConnectionMessageEventAndInitialisedFalse', () => { + const connection = getWSConnection(); + connection.on = jest.fn(); + connection.connection.emit('message'); + expect(connection.on.mock.calls[0][0]).toContain('initialised'); +}); + +test('WSConnection_CallMessage_OnMessageEventAndInitialiseEvent', () => { + const connection = getWSConnection(); + connection.connection.emit('message'); + connection.emit('initialised'); + expect(connection.message.mock.calls.length).toEqual(1); +});