Browse Source

first commit

unknown 2 years ago
commit
de83be88f9
62 changed files with 26668 additions and 0 deletions
  1. 1 0
      .eslintignore
  2. 16 0
      .eslintrc.js
  3. 2 0
      .gitignore
  4. 34 0
      app.js
  5. 17 0
      bin/server.js
  6. 23 0
      config/passport.js
  7. 117 0
      controllers/contacts.js
  8. 166 0
      controllers/user.js
  9. 287 0
      coverage/clover.xml
  10. 13 0
      coverage/coverage-final.json
  11. 224 0
      coverage/lcov-report/base.css
  12. 79 0
      coverage/lcov-report/block-navigation.js
  13. BIN
      coverage/lcov-report/favicon.png
  14. 186 0
      coverage/lcov-report/index.html
  15. 236 0
      coverage/lcov-report/nodejs-homework-API/app.js.html
  16. 111 0
      coverage/lcov-report/nodejs-homework-API/config/index.html
  17. 161 0
      coverage/lcov-report/nodejs-homework-API/config/passport.js.html
  18. 440 0
      coverage/lcov-report/nodejs-homework-API/controllers/contacts.js.html
  19. 126 0
      coverage/lcov-report/nodejs-homework-API/controllers/index.html
  20. 542 0
      coverage/lcov-report/nodejs-homework-API/controllers/user.js.html
  21. 128 0
      coverage/lcov-report/nodejs-homework-API/helpers/apiLimiter.js.html
  22. 125 0
      coverage/lcov-report/nodejs-homework-API/helpers/create-directory.js.html
  23. 143 0
      coverage/lcov-report/nodejs-homework-API/helpers/guard.js.html
  24. 156 0
      coverage/lcov-report/nodejs-homework-API/helpers/index.html
  25. 161 0
      coverage/lcov-report/nodejs-homework-API/helpers/upload.js.html
  26. 111 0
      coverage/lcov-report/nodejs-homework-API/index.html
  27. 125 0
      coverage/lcov-report/nodejs-homework-API/routes/api/contacts.js.html
  28. 126 0
      coverage/lcov-report/nodejs-homework-API/routes/api/index.html
  29. 137 0
      coverage/lcov-report/nodejs-homework-API/routes/api/user.js.html
  30. 126 0
      coverage/lcov-report/nodejs-homework-API/routes/api/validation/index.html
  31. 227 0
      coverage/lcov-report/nodejs-homework-API/routes/api/validation/validationContact.js.html
  32. 203 0
      coverage/lcov-report/nodejs-homework-API/routes/api/validation/validationUser.js.html
  33. 1 0
      coverage/lcov-report/prettify.css
  34. 2 0
      coverage/lcov-report/prettify.js
  35. BIN
      coverage/lcov-report/sort-arrow-sprite.png
  36. 170 0
      coverage/lcov-report/sorter.js
  37. 436 0
      coverage/lcov.info
  38. 16 0
      helpers/apiLimiter.js
  39. 13 0
      helpers/create-directory.js
  40. 21 0
      helpers/guard.js
  41. 6 0
      helpers/twilio.js
  42. 24 0
      helpers/upload.js
  43. BIN
      images/60d465e986100d3930569f4b/6-1@x2.png
  44. 3 0
      jest.config.js
  45. 43 0
      model/__mocks__/contact.js
  46. 43 0
      model/__mocks__/data.js
  47. 36 0
      model/__mocks__/user.js
  48. 60 0
      model/contact.js
  49. 32 0
      model/db/index.js
  50. 34 0
      model/schemas/contact.js
  51. 43 0
      model/schemas/user.js
  52. 34 0
      model/user.js
  53. 3 0
      nodemon.json
  54. 20752 0
      package-lock.json
  55. 51 0
      package.json
  56. 1 0
      readme.md
  57. 15 0
      routes/contacts.js
  58. 19 0
      routes/user.js
  59. 176 0
      test/contacts.e2.test.js
  60. 33 0
      validation/contact.js
  61. 39 0
      validation/user.js
  62. 13 0
      validation/validate.js

+ 1 - 0
.eslintignore

@@ -0,0 +1 @@
+node_modules

+ 16 - 0
.eslintrc.js

@@ -0,0 +1,16 @@
+module.exports = {
+  env: {
+    commonjs: true,
+    es2021: true,
+    node: true,
+    "jest/globals": true,
+  },
+  extends: ["standard", "plugin:json/recommended", "prettier"],
+  parserOptions: {
+    ecmaVersion: 12,
+  },
+  rules: {
+    // 'comma-dangle': 'off',
+    // 'space-before-function-paren': 'off',
+  },
+};

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+node_modules
+.env

+ 34 - 0
app.js

@@ -0,0 +1,34 @@
+const express = require('express');
+const path = require('path');
+const logger = require('morgan');
+const cors = require('cors');
+const helmet = require('helmet');
+const apiLimiter = require('./helpers/apiLimiter');
+
+const contactsRouter = require('./routes/contacts');
+const authenticationRoute = require('./routes/user');
+const app = express();
+
+const FOLDER_IMAGES = process.env.DIR_IMAGES;
+
+app.use(express.static(path.join(__dirname, FOLDER_IMAGES)));
+
+const formatsLogger = app.get('env') === 'development' ? 'dev' : 'short';
+
+app.use(helmet());
+app.use(logger(formatsLogger));
+app.use(cors());
+app.use(express.json());
+
+app.use('/contacts', apiLimiter, contactsRouter);
+app.use('/auth', apiLimiter, authenticationRoute);
+
+app.use((_req, res) => {
+	res.status(404).json({ message: 'Not found' });
+});
+
+app.use((err, _req, res, _next) => {
+	res.status(500).json({ message: err.message });
+});
+
+module.exports = app;

+ 17 - 0
bin/server.js

@@ -0,0 +1,17 @@
+const app = require('../app');
+const db = require('../model/db');
+const createFolderIsExist = require('../helpers/create-directory');
+const PORT = process.env.PORT || 3000;
+
+db.then(() => {
+	app.listen(PORT, async () => {
+		const DIR_UPLOAD = process.env.DIR_UPLOAD;
+		const DIR_IMAGES = process.env.DIR_IMAGES;
+		await createFolderIsExist(DIR_UPLOAD);
+		await createFolderIsExist(DIR_IMAGES);
+		console.log(`Server running. Use our API on port: ${PORT}`);
+	});
+}).catch((err) => {
+	console.log(`Server not running. Error message: ${err.message}`);
+	process.exit(1);
+});

+ 23 - 0
config/passport.js

@@ -0,0 +1,23 @@
+const passport = require("passport");
+const UserModel = require("../model/user");
+const { Strategy, ExtractJwt } = require("passport-jwt");
+require("dotenv").config();
+const SECRET_KEY = process.env.JWT_SECRET;
+
+const params = {
+  secretOrKey: SECRET_KEY,
+  jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
+};
+
+passport.use(
+  new Strategy(params, async ({ id }, done) => {
+    try {
+      const user = await UserModel.findById(id);
+      if (!user) return done(new Error("User not found"));
+      if (!user.token) return done(null, false);
+      return done(null, user);
+    } catch (err) {
+      done(err);
+    }
+  })
+);

+ 117 - 0
controllers/contacts.js

@@ -0,0 +1,117 @@
+const Contact = require("../model/contact");
+
+const listContacts = async (req, res, next) => {
+  try {
+    const userId = req.user.id;
+    const contacts = await Contact.getList(userId, req.query);
+    return res.json({
+      status: "success",
+      code: 200,
+      data: {
+        ...contacts,
+      },
+    });
+  } catch (e) {
+    next(e);
+  }
+};
+
+const getContactById = async (req, res, next) => {
+  try {
+    const id = req.params.id;
+    const userId = req.user.id;
+    const contact = await Contact.getById(id, userId);
+    if (contact)
+      return res.json({
+        status: "success",
+        code: 200,
+        data: {
+          contact,
+        },
+      });
+    return res.status(404).json({
+      status: "error",
+      code: 404,
+      data: "Not Found",
+    });
+  } catch (e) {
+    next(e);
+  }
+};
+
+const addContact = async (req, res, next) => {
+  try {
+    const contact = req.body;
+    const userId = req.user.id;
+    if (contact) {
+      await Contact.add({ ...contact, owner: userId });
+      return res.status(201).json({
+        status: "success",
+        code: 201,
+        data: {
+          contact,
+        },
+      });
+    }
+  } catch (e) {
+    next(e);
+  }
+};
+
+const removeContact = async (req, res, next) => {
+  try {
+    const id = req.params.id;
+    const userId = req.user.id;
+    const contact = await Contact.remove(id, userId);
+    if (contact) {
+      return res.json({
+        status: "success",
+        code: 200,
+        data: {
+          contact,
+        },
+      });
+    } else {
+      return res.status(404).json({
+        status: "error",
+        code: 404,
+        data: "Not Found",
+      });
+    }
+  } catch (e) {
+    next(e);
+  }
+};
+
+const updateContact = async (req, res, next) => {
+  try {
+    const id = req.params.id;
+    const userId = req.user.id;
+    const contact = await Contact.update(id, userId, req.body);
+    if (contact) {
+      return res.status(200).json({
+        status: "success",
+        code: 200,
+        data: {
+          contact,
+        },
+      });
+    } else {
+      return res.status(404).json({
+        status: "error",
+        code: 404,
+        data: "Not Found",
+      });
+    }
+  } catch (e) {
+    next(e);
+  }
+};
+
+module.exports = {
+  listContacts,
+  getContactById,
+  addContact,
+  removeContact,
+  updateContact,
+};

+ 166 - 0
controllers/user.js

@@ -0,0 +1,166 @@
+const UserModel = require('../model/user');
+const fs = require('fs').promises;
+const path = require('path');
+const Jimp = require('jimp');
+const jwt = require('jsonwebtoken');
+const createFolderIsExist = require('../helpers/create-directory');
+require('dotenv').config();
+const client = require('../helpers/twilio');
+const phoneToken = require('generate-sms-verification-code');
+const SECRET_KEY = process.env.JWT_SECRET;
+
+const saveAvatarForStatic = async (req, res, next) => {
+	try {
+		const userId = req.user.id;
+		const DIR_IMAGES = process.env.DIR_IMAGES;
+		const pathToFile = req.file.path;
+		const newNameAvatar = req.file.originalname;
+
+		const img = await Jimp.read(pathToFile);
+		await img
+			.autocrop()
+			.cover(
+				250,
+				250,
+				Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_MIDDLE
+			)
+			.writeAsync(pathToFile);
+
+		await createFolderIsExist(path.join(DIR_IMAGES, userId));
+		await fs.rename(pathToFile, path.join(DIR_IMAGES, userId, newNameAvatar));
+		const newUrlAvatar = path.normalize(path.join(userId, newNameAvatar));
+		const newUrl = `http://localhost:3000/${userId}/${newNameAvatar}`;
+		await UserModel.updateAvatar(userId, newUrlAvatar);
+		return res.status(200).json({
+			status: 'success',
+			code: 200,
+			data: {
+				newUrl,
+			},
+		});
+	} catch (e) {
+		next(e);
+	}
+};
+
+const createNewUser = async (req, res, next) => {
+	try {
+		const code = phoneToken(8, { type: 'number' });
+		const number = req.body.number;
+		const isExist = await UserModel.findByNumber(number);
+		if (isExist) {
+			const id = isExist._id;
+			await UserModel.updateCode(id, code);
+		} else {
+			await UserModel.createUser({ number, code });
+		}
+		client.messages.create({
+			body: `${code}`,
+			to: `${number}`,
+			from: '+18305875860',
+		});
+		return res.status(201).json({
+			status: 'success',
+			code: 201,
+			data: code,
+		});
+	} catch (e) {
+		next(e);
+	}
+};
+
+const logIn = async (req, res, next) => {
+	try {
+		const { number, code } = req.body;
+		const user = await UserModel.findByNumber(number);
+		if (!user || user.code !== code)
+			return res.status(401).json({
+				status: 'error',
+				code: 401,
+				data: 'UNAUTHORIZED',
+				message: 'Invalid credentials',
+			});
+
+		const id = user._id;
+		const payload = { id };
+		const token = jwt.sign(payload, SECRET_KEY, { expiresIn: '24h' });
+		const { name, lastName, avatarUrl } = user;
+		await UserModel.updateToken(id, token);
+		await UserModel.updateCode(id, '');
+		return res.status(200).json({
+			status: 'success',
+			code: 200,
+			data: {
+				token,
+				number,
+				name,
+				lastName,
+				avatarUrl,
+			},
+		});
+	} catch (e) {
+		next(e);
+	}
+};
+
+const logOut = async (req, res, next) => {
+	try {
+		const id = req.user.id;
+		const user = await UserModel.findById(id);
+		if (!user)
+			return res.status(401).json({
+				status: 'error',
+				code: 401,
+				data: 'UNAUTHORIZED',
+				message: 'Invalid credentials',
+			});
+		await UserModel.updateToken(id, null);
+		return res.status(204).json({});
+	} catch (e) {
+		next(e);
+	}
+};
+
+const getCurrent = async (req, res, next) => {
+	try {
+		const user = req.user;
+		if (!user)
+			return res.status(401).json({
+				status: 'error',
+				code: 401,
+				data: 'UNAUTHORIZED',
+				message: 'Invalid credentials',
+			});
+
+		return res.status(200).json({
+			status: 'success',
+			code: 200,
+			data: {
+				user,
+			},
+		});
+	} catch (e) {
+		next(e);
+	}
+};
+
+const updateName = async (req, res, next) => {
+	try {
+		const id = req.user.id;
+		const user = await UserModel.updateName(id, req.body.name);
+		return res.status(200).json({
+			data: user,
+		});
+	} catch (e) {
+		next(e);
+	}
+};
+
+module.exports = {
+	saveAvatarForStatic,
+	createNewUser,
+	logIn,
+	logOut,
+	getCurrent,
+	updateName,
+};

+ 287 - 0
coverage/clover.xml

@@ -0,0 +1,287 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<coverage generated="1615807430309" clover="3.2.0">
+  <project timestamp="1615807430309" name="All files">
+    <metrics statements="227" coveredstatements="146" conditionals="39" coveredconditionals="17" methods="31" coveredmethods="12" elements="297" coveredelements="175" complexity="0" loc="227" ncloc="227" packages="6" files="12" classes="12"/>
+    <package name="nodejs-homework-API">
+      <metrics statements="24" coveredstatements="23" conditionals="2" coveredconditionals="1" methods="2" coveredmethods="1"/>
+      <file name="app.js" path="D:\web\NodeHomework02-06\nodejs-homework-API\app.js">
+        <metrics statements="24" coveredstatements="23" conditionals="2" coveredconditionals="1" methods="2" coveredmethods="1"/>
+        <line num="1" count="1" type="stmt"/>
+        <line num="2" count="1" type="stmt"/>
+        <line num="3" count="1" type="stmt"/>
+        <line num="4" count="1" type="stmt"/>
+        <line num="5" count="1" type="stmt"/>
+        <line num="6" count="1" type="stmt"/>
+        <line num="8" count="1" type="stmt"/>
+        <line num="9" count="1" type="stmt"/>
+        <line num="10" count="1" type="stmt"/>
+        <line num="12" count="1" type="stmt"/>
+        <line num="13" count="1" type="stmt"/>
+        <line num="14" count="1" type="stmt"/>
+        <line num="15" count="1" type="cond" truecount="1" falsecount="1"/>
+        <line num="17" count="1" type="stmt"/>
+        <line num="18" count="1" type="stmt"/>
+        <line num="19" count="1" type="stmt"/>
+        <line num="20" count="1" type="stmt"/>
+        <line num="22" count="1" type="stmt"/>
+        <line num="23" count="1" type="stmt"/>
+        <line num="25" count="1" type="stmt"/>
+        <line num="26" count="0" type="stmt"/>
+        <line num="29" count="1" type="stmt"/>
+        <line num="30" count="3" type="stmt"/>
+        <line num="33" count="1" type="stmt"/>
+      </file>
+    </package>
+    <package name="nodejs-homework-API.config">
+      <metrics statements="15" coveredstatements="12" conditionals="4" coveredconditionals="2" methods="1" coveredmethods="1"/>
+      <file name="passport.js" path="D:\web\NodeHomework02-06\nodejs-homework-API\config\passport.js">
+        <metrics statements="15" coveredstatements="12" conditionals="4" coveredconditionals="2" methods="1" coveredmethods="1"/>
+        <line num="1" count="1" type="stmt"/>
+        <line num="2" count="1" type="stmt"/>
+        <line num="3" count="1" type="stmt"/>
+        <line num="4" count="1" type="stmt"/>
+        <line num="5" count="1" type="stmt"/>
+        <line num="7" count="1" type="stmt"/>
+        <line num="12" count="1" type="stmt"/>
+        <line num="14" count="11" type="stmt"/>
+        <line num="15" count="11" type="stmt"/>
+        <line num="16" count="11" type="cond" truecount="1" falsecount="1"/>
+        <line num="17" count="0" type="stmt"/>
+        <line num="19" count="11" type="cond" truecount="1" falsecount="1"/>
+        <line num="20" count="0" type="stmt"/>
+        <line num="22" count="11" type="stmt"/>
+        <line num="24" count="0" type="stmt"/>
+      </file>
+    </package>
+    <package name="nodejs-homework-API.controllers">
+      <metrics statements="105" coveredstatements="50" conditionals="18" coveredconditionals="7" methods="11" coveredmethods="5"/>
+      <file name="contacts.js" path="D:\web\NodeHomework02-06\nodejs-homework-API\controllers\contacts.js">
+        <metrics statements="40" coveredstatements="35" conditionals="8" coveredconditionals="7" methods="5" coveredmethods="5"/>
+        <line num="1" count="1" type="stmt"/>
+        <line num="3" count="1" type="stmt"/>
+        <line num="4" count="1" type="stmt"/>
+        <line num="5" count="1" type="stmt"/>
+        <line num="6" count="1" type="stmt"/>
+        <line num="7" count="1" type="stmt"/>
+        <line num="15" count="0" type="stmt"/>
+        <line num="19" count="1" type="stmt"/>
+        <line num="20" count="2" type="stmt"/>
+        <line num="21" count="2" type="stmt"/>
+        <line num="22" count="2" type="stmt"/>
+        <line num="23" count="2" type="cond" truecount="2" falsecount="0"/>
+        <line num="24" count="1" type="stmt"/>
+        <line num="32" count="1" type="stmt"/>
+        <line num="39" count="0" type="stmt"/>
+        <line num="43" count="1" type="stmt"/>
+        <line num="44" count="1" type="stmt"/>
+        <line num="45" count="1" type="stmt"/>
+        <line num="46" count="1" type="stmt"/>
+        <line num="47" count="1" type="cond" truecount="1" falsecount="1"/>
+        <line num="48" count="1" type="stmt"/>
+        <line num="49" count="1" type="stmt"/>
+        <line num="58" count="0" type="stmt"/>
+        <line num="62" count="1" type="stmt"/>
+        <line num="63" count="2" type="stmt"/>
+        <line num="64" count="2" type="stmt"/>
+        <line num="65" count="2" type="stmt"/>
+        <line num="66" count="2" type="cond" truecount="2" falsecount="0"/>
+        <line num="67" count="1" type="stmt"/>
+        <line num="75" count="1" type="stmt"/>
+        <line num="82" count="0" type="stmt"/>
+        <line num="86" count="1" type="stmt"/>
+        <line num="87" count="2" type="stmt"/>
+        <line num="88" count="2" type="stmt"/>
+        <line num="89" count="2" type="stmt"/>
+        <line num="94" count="2" type="cond" truecount="2" falsecount="0"/>
+        <line num="95" count="1" type="stmt"/>
+        <line num="103" count="1" type="stmt"/>
+        <line num="110" count="0" type="stmt"/>
+        <line num="114" count="1" type="stmt"/>
+      </file>
+      <file name="user.js" path="D:\web\NodeHomework02-06\nodejs-homework-API\controllers\user.js">
+        <metrics statements="65" coveredstatements="15" conditionals="10" coveredconditionals="0" methods="6" coveredmethods="0"/>
+        <line num="1" count="1" type="stmt"/>
+        <line num="2" count="1" type="stmt"/>
+        <line num="3" count="1" type="stmt"/>
+        <line num="4" count="1" type="stmt"/>
+        <line num="5" count="1" type="stmt"/>
+        <line num="6" count="1" type="stmt"/>
+        <line num="7" count="1" type="stmt"/>
+        <line num="8" count="1" type="stmt"/>
+        <line num="10" count="1" type="stmt"/>
+        <line num="11" count="0" type="stmt"/>
+        <line num="12" count="0" type="stmt"/>
+        <line num="13" count="0" type="stmt"/>
+        <line num="14" count="0" type="stmt"/>
+        <line num="15" count="0" type="stmt"/>
+        <line num="17" count="0" type="stmt"/>
+        <line num="18" count="0" type="stmt"/>
+        <line num="27" count="0" type="stmt"/>
+        <line num="28" count="0" type="stmt"/>
+        <line num="29" count="0" type="stmt"/>
+        <line num="30" count="0" type="stmt"/>
+        <line num="31" count="0" type="stmt"/>
+        <line num="32" count="0" type="stmt"/>
+        <line num="40" count="0" type="stmt"/>
+        <line num="44" count="1" type="stmt"/>
+        <line num="45" count="0" type="stmt"/>
+        <line num="46" count="0" type="stmt"/>
+        <line num="47" count="0" type="stmt"/>
+        <line num="48" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="49" count="0" type="stmt"/>
+        <line num="56" count="0" type="stmt"/>
+        <line num="57" count="0" type="stmt"/>
+        <line num="68" count="0" type="stmt"/>
+        <line num="72" count="1" type="stmt"/>
+        <line num="73" count="0" type="stmt"/>
+        <line num="74" count="0" type="stmt"/>
+        <line num="75" count="0" type="stmt"/>
+        <line num="76" count="0" type="stmt"/>
+        <line num="77" count="0" type="cond" truecount="0" falsecount="4"/>
+        <line num="78" count="0" type="stmt"/>
+        <line num="85" count="0" type="stmt"/>
+        <line num="86" count="0" type="stmt"/>
+        <line num="87" count="0" type="stmt"/>
+        <line num="88" count="0" type="stmt"/>
+        <line num="89" count="0" type="stmt"/>
+        <line num="97" count="0" type="stmt"/>
+        <line num="101" count="1" type="stmt"/>
+        <line num="102" count="0" type="stmt"/>
+        <line num="103" count="0" type="stmt"/>
+        <line num="104" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="105" count="0" type="stmt"/>
+        <line num="112" count="0" type="stmt"/>
+        <line num="113" count="0" type="stmt"/>
+        <line num="115" count="1" type="stmt"/>
+        <line num="116" count="0" type="stmt"/>
+        <line num="117" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="118" count="0" type="stmt"/>
+        <line num="126" count="0" type="stmt"/>
+        <line num="134" count="1" type="stmt"/>
+        <line num="135" count="0" type="stmt"/>
+        <line num="136" count="0" type="stmt"/>
+        <line num="137" count="0" type="stmt"/>
+        <line num="138" count="0" type="stmt"/>
+        <line num="139" count="0" type="stmt"/>
+        <line num="143" count="0" type="stmt"/>
+        <line num="147" count="1" type="stmt"/>
+      </file>
+    </package>
+    <package name="nodejs-homework-API.helpers">
+      <metrics statements="37" coveredstatements="25" conditionals="9" coveredconditionals="5" methods="10" coveredmethods="2"/>
+      <file name="apiLimiter.js" path="D:\web\NodeHomework02-06\nodejs-homework-API\helpers\apiLimiter.js">
+        <metrics statements="4" coveredstatements="3" conditionals="0" coveredconditionals="0" methods="1" coveredmethods="0"/>
+        <line num="1" count="1" type="stmt"/>
+        <line num="3" count="1" type="stmt"/>
+        <line num="7" count="0" type="stmt"/>
+        <line num="16" count="1" type="stmt"/>
+      </file>
+      <file name="create-directory.js" path="D:\web\NodeHomework02-06\nodejs-homework-API\helpers\create-directory.js">
+        <metrics statements="9" coveredstatements="4" conditionals="2" coveredconditionals="0" methods="4" coveredmethods="0"/>
+        <line num="1" count="1" type="stmt"/>
+        <line num="3" count="1" type="stmt"/>
+        <line num="4" count="0" type="stmt"/>
+        <line num="6" count="0" type="stmt"/>
+        <line num="7" count="0" type="stmt"/>
+        <line num="10" count="1" type="stmt"/>
+        <line num="11" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="12" count="0" type="stmt"/>
+        <line num="15" count="1" type="stmt"/>
+      </file>
+      <file name="guard.js" path="D:\web\NodeHomework02-06\nodejs-homework-API\helpers\guard.js">
+        <metrics statements="11" coveredstatements="11" conditionals="5" coveredconditionals="5" methods="2" coveredmethods="2"/>
+        <line num="1" count="1" type="stmt"/>
+        <line num="2" count="1" type="stmt"/>
+        <line num="3" count="1" type="stmt"/>
+        <line num="5" count="1" type="stmt"/>
+        <line num="6" count="16" type="stmt"/>
+        <line num="7" count="16" type="stmt"/>
+        <line num="8" count="16" type="cond" truecount="5" falsecount="0"/>
+        <line num="9" count="5" type="stmt"/>
+        <line num="16" count="11" type="stmt"/>
+        <line num="17" count="11" type="stmt"/>
+        <line num="21" count="1" type="stmt"/>
+      </file>
+      <file name="upload.js" path="D:\web\NodeHomework02-06\nodejs-homework-API\helpers\upload.js">
+        <metrics statements="13" coveredstatements="7" conditionals="2" coveredconditionals="0" methods="3" coveredmethods="0"/>
+        <line num="1" count="1" type="stmt"/>
+        <line num="2" count="1" type="stmt"/>
+        <line num="3" count="1" type="stmt"/>
+        <line num="4" count="1" type="stmt"/>
+        <line num="6" count="1" type="stmt"/>
+        <line num="8" count="0" type="stmt"/>
+        <line num="11" count="0" type="stmt"/>
+        <line num="15" count="1" type="stmt"/>
+        <line num="19" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="20" count="0" type="stmt"/>
+        <line num="21" count="0" type="stmt"/>
+        <line num="23" count="0" type="stmt"/>
+        <line num="27" count="1" type="stmt"/>
+      </file>
+    </package>
+    <package name="nodejs-homework-API.routes.api">
+      <metrics statements="16" coveredstatements="16" conditionals="0" coveredconditionals="0" methods="0" coveredmethods="0"/>
+      <file name="contacts.js" path="D:\web\NodeHomework02-06\nodejs-homework-API\routes\api\contacts.js">
+        <metrics statements="8" coveredstatements="8" conditionals="0" coveredconditionals="0" methods="0" coveredmethods="0"/>
+        <line num="1" count="1" type="stmt"/>
+        <line num="2" count="1" type="stmt"/>
+        <line num="3" count="1" type="stmt"/>
+        <line num="4" count="1" type="stmt"/>
+        <line num="5" count="1" type="stmt"/>
+        <line num="6" count="1" type="stmt"/>
+        <line num="10" count="1" type="stmt"/>
+        <line num="15" count="1" type="stmt"/>
+      </file>
+      <file name="user.js" path="D:\web\NodeHomework02-06\nodejs-homework-API\routes\api\user.js">
+        <metrics statements="8" coveredstatements="8" conditionals="0" coveredconditionals="0" methods="0" coveredmethods="0"/>
+        <line num="1" count="1" type="stmt"/>
+        <line num="2" count="1" type="stmt"/>
+        <line num="3" count="1" type="stmt"/>
+        <line num="4" count="1" type="stmt"/>
+        <line num="5" count="1" type="stmt"/>
+        <line num="6" count="1" type="stmt"/>
+        <line num="8" count="1" type="stmt"/>
+        <line num="19" count="1" type="stmt"/>
+      </file>
+    </package>
+    <package name="nodejs-homework-API.routes.api.validation">
+      <metrics statements="30" coveredstatements="20" conditionals="6" coveredconditionals="2" methods="7" coveredmethods="3"/>
+      <file name="validationContact.js" path="D:\web\NodeHomework02-06\nodejs-homework-API\routes\api\validation\validationContact.js">
+        <metrics statements="13" coveredstatements="13" conditionals="2" coveredconditionals="2" methods="3" coveredmethods="3"/>
+        <line num="1" count="1" type="stmt"/>
+        <line num="3" count="1" type="stmt"/>
+        <line num="17" count="1" type="stmt"/>
+        <line num="31" count="1" type="stmt"/>
+        <line num="32" count="6" type="stmt"/>
+        <line num="33" count="6" type="cond" truecount="2" falsecount="0"/>
+        <line num="34" count="3" type="stmt"/>
+        <line num="35" count="3" type="stmt"/>
+        <line num="40" count="3" type="stmt"/>
+        <line num="43" count="1" type="stmt"/>
+        <line num="44" count="3" type="stmt"/>
+        <line num="47" count="1" type="stmt"/>
+        <line num="48" count="3" type="stmt"/>
+      </file>
+      <file name="validationUser.js" path="D:\web\NodeHomework02-06\nodejs-homework-API\routes\api\validation\validationUser.js">
+        <metrics statements="17" coveredstatements="7" conditionals="4" coveredconditionals="0" methods="4" coveredmethods="0"/>
+        <line num="1" count="1" type="stmt"/>
+        <line num="3" count="1" type="stmt"/>
+        <line num="8" count="1" type="stmt"/>
+        <line num="13" count="1" type="stmt"/>
+        <line num="14" count="0" type="stmt"/>
+        <line num="15" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="16" count="0" type="stmt"/>
+        <line num="17" count="0" type="stmt"/>
+        <line num="22" count="0" type="stmt"/>
+        <line num="25" count="1" type="stmt"/>
+        <line num="26" count="0" type="stmt"/>
+        <line num="28" count="1" type="stmt"/>
+        <line num="29" count="0" type="stmt"/>
+        <line num="31" count="1" type="stmt"/>
+        <line num="32" count="0" type="cond" truecount="0" falsecount="2"/>
+        <line num="33" count="0" type="stmt"/>
+        <line num="40" count="0" type="stmt"/>
+      </file>
+    </package>
+  </project>
+</coverage>

File diff suppressed because it is too large
+ 13 - 0
coverage/coverage-final.json


+ 224 - 0
coverage/lcov-report/base.css

@@ -0,0 +1,224 @@
+body, html {
+  margin:0; padding: 0;
+  height: 100%;
+}
+body {
+    font-family: Helvetica Neue, Helvetica, Arial;
+    font-size: 14px;
+    color:#333;
+}
+.small { font-size: 12px; }
+*, *:after, *:before {
+  -webkit-box-sizing:border-box;
+     -moz-box-sizing:border-box;
+          box-sizing:border-box;
+  }
+h1 { font-size: 20px; margin: 0;}
+h2 { font-size: 14px; }
+pre {
+    font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
+    margin: 0;
+    padding: 0;
+    -moz-tab-size: 2;
+    -o-tab-size:  2;
+    tab-size: 2;
+}
+a { color:#0074D9; text-decoration:none; }
+a:hover { text-decoration:underline; }
+.strong { font-weight: bold; }
+.space-top1 { padding: 10px 0 0 0; }
+.pad2y { padding: 20px 0; }
+.pad1y { padding: 10px 0; }
+.pad2x { padding: 0 20px; }
+.pad2 { padding: 20px; }
+.pad1 { padding: 10px; }
+.space-left2 { padding-left:55px; }
+.space-right2 { padding-right:20px; }
+.center { text-align:center; }
+.clearfix { display:block; }
+.clearfix:after {
+  content:'';
+  display:block;
+  height:0;
+  clear:both;
+  visibility:hidden;
+  }
+.fl { float: left; }
+@media only screen and (max-width:640px) {
+  .col3 { width:100%; max-width:100%; }
+  .hide-mobile { display:none!important; }
+}
+
+.quiet {
+  color: #7f7f7f;
+  color: rgba(0,0,0,0.5);
+}
+.quiet a { opacity: 0.7; }
+
+.fraction {
+  font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
+  font-size: 10px;
+  color: #555;
+  background: #E8E8E8;
+  padding: 4px 5px;
+  border-radius: 3px;
+  vertical-align: middle;
+}
+
+div.path a:link, div.path a:visited { color: #333; }
+table.coverage {
+  border-collapse: collapse;
+  margin: 10px 0 0 0;
+  padding: 0;
+}
+
+table.coverage td {
+  margin: 0;
+  padding: 0;
+  vertical-align: top;
+}
+table.coverage td.line-count {
+    text-align: right;
+    padding: 0 5px 0 20px;
+}
+table.coverage td.line-coverage {
+    text-align: right;
+    padding-right: 10px;
+    min-width:20px;
+}
+
+table.coverage td span.cline-any {
+    display: inline-block;
+    padding: 0 5px;
+    width: 100%;
+}
+.missing-if-branch {
+    display: inline-block;
+    margin-right: 5px;
+    border-radius: 3px;
+    position: relative;
+    padding: 0 4px;
+    background: #333;
+    color: yellow;
+}
+
+.skip-if-branch {
+    display: none;
+    margin-right: 10px;
+    position: relative;
+    padding: 0 4px;
+    background: #ccc;
+    color: white;
+}
+.missing-if-branch .typ, .skip-if-branch .typ {
+    color: inherit !important;
+}
+.coverage-summary {
+  border-collapse: collapse;
+  width: 100%;
+}
+.coverage-summary tr { border-bottom: 1px solid #bbb; }
+.keyline-all { border: 1px solid #ddd; }
+.coverage-summary td, .coverage-summary th { padding: 10px; }
+.coverage-summary tbody { border: 1px solid #bbb; }
+.coverage-summary td { border-right: 1px solid #bbb; }
+.coverage-summary td:last-child { border-right: none; }
+.coverage-summary th {
+  text-align: left;
+  font-weight: normal;
+  white-space: nowrap;
+}
+.coverage-summary th.file { border-right: none !important; }
+.coverage-summary th.pct { }
+.coverage-summary th.pic,
+.coverage-summary th.abs,
+.coverage-summary td.pct,
+.coverage-summary td.abs { text-align: right; }
+.coverage-summary td.file { white-space: nowrap;  }
+.coverage-summary td.pic { min-width: 120px !important;  }
+.coverage-summary tfoot td { }
+
+.coverage-summary .sorter {
+    height: 10px;
+    width: 7px;
+    display: inline-block;
+    margin-left: 0.5em;
+    background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
+}
+.coverage-summary .sorted .sorter {
+    background-position: 0 -20px;
+}
+.coverage-summary .sorted-desc .sorter {
+    background-position: 0 -10px;
+}
+.status-line {  height: 10px; }
+/* yellow */
+.cbranch-no { background: yellow !important; color: #111; }
+/* dark red */
+.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
+.low .chart { border:1px solid #C21F39 }
+.highlighted,
+.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
+  background: #C21F39 !important;
+}
+/* medium red */
+.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
+/* light red */
+.low, .cline-no { background:#FCE1E5 }
+/* light green */
+.high, .cline-yes { background:rgb(230,245,208) }
+/* medium green */
+.cstat-yes { background:rgb(161,215,106) }
+/* dark green */
+.status-line.high, .high .cover-fill { background:rgb(77,146,33) }
+.high .chart { border:1px solid rgb(77,146,33) }
+/* dark yellow (gold) */
+.status-line.medium, .medium .cover-fill { background: #f9cd0b; }
+.medium .chart { border:1px solid #f9cd0b; }
+/* light yellow */
+.medium { background: #fff4c2; }
+
+.cstat-skip { background: #ddd; color: #111; }
+.fstat-skip { background: #ddd; color: #111 !important; }
+.cbranch-skip { background: #ddd !important; color: #111; }
+
+span.cline-neutral { background: #eaeaea; }
+
+.coverage-summary td.empty {
+    opacity: .5;
+    padding-top: 4px;
+    padding-bottom: 4px;
+    line-height: 1;
+    color: #888;
+}
+
+.cover-fill, .cover-empty {
+  display:inline-block;
+  height: 12px;
+}
+.chart {
+  line-height: 0;
+}
+.cover-empty {
+    background: white;
+}
+.cover-full {
+    border-right: none !important;
+}
+pre.prettyprint {
+    border: none !important;
+    padding: 0 !important;
+    margin: 0 !important;
+}
+.com { color: #999 !important; }
+.ignore-none { color: #999; font-weight: normal; }
+
+.wrapper {
+  min-height: 100%;
+  height: auto !important;
+  height: 100%;
+  margin: 0 auto -48px;
+}
+.footer, .push {
+  height: 48px;
+}

+ 79 - 0
coverage/lcov-report/block-navigation.js

@@ -0,0 +1,79 @@
+/* eslint-disable */
+var jumpToCode = (function init() {
+    // Classes of code we would like to highlight in the file view
+    var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no'];
+
+    // Elements to highlight in the file listing view
+    var fileListingElements = ['td.pct.low'];
+
+    // We don't want to select elements that are direct descendants of another match
+    var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
+
+    // Selecter that finds elements on the page to which we can jump
+    var selector =
+        fileListingElements.join(', ') +
+        ', ' +
+        notSelector +
+        missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
+
+    // The NodeList of matching elements
+    var missingCoverageElements = document.querySelectorAll(selector);
+
+    var currentIndex;
+
+    function toggleClass(index) {
+        missingCoverageElements
+            .item(currentIndex)
+            .classList.remove('highlighted');
+        missingCoverageElements.item(index).classList.add('highlighted');
+    }
+
+    function makeCurrent(index) {
+        toggleClass(index);
+        currentIndex = index;
+        missingCoverageElements.item(index).scrollIntoView({
+            behavior: 'smooth',
+            block: 'center',
+            inline: 'center'
+        });
+    }
+
+    function goToPrevious() {
+        var nextIndex = 0;
+        if (typeof currentIndex !== 'number' || currentIndex === 0) {
+            nextIndex = missingCoverageElements.length - 1;
+        } else if (missingCoverageElements.length > 1) {
+            nextIndex = currentIndex - 1;
+        }
+
+        makeCurrent(nextIndex);
+    }
+
+    function goToNext() {
+        var nextIndex = 0;
+
+        if (
+            typeof currentIndex === 'number' &&
+            currentIndex < missingCoverageElements.length - 1
+        ) {
+            nextIndex = currentIndex + 1;
+        }
+
+        makeCurrent(nextIndex);
+    }
+
+    return function jump(event) {
+        switch (event.which) {
+            case 78: // n
+            case 74: // j
+                goToNext();
+                break;
+            case 66: // b
+            case 75: // k
+            case 80: // p
+                goToPrevious();
+                break;
+        }
+    };
+})();
+window.addEventListener('keydown', jumpToCode);

BIN
coverage/lcov-report/favicon.png


+ 186 - 0
coverage/lcov-report/index.html

@@ -0,0 +1,186 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for All files</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="prettify.css" />
+    <link rel="stylesheet" href="base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1>All files</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">64.32% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>146/227</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">43.59% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>17/39</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">38.71% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>12/31</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">64.32% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>146/227</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line medium'></div>
+    <div class="pad1">
+<table class="coverage-summary">
+<thead>
+<tr>
+   <th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
+   <th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
+   <th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
+   <th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
+   <th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
+   <th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
+   <th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
+</tr>
+</thead>
+<tbody><tr>
+	<td class="file high" data-value="nodejs-homework-API"><a href="nodejs-homework-API/index.html">nodejs-homework-API</a></td>
+	<td data-value="95.83" class="pic high">
+	<div class="chart"><div class="cover-fill" style="width: 95%"></div><div class="cover-empty" style="width: 5%"></div></div>
+	</td>
+	<td data-value="95.83" class="pct high">95.83%</td>
+	<td data-value="24" class="abs high">23/24</td>
+	<td data-value="50" class="pct medium">50%</td>
+	<td data-value="2" class="abs medium">1/2</td>
+	<td data-value="50" class="pct medium">50%</td>
+	<td data-value="2" class="abs medium">1/2</td>
+	<td data-value="95.83" class="pct high">95.83%</td>
+	<td data-value="24" class="abs high">23/24</td>
+	</tr>
+
+<tr>
+	<td class="file high" data-value="nodejs-homework-API/config"><a href="nodejs-homework-API/config/index.html">nodejs-homework-API/config</a></td>
+	<td data-value="80" class="pic high">
+	<div class="chart"><div class="cover-fill" style="width: 80%"></div><div class="cover-empty" style="width: 20%"></div></div>
+	</td>
+	<td data-value="80" class="pct high">80%</td>
+	<td data-value="15" class="abs high">12/15</td>
+	<td data-value="50" class="pct medium">50%</td>
+	<td data-value="4" class="abs medium">2/4</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="1" class="abs high">1/1</td>
+	<td data-value="80" class="pct high">80%</td>
+	<td data-value="15" class="abs high">12/15</td>
+	</tr>
+
+<tr>
+	<td class="file low" data-value="nodejs-homework-API/controllers"><a href="nodejs-homework-API/controllers/index.html">nodejs-homework-API/controllers</a></td>
+	<td data-value="47.62" class="pic low">
+	<div class="chart"><div class="cover-fill" style="width: 47%"></div><div class="cover-empty" style="width: 53%"></div></div>
+	</td>
+	<td data-value="47.62" class="pct low">47.62%</td>
+	<td data-value="105" class="abs low">50/105</td>
+	<td data-value="38.89" class="pct low">38.89%</td>
+	<td data-value="18" class="abs low">7/18</td>
+	<td data-value="45.45" class="pct low">45.45%</td>
+	<td data-value="11" class="abs low">5/11</td>
+	<td data-value="47.62" class="pct low">47.62%</td>
+	<td data-value="105" class="abs low">50/105</td>
+	</tr>
+
+<tr>
+	<td class="file medium" data-value="nodejs-homework-API/helpers"><a href="nodejs-homework-API/helpers/index.html">nodejs-homework-API/helpers</a></td>
+	<td data-value="67.57" class="pic medium">
+	<div class="chart"><div class="cover-fill" style="width: 67%"></div><div class="cover-empty" style="width: 33%"></div></div>
+	</td>
+	<td data-value="67.57" class="pct medium">67.57%</td>
+	<td data-value="37" class="abs medium">25/37</td>
+	<td data-value="55.56" class="pct medium">55.56%</td>
+	<td data-value="9" class="abs medium">5/9</td>
+	<td data-value="20" class="pct low">20%</td>
+	<td data-value="10" class="abs low">2/10</td>
+	<td data-value="67.57" class="pct medium">67.57%</td>
+	<td data-value="37" class="abs medium">25/37</td>
+	</tr>
+
+<tr>
+	<td class="file high" data-value="nodejs-homework-API/routes/api"><a href="nodejs-homework-API/routes/api/index.html">nodejs-homework-API/routes/api</a></td>
+	<td data-value="100" class="pic high">
+	<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
+	</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="16" class="abs high">16/16</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="0" class="abs high">0/0</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="0" class="abs high">0/0</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="16" class="abs high">16/16</td>
+	</tr>
+
+<tr>
+	<td class="file medium" data-value="nodejs-homework-API/routes/api/validation"><a href="nodejs-homework-API/routes/api/validation/index.html">nodejs-homework-API/routes/api/validation</a></td>
+	<td data-value="66.67" class="pic medium">
+	<div class="chart"><div class="cover-fill" style="width: 66%"></div><div class="cover-empty" style="width: 34%"></div></div>
+	</td>
+	<td data-value="66.67" class="pct medium">66.67%</td>
+	<td data-value="30" class="abs medium">20/30</td>
+	<td data-value="33.33" class="pct low">33.33%</td>
+	<td data-value="6" class="abs low">2/6</td>
+	<td data-value="42.86" class="pct low">42.86%</td>
+	<td data-value="7" class="abs low">3/7</td>
+	<td data-value="66.67" class="pct medium">66.67%</td>
+	<td data-value="30" class="abs medium">20/30</td>
+	</tr>
+
+</tbody>
+</table>
+</div>
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Mon Mar 15 2021 13:23:50 GMT+0200 (GMT+02:00)
+            </div>
+        </div>
+        <script src="prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="sorter.js"></script>
+        <script src="block-navigation.js"></script>
+    </body>
+</html>
+    

+ 236 - 0
coverage/lcov-report/nodejs-homework-API/app.js.html

@@ -0,0 +1,236 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for nodejs-homework-API/app.js</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../prettify.css" />
+    <link rel="stylesheet" href="../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../index.html">All files</a> / <a href="index.html">nodejs-homework-API</a> app.js</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">95.83% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>23/24</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">50% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>1/2</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">50% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>1/2</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">95.83% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>23/24</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line high'></div>
+    <pre><table class="coverage">
+<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
+<a name='L2'></a><a href='#L2'>2</a>
+<a name='L3'></a><a href='#L3'>3</a>
+<a name='L4'></a><a href='#L4'>4</a>
+<a name='L5'></a><a href='#L5'>5</a>
+<a name='L6'></a><a href='#L6'>6</a>
+<a name='L7'></a><a href='#L7'>7</a>
+<a name='L8'></a><a href='#L8'>8</a>
+<a name='L9'></a><a href='#L9'>9</a>
+<a name='L10'></a><a href='#L10'>10</a>
+<a name='L11'></a><a href='#L11'>11</a>
+<a name='L12'></a><a href='#L12'>12</a>
+<a name='L13'></a><a href='#L13'>13</a>
+<a name='L14'></a><a href='#L14'>14</a>
+<a name='L15'></a><a href='#L15'>15</a>
+<a name='L16'></a><a href='#L16'>16</a>
+<a name='L17'></a><a href='#L17'>17</a>
+<a name='L18'></a><a href='#L18'>18</a>
+<a name='L19'></a><a href='#L19'>19</a>
+<a name='L20'></a><a href='#L20'>20</a>
+<a name='L21'></a><a href='#L21'>21</a>
+<a name='L22'></a><a href='#L22'>22</a>
+<a name='L23'></a><a href='#L23'>23</a>
+<a name='L24'></a><a href='#L24'>24</a>
+<a name='L25'></a><a href='#L25'>25</a>
+<a name='L26'></a><a href='#L26'>26</a>
+<a name='L27'></a><a href='#L27'>27</a>
+<a name='L28'></a><a href='#L28'>28</a>
+<a name='L29'></a><a href='#L29'>29</a>
+<a name='L30'></a><a href='#L30'>30</a>
+<a name='L31'></a><a href='#L31'>31</a>
+<a name='L32'></a><a href='#L32'>32</a>
+<a name='L33'></a><a href='#L33'>33</a>
+<a name='L34'></a><a href='#L34'>34</a>
+<a name='L35'></a><a href='#L35'>35</a>
+<a name='L36'></a><a href='#L36'>36</a>
+<a name='L37'></a><a href='#L37'>37</a>
+<a name='L38'></a><a href='#L38'>38</a>
+<a name='L39'></a><a href='#L39'>39</a>
+<a name='L40'></a><a href='#L40'>40</a>
+<a name='L41'></a><a href='#L41'>41</a>
+<a name='L42'></a><a href='#L42'>42</a>
+<a name='L43'></a><a href='#L43'>43</a>
+<a name='L44'></a><a href='#L44'>44</a>
+<a name='L45'></a><a href='#L45'>45</a>
+<a name='L46'></a><a href='#L46'>46</a>
+<a name='L47'></a><a href='#L47'>47</a>
+<a name='L48'></a><a href='#L48'>48</a>
+<a name='L49'></a><a href='#L49'>49</a>
+<a name='L50'></a><a href='#L50'>50</a>
+<a name='L51'></a><a href='#L51'>51</a>
+<a name='L52'></a><a href='#L52'>52</a>
+<a name='L53'></a><a href='#L53'>53</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">3x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">const express = require("express");
+const path = require("path");
+const logger = require("morgan");
+const cors = require("cors");
+const helmet = require("helmet");
+const apiLimiter = require("./helpers/apiLimiter");
+&nbsp;
+const contactsRouter = require("./routes/api/contacts");
+const authenticationRoute = require("./routes/api/user");
+const app = express();
+&nbsp;
+const FOLDER_IMAGES = process.env.DIR_IMAGES;
+app.use(express.static(path.join(__dirname, FOLDER_IMAGES)));
+console.log(path.join(__dirname, FOLDER_IMAGES));
+const formatsLogger = app.get("env") === "development" ? <span class="branch-0 cbranch-no" title="branch not covered" >"dev" </span>: "short";
+&nbsp;
+app.use(helmet());
+app.use(logger(formatsLogger));
+app.use(cors());
+app.use(express.json());
+&nbsp;
+app.use("/api/contacts", apiLimiter, contactsRouter);
+app.use("/api/auth", apiLimiter, authenticationRoute);
+&nbsp;
+app.use(<span class="fstat-no" title="function not covered" >(r</span>eq, res) =&gt; {
+<span class="cstat-no" title="statement not covered" >  res.status(404).json({ message: "Not found" });</span>
+});
+&nbsp;
+app.use((err, req, res, next) =&gt; {
+  res.status(500).json({ message: err.message });
+});
+&nbsp;
+module.exports = app;
+// Дополнительное задание - необязательное
+// 1. Написать юнит-тесты для мидлвара по авторизации
+// (при помощи mocha, sinon)
+// все методы и функции, вызываемые мидлваром (вместе с next) должны быть заглушены при помощи sinon
+// нужно проверить количество вызовов заглушок и аргументы с которыми они вызывались в случаях, когда:
+// пользователь не передал токен в Authorization заголовке
+// токен пользователя невалидный
+// токен пользователя валидный
+// Подсказка:
+// Иногда Вам может понадобится переопределить возвращаемые значения
+// методов-заглушок
+// 2. Написать приемочные тесты для ендпоинта обновления аватарок
+// (дополнительно нужно будет использовать supertest)
+// Тесты должны проверять:
+// возвращается ли ответ со статус кодом 401, если токен пользователя невалидный
+// В случае, если все прошло успешно, проверить:
+// возвращается ли ответ со статус кодом 200
+// возвращается ли тело ответа в правильном формате
+// добавляется ли avatarUrl в документ целевого пользователя
+&nbsp;</pre></td></tr></table></pre>
+
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Mon Mar 15 2021 13:23:50 GMT+0200 (GMT+02:00)
+            </div>
+        </div>
+        <script src="../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../sorter.js"></script>
+        <script src="../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 111 - 0
coverage/lcov-report/nodejs-homework-API/config/index.html

@@ -0,0 +1,111 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for nodejs-homework-API/config</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../../prettify.css" />
+    <link rel="stylesheet" href="../../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../../index.html">All files</a> nodejs-homework-API/config</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">80% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>12/15</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">50% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>2/4</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>1/1</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">80% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>12/15</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line high'></div>
+    <div class="pad1">
+<table class="coverage-summary">
+<thead>
+<tr>
+   <th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
+   <th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
+   <th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
+   <th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
+   <th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
+   <th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
+   <th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
+</tr>
+</thead>
+<tbody><tr>
+	<td class="file high" data-value="passport.js"><a href="passport.js.html">passport.js</a></td>
+	<td data-value="80" class="pic high">
+	<div class="chart"><div class="cover-fill" style="width: 80%"></div><div class="cover-empty" style="width: 20%"></div></div>
+	</td>
+	<td data-value="80" class="pct high">80%</td>
+	<td data-value="15" class="abs high">12/15</td>
+	<td data-value="50" class="pct medium">50%</td>
+	<td data-value="4" class="abs medium">2/4</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="1" class="abs high">1/1</td>
+	<td data-value="80" class="pct high">80%</td>
+	<td data-value="15" class="abs high">12/15</td>
+	</tr>
+
+</tbody>
+</table>
+</div>
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Mon Mar 15 2021 13:23:50 GMT+0200 (GMT+02:00)
+            </div>
+        </div>
+        <script src="../../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../../sorter.js"></script>
+        <script src="../../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 161 - 0
coverage/lcov-report/nodejs-homework-API/config/passport.js.html

@@ -0,0 +1,161 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for nodejs-homework-API/config/passport.js</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../../prettify.css" />
+    <link rel="stylesheet" href="../../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../../index.html">All files</a> / <a href="index.html">nodejs-homework-API/config</a> passport.js</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">80% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>12/15</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">50% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>2/4</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>1/1</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">80% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>12/15</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line high'></div>
+    <pre><table class="coverage">
+<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
+<a name='L2'></a><a href='#L2'>2</a>
+<a name='L3'></a><a href='#L3'>3</a>
+<a name='L4'></a><a href='#L4'>4</a>
+<a name='L5'></a><a href='#L5'>5</a>
+<a name='L6'></a><a href='#L6'>6</a>
+<a name='L7'></a><a href='#L7'>7</a>
+<a name='L8'></a><a href='#L8'>8</a>
+<a name='L9'></a><a href='#L9'>9</a>
+<a name='L10'></a><a href='#L10'>10</a>
+<a name='L11'></a><a href='#L11'>11</a>
+<a name='L12'></a><a href='#L12'>12</a>
+<a name='L13'></a><a href='#L13'>13</a>
+<a name='L14'></a><a href='#L14'>14</a>
+<a name='L15'></a><a href='#L15'>15</a>
+<a name='L16'></a><a href='#L16'>16</a>
+<a name='L17'></a><a href='#L17'>17</a>
+<a name='L18'></a><a href='#L18'>18</a>
+<a name='L19'></a><a href='#L19'>19</a>
+<a name='L20'></a><a href='#L20'>20</a>
+<a name='L21'></a><a href='#L21'>21</a>
+<a name='L22'></a><a href='#L22'>22</a>
+<a name='L23'></a><a href='#L23'>23</a>
+<a name='L24'></a><a href='#L24'>24</a>
+<a name='L25'></a><a href='#L25'>25</a>
+<a name='L26'></a><a href='#L26'>26</a>
+<a name='L27'></a><a href='#L27'>27</a>
+<a name='L28'></a><a href='#L28'>28</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">11x</span>
+<span class="cline-any cline-yes">11x</span>
+<span class="cline-any cline-yes">11x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">11x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">11x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">const passport = require("passport");
+const UserModel = require("../model/user");
+const { Strategy, ExtractJwt } = require("passport-jwt");
+require("dotenv").config();
+const SECRET_KEY = process.env.JWT_SECRET;
+&nbsp;
+const params = {
+  secretOrKey: SECRET_KEY,
+  jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
+};
+&nbsp;
+passport.use(
+  new Strategy(params, async (payload, done) =&gt; {
+    try {
+      const user = await UserModel.findById(payload.id);
+      <span class="missing-if-branch" title="if path not taken" >I</span>if (!user) {
+<span class="cstat-no" title="statement not covered" >        return done(new Error("User not found"));</span>
+      }
+      <span class="missing-if-branch" title="if path not taken" >I</span>if (!user.token) {
+<span class="cstat-no" title="statement not covered" >        return done(null, false);</span>
+      }
+      return done(null, user);
+    } catch (err) {
+<span class="cstat-no" title="statement not covered" >      done(err);</span>
+    }
+  })
+);
+&nbsp;</pre></td></tr></table></pre>
+
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Mon Mar 15 2021 13:23:50 GMT+0200 (GMT+02:00)
+            </div>
+        </div>
+        <script src="../../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../../sorter.js"></script>
+        <script src="../../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 440 - 0
coverage/lcov-report/nodejs-homework-API/controllers/contacts.js.html

@@ -0,0 +1,440 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for nodejs-homework-API/controllers/contacts.js</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../../prettify.css" />
+    <link rel="stylesheet" href="../../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../../index.html">All files</a> / <a href="index.html">nodejs-homework-API/controllers</a> contacts.js</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">87.5% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>35/40</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">87.5% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>7/8</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>5/5</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">87.5% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>35/40</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line high'></div>
+    <pre><table class="coverage">
+<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
+<a name='L2'></a><a href='#L2'>2</a>
+<a name='L3'></a><a href='#L3'>3</a>
+<a name='L4'></a><a href='#L4'>4</a>
+<a name='L5'></a><a href='#L5'>5</a>
+<a name='L6'></a><a href='#L6'>6</a>
+<a name='L7'></a><a href='#L7'>7</a>
+<a name='L8'></a><a href='#L8'>8</a>
+<a name='L9'></a><a href='#L9'>9</a>
+<a name='L10'></a><a href='#L10'>10</a>
+<a name='L11'></a><a href='#L11'>11</a>
+<a name='L12'></a><a href='#L12'>12</a>
+<a name='L13'></a><a href='#L13'>13</a>
+<a name='L14'></a><a href='#L14'>14</a>
+<a name='L15'></a><a href='#L15'>15</a>
+<a name='L16'></a><a href='#L16'>16</a>
+<a name='L17'></a><a href='#L17'>17</a>
+<a name='L18'></a><a href='#L18'>18</a>
+<a name='L19'></a><a href='#L19'>19</a>
+<a name='L20'></a><a href='#L20'>20</a>
+<a name='L21'></a><a href='#L21'>21</a>
+<a name='L22'></a><a href='#L22'>22</a>
+<a name='L23'></a><a href='#L23'>23</a>
+<a name='L24'></a><a href='#L24'>24</a>
+<a name='L25'></a><a href='#L25'>25</a>
+<a name='L26'></a><a href='#L26'>26</a>
+<a name='L27'></a><a href='#L27'>27</a>
+<a name='L28'></a><a href='#L28'>28</a>
+<a name='L29'></a><a href='#L29'>29</a>
+<a name='L30'></a><a href='#L30'>30</a>
+<a name='L31'></a><a href='#L31'>31</a>
+<a name='L32'></a><a href='#L32'>32</a>
+<a name='L33'></a><a href='#L33'>33</a>
+<a name='L34'></a><a href='#L34'>34</a>
+<a name='L35'></a><a href='#L35'>35</a>
+<a name='L36'></a><a href='#L36'>36</a>
+<a name='L37'></a><a href='#L37'>37</a>
+<a name='L38'></a><a href='#L38'>38</a>
+<a name='L39'></a><a href='#L39'>39</a>
+<a name='L40'></a><a href='#L40'>40</a>
+<a name='L41'></a><a href='#L41'>41</a>
+<a name='L42'></a><a href='#L42'>42</a>
+<a name='L43'></a><a href='#L43'>43</a>
+<a name='L44'></a><a href='#L44'>44</a>
+<a name='L45'></a><a href='#L45'>45</a>
+<a name='L46'></a><a href='#L46'>46</a>
+<a name='L47'></a><a href='#L47'>47</a>
+<a name='L48'></a><a href='#L48'>48</a>
+<a name='L49'></a><a href='#L49'>49</a>
+<a name='L50'></a><a href='#L50'>50</a>
+<a name='L51'></a><a href='#L51'>51</a>
+<a name='L52'></a><a href='#L52'>52</a>
+<a name='L53'></a><a href='#L53'>53</a>
+<a name='L54'></a><a href='#L54'>54</a>
+<a name='L55'></a><a href='#L55'>55</a>
+<a name='L56'></a><a href='#L56'>56</a>
+<a name='L57'></a><a href='#L57'>57</a>
+<a name='L58'></a><a href='#L58'>58</a>
+<a name='L59'></a><a href='#L59'>59</a>
+<a name='L60'></a><a href='#L60'>60</a>
+<a name='L61'></a><a href='#L61'>61</a>
+<a name='L62'></a><a href='#L62'>62</a>
+<a name='L63'></a><a href='#L63'>63</a>
+<a name='L64'></a><a href='#L64'>64</a>
+<a name='L65'></a><a href='#L65'>65</a>
+<a name='L66'></a><a href='#L66'>66</a>
+<a name='L67'></a><a href='#L67'>67</a>
+<a name='L68'></a><a href='#L68'>68</a>
+<a name='L69'></a><a href='#L69'>69</a>
+<a name='L70'></a><a href='#L70'>70</a>
+<a name='L71'></a><a href='#L71'>71</a>
+<a name='L72'></a><a href='#L72'>72</a>
+<a name='L73'></a><a href='#L73'>73</a>
+<a name='L74'></a><a href='#L74'>74</a>
+<a name='L75'></a><a href='#L75'>75</a>
+<a name='L76'></a><a href='#L76'>76</a>
+<a name='L77'></a><a href='#L77'>77</a>
+<a name='L78'></a><a href='#L78'>78</a>
+<a name='L79'></a><a href='#L79'>79</a>
+<a name='L80'></a><a href='#L80'>80</a>
+<a name='L81'></a><a href='#L81'>81</a>
+<a name='L82'></a><a href='#L82'>82</a>
+<a name='L83'></a><a href='#L83'>83</a>
+<a name='L84'></a><a href='#L84'>84</a>
+<a name='L85'></a><a href='#L85'>85</a>
+<a name='L86'></a><a href='#L86'>86</a>
+<a name='L87'></a><a href='#L87'>87</a>
+<a name='L88'></a><a href='#L88'>88</a>
+<a name='L89'></a><a href='#L89'>89</a>
+<a name='L90'></a><a href='#L90'>90</a>
+<a name='L91'></a><a href='#L91'>91</a>
+<a name='L92'></a><a href='#L92'>92</a>
+<a name='L93'></a><a href='#L93'>93</a>
+<a name='L94'></a><a href='#L94'>94</a>
+<a name='L95'></a><a href='#L95'>95</a>
+<a name='L96'></a><a href='#L96'>96</a>
+<a name='L97'></a><a href='#L97'>97</a>
+<a name='L98'></a><a href='#L98'>98</a>
+<a name='L99'></a><a href='#L99'>99</a>
+<a name='L100'></a><a href='#L100'>100</a>
+<a name='L101'></a><a href='#L101'>101</a>
+<a name='L102'></a><a href='#L102'>102</a>
+<a name='L103'></a><a href='#L103'>103</a>
+<a name='L104'></a><a href='#L104'>104</a>
+<a name='L105'></a><a href='#L105'>105</a>
+<a name='L106'></a><a href='#L106'>106</a>
+<a name='L107'></a><a href='#L107'>107</a>
+<a name='L108'></a><a href='#L108'>108</a>
+<a name='L109'></a><a href='#L109'>109</a>
+<a name='L110'></a><a href='#L110'>110</a>
+<a name='L111'></a><a href='#L111'>111</a>
+<a name='L112'></a><a href='#L112'>112</a>
+<a name='L113'></a><a href='#L113'>113</a>
+<a name='L114'></a><a href='#L114'>114</a>
+<a name='L115'></a><a href='#L115'>115</a>
+<a name='L116'></a><a href='#L116'>116</a>
+<a name='L117'></a><a href='#L117'>117</a>
+<a name='L118'></a><a href='#L118'>118</a>
+<a name='L119'></a><a href='#L119'>119</a>
+<a name='L120'></a><a href='#L120'>120</a>
+<a name='L121'></a><a href='#L121'>121</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">const Contact = require("../model/contact");
+&nbsp;
+const listContacts = async (req, res, next) =&gt; {
+  try {
+    const userId = req.user.id;
+    const contacts = await Contact.getList(userId, req.query);
+    return res.json({
+      status: "success",
+      code: 200,
+      data: {
+        ...contacts,
+      },
+    });
+  } catch (e) {
+<span class="cstat-no" title="statement not covered" >    next(e);</span>
+  }
+};
+&nbsp;
+const getContactById = async (req, res, next) =&gt; {
+  try {
+    const userId = req.user.id;
+    const contact = await Contact.getById(req.params.contactId, userId);
+    if (contact) {
+      return res.json({
+        status: "success",
+        code: 200,
+        data: {
+          contact,
+        },
+      });
+    } else {
+      return res.status(404).json({
+        status: "error",
+        code: 404,
+        data: "Not Found",
+      });
+    }
+  } catch (e) {
+<span class="cstat-no" title="statement not covered" >    next(e);</span>
+  }
+};
+&nbsp;
+const addContact = async (req, res, next) =&gt; {
+  try {
+    const contact = req.body;
+    const userId = req.user.id;
+    <span class="missing-if-branch" title="else path not taken" >E</span>if (contact) {
+      await Contact.add({ ...contact, owner: userId });
+      return res.status(201).json({
+        status: "success",
+        code: 201,
+        data: {
+          contact,
+        },
+      });
+    }
+  } catch (e) {
+<span class="cstat-no" title="statement not covered" >    next(e);</span>
+  }
+};
+&nbsp;
+const removeContact = async (req, res, next) =&gt; {
+  try {
+    const userId = req.user.id;
+    const contact = await Contact.remove(req.params.contactId, userId);
+    if (contact) {
+      return res.json({
+        status: "success",
+        code: 200,
+        data: {
+          contact,
+        },
+      });
+    } else {
+      return res.status(404).json({
+        status: "error",
+        code: 404,
+        data: "Not Found",
+      });
+    }
+  } catch (e) {
+<span class="cstat-no" title="statement not covered" >    next(e);</span>
+  }
+};
+&nbsp;
+const updateContact = async (req, res, next) =&gt; {
+  try {
+    const userId = req.user.id;
+    const contact = await Contact.update(
+      req.params.contactId,
+      req.body,
+      userId
+    );
+    if (contact) {
+      return res.status(200).json({
+        status: "success",
+        code: 200,
+        data: {
+          contact,
+        },
+      });
+    } else {
+      return res.status(404).json({
+        status: "error",
+        code: 404,
+        data: "Not Found",
+      });
+    }
+  } catch (e) {
+<span class="cstat-no" title="statement not covered" >    next(e);</span>
+  }
+};
+&nbsp;
+module.exports = {
+  listContacts,
+  getContactById,
+  addContact,
+  removeContact,
+  updateContact,
+};
+&nbsp;</pre></td></tr></table></pre>
+
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Mon Mar 15 2021 13:23:50 GMT+0200 (GMT+02:00)
+            </div>
+        </div>
+        <script src="../../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../../sorter.js"></script>
+        <script src="../../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 126 - 0
coverage/lcov-report/nodejs-homework-API/controllers/index.html

@@ -0,0 +1,126 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for nodejs-homework-API/controllers</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../../prettify.css" />
+    <link rel="stylesheet" href="../../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../../index.html">All files</a> nodejs-homework-API/controllers</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">47.62% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>50/105</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">38.89% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>7/18</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">45.45% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>5/11</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">47.62% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>50/105</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line low'></div>
+    <div class="pad1">
+<table class="coverage-summary">
+<thead>
+<tr>
+   <th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
+   <th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
+   <th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
+   <th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
+   <th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
+   <th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
+   <th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
+</tr>
+</thead>
+<tbody><tr>
+	<td class="file high" data-value="contacts.js"><a href="contacts.js.html">contacts.js</a></td>
+	<td data-value="87.5" class="pic high">
+	<div class="chart"><div class="cover-fill" style="width: 87%"></div><div class="cover-empty" style="width: 13%"></div></div>
+	</td>
+	<td data-value="87.5" class="pct high">87.5%</td>
+	<td data-value="40" class="abs high">35/40</td>
+	<td data-value="87.5" class="pct high">87.5%</td>
+	<td data-value="8" class="abs high">7/8</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="5" class="abs high">5/5</td>
+	<td data-value="87.5" class="pct high">87.5%</td>
+	<td data-value="40" class="abs high">35/40</td>
+	</tr>
+
+<tr>
+	<td class="file low" data-value="user.js"><a href="user.js.html">user.js</a></td>
+	<td data-value="23.08" class="pic low">
+	<div class="chart"><div class="cover-fill" style="width: 23%"></div><div class="cover-empty" style="width: 77%"></div></div>
+	</td>
+	<td data-value="23.08" class="pct low">23.08%</td>
+	<td data-value="65" class="abs low">15/65</td>
+	<td data-value="0" class="pct low">0%</td>
+	<td data-value="10" class="abs low">0/10</td>
+	<td data-value="0" class="pct low">0%</td>
+	<td data-value="6" class="abs low">0/6</td>
+	<td data-value="23.08" class="pct low">23.08%</td>
+	<td data-value="65" class="abs low">15/65</td>
+	</tr>
+
+</tbody>
+</table>
+</div>
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Mon Mar 15 2021 13:23:50 GMT+0200 (GMT+02:00)
+            </div>
+        </div>
+        <script src="../../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../../sorter.js"></script>
+        <script src="../../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 542 - 0
coverage/lcov-report/nodejs-homework-API/controllers/user.js.html

@@ -0,0 +1,542 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for nodejs-homework-API/controllers/user.js</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../../prettify.css" />
+    <link rel="stylesheet" href="../../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../../index.html">All files</a> / <a href="index.html">nodejs-homework-API/controllers</a> user.js</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">23.08% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>15/65</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">0% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>0/10</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">0% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>0/6</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">23.08% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>15/65</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line low'></div>
+    <pre><table class="coverage">
+<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
+<a name='L2'></a><a href='#L2'>2</a>
+<a name='L3'></a><a href='#L3'>3</a>
+<a name='L4'></a><a href='#L4'>4</a>
+<a name='L5'></a><a href='#L5'>5</a>
+<a name='L6'></a><a href='#L6'>6</a>
+<a name='L7'></a><a href='#L7'>7</a>
+<a name='L8'></a><a href='#L8'>8</a>
+<a name='L9'></a><a href='#L9'>9</a>
+<a name='L10'></a><a href='#L10'>10</a>
+<a name='L11'></a><a href='#L11'>11</a>
+<a name='L12'></a><a href='#L12'>12</a>
+<a name='L13'></a><a href='#L13'>13</a>
+<a name='L14'></a><a href='#L14'>14</a>
+<a name='L15'></a><a href='#L15'>15</a>
+<a name='L16'></a><a href='#L16'>16</a>
+<a name='L17'></a><a href='#L17'>17</a>
+<a name='L18'></a><a href='#L18'>18</a>
+<a name='L19'></a><a href='#L19'>19</a>
+<a name='L20'></a><a href='#L20'>20</a>
+<a name='L21'></a><a href='#L21'>21</a>
+<a name='L22'></a><a href='#L22'>22</a>
+<a name='L23'></a><a href='#L23'>23</a>
+<a name='L24'></a><a href='#L24'>24</a>
+<a name='L25'></a><a href='#L25'>25</a>
+<a name='L26'></a><a href='#L26'>26</a>
+<a name='L27'></a><a href='#L27'>27</a>
+<a name='L28'></a><a href='#L28'>28</a>
+<a name='L29'></a><a href='#L29'>29</a>
+<a name='L30'></a><a href='#L30'>30</a>
+<a name='L31'></a><a href='#L31'>31</a>
+<a name='L32'></a><a href='#L32'>32</a>
+<a name='L33'></a><a href='#L33'>33</a>
+<a name='L34'></a><a href='#L34'>34</a>
+<a name='L35'></a><a href='#L35'>35</a>
+<a name='L36'></a><a href='#L36'>36</a>
+<a name='L37'></a><a href='#L37'>37</a>
+<a name='L38'></a><a href='#L38'>38</a>
+<a name='L39'></a><a href='#L39'>39</a>
+<a name='L40'></a><a href='#L40'>40</a>
+<a name='L41'></a><a href='#L41'>41</a>
+<a name='L42'></a><a href='#L42'>42</a>
+<a name='L43'></a><a href='#L43'>43</a>
+<a name='L44'></a><a href='#L44'>44</a>
+<a name='L45'></a><a href='#L45'>45</a>
+<a name='L46'></a><a href='#L46'>46</a>
+<a name='L47'></a><a href='#L47'>47</a>
+<a name='L48'></a><a href='#L48'>48</a>
+<a name='L49'></a><a href='#L49'>49</a>
+<a name='L50'></a><a href='#L50'>50</a>
+<a name='L51'></a><a href='#L51'>51</a>
+<a name='L52'></a><a href='#L52'>52</a>
+<a name='L53'></a><a href='#L53'>53</a>
+<a name='L54'></a><a href='#L54'>54</a>
+<a name='L55'></a><a href='#L55'>55</a>
+<a name='L56'></a><a href='#L56'>56</a>
+<a name='L57'></a><a href='#L57'>57</a>
+<a name='L58'></a><a href='#L58'>58</a>
+<a name='L59'></a><a href='#L59'>59</a>
+<a name='L60'></a><a href='#L60'>60</a>
+<a name='L61'></a><a href='#L61'>61</a>
+<a name='L62'></a><a href='#L62'>62</a>
+<a name='L63'></a><a href='#L63'>63</a>
+<a name='L64'></a><a href='#L64'>64</a>
+<a name='L65'></a><a href='#L65'>65</a>
+<a name='L66'></a><a href='#L66'>66</a>
+<a name='L67'></a><a href='#L67'>67</a>
+<a name='L68'></a><a href='#L68'>68</a>
+<a name='L69'></a><a href='#L69'>69</a>
+<a name='L70'></a><a href='#L70'>70</a>
+<a name='L71'></a><a href='#L71'>71</a>
+<a name='L72'></a><a href='#L72'>72</a>
+<a name='L73'></a><a href='#L73'>73</a>
+<a name='L74'></a><a href='#L74'>74</a>
+<a name='L75'></a><a href='#L75'>75</a>
+<a name='L76'></a><a href='#L76'>76</a>
+<a name='L77'></a><a href='#L77'>77</a>
+<a name='L78'></a><a href='#L78'>78</a>
+<a name='L79'></a><a href='#L79'>79</a>
+<a name='L80'></a><a href='#L80'>80</a>
+<a name='L81'></a><a href='#L81'>81</a>
+<a name='L82'></a><a href='#L82'>82</a>
+<a name='L83'></a><a href='#L83'>83</a>
+<a name='L84'></a><a href='#L84'>84</a>
+<a name='L85'></a><a href='#L85'>85</a>
+<a name='L86'></a><a href='#L86'>86</a>
+<a name='L87'></a><a href='#L87'>87</a>
+<a name='L88'></a><a href='#L88'>88</a>
+<a name='L89'></a><a href='#L89'>89</a>
+<a name='L90'></a><a href='#L90'>90</a>
+<a name='L91'></a><a href='#L91'>91</a>
+<a name='L92'></a><a href='#L92'>92</a>
+<a name='L93'></a><a href='#L93'>93</a>
+<a name='L94'></a><a href='#L94'>94</a>
+<a name='L95'></a><a href='#L95'>95</a>
+<a name='L96'></a><a href='#L96'>96</a>
+<a name='L97'></a><a href='#L97'>97</a>
+<a name='L98'></a><a href='#L98'>98</a>
+<a name='L99'></a><a href='#L99'>99</a>
+<a name='L100'></a><a href='#L100'>100</a>
+<a name='L101'></a><a href='#L101'>101</a>
+<a name='L102'></a><a href='#L102'>102</a>
+<a name='L103'></a><a href='#L103'>103</a>
+<a name='L104'></a><a href='#L104'>104</a>
+<a name='L105'></a><a href='#L105'>105</a>
+<a name='L106'></a><a href='#L106'>106</a>
+<a name='L107'></a><a href='#L107'>107</a>
+<a name='L108'></a><a href='#L108'>108</a>
+<a name='L109'></a><a href='#L109'>109</a>
+<a name='L110'></a><a href='#L110'>110</a>
+<a name='L111'></a><a href='#L111'>111</a>
+<a name='L112'></a><a href='#L112'>112</a>
+<a name='L113'></a><a href='#L113'>113</a>
+<a name='L114'></a><a href='#L114'>114</a>
+<a name='L115'></a><a href='#L115'>115</a>
+<a name='L116'></a><a href='#L116'>116</a>
+<a name='L117'></a><a href='#L117'>117</a>
+<a name='L118'></a><a href='#L118'>118</a>
+<a name='L119'></a><a href='#L119'>119</a>
+<a name='L120'></a><a href='#L120'>120</a>
+<a name='L121'></a><a href='#L121'>121</a>
+<a name='L122'></a><a href='#L122'>122</a>
+<a name='L123'></a><a href='#L123'>123</a>
+<a name='L124'></a><a href='#L124'>124</a>
+<a name='L125'></a><a href='#L125'>125</a>
+<a name='L126'></a><a href='#L126'>126</a>
+<a name='L127'></a><a href='#L127'>127</a>
+<a name='L128'></a><a href='#L128'>128</a>
+<a name='L129'></a><a href='#L129'>129</a>
+<a name='L130'></a><a href='#L130'>130</a>
+<a name='L131'></a><a href='#L131'>131</a>
+<a name='L132'></a><a href='#L132'>132</a>
+<a name='L133'></a><a href='#L133'>133</a>
+<a name='L134'></a><a href='#L134'>134</a>
+<a name='L135'></a><a href='#L135'>135</a>
+<a name='L136'></a><a href='#L136'>136</a>
+<a name='L137'></a><a href='#L137'>137</a>
+<a name='L138'></a><a href='#L138'>138</a>
+<a name='L139'></a><a href='#L139'>139</a>
+<a name='L140'></a><a href='#L140'>140</a>
+<a name='L141'></a><a href='#L141'>141</a>
+<a name='L142'></a><a href='#L142'>142</a>
+<a name='L143'></a><a href='#L143'>143</a>
+<a name='L144'></a><a href='#L144'>144</a>
+<a name='L145'></a><a href='#L145'>145</a>
+<a name='L146'></a><a href='#L146'>146</a>
+<a name='L147'></a><a href='#L147'>147</a>
+<a name='L148'></a><a href='#L148'>148</a>
+<a name='L149'></a><a href='#L149'>149</a>
+<a name='L150'></a><a href='#L150'>150</a>
+<a name='L151'></a><a href='#L151'>151</a>
+<a name='L152'></a><a href='#L152'>152</a>
+<a name='L153'></a><a href='#L153'>153</a>
+<a name='L154'></a><a href='#L154'>154</a>
+<a name='L155'></a><a href='#L155'>155</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">const UserModel = require("../model/user");
+const fs = require("fs").promises;
+const path = require("path");
+const Jimp = require("jimp");
+const jwt = require("jsonwebtoken");
+const createFolderIsExist = require("../helpers/create-directory");
+require("dotenv").config();
+const SECRET_KEY = process.env.JWT_SECRET;
+&nbsp;
+const saveAvatarForStatic = <span class="fstat-no" title="function not covered" >as</span>ync (req, res, next) =&gt; {
+<span class="cstat-no" title="statement not covered" >  try {</span>
+    const id = <span class="cstat-no" title="statement not covered" >req.user.id;</span>
+    const DIR_IMAGES = <span class="cstat-no" title="statement not covered" >process.env.DIR_IMAGES;</span>
+    const pathToFile = <span class="cstat-no" title="statement not covered" >req.file.path;</span>
+    const newNameAvatar = <span class="cstat-no" title="statement not covered" >req.file.originalname;</span>
+&nbsp;
+    const img = <span class="cstat-no" title="statement not covered" >await Jimp.read(pathToFile);</span>
+<span class="cstat-no" title="statement not covered" >    await img</span>
+      .autocrop()
+      .cover(
+        250,
+        250,
+        Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_MIDDLE
+      )
+      .writeAsync(pathToFile);
+&nbsp;
+<span class="cstat-no" title="statement not covered" >    await createFolderIsExist(path.join(DIR_IMAGES, id));</span>
+<span class="cstat-no" title="statement not covered" >    await fs.rename(pathToFile, path.join(DIR_IMAGES, id, newNameAvatar));</span>
+    const newUrlAvatar = <span class="cstat-no" title="statement not covered" >path.normalize(path.join(id, newNameAvatar));</span>
+    const newUrl = <span class="cstat-no" title="statement not covered" >`http://localhost:3000/${id}/${newNameAvatar}`;</span>
+<span class="cstat-no" title="statement not covered" >    await UserModel.updateAvatar(id, newUrlAvatar);</span>
+<span class="cstat-no" title="statement not covered" >    return res.status(200).json({</span>
+      status: "success",
+      code: 200,
+      data: {
+        newUrl,
+      },
+    });
+  } catch (e) {
+<span class="cstat-no" title="statement not covered" >    next(e);</span>
+  }
+};
+&nbsp;
+const createNewUser = <span class="fstat-no" title="function not covered" >as</span>ync (req, res, next) =&gt; {
+<span class="cstat-no" title="statement not covered" >  try {</span>
+    const { email } = <span class="cstat-no" title="statement not covered" >req.body;</span>
+    const isExist = <span class="cstat-no" title="statement not covered" >await UserModel.findByEmail(email);</span>
+<span class="cstat-no" title="statement not covered" >    if (isExist) {</span>
+<span class="cstat-no" title="statement not covered" >      return res.status(409).json({</span>
+        status: "error",
+        code: 409,
+        data: "Conflict",
+        message: "Email has used already!",
+      });
+    }
+    const newUser = <span class="cstat-no" title="statement not covered" >await UserModel.createUser(req.body);</span>
+<span class="cstat-no" title="statement not covered" >    return res.status(201).json({</span>
+      status: "success",
+      code: 201,
+      data: {
+        id: newUser.id,
+        email: newUser.email,
+        name: newUser.name,
+        avatar: newUser.avatar,
+      },
+    });
+  } catch (e) {
+<span class="cstat-no" title="statement not covered" >    next(e);</span>
+  }
+};
+&nbsp;
+const logIn = <span class="fstat-no" title="function not covered" >as</span>ync (req, res, next) =&gt; {
+<span class="cstat-no" title="statement not covered" >  try {</span>
+    const { email, password } = <span class="cstat-no" title="statement not covered" >req.body;</span>
+    const user = <span class="cstat-no" title="statement not covered" >await UserModel.findByEmail(email);</span>
+    const isValidPassword = <span class="cstat-no" title="statement not covered" >await user.validPassword(password);</span>
+<span class="cstat-no" title="statement not covered" >    if (!user || !isValidPassword) {</span>
+<span class="cstat-no" title="statement not covered" >      return res.status(401).json({</span>
+        status: "error",
+        code: 401,
+        data: "UNAUTHORIZED",
+        message: "Invalid credentials",
+      });
+    }
+    const id = <span class="cstat-no" title="statement not covered" >user._id;</span>
+    const payload = <span class="cstat-no" title="statement not covered" >{ id };</span>
+    const token = <span class="cstat-no" title="statement not covered" >jwt.sign(payload, SECRET_KEY, { expiresIn: "2h" });</span>
+<span class="cstat-no" title="statement not covered" >    await UserModel.updateToken(id, token);</span>
+<span class="cstat-no" title="statement not covered" >    return res.status(200).json({</span>
+      status: "success",
+      code: 200,
+      data: {
+        token,
+      },
+    });
+  } catch (e) {
+<span class="cstat-no" title="statement not covered" >    next(e);</span>
+  }
+};
+&nbsp;
+const logout = <span class="fstat-no" title="function not covered" >as</span>ync (req, res, next) =&gt; {
+  const id = <span class="cstat-no" title="statement not covered" >req.user.id;</span>
+  const user = <span class="cstat-no" title="statement not covered" >await UserModel.findById(id);</span>
+<span class="cstat-no" title="statement not covered" >  if (!user) {</span>
+<span class="cstat-no" title="statement not covered" >    return res.status(401).json({</span>
+      status: "error",
+      code: 401,
+      data: "UNAUTHORIZED",
+      message: "Invalid credentials",
+    });
+  }
+<span class="cstat-no" title="statement not covered" >  await UserModel.updateToken(id, null);</span>
+<span class="cstat-no" title="statement not covered" >  return res.status(204).json({});</span>
+};
+const getCurrent = <span class="fstat-no" title="function not covered" >as</span>ync (req, res, next) =&gt; {
+  const user = <span class="cstat-no" title="statement not covered" >req.user;</span>
+<span class="cstat-no" title="statement not covered" >  if (!user) {</span>
+<span class="cstat-no" title="statement not covered" >    return res.status(401).json({</span>
+      status: "error",
+      code: 401,
+      data: "UNAUTHORIZED",
+      message: "Invalid credentials",
+    });
+  }
+&nbsp;
+<span class="cstat-no" title="statement not covered" >  return res.status(200).json({</span>
+    status: "success",
+    code: 200,
+    data: {
+      user,
+    },
+  });
+};
+const updateSubscription = <span class="fstat-no" title="function not covered" >as</span>ync (req, res, next) =&gt; {
+<span class="cstat-no" title="statement not covered" >  try {</span>
+    const id = <span class="cstat-no" title="statement not covered" >req.user.id;</span>
+<span class="cstat-no" title="statement not covered" >    console.log(req.body);</span>
+    const update = <span class="cstat-no" title="statement not covered" >await UserModel.updateSubscrip(id, req.body);</span>
+<span class="cstat-no" title="statement not covered" >    return res.status(200).json({</span>
+      data: update,
+    });
+  } catch (e) {
+<span class="cstat-no" title="statement not covered" >    next(e);</span>
+  }
+};
+&nbsp;
+module.exports = {
+  saveAvatarForStatic,
+  createNewUser,
+  logIn,
+  logout,
+  getCurrent,
+  updateSubscription,
+};
+&nbsp;</pre></td></tr></table></pre>
+
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Mon Mar 15 2021 13:23:50 GMT+0200 (GMT+02:00)
+            </div>
+        </div>
+        <script src="../../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../../sorter.js"></script>
+        <script src="../../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 128 - 0
coverage/lcov-report/nodejs-homework-API/helpers/apiLimiter.js.html

@@ -0,0 +1,128 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for nodejs-homework-API/helpers/apiLimiter.js</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../../prettify.css" />
+    <link rel="stylesheet" href="../../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../../index.html">All files</a> / <a href="index.html">nodejs-homework-API/helpers</a> apiLimiter.js</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">75% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>3/4</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>0/0</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">0% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>0/1</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">75% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>3/4</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line medium'></div>
+    <pre><table class="coverage">
+<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
+<a name='L2'></a><a href='#L2'>2</a>
+<a name='L3'></a><a href='#L3'>3</a>
+<a name='L4'></a><a href='#L4'>4</a>
+<a name='L5'></a><a href='#L5'>5</a>
+<a name='L6'></a><a href='#L6'>6</a>
+<a name='L7'></a><a href='#L7'>7</a>
+<a name='L8'></a><a href='#L8'>8</a>
+<a name='L9'></a><a href='#L9'>9</a>
+<a name='L10'></a><a href='#L10'>10</a>
+<a name='L11'></a><a href='#L11'>11</a>
+<a name='L12'></a><a href='#L12'>12</a>
+<a name='L13'></a><a href='#L13'>13</a>
+<a name='L14'></a><a href='#L14'>14</a>
+<a name='L15'></a><a href='#L15'>15</a>
+<a name='L16'></a><a href='#L16'>16</a>
+<a name='L17'></a><a href='#L17'>17</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">const rateLimit = require("express-rate-limit");
+&nbsp;
+const apiLimiter = rateLimit({
+  windowMs: 15 * 60 * 1000, // 15 minutes
+  max: 200,
+  handler: <span class="fstat-no" title="function not covered" >(r</span>eq, res, next) =&gt; {
+<span class="cstat-no" title="statement not covered" >    return res.status(400).json({</span>
+      status: "error",
+      code: 400,
+      data: "Bad request",
+      message: "Too many requests, please try again later.",
+    });
+  },
+});
+&nbsp;
+module.exports = apiLimiter;
+&nbsp;</pre></td></tr></table></pre>
+
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Mon Mar 15 2021 13:23:50 GMT+0200 (GMT+02:00)
+            </div>
+        </div>
+        <script src="../../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../../sorter.js"></script>
+        <script src="../../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 125 - 0
coverage/lcov-report/nodejs-homework-API/helpers/create-directory.js.html

@@ -0,0 +1,125 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for nodejs-homework-API/helpers/create-directory.js</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../../prettify.css" />
+    <link rel="stylesheet" href="../../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../../index.html">All files</a> / <a href="index.html">nodejs-homework-API/helpers</a> create-directory.js</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">44.44% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>4/9</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">0% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>0/2</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">0% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>0/4</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">44.44% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>4/9</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line low'></div>
+    <pre><table class="coverage">
+<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
+<a name='L2'></a><a href='#L2'>2</a>
+<a name='L3'></a><a href='#L3'>3</a>
+<a name='L4'></a><a href='#L4'>4</a>
+<a name='L5'></a><a href='#L5'>5</a>
+<a name='L6'></a><a href='#L6'>6</a>
+<a name='L7'></a><a href='#L7'>7</a>
+<a name='L8'></a><a href='#L8'>8</a>
+<a name='L9'></a><a href='#L9'>9</a>
+<a name='L10'></a><a href='#L10'>10</a>
+<a name='L11'></a><a href='#L11'>11</a>
+<a name='L12'></a><a href='#L12'>12</a>
+<a name='L13'></a><a href='#L13'>13</a>
+<a name='L14'></a><a href='#L14'>14</a>
+<a name='L15'></a><a href='#L15'>15</a>
+<a name='L16'></a><a href='#L16'>16</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">const fs = require("fs/promises");
+&nbsp;
+const isAccessible = <span class="fstat-no" title="function not covered" >(p</span>athFolder) =&gt; {
+<span class="cstat-no" title="statement not covered" >  return fs</span>
+    .access(pathFolder)
+    .then(<span class="fstat-no" title="function not covered" >()</span> =&gt; <span class="cstat-no" title="statement not covered" >true)</span>
+    .catch(<span class="fstat-no" title="function not covered" >()</span> =&gt; <span class="cstat-no" title="statement not covered" >false)</span>;
+};
+&nbsp;
+const createFolderIsExist = <span class="fstat-no" title="function not covered" >as</span>ync (folder) =&gt; {
+<span class="cstat-no" title="statement not covered" >  if (!(await isAccessible(folder))) {</span>
+<span class="cstat-no" title="statement not covered" >    await fs.mkdir(folder);</span>
+  }
+};
+module.exports = createFolderIsExist;
+&nbsp;</pre></td></tr></table></pre>
+
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Mon Mar 15 2021 13:23:50 GMT+0200 (GMT+02:00)
+            </div>
+        </div>
+        <script src="../../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../../sorter.js"></script>
+        <script src="../../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 143 - 0
coverage/lcov-report/nodejs-homework-API/helpers/guard.js.html

@@ -0,0 +1,143 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for nodejs-homework-API/helpers/guard.js</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../../prettify.css" />
+    <link rel="stylesheet" href="../../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../../index.html">All files</a> / <a href="index.html">nodejs-homework-API/helpers</a> guard.js</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>11/11</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>5/5</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>2/2</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>11/11</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line high'></div>
+    <pre><table class="coverage">
+<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
+<a name='L2'></a><a href='#L2'>2</a>
+<a name='L3'></a><a href='#L3'>3</a>
+<a name='L4'></a><a href='#L4'>4</a>
+<a name='L5'></a><a href='#L5'>5</a>
+<a name='L6'></a><a href='#L6'>6</a>
+<a name='L7'></a><a href='#L7'>7</a>
+<a name='L8'></a><a href='#L8'>8</a>
+<a name='L9'></a><a href='#L9'>9</a>
+<a name='L10'></a><a href='#L10'>10</a>
+<a name='L11'></a><a href='#L11'>11</a>
+<a name='L12'></a><a href='#L12'>12</a>
+<a name='L13'></a><a href='#L13'>13</a>
+<a name='L14'></a><a href='#L14'>14</a>
+<a name='L15'></a><a href='#L15'>15</a>
+<a name='L16'></a><a href='#L16'>16</a>
+<a name='L17'></a><a href='#L17'>17</a>
+<a name='L18'></a><a href='#L18'>18</a>
+<a name='L19'></a><a href='#L19'>19</a>
+<a name='L20'></a><a href='#L20'>20</a>
+<a name='L21'></a><a href='#L21'>21</a>
+<a name='L22'></a><a href='#L22'>22</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">16x</span>
+<span class="cline-any cline-yes">16x</span>
+<span class="cline-any cline-yes">16x</span>
+<span class="cline-any cline-yes">5x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">11x</span>
+<span class="cline-any cline-yes">11x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">const passport = require("passport");
+require("../config/passport");
+const chalk = require("chalk");
+&nbsp;
+const guard = (req, res, next) =&gt; {
+  passport.authenticate("jwt", { session: false }, (err, user) =&gt; {
+    const token = req.get("Authorization")?.split(" ")[1];
+    if (!user || err || token !== user.token) {
+      return res.status(403).json({
+        status: "error",
+        code: 403,
+        data: "Forbidden",
+        message: "Access is denied",
+      });
+    }
+    req.user = user;
+    return next();
+  })(req, res, next);
+};
+&nbsp;
+module.exports = guard;
+&nbsp;</pre></td></tr></table></pre>
+
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Mon Mar 15 2021 13:23:50 GMT+0200 (GMT+02:00)
+            </div>
+        </div>
+        <script src="../../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../../sorter.js"></script>
+        <script src="../../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 156 - 0
coverage/lcov-report/nodejs-homework-API/helpers/index.html

@@ -0,0 +1,156 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for nodejs-homework-API/helpers</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../../prettify.css" />
+    <link rel="stylesheet" href="../../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../../index.html">All files</a> nodejs-homework-API/helpers</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">67.57% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>25/37</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">55.56% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>5/9</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">20% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>2/10</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">67.57% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>25/37</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line medium'></div>
+    <div class="pad1">
+<table class="coverage-summary">
+<thead>
+<tr>
+   <th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
+   <th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
+   <th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
+   <th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
+   <th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
+   <th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
+   <th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
+</tr>
+</thead>
+<tbody><tr>
+	<td class="file medium" data-value="apiLimiter.js"><a href="apiLimiter.js.html">apiLimiter.js</a></td>
+	<td data-value="75" class="pic medium">
+	<div class="chart"><div class="cover-fill" style="width: 75%"></div><div class="cover-empty" style="width: 25%"></div></div>
+	</td>
+	<td data-value="75" class="pct medium">75%</td>
+	<td data-value="4" class="abs medium">3/4</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="0" class="abs high">0/0</td>
+	<td data-value="0" class="pct low">0%</td>
+	<td data-value="1" class="abs low">0/1</td>
+	<td data-value="75" class="pct medium">75%</td>
+	<td data-value="4" class="abs medium">3/4</td>
+	</tr>
+
+<tr>
+	<td class="file low" data-value="create-directory.js"><a href="create-directory.js.html">create-directory.js</a></td>
+	<td data-value="44.44" class="pic low">
+	<div class="chart"><div class="cover-fill" style="width: 44%"></div><div class="cover-empty" style="width: 56%"></div></div>
+	</td>
+	<td data-value="44.44" class="pct low">44.44%</td>
+	<td data-value="9" class="abs low">4/9</td>
+	<td data-value="0" class="pct low">0%</td>
+	<td data-value="2" class="abs low">0/2</td>
+	<td data-value="0" class="pct low">0%</td>
+	<td data-value="4" class="abs low">0/4</td>
+	<td data-value="44.44" class="pct low">44.44%</td>
+	<td data-value="9" class="abs low">4/9</td>
+	</tr>
+
+<tr>
+	<td class="file high" data-value="guard.js"><a href="guard.js.html">guard.js</a></td>
+	<td data-value="100" class="pic high">
+	<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
+	</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="11" class="abs high">11/11</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="5" class="abs high">5/5</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="2" class="abs high">2/2</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="11" class="abs high">11/11</td>
+	</tr>
+
+<tr>
+	<td class="file medium" data-value="upload.js"><a href="upload.js.html">upload.js</a></td>
+	<td data-value="53.85" class="pic medium">
+	<div class="chart"><div class="cover-fill" style="width: 53%"></div><div class="cover-empty" style="width: 47%"></div></div>
+	</td>
+	<td data-value="53.85" class="pct medium">53.85%</td>
+	<td data-value="13" class="abs medium">7/13</td>
+	<td data-value="0" class="pct low">0%</td>
+	<td data-value="2" class="abs low">0/2</td>
+	<td data-value="0" class="pct low">0%</td>
+	<td data-value="3" class="abs low">0/3</td>
+	<td data-value="53.85" class="pct medium">53.85%</td>
+	<td data-value="13" class="abs medium">7/13</td>
+	</tr>
+
+</tbody>
+</table>
+</div>
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Mon Mar 15 2021 13:23:50 GMT+0200 (GMT+02:00)
+            </div>
+        </div>
+        <script src="../../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../../sorter.js"></script>
+        <script src="../../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 161 - 0
coverage/lcov-report/nodejs-homework-API/helpers/upload.js.html

@@ -0,0 +1,161 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for nodejs-homework-API/helpers/upload.js</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../../prettify.css" />
+    <link rel="stylesheet" href="../../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../../index.html">All files</a> / <a href="index.html">nodejs-homework-API/helpers</a> upload.js</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">53.85% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>7/13</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">0% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>0/2</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">0% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>0/3</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">53.85% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>7/13</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line medium'></div>
+    <pre><table class="coverage">
+<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
+<a name='L2'></a><a href='#L2'>2</a>
+<a name='L3'></a><a href='#L3'>3</a>
+<a name='L4'></a><a href='#L4'>4</a>
+<a name='L5'></a><a href='#L5'>5</a>
+<a name='L6'></a><a href='#L6'>6</a>
+<a name='L7'></a><a href='#L7'>7</a>
+<a name='L8'></a><a href='#L8'>8</a>
+<a name='L9'></a><a href='#L9'>9</a>
+<a name='L10'></a><a href='#L10'>10</a>
+<a name='L11'></a><a href='#L11'>11</a>
+<a name='L12'></a><a href='#L12'>12</a>
+<a name='L13'></a><a href='#L13'>13</a>
+<a name='L14'></a><a href='#L14'>14</a>
+<a name='L15'></a><a href='#L15'>15</a>
+<a name='L16'></a><a href='#L16'>16</a>
+<a name='L17'></a><a href='#L17'>17</a>
+<a name='L18'></a><a href='#L18'>18</a>
+<a name='L19'></a><a href='#L19'>19</a>
+<a name='L20'></a><a href='#L20'>20</a>
+<a name='L21'></a><a href='#L21'>21</a>
+<a name='L22'></a><a href='#L22'>22</a>
+<a name='L23'></a><a href='#L23'>23</a>
+<a name='L24'></a><a href='#L24'>24</a>
+<a name='L25'></a><a href='#L25'>25</a>
+<a name='L26'></a><a href='#L26'>26</a>
+<a name='L27'></a><a href='#L27'>27</a>
+<a name='L28'></a><a href='#L28'>28</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">const multer = require("multer");
+const path = require("path");
+require("dotenv").config();
+const DIR_UPLOAD = path.join(process.cwd(), process.env.DIR_UPLOAD);
+&nbsp;
+const storage = multer.diskStorage({
+  destination: <span class="fstat-no" title="function not covered" >fu</span>nction (_req, _file, cb) {
+<span class="cstat-no" title="statement not covered" >    cb(null, DIR_UPLOAD);</span>
+  },
+  filename: <span class="fstat-no" title="function not covered" >fu</span>nction (_req, file, cb) {
+<span class="cstat-no" title="statement not covered" >    cb(null, file.originalname);</span>
+  },
+});
+&nbsp;
+const upload = multer({
+  storage: storage,
+  limits: { fileSize: 2000000 },
+  fileFilter: <span class="fstat-no" title="function not covered" >(_</span>req, file, cb) =&gt; {
+<span class="cstat-no" title="statement not covered" >    if (file.mimetype.includes("image")) {</span>
+<span class="cstat-no" title="statement not covered" >      cb(null, true);</span>
+<span class="cstat-no" title="statement not covered" >      return;</span>
+    }
+<span class="cstat-no" title="statement not covered" >    cb(null, false);</span>
+  },
+});
+&nbsp;
+module.exports = upload;
+&nbsp;</pre></td></tr></table></pre>
+
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Mon Mar 15 2021 13:23:50 GMT+0200 (GMT+02:00)
+            </div>
+        </div>
+        <script src="../../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../../sorter.js"></script>
+        <script src="../../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 111 - 0
coverage/lcov-report/nodejs-homework-API/index.html

@@ -0,0 +1,111 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for nodejs-homework-API</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../prettify.css" />
+    <link rel="stylesheet" href="../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../index.html">All files</a> nodejs-homework-API</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">95.83% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>23/24</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">50% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>1/2</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">50% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>1/2</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">95.83% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>23/24</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line high'></div>
+    <div class="pad1">
+<table class="coverage-summary">
+<thead>
+<tr>
+   <th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
+   <th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
+   <th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
+   <th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
+   <th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
+   <th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
+   <th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
+</tr>
+</thead>
+<tbody><tr>
+	<td class="file high" data-value="app.js"><a href="app.js.html">app.js</a></td>
+	<td data-value="95.83" class="pic high">
+	<div class="chart"><div class="cover-fill" style="width: 95%"></div><div class="cover-empty" style="width: 5%"></div></div>
+	</td>
+	<td data-value="95.83" class="pct high">95.83%</td>
+	<td data-value="24" class="abs high">23/24</td>
+	<td data-value="50" class="pct medium">50%</td>
+	<td data-value="2" class="abs medium">1/2</td>
+	<td data-value="50" class="pct medium">50%</td>
+	<td data-value="2" class="abs medium">1/2</td>
+	<td data-value="95.83" class="pct high">95.83%</td>
+	<td data-value="24" class="abs high">23/24</td>
+	</tr>
+
+</tbody>
+</table>
+</div>
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Mon Mar 15 2021 13:23:50 GMT+0200 (GMT+02:00)
+            </div>
+        </div>
+        <script src="../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../sorter.js"></script>
+        <script src="../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 125 - 0
coverage/lcov-report/nodejs-homework-API/routes/api/contacts.js.html

@@ -0,0 +1,125 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for nodejs-homework-API/routes/api/contacts.js</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../../../prettify.css" />
+    <link rel="stylesheet" href="../../../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../../../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../../../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../../../index.html">All files</a> / <a href="index.html">nodejs-homework-API/routes/api</a> contacts.js</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>8/8</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>0/0</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>0/0</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>8/8</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line high'></div>
+    <pre><table class="coverage">
+<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
+<a name='L2'></a><a href='#L2'>2</a>
+<a name='L3'></a><a href='#L3'>3</a>
+<a name='L4'></a><a href='#L4'>4</a>
+<a name='L5'></a><a href='#L5'>5</a>
+<a name='L6'></a><a href='#L6'>6</a>
+<a name='L7'></a><a href='#L7'>7</a>
+<a name='L8'></a><a href='#L8'>8</a>
+<a name='L9'></a><a href='#L9'>9</a>
+<a name='L10'></a><a href='#L10'>10</a>
+<a name='L11'></a><a href='#L11'>11</a>
+<a name='L12'></a><a href='#L12'>12</a>
+<a name='L13'></a><a href='#L13'>13</a>
+<a name='L14'></a><a href='#L14'>14</a>
+<a name='L15'></a><a href='#L15'>15</a>
+<a name='L16'></a><a href='#L16'>16</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">const express = require("express");
+const router = express.Router();
+const guard = require("../../helpers/guard");
+const controllers = require("../../controllers/contacts");
+const validation = require("./validation/validationContact");
+router
+  .get("/", guard, controllers.listContacts)
+  .post("/", guard, validation.createContact, controllers.addContact);
+&nbsp;
+router
+  .get("/:contactId", guard, controllers.getContactById)
+  .delete("/:contactId", guard, controllers.removeContact)
+  .put("/:contactId", guard, validation.update, controllers.updateContact);
+&nbsp;
+module.exports = router;
+&nbsp;</pre></td></tr></table></pre>
+
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Mon Mar 15 2021 13:23:50 GMT+0200 (GMT+02:00)
+            </div>
+        </div>
+        <script src="../../../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../../../sorter.js"></script>
+        <script src="../../../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 126 - 0
coverage/lcov-report/nodejs-homework-API/routes/api/index.html

@@ -0,0 +1,126 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for nodejs-homework-API/routes/api</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../../../prettify.css" />
+    <link rel="stylesheet" href="../../../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../../../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../../../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../../../index.html">All files</a> nodejs-homework-API/routes/api</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>16/16</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>0/0</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>0/0</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>16/16</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line high'></div>
+    <div class="pad1">
+<table class="coverage-summary">
+<thead>
+<tr>
+   <th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
+   <th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
+   <th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
+   <th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
+   <th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
+   <th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
+   <th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
+</tr>
+</thead>
+<tbody><tr>
+	<td class="file high" data-value="contacts.js"><a href="contacts.js.html">contacts.js</a></td>
+	<td data-value="100" class="pic high">
+	<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
+	</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="8" class="abs high">8/8</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="0" class="abs high">0/0</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="0" class="abs high">0/0</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="8" class="abs high">8/8</td>
+	</tr>
+
+<tr>
+	<td class="file high" data-value="user.js"><a href="user.js.html">user.js</a></td>
+	<td data-value="100" class="pic high">
+	<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
+	</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="8" class="abs high">8/8</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="0" class="abs high">0/0</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="0" class="abs high">0/0</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="8" class="abs high">8/8</td>
+	</tr>
+
+</tbody>
+</table>
+</div>
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Mon Mar 15 2021 13:23:50 GMT+0200 (GMT+02:00)
+            </div>
+        </div>
+        <script src="../../../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../../../sorter.js"></script>
+        <script src="../../../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 137 - 0
coverage/lcov-report/nodejs-homework-API/routes/api/user.js.html

@@ -0,0 +1,137 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for nodejs-homework-API/routes/api/user.js</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../../../prettify.css" />
+    <link rel="stylesheet" href="../../../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../../../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../../../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../../../index.html">All files</a> / <a href="index.html">nodejs-homework-API/routes/api</a> user.js</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>8/8</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>0/0</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>0/0</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>8/8</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line high'></div>
+    <pre><table class="coverage">
+<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
+<a name='L2'></a><a href='#L2'>2</a>
+<a name='L3'></a><a href='#L3'>3</a>
+<a name='L4'></a><a href='#L4'>4</a>
+<a name='L5'></a><a href='#L5'>5</a>
+<a name='L6'></a><a href='#L6'>6</a>
+<a name='L7'></a><a href='#L7'>7</a>
+<a name='L8'></a><a href='#L8'>8</a>
+<a name='L9'></a><a href='#L9'>9</a>
+<a name='L10'></a><a href='#L10'>10</a>
+<a name='L11'></a><a href='#L11'>11</a>
+<a name='L12'></a><a href='#L12'>12</a>
+<a name='L13'></a><a href='#L13'>13</a>
+<a name='L14'></a><a href='#L14'>14</a>
+<a name='L15'></a><a href='#L15'>15</a>
+<a name='L16'></a><a href='#L16'>16</a>
+<a name='L17'></a><a href='#L17'>17</a>
+<a name='L18'></a><a href='#L18'>18</a>
+<a name='L19'></a><a href='#L19'>19</a>
+<a name='L20'></a><a href='#L20'>20</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">const express = require("express");
+const router = express.Router();
+const controllers = require("../../controllers/user");
+const validation = require("./validation/validationUser");
+const guard = require("../../helpers/guard");
+const upload = require("../../helpers/upload");
+&nbsp;
+router
+  .post("/register", validation.registration, controllers.createNewUser)
+  .post("/login", validation.logIn, controllers.logIn)
+  .post("/logout", guard, controllers.logout)
+  .patch("/users", guard, controllers.updateSubscription)
+  .patch(
+    "/avatars",
+    [guard, upload.single("avatar"), validation.validateUploadAvatar],
+    controllers.saveAvatarForStatic
+  )
+  .get("/users/current", guard, controllers.getCurrent);
+module.exports = router;
+&nbsp;</pre></td></tr></table></pre>
+
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Mon Mar 15 2021 13:23:50 GMT+0200 (GMT+02:00)
+            </div>
+        </div>
+        <script src="../../../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../../../sorter.js"></script>
+        <script src="../../../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 126 - 0
coverage/lcov-report/nodejs-homework-API/routes/api/validation/index.html

@@ -0,0 +1,126 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for nodejs-homework-API/routes/api/validation</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../../../../prettify.css" />
+    <link rel="stylesheet" href="../../../../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../../../../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../../../../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../../../../index.html">All files</a> nodejs-homework-API/routes/api/validation</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">66.67% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>20/30</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">33.33% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>2/6</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">42.86% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>3/7</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">66.67% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>20/30</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line medium'></div>
+    <div class="pad1">
+<table class="coverage-summary">
+<thead>
+<tr>
+   <th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
+   <th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
+   <th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
+   <th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
+   <th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
+   <th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
+   <th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
+   <th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
+</tr>
+</thead>
+<tbody><tr>
+	<td class="file high" data-value="validationContact.js"><a href="validationContact.js.html">validationContact.js</a></td>
+	<td data-value="100" class="pic high">
+	<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
+	</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="13" class="abs high">13/13</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="2" class="abs high">2/2</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="3" class="abs high">3/3</td>
+	<td data-value="100" class="pct high">100%</td>
+	<td data-value="13" class="abs high">13/13</td>
+	</tr>
+
+<tr>
+	<td class="file low" data-value="validationUser.js"><a href="validationUser.js.html">validationUser.js</a></td>
+	<td data-value="41.18" class="pic low">
+	<div class="chart"><div class="cover-fill" style="width: 41%"></div><div class="cover-empty" style="width: 59%"></div></div>
+	</td>
+	<td data-value="41.18" class="pct low">41.18%</td>
+	<td data-value="17" class="abs low">7/17</td>
+	<td data-value="0" class="pct low">0%</td>
+	<td data-value="4" class="abs low">0/4</td>
+	<td data-value="0" class="pct low">0%</td>
+	<td data-value="4" class="abs low">0/4</td>
+	<td data-value="41.18" class="pct low">41.18%</td>
+	<td data-value="17" class="abs low">7/17</td>
+	</tr>
+
+</tbody>
+</table>
+</div>
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Mon Mar 15 2021 13:23:50 GMT+0200 (GMT+02:00)
+            </div>
+        </div>
+        <script src="../../../../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../../../../sorter.js"></script>
+        <script src="../../../../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 227 - 0
coverage/lcov-report/nodejs-homework-API/routes/api/validation/validationContact.js.html

@@ -0,0 +1,227 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for nodejs-homework-API/routes/api/validation/validationContact.js</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../../../../prettify.css" />
+    <link rel="stylesheet" href="../../../../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../../../../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../../../../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../../../../index.html">All files</a> / <a href="index.html">nodejs-homework-API/routes/api/validation</a> validationContact.js</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>13/13</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>2/2</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>3/3</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">100% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>13/13</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line high'></div>
+    <pre><table class="coverage">
+<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
+<a name='L2'></a><a href='#L2'>2</a>
+<a name='L3'></a><a href='#L3'>3</a>
+<a name='L4'></a><a href='#L4'>4</a>
+<a name='L5'></a><a href='#L5'>5</a>
+<a name='L6'></a><a href='#L6'>6</a>
+<a name='L7'></a><a href='#L7'>7</a>
+<a name='L8'></a><a href='#L8'>8</a>
+<a name='L9'></a><a href='#L9'>9</a>
+<a name='L10'></a><a href='#L10'>10</a>
+<a name='L11'></a><a href='#L11'>11</a>
+<a name='L12'></a><a href='#L12'>12</a>
+<a name='L13'></a><a href='#L13'>13</a>
+<a name='L14'></a><a href='#L14'>14</a>
+<a name='L15'></a><a href='#L15'>15</a>
+<a name='L16'></a><a href='#L16'>16</a>
+<a name='L17'></a><a href='#L17'>17</a>
+<a name='L18'></a><a href='#L18'>18</a>
+<a name='L19'></a><a href='#L19'>19</a>
+<a name='L20'></a><a href='#L20'>20</a>
+<a name='L21'></a><a href='#L21'>21</a>
+<a name='L22'></a><a href='#L22'>22</a>
+<a name='L23'></a><a href='#L23'>23</a>
+<a name='L24'></a><a href='#L24'>24</a>
+<a name='L25'></a><a href='#L25'>25</a>
+<a name='L26'></a><a href='#L26'>26</a>
+<a name='L27'></a><a href='#L27'>27</a>
+<a name='L28'></a><a href='#L28'>28</a>
+<a name='L29'></a><a href='#L29'>29</a>
+<a name='L30'></a><a href='#L30'>30</a>
+<a name='L31'></a><a href='#L31'>31</a>
+<a name='L32'></a><a href='#L32'>32</a>
+<a name='L33'></a><a href='#L33'>33</a>
+<a name='L34'></a><a href='#L34'>34</a>
+<a name='L35'></a><a href='#L35'>35</a>
+<a name='L36'></a><a href='#L36'>36</a>
+<a name='L37'></a><a href='#L37'>37</a>
+<a name='L38'></a><a href='#L38'>38</a>
+<a name='L39'></a><a href='#L39'>39</a>
+<a name='L40'></a><a href='#L40'>40</a>
+<a name='L41'></a><a href='#L41'>41</a>
+<a name='L42'></a><a href='#L42'>42</a>
+<a name='L43'></a><a href='#L43'>43</a>
+<a name='L44'></a><a href='#L44'>44</a>
+<a name='L45'></a><a href='#L45'>45</a>
+<a name='L46'></a><a href='#L46'>46</a>
+<a name='L47'></a><a href='#L47'>47</a>
+<a name='L48'></a><a href='#L48'>48</a>
+<a name='L49'></a><a href='#L49'>49</a>
+<a name='L50'></a><a href='#L50'>50</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">6x</span>
+<span class="cline-any cline-yes">6x</span>
+<span class="cline-any cline-yes">3x</span>
+<span class="cline-any cline-yes">3x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">3x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">3x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">3x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">const Joi = require("joi");
+&nbsp;
+const schemaCreateContact = Joi.object({
+  name: Joi.string().alphanum().min(3).max(30).required().trim(),
+  email: Joi.string().email().min(3).max(30).required(),
+  phone: Joi.string()
+    .regex(/^[0-9]{10}$/)
+    .messages({
+      "string.pattern.base": `Phone number must have 10 digits and only numbers characters.`,
+    })
+    .required(),
+  password: Joi.string().required(),
+  subscription: Joi.string().optional(),
+  token: Joi.string().optional(),
+}).min(4);
+&nbsp;
+const schemaUpdateContact = Joi.object({
+  name: Joi.string().alphanum().min(3).max(30).optional().trim().optional(),
+  email: Joi.string().email().min(3).max(30).optional(),
+  phone: Joi.string()
+    .regex(/^[0-9]{10}$/)
+    .messages({
+      "string.pattern.base": `Phone number must have 10 digits and only numbers characters.`,
+    })
+    .optional(),
+  password: Joi.string().optional(),
+  subscription: Joi.string().optional(),
+  token: Joi.string().optional(),
+}).min(1);
+&nbsp;
+const validate = (schema, obj, next) =&gt; {
+  const { error } = schema.validate(obj);
+  if (error) {
+    const [{ message }] = error.details;
+    return next({
+      status: 400,
+      message: `Filed: ${message.replace(/"/g, "")}`,
+    });
+  }
+  next();
+};
+&nbsp;
+module.exports.createContact = (req, _res, next) =&gt; {
+  return validate(schemaCreateContact, req.body, next);
+};
+&nbsp;
+module.exports.update = (req, _res, next) =&gt; {
+  return validate(schemaUpdateContact, req.body, next);
+};
+&nbsp;</pre></td></tr></table></pre>
+
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Mon Mar 15 2021 13:23:50 GMT+0200 (GMT+02:00)
+            </div>
+        </div>
+        <script src="../../../../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../../../../sorter.js"></script>
+        <script src="../../../../block-navigation.js"></script>
+    </body>
+</html>
+    

+ 203 - 0
coverage/lcov-report/nodejs-homework-API/routes/api/validation/validationUser.js.html

@@ -0,0 +1,203 @@
+
+<!doctype html>
+<html lang="en">
+
+<head>
+    <title>Code coverage report for nodejs-homework-API/routes/api/validation/validationUser.js</title>
+    <meta charset="utf-8" />
+    <link rel="stylesheet" href="../../../../prettify.css" />
+    <link rel="stylesheet" href="../../../../base.css" />
+    <link rel="shortcut icon" type="image/x-icon" href="../../../../favicon.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <style type='text/css'>
+        .coverage-summary .sorter {
+            background-image: url(../../../../sort-arrow-sprite.png);
+        }
+    </style>
+</head>
+    
+<body>
+<div class='wrapper'>
+    <div class='pad1'>
+        <h1><a href="../../../../index.html">All files</a> / <a href="index.html">nodejs-homework-API/routes/api/validation</a> validationUser.js</h1>
+        <div class='clearfix'>
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">41.18% </span>
+                <span class="quiet">Statements</span>
+                <span class='fraction'>7/17</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">0% </span>
+                <span class="quiet">Branches</span>
+                <span class='fraction'>0/4</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">0% </span>
+                <span class="quiet">Functions</span>
+                <span class='fraction'>0/4</span>
+            </div>
+        
+            
+            <div class='fl pad1y space-right2'>
+                <span class="strong">41.18% </span>
+                <span class="quiet">Lines</span>
+                <span class='fraction'>7/17</span>
+            </div>
+        
+            
+        </div>
+        <p class="quiet">
+            Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
+        </p>
+    </div>
+    <div class='status-line low'></div>
+    <pre><table class="coverage">
+<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
+<a name='L2'></a><a href='#L2'>2</a>
+<a name='L3'></a><a href='#L3'>3</a>
+<a name='L4'></a><a href='#L4'>4</a>
+<a name='L5'></a><a href='#L5'>5</a>
+<a name='L6'></a><a href='#L6'>6</a>
+<a name='L7'></a><a href='#L7'>7</a>
+<a name='L8'></a><a href='#L8'>8</a>
+<a name='L9'></a><a href='#L9'>9</a>
+<a name='L10'></a><a href='#L10'>10</a>
+<a name='L11'></a><a href='#L11'>11</a>
+<a name='L12'></a><a href='#L12'>12</a>
+<a name='L13'></a><a href='#L13'>13</a>
+<a name='L14'></a><a href='#L14'>14</a>
+<a name='L15'></a><a href='#L15'>15</a>
+<a name='L16'></a><a href='#L16'>16</a>
+<a name='L17'></a><a href='#L17'>17</a>
+<a name='L18'></a><a href='#L18'>18</a>
+<a name='L19'></a><a href='#L19'>19</a>
+<a name='L20'></a><a href='#L20'>20</a>
+<a name='L21'></a><a href='#L21'>21</a>
+<a name='L22'></a><a href='#L22'>22</a>
+<a name='L23'></a><a href='#L23'>23</a>
+<a name='L24'></a><a href='#L24'>24</a>
+<a name='L25'></a><a href='#L25'>25</a>
+<a name='L26'></a><a href='#L26'>26</a>
+<a name='L27'></a><a href='#L27'>27</a>
+<a name='L28'></a><a href='#L28'>28</a>
+<a name='L29'></a><a href='#L29'>29</a>
+<a name='L30'></a><a href='#L30'>30</a>
+<a name='L31'></a><a href='#L31'>31</a>
+<a name='L32'></a><a href='#L32'>32</a>
+<a name='L33'></a><a href='#L33'>33</a>
+<a name='L34'></a><a href='#L34'>34</a>
+<a name='L35'></a><a href='#L35'>35</a>
+<a name='L36'></a><a href='#L36'>36</a>
+<a name='L37'></a><a href='#L37'>37</a>
+<a name='L38'></a><a href='#L38'>38</a>
+<a name='L39'></a><a href='#L39'>39</a>
+<a name='L40'></a><a href='#L40'>40</a>
+<a name='L41'></a><a href='#L41'>41</a>
+<a name='L42'></a><a href='#L42'>42</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">const Joi = require("joi");
+&nbsp;
+const schemaCreateNewUser = Joi.object({
+  email: Joi.string().email().min(3).max(30).required(),
+  password: Joi.string().required(),
+}).min(2);
+&nbsp;
+const schemaLogIn = Joi.object({
+  email: Joi.string().email().min(3).max(30).required(),
+  password: Joi.string().required(),
+}).min(2);
+&nbsp;
+const validate = <span class="fstat-no" title="function not covered" >(s</span>chema, obj, next) =&gt; {
+  const { error } = <span class="cstat-no" title="statement not covered" >schema.validate(obj);</span>
+<span class="cstat-no" title="statement not covered" >  if (error) {</span>
+    const [{ message }] = <span class="cstat-no" title="statement not covered" >error.details;</span>
+<span class="cstat-no" title="statement not covered" >    return next({</span>
+      status: 400,
+      message: `Filed: ${message.replace(/"/g, "")}`,
+    });
+  }
+<span class="cstat-no" title="statement not covered" >  next();</span>
+};
+&nbsp;
+module.exports.registration = <span class="fstat-no" title="function not covered" >(r</span>eq, _res, next) =&gt; {
+<span class="cstat-no" title="statement not covered" >  return validate(schemaCreateNewUser, req.body, next);</span>
+};
+module.exports.logIn = <span class="fstat-no" title="function not covered" >(r</span>eq, _res, next) =&gt; {
+<span class="cstat-no" title="statement not covered" >  return validate(schemaLogIn, req.body, next);</span>
+};
+module.exports.validateUploadAvatar = <span class="fstat-no" title="function not covered" >(r</span>eq, res, next) =&gt; {
+<span class="cstat-no" title="statement not covered" >  if (!req.file) {</span>
+<span class="cstat-no" title="statement not covered" >    return res.status(400).json({</span>
+      status: "error",
+      code: 400,
+      data: "Bad request",
+      message: "Field of avatar with file not found",
+    });
+  }
+<span class="cstat-no" title="statement not covered" >  next();</span>
+};
+&nbsp;</pre></td></tr></table></pre>
+
+                <div class='push'></div><!-- for sticky footer -->
+            </div><!-- /wrapper -->
+            <div class='footer quiet pad2 space-top1 center small'>
+                Code coverage generated by
+                <a href="https://istanbul.js.org/" target="_blank">istanbul</a>
+                at Mon Mar 15 2021 13:23:50 GMT+0200 (GMT+02:00)
+            </div>
+        </div>
+        <script src="../../../../prettify.js"></script>
+        <script>
+            window.onload = function () {
+                prettyPrint();
+            };
+        </script>
+        <script src="../../../../sorter.js"></script>
+        <script src="../../../../block-navigation.js"></script>
+    </body>
+</html>
+    

File diff suppressed because it is too large
+ 1 - 0
coverage/lcov-report/prettify.css


File diff suppressed because it is too large
+ 2 - 0
coverage/lcov-report/prettify.js


BIN
coverage/lcov-report/sort-arrow-sprite.png


+ 170 - 0
coverage/lcov-report/sorter.js

@@ -0,0 +1,170 @@
+/* eslint-disable */
+var addSorting = (function() {
+    'use strict';
+    var cols,
+        currentSort = {
+            index: 0,
+            desc: false
+        };
+
+    // returns the summary table element
+    function getTable() {
+        return document.querySelector('.coverage-summary');
+    }
+    // returns the thead element of the summary table
+    function getTableHeader() {
+        return getTable().querySelector('thead tr');
+    }
+    // returns the tbody element of the summary table
+    function getTableBody() {
+        return getTable().querySelector('tbody');
+    }
+    // returns the th element for nth column
+    function getNthColumn(n) {
+        return getTableHeader().querySelectorAll('th')[n];
+    }
+
+    // loads all columns
+    function loadColumns() {
+        var colNodes = getTableHeader().querySelectorAll('th'),
+            colNode,
+            cols = [],
+            col,
+            i;
+
+        for (i = 0; i < colNodes.length; i += 1) {
+            colNode = colNodes[i];
+            col = {
+                key: colNode.getAttribute('data-col'),
+                sortable: !colNode.getAttribute('data-nosort'),
+                type: colNode.getAttribute('data-type') || 'string'
+            };
+            cols.push(col);
+            if (col.sortable) {
+                col.defaultDescSort = col.type === 'number';
+                colNode.innerHTML =
+                    colNode.innerHTML + '<span class="sorter"></span>';
+            }
+        }
+        return cols;
+    }
+    // attaches a data attribute to every tr element with an object
+    // of data values keyed by column name
+    function loadRowData(tableRow) {
+        var tableCols = tableRow.querySelectorAll('td'),
+            colNode,
+            col,
+            data = {},
+            i,
+            val;
+        for (i = 0; i < tableCols.length; i += 1) {
+            colNode = tableCols[i];
+            col = cols[i];
+            val = colNode.getAttribute('data-value');
+            if (col.type === 'number') {
+                val = Number(val);
+            }
+            data[col.key] = val;
+        }
+        return data;
+    }
+    // loads all row data
+    function loadData() {
+        var rows = getTableBody().querySelectorAll('tr'),
+            i;
+
+        for (i = 0; i < rows.length; i += 1) {
+            rows[i].data = loadRowData(rows[i]);
+        }
+    }
+    // sorts the table using the data for the ith column
+    function sortByIndex(index, desc) {
+        var key = cols[index].key,
+            sorter = function(a, b) {
+                a = a.data[key];
+                b = b.data[key];
+                return a < b ? -1 : a > b ? 1 : 0;
+            },
+            finalSorter = sorter,
+            tableBody = document.querySelector('.coverage-summary tbody'),
+            rowNodes = tableBody.querySelectorAll('tr'),
+            rows = [],
+            i;
+
+        if (desc) {
+            finalSorter = function(a, b) {
+                return -1 * sorter(a, b);
+            };
+        }
+
+        for (i = 0; i < rowNodes.length; i += 1) {
+            rows.push(rowNodes[i]);
+            tableBody.removeChild(rowNodes[i]);
+        }
+
+        rows.sort(finalSorter);
+
+        for (i = 0; i < rows.length; i += 1) {
+            tableBody.appendChild(rows[i]);
+        }
+    }
+    // removes sort indicators for current column being sorted
+    function removeSortIndicators() {
+        var col = getNthColumn(currentSort.index),
+            cls = col.className;
+
+        cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, '');
+        col.className = cls;
+    }
+    // adds sort indicators for current column being sorted
+    function addSortIndicators() {
+        getNthColumn(currentSort.index).className += currentSort.desc
+            ? ' sorted-desc'
+            : ' sorted';
+    }
+    // adds event listeners for all sorter widgets
+    function enableUI() {
+        var i,
+            el,
+            ithSorter = function ithSorter(i) {
+                var col = cols[i];
+
+                return function() {
+                    var desc = col.defaultDescSort;
+
+                    if (currentSort.index === i) {
+                        desc = !currentSort.desc;
+                    }
+                    sortByIndex(i, desc);
+                    removeSortIndicators();
+                    currentSort.index = i;
+                    currentSort.desc = desc;
+                    addSortIndicators();
+                };
+            };
+        for (i = 0; i < cols.length; i += 1) {
+            if (cols[i].sortable) {
+                // add the click event handler on the th so users
+                // dont have to click on those tiny arrows
+                el = getNthColumn(i).querySelector('.sorter').parentElement;
+                if (el.addEventListener) {
+                    el.addEventListener('click', ithSorter(i));
+                } else {
+                    el.attachEvent('onclick', ithSorter(i));
+                }
+            }
+        }
+    }
+    // adds sorting functionality to the UI
+    return function() {
+        if (!getTable()) {
+            return;
+        }
+        cols = loadColumns();
+        loadData();
+        addSortIndicators();
+        enableUI();
+    };
+})();
+
+window.addEventListener('load', addSorting);

+ 436 - 0
coverage/lcov.info

@@ -0,0 +1,436 @@
+TN:
+SF:app.js
+FN:25,(anonymous_0)
+FN:29,(anonymous_1)
+FNF:2
+FNH:1
+FNDA:0,(anonymous_0)
+FNDA:3,(anonymous_1)
+DA:1,1
+DA:2,1
+DA:3,1
+DA:4,1
+DA:5,1
+DA:6,1
+DA:8,1
+DA:9,1
+DA:10,1
+DA:12,1
+DA:13,1
+DA:14,1
+DA:15,1
+DA:17,1
+DA:18,1
+DA:19,1
+DA:20,1
+DA:22,1
+DA:23,1
+DA:25,1
+DA:26,0
+DA:29,1
+DA:30,3
+DA:33,1
+LF:24
+LH:23
+BRDA:15,0,0,0
+BRDA:15,0,1,1
+BRF:2
+BRH:1
+end_of_record
+TN:
+SF:config\passport.js
+FN:13,(anonymous_0)
+FNF:1
+FNH:1
+FNDA:11,(anonymous_0)
+DA:1,1
+DA:2,1
+DA:3,1
+DA:4,1
+DA:5,1
+DA:7,1
+DA:12,1
+DA:14,11
+DA:15,11
+DA:16,11
+DA:17,0
+DA:19,11
+DA:20,0
+DA:22,11
+DA:24,0
+LF:15
+LH:12
+BRDA:16,0,0,0
+BRDA:16,0,1,11
+BRDA:19,1,0,0
+BRDA:19,1,1,11
+BRF:4
+BRH:2
+end_of_record
+TN:
+SF:controllers\contacts.js
+FN:3,(anonymous_0)
+FN:19,(anonymous_1)
+FN:43,(anonymous_2)
+FN:62,(anonymous_3)
+FN:86,(anonymous_4)
+FNF:5
+FNH:5
+FNDA:1,(anonymous_0)
+FNDA:2,(anonymous_1)
+FNDA:1,(anonymous_2)
+FNDA:2,(anonymous_3)
+FNDA:2,(anonymous_4)
+DA:1,1
+DA:3,1
+DA:4,1
+DA:5,1
+DA:6,1
+DA:7,1
+DA:15,0
+DA:19,1
+DA:20,2
+DA:21,2
+DA:22,2
+DA:23,2
+DA:24,1
+DA:32,1
+DA:39,0
+DA:43,1
+DA:44,1
+DA:45,1
+DA:46,1
+DA:47,1
+DA:48,1
+DA:49,1
+DA:58,0
+DA:62,1
+DA:63,2
+DA:64,2
+DA:65,2
+DA:66,2
+DA:67,1
+DA:75,1
+DA:82,0
+DA:86,1
+DA:87,2
+DA:88,2
+DA:89,2
+DA:94,2
+DA:95,1
+DA:103,1
+DA:110,0
+DA:114,1
+LF:40
+LH:35
+BRDA:23,0,0,1
+BRDA:23,0,1,1
+BRDA:47,1,0,1
+BRDA:47,1,1,0
+BRDA:66,2,0,1
+BRDA:66,2,1,1
+BRDA:94,3,0,1
+BRDA:94,3,1,1
+BRF:8
+BRH:7
+end_of_record
+TN:
+SF:controllers\user.js
+FN:10,(anonymous_0)
+FN:44,(anonymous_1)
+FN:72,(anonymous_2)
+FN:101,(anonymous_3)
+FN:115,(anonymous_4)
+FN:134,(anonymous_5)
+FNF:6
+FNH:0
+FNDA:0,(anonymous_0)
+FNDA:0,(anonymous_1)
+FNDA:0,(anonymous_2)
+FNDA:0,(anonymous_3)
+FNDA:0,(anonymous_4)
+FNDA:0,(anonymous_5)
+DA:1,1
+DA:2,1
+DA:3,1
+DA:4,1
+DA:5,1
+DA:6,1
+DA:7,1
+DA:8,1
+DA:10,1
+DA:11,0
+DA:12,0
+DA:13,0
+DA:14,0
+DA:15,0
+DA:17,0
+DA:18,0
+DA:27,0
+DA:28,0
+DA:29,0
+DA:30,0
+DA:31,0
+DA:32,0
+DA:40,0
+DA:44,1
+DA:45,0
+DA:46,0
+DA:47,0
+DA:48,0
+DA:49,0
+DA:56,0
+DA:57,0
+DA:68,0
+DA:72,1
+DA:73,0
+DA:74,0
+DA:75,0
+DA:76,0
+DA:77,0
+DA:78,0
+DA:85,0
+DA:86,0
+DA:87,0
+DA:88,0
+DA:89,0
+DA:97,0
+DA:101,1
+DA:102,0
+DA:103,0
+DA:104,0
+DA:105,0
+DA:112,0
+DA:113,0
+DA:115,1
+DA:116,0
+DA:117,0
+DA:118,0
+DA:126,0
+DA:134,1
+DA:135,0
+DA:136,0
+DA:137,0
+DA:138,0
+DA:139,0
+DA:143,0
+DA:147,1
+LF:65
+LH:15
+BRDA:48,0,0,0
+BRDA:48,0,1,0
+BRDA:77,1,0,0
+BRDA:77,1,1,0
+BRDA:77,2,0,0
+BRDA:77,2,1,0
+BRDA:104,3,0,0
+BRDA:104,3,1,0
+BRDA:117,4,0,0
+BRDA:117,4,1,0
+BRF:10
+BRH:0
+end_of_record
+TN:
+SF:helpers\apiLimiter.js
+FN:6,(anonymous_0)
+FNF:1
+FNH:0
+FNDA:0,(anonymous_0)
+DA:1,1
+DA:3,1
+DA:7,0
+DA:16,1
+LF:4
+LH:3
+BRF:0
+BRH:0
+end_of_record
+TN:
+SF:helpers\create-directory.js
+FN:3,(anonymous_0)
+FN:6,(anonymous_1)
+FN:7,(anonymous_2)
+FN:10,(anonymous_3)
+FNF:4
+FNH:0
+FNDA:0,(anonymous_0)
+FNDA:0,(anonymous_1)
+FNDA:0,(anonymous_2)
+FNDA:0,(anonymous_3)
+DA:1,1
+DA:3,1
+DA:4,0
+DA:6,0
+DA:7,0
+DA:10,1
+DA:11,0
+DA:12,0
+DA:15,1
+LF:9
+LH:4
+BRDA:11,0,0,0
+BRDA:11,0,1,0
+BRF:2
+BRH:0
+end_of_record
+TN:
+SF:helpers\guard.js
+FN:5,(anonymous_0)
+FN:6,(anonymous_1)
+FNF:2
+FNH:2
+FNDA:16,(anonymous_0)
+FNDA:16,(anonymous_1)
+DA:1,1
+DA:2,1
+DA:3,1
+DA:5,1
+DA:6,16
+DA:7,16
+DA:8,16
+DA:9,5
+DA:16,11
+DA:17,11
+DA:21,1
+LF:11
+LH:11
+BRDA:8,0,0,5
+BRDA:8,0,1,11
+BRDA:8,1,0,16
+BRDA:8,1,1,11
+BRDA:8,1,2,11
+BRF:5
+BRH:5
+end_of_record
+TN:
+SF:helpers\upload.js
+FN:7,(anonymous_0)
+FN:10,(anonymous_1)
+FN:18,(anonymous_2)
+FNF:3
+FNH:0
+FNDA:0,(anonymous_0)
+FNDA:0,(anonymous_1)
+FNDA:0,(anonymous_2)
+DA:1,1
+DA:2,1
+DA:3,1
+DA:4,1
+DA:6,1
+DA:8,0
+DA:11,0
+DA:15,1
+DA:19,0
+DA:20,0
+DA:21,0
+DA:23,0
+DA:27,1
+LF:13
+LH:7
+BRDA:19,0,0,0
+BRDA:19,0,1,0
+BRF:2
+BRH:0
+end_of_record
+TN:
+SF:routes\api\contacts.js
+FNF:0
+FNH:0
+DA:1,1
+DA:2,1
+DA:3,1
+DA:4,1
+DA:5,1
+DA:6,1
+DA:10,1
+DA:15,1
+LF:8
+LH:8
+BRF:0
+BRH:0
+end_of_record
+TN:
+SF:routes\api\user.js
+FNF:0
+FNH:0
+DA:1,1
+DA:2,1
+DA:3,1
+DA:4,1
+DA:5,1
+DA:6,1
+DA:8,1
+DA:19,1
+LF:8
+LH:8
+BRF:0
+BRH:0
+end_of_record
+TN:
+SF:routes\api\validation\validationContact.js
+FN:31,(anonymous_0)
+FN:43,(anonymous_1)
+FN:47,(anonymous_2)
+FNF:3
+FNH:3
+FNDA:6,(anonymous_0)
+FNDA:3,(anonymous_1)
+FNDA:3,(anonymous_2)
+DA:1,1
+DA:3,1
+DA:17,1
+DA:31,1
+DA:32,6
+DA:33,6
+DA:34,3
+DA:35,3
+DA:40,3
+DA:43,1
+DA:44,3
+DA:47,1
+DA:48,3
+LF:13
+LH:13
+BRDA:33,0,0,3
+BRDA:33,0,1,3
+BRF:2
+BRH:2
+end_of_record
+TN:
+SF:routes\api\validation\validationUser.js
+FN:13,(anonymous_0)
+FN:25,(anonymous_1)
+FN:28,(anonymous_2)
+FN:31,(anonymous_3)
+FNF:4
+FNH:0
+FNDA:0,(anonymous_0)
+FNDA:0,(anonymous_1)
+FNDA:0,(anonymous_2)
+FNDA:0,(anonymous_3)
+DA:1,1
+DA:3,1
+DA:8,1
+DA:13,1
+DA:14,0
+DA:15,0
+DA:16,0
+DA:17,0
+DA:22,0
+DA:25,1
+DA:26,0
+DA:28,1
+DA:29,0
+DA:31,1
+DA:32,0
+DA:33,0
+DA:40,0
+LF:17
+LH:7
+BRDA:15,0,0,0
+BRDA:15,0,1,0
+BRDA:32,1,0,0
+BRDA:32,1,1,0
+BRF:4
+BRH:0
+end_of_record

+ 16 - 0
helpers/apiLimiter.js

@@ -0,0 +1,16 @@
+const rateLimit = require("express-rate-limit");
+
+const apiLimiter = rateLimit({
+  windowMs: 15 * 60 * 1000,
+  max: 200,
+  handler: (req, res, next) => {
+    return res.status(400).json({
+      status: "error",
+      code: 400,
+      data: "Bad request",
+      message: "Too many requests, please try again later.",
+    });
+  },
+});
+
+module.exports = apiLimiter;

+ 13 - 0
helpers/create-directory.js

@@ -0,0 +1,13 @@
+const fs = require("fs/promises");
+
+const isAccessible = (pathFolder) => {
+  return fs
+    .access(pathFolder)
+    .then(() => true)
+    .catch(() => false);
+};
+
+const createFolderIsExist = async (folder) => {
+  if (!(await isAccessible(folder))) await fs.mkdir(folder);
+};
+module.exports = createFolderIsExist;

+ 21 - 0
helpers/guard.js

@@ -0,0 +1,21 @@
+const passport = require("passport");
+require("../config/passport");
+const chalk = require("chalk");
+
+const guard = (req, res, next) => {
+  passport.authenticate("jwt", { session: false }, (err, user) => {
+    const token = req.get("Authorization")?.split(" ")[1];
+    if (!user || err || token !== user.token) {
+      return res.status(403).json({
+        status: "error",
+        code: 403,
+        data: "Forbidden",
+        message: "Access is denied",
+      });
+    }
+    req.user = user;
+    return next();
+  })(req, res, next);
+};
+
+module.exports = guard;

+ 6 - 0
helpers/twilio.js

@@ -0,0 +1,6 @@
+const ACCOUNT_SID = process.env.ACCOUNT_SID;
+const AUTH_TOKEN = process.env.AUTH_TOKEN;
+const twilio = require('twilio');
+const client = new twilio(ACCOUNT_SID, AUTH_TOKEN);
+
+module.exports = client;

+ 24 - 0
helpers/upload.js

@@ -0,0 +1,24 @@
+const multer = require("multer");
+const path = require("path");
+require("dotenv").config();
+const DIR_UPLOAD = path.join(process.cwd(), process.env.DIR_UPLOAD);
+
+const storage = multer.diskStorage({
+  destination: function (_req, _file, cb) {
+    cb(null, DIR_UPLOAD);
+  },
+  filename: function (_req, file, cb) {
+    cb(null, file.originalname);
+  },
+});
+
+const upload = multer({
+  storage: storage,
+  limits: { fileSize: 2000000 },
+  fileFilter: (_req, file, cb) => {
+    if (file.mimetype.includes("image")) return cb(null, true);
+    cb(null, false);
+  },
+});
+
+module.exports = upload;

BIN
images/60d465e986100d3930569f4b/6-1@x2.png


+ 3 - 0
jest.config.js

@@ -0,0 +1,3 @@
+module.exports = {
+  testEnvironment: "node",
+};

+ 43 - 0
model/__mocks__/contact.js

@@ -0,0 +1,43 @@
+const { contacts } = require("./data");
+
+const getList = jest.fn(
+  (
+    _userId,
+    { _sortBy, _sortByDesc, _filter, limit = "5", page = "1", _sub }
+  ) => {
+    return { contacts, total: contacts.length, limit, page };
+  }
+);
+
+const getById = jest.fn((id, _userId) => {
+  const [contact] = contacts.filter((el) => el._id === id);
+
+  return contact;
+});
+
+const add = jest.fn((newContact) => {
+  const contact = { ...newContact, _id: "604cec3345d8c632bc1fake3" };
+  contacts.push(contact);
+  return contact;
+});
+
+const update = jest.fn((id, body, _userId) => {
+  let [contact] = contacts.filter((el) => el._id === id);
+  if (contact) return (contact = { ...contact, ...body });
+});
+
+const remove = jest.fn((id, _userId) => {
+  const index = contacts.findIndex((el) => String(el._id) === String(id));
+  if (index === -1) return null;
+
+  const [contact] = contacts.splice(index, 1);
+  return contact;
+});
+
+module.exports = {
+  getList,
+  getById,
+  add,
+  remove,
+  update,
+};

+ 43 - 0
model/__mocks__/data.js

@@ -0,0 +1,43 @@
+const contacts = [
+  {
+    _id: "604cec2a45d8c632bc1fake1",
+    name: "FAKE-ONE",
+    phone: "0915088491",
+    subscription: "free",
+  },
+  {
+    _id: "604cec3345d8c632bc1fake2",
+    name: "FAKE-SECOND",
+    phone: "0914088491",
+    subscription: "free",
+  },
+  {
+    _id: "604cec3345d8c632bc1fake3",
+    name: "FAKE-SECOND",
+    phone: "0914088491",
+    subscription: "free",
+  },
+];
+const newContact = {
+  name: "newUser",
+  phone: "9810088491",
+  subscription: "free",
+};
+
+const User = {
+  subscription: "free",
+  token:
+    "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYwNGUzMTY4YTA1ZDNkMzQzMDBjMDA0ZiIsImlhdCI6MTYxNTczNzIyNSwiZXhwIjoxNjE1NzQ0NDI1fQ.8byA4KylqGkp9j9AV1eqL1EogAA0VDxr2GbBB7YIkoA",
+  _id: "604cec3345d8c632bc1fake2",
+  email: "12345678@gmail.com",
+  password: "$2a$08$7Ri2ggA1VXgal7.MUrnRa.rS1lja0y6j0VRQMu495ssO5PFyQ1hQO",
+  avatarUrl:
+    "https://s.gravatar.com/avatar/0ffd40d14c2320ab59aa28ff91e8d813?s=250",
+};
+
+const users = [];
+users[0] = User;
+
+const newUser = { email: "newuser@gmail.com", password: "newUser" };
+
+module.exports = { contacts, newContact, users, User, newUser };

+ 36 - 0
model/__mocks__/user.js

@@ -0,0 +1,36 @@
+const { users } = require("./data");
+
+const findByEmail = jest.fn((email) => {
+  const [user] = users.filter((el) => String(el.email) === String(email));
+  return user;
+});
+
+const findById = jest.fn((id) => {
+  const [user] = users.filter((el) => String(el._id) === String(id));
+  return user;
+});
+
+const createUser = jest.fn(({ name, email, password }) => {
+  return {};
+});
+
+const updateName = jest.fn((id, body) => {
+  return {};
+});
+
+const updateToken = jest.fn((id, token) => {
+  return {};
+});
+
+const updateAvatar = jest.fn((id, avatarUrl) => {
+  return {};
+});
+
+module.exports = {
+  findByEmail,
+  createUser,
+  updateToken,
+  updateName,
+  updateAvatar,
+  findById,
+};

+ 60 - 0
model/contact.js

@@ -0,0 +1,60 @@
+const Contact = require("./schemas/contact");
+
+const getList = async (
+  userId,
+  { sortBy, sortByDesc, filter, limit = "5", page = "1", sub }
+) => {
+  const options = { owner: userId };
+  if (sub) options.subscription = { $all: [sub] };
+  const results = await Contact.paginate(options, {
+    limit,
+    page,
+    sort: {
+      ...(sortBy ? { [`${sortBy}`]: 1 } : {}),
+      ...(sortByDesc ? { [`${sortByDesc}`]: -1 } : {}),
+    },
+    select: filter ? filter.split("|").join(" ") : "",
+    populate: {
+      path: "owner",
+      select: "email password subscription token -_id",
+    },
+  });
+  const { docs: contacts, totalDocs: total } = results;
+  return { total: total.toString(), limit, page, contacts };
+};
+
+const getById = async (id, userId) => {
+  const foundContact = await Contact.findById({
+    _id: id,
+    owner: userId,
+  });
+  return foundContact;
+};
+const add = async (obj) => {
+  const contact = await Contact.create(obj);
+  return contact;
+};
+
+const remove = async (id, userId) => {
+  const removedContact = await Contact.findByIdAndRemove({
+    _id: id,
+    owner: userId,
+  });
+  return removedContact;
+};
+const update = async (id, userId, body) => {
+  const contact = await Contact.findByIdAndUpdate(
+    { _id: id, owner: userId },
+    { ...body },
+    { new: true }
+  );
+  return contact;
+};
+
+module.exports = {
+  getList,
+  getById,
+  add,
+  remove,
+  update,
+};

+ 32 - 0
model/db/index.js

@@ -0,0 +1,32 @@
+const mongoose = require("mongoose");
+
+require("dotenv").config();
+
+const uriDb = process.env.URI_DB;
+
+const db = mongoose.connect(uriDb, {
+  useNewUrlParser: true,
+  useCreateIndex: true,
+  useUnifiedTopology: true,
+  useFindAndModify: false,
+});
+
+mongoose.connection.on("connected", () =>
+  console.log("mongoose Database was connect successful to db!")
+);
+
+mongoose.connection.on("error", () =>
+  console.log("mongoose Database was not connect successful to db, error!")
+);
+
+mongoose.connection.on("disconnected", () =>
+  console.log("mongoose Database was disconnected")
+);
+
+process.on("SIGINT", async () => {
+  await mongoose.connection.close();
+  console.log("Connection for db is closed");
+  process.exit(1);
+});
+
+module.exports = db;

+ 34 - 0
model/schemas/contact.js

@@ -0,0 +1,34 @@
+const mongoose = require("mongoose");
+const { Schema, model, SchemaTypes } = mongoose;
+const mongoosePaginate = require("mongoose-paginate-v2");
+
+mongoose.Types.ObjectId.isValid();
+
+const contactSchema = new Schema(
+  {
+    name: {
+      type: String,
+      required: [true, "Set name for contact"],
+    },
+    phone: {
+      type: String,
+      unique: true,
+      required: [true, "Set phone number for current user"],
+    },
+    subscription: {
+      type: String,
+      enum: ["free", "pro", "premium"],
+      required: [true, "Set subscription for current user"],
+    },
+    owner: {
+      type: SchemaTypes.ObjectId,
+      ref: "user",
+    },
+  },
+
+  { timestamps: true }
+);
+contactSchema.plugin(mongoosePaginate);
+const Contact = model("contact", contactSchema);
+
+module.exports = Contact;

+ 43 - 0
model/schemas/user.js

@@ -0,0 +1,43 @@
+const mongoose = require('mongoose');
+const { Schema, model } = mongoose;
+const gravatar = require('gravatar');
+mongoose.Types.ObjectId.isValid();
+
+const userSchema = new Schema(
+	{
+		name: {
+			type: String,
+			default: null,
+		},
+		lastName: {
+			type: String,
+			default: null,
+		},
+		number: {
+			type: String,
+			required: [true, 'Number required'],
+			unique: true,
+			min: 8,
+			max: 14,
+		},
+		avatarUrl: {
+			type: String,
+			default: function () {
+				return gravatar.url(this.email, { s: '250' }, true);
+			},
+		},
+		token: {
+			type: String,
+			default: null,
+		},
+		code: {
+			type: String,
+			default: null,
+		},
+	},
+	{ timestamps: true }
+);
+
+const User = model('user', userSchema);
+
+module.exports = User;

+ 34 - 0
model/user.js

@@ -0,0 +1,34 @@
+const User = require('./schemas/user');
+
+const findByNumber = async (number) => {
+	return await User.findOne({ number });
+};
+const findById = async (id) => {
+	return await User.findById(id);
+};
+const createUser = async (body) => {
+	const user = new User({ ...body });
+	return await user.save();
+};
+const updateCode = async (id, code) => {
+	return await User.updateOne({ _id: id }, { code });
+};
+const updateToken = async (id, token) => {
+	return await User.updateOne({ _id: id }, { token });
+};
+const updateName = async (id, name) => {
+	return await User.findByIdAndUpdate({ _id: id }, { name }, { new: true });
+};
+const updateAvatar = async (id, avatarUrl) => {
+	return await User.updateOne({ _id: id }, { avatarUrl });
+};
+
+module.exports = {
+	findByNumber,
+	createUser,
+	updateCode,
+	updateToken,
+	updateName,
+	updateAvatar,
+	findById,
+};

+ 3 - 0
nodemon.json

@@ -0,0 +1,3 @@
+{
+  "ignore": ["node_modules", "model/contacts.json"]
+}

File diff suppressed because it is too large
+ 20752 - 0
package-lock.json


+ 51 - 0
package.json

@@ -0,0 +1,51 @@
+{
+  "name": "template",
+  "version": "0.0.0",
+  "private": true,
+  "scripts": {
+    "start": "cross-env NODE_ENV=production node ./bin/server.js",
+    "start:dev": "cross-env NODE_ENV=development nodemon ./bin/server.js",
+    "lint": "eslint **/*.{js,json}",
+    "lint:fix": "eslint --fix **/*.{js,json}",
+    "dev:debug": "node --inspect ./bin/server.js",
+    "test": "cross-env NODE_ENV=test jest --no-cache --verbose",
+    "test:coverage": "jest --coverage",
+    "test:watch": "jest --watchAll"
+  },
+  "dependencies": {
+    "bcryptjs": "2.4.3",
+    "chalk": "^3.0.0",
+    "cloudinary": "1.25.0",
+    "cors": "2.8.5",
+    "cross-env": "7.0.3",
+    "dotenv": "8.2.0",
+    "express": "4.17.1",
+    "express-rate-limit": "5.2.6",
+    "generate-sms-verification-code": "^1.0.5",
+    "gravatar": "1.8.1",
+    "helmet": "4.4.1",
+    "jimp": "0.16.1",
+    "joi": "17.4.0",
+    "jsonwebtoken": "8.5.1",
+    "mongoose": "5.11.18",
+    "mongoose-paginate-v2": "1.3.16",
+    "morgan": "1.10.0",
+    "multer": "^1.4.2",
+    "passport": "0.4.1",
+    "passport-jwt": "4.0.0",
+    "shortid": "^2.2.16",
+    "twilio": "^3.73.1"
+  },
+  "devDependencies": {
+    "eslint": "^7.19.0",
+    "eslint-config-prettier": "7.2.0",
+    "eslint-config-standard": "^16.0.2",
+    "eslint-plugin-import": "^2.22.1",
+    "eslint-plugin-json": "2.1.2",
+    "eslint-plugin-node": "^11.1.0",
+    "eslint-plugin-promise": "^4.2.1",
+    "jest": "26.6.3",
+    "nodemon": "2.0.7",
+    "supertest": "^6.1.3"
+  }
+}

File diff suppressed because it is too large
+ 1 - 0
readme.md


+ 15 - 0
routes/contacts.js

@@ -0,0 +1,15 @@
+const express = require("express");
+const router = express.Router();
+const guard = require("../helpers/guard");
+const controllers = require("../controllers/contacts");
+const validation = require("../validation/contact");
+router
+  .get("/", guard, controllers.listContacts)
+  .post("/", guard, validation.createContact, controllers.addContact);
+
+router
+  .get("/:id", guard, controllers.getContactById)
+  .delete("/:id", guard, controllers.removeContact)
+  .patch("/:id", guard, validation.update, controllers.updateContact);
+
+module.exports = router;

+ 19 - 0
routes/user.js

@@ -0,0 +1,19 @@
+const express = require('express');
+const router = express.Router();
+const controllers = require('../controllers/user');
+const validation = require('../validation/user');
+const guard = require('../helpers/guard');
+const upload = require('../helpers/upload');
+
+router
+	.post('/register', validation.registration, controllers.createNewUser)
+	.post('/login', validation.logIn, controllers.logIn)
+	.post('/logout', guard, controllers.logOut)
+	.patch('/users', [guard, validation.update], controllers.updateName)
+	.patch(
+		'/avatars',
+		[guard, upload.single('avatar'), validation.validateUploadAvatar],
+		controllers.saveAvatarForStatic
+	)
+	.get('/users/current', guard, controllers.getCurrent);
+module.exports = router;

+ 176 - 0
test/contacts.e2.test.js

@@ -0,0 +1,176 @@
+const jwt = require("jsonwebtoken");
+require("dotenv").config();
+const request = require("supertest");
+const { User, contacts, newContact } = require("../model/__mocks__/data");
+const app = require("../app");
+
+const SECRET_KEY = process.env.JWT_SECRET;
+const issueToken = (payload, secret) => jwt.sign(payload, secret);
+const token = issueToken({ id: User._id }, SECRET_KEY);
+User.token = token;
+const wrongId = "lol";
+const wrongToken = "lol";
+const contact = contacts[0];
+
+jest.mock("../model/contact.js");
+jest.mock("../model/user.js");
+
+describe("Test for route /api/contacts", () => {
+  let idNewContact;
+  describe("should handle request PATCH contact by id ", () => {
+    it("should return 200 on request PATCH contact", async (done) => {
+      const res = await request(app)
+        .patch(`/api/contacts/${contact._id}`)
+        .set("Authorization", `Bearer ${token}`)
+        .send({ phone: "0995688412" })
+        .set("Accept", "application/json");
+      expect(res.status).toEqual(200);
+      done();
+    });
+    it("should return 404 on request PATCH  contacts by id", async (done) => {
+      const res = await request(app)
+        .patch(`/api/contacts/${wrongId}`)
+        .set("Authorization", `Bearer ${token}`)
+        .send({ name: "Grigore" });
+      expect(res.status).toEqual(404);
+      expect(res.body).toBeDefined();
+      done();
+    });
+    it("should return 403 on request on request PATCH  contacts by id without token", async (done) => {
+      const res = await request(app)
+        .patch(`/api/contacts/${contact._id}`)
+        .set("Authorization", `Bearer ${wrongToken}`)
+        .send({ name: "Grigore" })
+        .set("Accept", "application/json");
+      expect(res.status).toEqual(403);
+      expect(res.body).toBeDefined();
+      done();
+    });
+    it("should return 500 on request on request PATCH  with wrong filed", async (done) => {
+      const res = await request(app)
+        .patch(`/api/contacts/${contact._id}`)
+        .set("Authorization", `Bearer ${token}`)
+        .send({ WrongField: "Grigore" })
+        .set("Accept", "application/json");
+      expect(res.status).toEqual(500);
+      expect(res.body).toBeDefined();
+      done();
+    });
+  });
+  describe("should handle GET request", () => {
+    it("should return 200 on GET request All contacts", async (done) => {
+      const res = await request(app)
+        .get(`/api/contacts`)
+        .set("Authorization", `Bearer ${token}`);
+      expect(res.status).toEqual(200);
+      expect(res.body).toBeDefined();
+      expect(res.body.data.contacts).toBeInstanceOf(Array);
+      done();
+    });
+    it("should return 403 on GET request All contacts by wrong  token", async (done) => {
+      const res = await request(app)
+        .get(`/api/contacts`)
+        .set("Authorization", `Bearer ${wrongToken}`);
+      expect(res.status).toEqual(403);
+      expect(res.body).toBeDefined();
+      done();
+    });
+    it("should return 200 on request GET  contact by ID", async (done) => {
+      const res = await request(app)
+        .get(`/api/contacts/${contact._id}`)
+        .set("Authorization", `Bearer ${token}`);
+      expect(res.status).toEqual(200);
+      expect(res.body).toBeDefined();
+      expect(res.body.data.contact).toHaveProperty("_id");
+      expect(res.body.data.contact._id).toBe(contact._id);
+      done();
+    });
+    it("should return 403 on GET request GET  contact by ID by wrong  token", async (done) => {
+      const res = await request(app)
+        .get(`/api/contacts/${contact._id}`)
+        .set("Authorization", `Bearer ${wrongToken}`);
+      expect(res.status).toEqual(403);
+      expect(res.body).toBeDefined();
+      done();
+    });
+
+    it("should return 404 on request GET  contact by wrong ID", async (done) => {
+      const res = await request(app)
+        .get(`/api/contacts/${wrongId}`)
+        .set("Authorization", `Bearer ${token}`);
+      expect(res.status).toEqual(404);
+      expect(res.body).toBeDefined();
+      done();
+    });
+  });
+  describe("should handle POST request", () => {
+    it("should return 201 on request POST  create new contact", async (done) => {
+      const res = await request(app)
+        .post(`/api/contacts`)
+        .set("Authorization", `Bearer ${token}`)
+        .send(newContact)
+        .set("Accept", "application/json");
+      expect(res.status).toEqual(201);
+      expect(res.body).toBeDefined();
+      idNewContact = res.body.data.contact._id;
+      done();
+    });
+    it("should return 500 on request POST  with wrong field or fields", async (done) => {
+      const res = await request(app)
+        .post(`/api/contacts`)
+        .set("Authorization", `Bearer ${token}`)
+        .send({ ...newContact, wrongFiled: "axe" })
+        .set("Accept", "application/json");
+      expect(res.status).toEqual(500);
+      expect(res.body).toBeDefined();
+      done();
+    });
+    it("should return 500 on request POST without required fields like : name,email,phone,password,subscription", async (done) => {
+      const res = await request(app)
+        .post(`/api/contacts`)
+        .set("Authorization", `Bearer ${token}`)
+        .send({ name: "Simon" })
+        .set("Accept", "application/json");
+      expect(res.status).toEqual(500);
+      expect(res.body).toBeDefined();
+      done();
+    });
+    it("should return 403 on request POST without token", async (done) => {
+      const res = await request(app)
+        .post(`/api/contacts`)
+        .set("Authorization", `Bearer ${wrongToken}`)
+        .send(newContact)
+        .set("Accept", "application/json");
+      expect(res.status).toEqual(403);
+      expect(res.body).toBeDefined();
+      done();
+    });
+  });
+  describe("should handle request DELETE contact by id ", () => {
+    it("should return 201 on request DELETE  contact", async (done) => {
+      const res = await request(app)
+        .delete(`/api/contacts/${contact._id}`)
+        .set("Authorization", `Bearer ${token}`);
+      expect(res.status).toEqual(200);
+      expect(res.body).toBeDefined();
+      contacts.pop(contact);
+      done();
+    });
+    it("should return 404 on request DELETE  contacts by id", async (done) => {
+      const res = await request(app)
+        .delete(`/api/contacts/${wrongId}`)
+        .set("Authorization", `Bearer ${token}`);
+      expect(res.status).toEqual(404);
+      expect(res.body).toBeDefined();
+      done();
+    });
+    it("should return 403 on request on request DELETE  contacts by id without token", async (done) => {
+      const res = await request(app)
+        .delete(`/api/contacts/${contact._id}`)
+        .set("Authorization", `Bearer ${wrongToken}`);
+      expect(res.status).toEqual(403);
+      expect(res.body).toBeDefined();
+      done();
+    });
+  });
+});

+ 33 - 0
validation/contact.js

@@ -0,0 +1,33 @@
+const Joi = require("joi");
+
+const validate = require("./validate");
+
+const schemaCreateContact = Joi.object({
+  name: Joi.string().alphanum().min(3).max(30).trim().required(),
+  phone: Joi.string()
+    .regex(/^[0-9]{10}$/)
+    .messages({
+      "string.pattern.base": `Phone number must have 10 digits and only numbers characters.`,
+    })
+    .required(),
+  subscription: Joi.string().optional(),
+}).min(3);
+
+const schemaUpdateContact = Joi.object({
+  name: Joi.string().alphanum().min(3).max(30).optional().trim().optional(),
+  phone: Joi.string()
+    .regex(/^[0-9]{10}$/)
+    .messages({
+      "string.pattern.base": `Phone number must have 10 digits and only numbers characters.`,
+    })
+    .optional(),
+  subscription: Joi.string().optional(),
+}).min(1);
+
+module.exports.createContact = (req, _res, next) => {
+  return validate(schemaCreateContact, req.body, next);
+};
+
+module.exports.update = (req, _res, next) => {
+  return validate(schemaUpdateContact, req.body, next);
+};

+ 39 - 0
validation/user.js

@@ -0,0 +1,39 @@
+const Joi = require('joi');
+
+const validate = require('./validate');
+
+const schemaCreateNewUser = Joi.object({
+	number: Joi.string().min(8).max(14).required(),
+}).min(1);
+
+const schemaUpdateUser = Joi.object({
+	name: Joi.string().alphanum().min(3).max(30).optional().trim().optional(),
+	lastName: Joi.string().alphanum().min(3).max(30).optional().trim().optional(),
+	number: Joi.string().min(8).max(14).optional(),
+});
+
+const schemaLogIn = Joi.object({
+	number: Joi.string().min(8).max(14).required(),
+	code: Joi.string().min(3).max(12).required(),
+}).min(2);
+
+module.exports.registration = (req, _res, next) => {
+	return validate(schemaCreateNewUser, req.body, next);
+};
+module.exports.update = (req, _res, next) => {
+	return validate(schemaUpdateUser, req.body, next);
+};
+module.exports.logIn = (req, _res, next) => {
+	return validate(schemaLogIn, req.body, next);
+};
+
+module.exports.validateUploadAvatar = (req, res, next) => {
+	if (!req.file)
+		return res.status(400).json({
+			status: 'error',
+			code: 400,
+			data: 'Bad request',
+			message: 'Field of avatar with file not found',
+		});
+	next();
+};

+ 13 - 0
validation/validate.js

@@ -0,0 +1,13 @@
+const validate = (schema, obj, next) => {
+  const { error } = schema.validate(obj);
+  if (error) {
+    const [{ message }] = error.details;
+    return next({
+      status: 400,
+      message: `Filed: ${message.replace(/"/g, "")}`,
+    });
+  }
+  next();
+};
+
+module.exports = validate;