Browse Source

Added user database and crud methods for it

Fela Maslen 7 năm trước cách đây
mục cha
commit
18625d1b37
4 tập tin đã thay đổi với 339 bổ sung2 xóa
  1. 84 2
      package-lock.json
  2. 3 0
      package.json
  3. 68 0
      src/server/modules/users.js
  4. 184 0
      test/server/modules/users.spec.js

+ 84 - 2
package-lock.json

@@ -2237,6 +2237,15 @@
         "unset-value": "^1.0.0"
       }
     },
+    "caller-id": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/caller-id/-/caller-id-0.1.0.tgz",
+      "integrity": "sha1-Wb2sCJPRLDhxQIJ5Ix+XRYNk8Hs=",
+      "dev": true,
+      "requires": {
+        "stack-trace": "~0.0.7"
+      }
+    },
     "callsites": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz",
@@ -3284,6 +3293,12 @@
         "domelementtype": "1"
       }
     },
+    "dot-notes": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/dot-notes/-/dot-notes-3.1.1.tgz",
+      "integrity": "sha1-eufhqUgTRSnzdosvsT9Mj6kqV88=",
+      "dev": true
+    },
     "dotenv": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-5.0.1.tgz",
@@ -5141,6 +5156,11 @@
         "minimalistic-crypto-utils": "^1.0.1"
       }
     },
+    "hoek": {
+      "version": "6.1.2",
+      "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.2.tgz",
+      "integrity": "sha512-6qhh/wahGYZHFSFw12tBbJw5fsAhhwrrG/y3Cs0YMTv2WzMnL0oLPnQJjv1QJvEfylRSOFuP+xCu+tdx0tD16Q=="
+    },
     "hoist-non-react-statics": {
       "version": "3.2.1",
       "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.2.1.tgz",
@@ -5731,6 +5751,14 @@
       "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
       "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
     },
+    "isemail": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz",
+      "integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==",
+      "requires": {
+        "punycode": "2.x.x"
+      }
+    },
     "isexe": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -5755,6 +5783,25 @@
       "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
       "dev": true
     },
+    "it-each": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/it-each/-/it-each-0.4.0.tgz",
+      "integrity": "sha512-fbOxSUiOByQnkgoFUEKPfzAuoUZ0mQRAXdWWsfI53gMJZ2oyhPcJBOCFx8yuMM36yP6OUUL3LgilYEqBiSACmQ==",
+      "dev": true,
+      "requires": {
+        "dot-notes": "3.1.1"
+      }
+    },
+    "joi": {
+      "version": "14.3.1",
+      "resolved": "https://registry.npmjs.org/joi/-/joi-14.3.1.tgz",
+      "integrity": "sha512-LQDdM+pkOrpAn4Lp+neNIFV3axv1Vna3j38bisbQhETPMANYRbFJFUyOZcOClYvM/hppMhGWuKSFEK9vjrB+bQ==",
+      "requires": {
+        "hoek": "6.x.x",
+        "isemail": "3.x.x",
+        "topo": "3.x.x"
+      }
+    },
     "js-base64": {
       "version": "2.5.0",
       "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.0.tgz",
@@ -6440,6 +6487,34 @@
         }
       }
     },
+    "mock-require": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/mock-require/-/mock-require-2.0.2.tgz",
+      "integrity": "sha1-HqpxqtIwE3c9En3H6Ro/u0g31g0=",
+      "dev": true,
+      "requires": {
+        "caller-id": "^0.1.0"
+      }
+    },
+    "mongo-mocker": {
+      "version": "0.0.8",
+      "resolved": "https://registry.npmjs.org/mongo-mocker/-/mongo-mocker-0.0.8.tgz",
+      "integrity": "sha512-30060IX9ZFkv4mBz8jlHfMacK/ZEbUaULOu/EADBAoV1JzSwmVDbBLCMDSJ7MksuHBfWUZ1MtLq5SZ4amKx7Vg==",
+      "dev": true,
+      "requires": {
+        "bson": "^0.5.7",
+        "lodash": "^4.17.2",
+        "mock-require": "^2.0.0"
+      },
+      "dependencies": {
+        "bson": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/bson/-/bson-0.5.7.tgz",
+          "integrity": "sha1-DRH+CTbB/uAp4R9wY/XQqyQi6j4=",
+          "dev": true
+        }
+      }
+    },
     "mongodb": {
       "version": "3.1.10",
       "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.1.10.tgz",
@@ -8541,8 +8616,7 @@
     "punycode": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
-      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
-      "dev": true
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
     },
     "qs": {
       "version": "6.5.2",
@@ -10347,6 +10421,14 @@
         "repeat-string": "^1.6.1"
       }
     },
+    "topo": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.3.tgz",
+      "integrity": "sha512-IgpPtvD4kjrJ7CRA3ov2FhWQADwv+Tdqbsf1ZnPUSAtCJ9e1Z44MmoSGDXGk4IppoZA7jd/QRkNddlLJWlUZsQ==",
+      "requires": {
+        "hoek": "6.x.x"
+      }
+    },
     "toposort": {
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz",

+ 3 - 0
package.json

@@ -49,8 +49,10 @@
     "html-loader": "^0.5.5",
     "html-webpack-plugin": "^3.2.0",
     "ignore-styles": "^5.0.1",
+    "it-each": "^0.4.0",
     "jsdom": "^13.1.0",
     "mini-css-extract-plugin": "^0.5.0",
+    "mongo-mocker": "0.0.8",
     "node-sass": "^4.11.0",
     "postcss-cssnext": "^3.1.0",
     "postcss-import": "^12.0.1",
@@ -65,6 +67,7 @@
   "dependencies": {
     "@babel/polyfill": "^7.2.5",
     "express": "^4.16.4",
+    "joi": "^14.3.1",
     "mongodb": "^3.1.10",
     "react": "^16.7.0",
     "react-dom": "^16.7.0",

+ 68 - 0
src/server/modules/users.js

@@ -0,0 +1,68 @@
+const joi = require('joi');
+
+const COLLECTION_USERS = 'users';
+
+async function insertUser(db, info) {
+    const { error, value } = joi.validate(info, {
+        name: joi.string().required(),
+        email: joi.string()
+            .email()
+            .required()
+    });
+
+    if (error) {
+        throw error;
+    }
+
+    const users = await db.collection(COLLECTION_USERS);
+
+    const newUser = await users.insert(value);
+
+    return newUser;
+}
+
+async function modifyUser(db, id, info) {
+    const { error, value } = joi.validate(info, {
+        name: joi.string(),
+        email: joi.string()
+            .email()
+    });
+
+    if (error) {
+        throw error;
+    }
+
+    const users = await db.collection(COLLECTION_USERS);
+
+    await users.update({ _id: id }, { $set: value }, {
+        upsert: false,
+        multi: false
+    });
+}
+
+async function deleteUser(db, id) {
+    const users = await db.collection(COLLECTION_USERS);
+
+    await users.deleteOne({ _id: id });
+}
+
+async function getUsers(db) {
+    const users = await db.collection(COLLECTION_USERS);
+
+    const rows = await users.find({})
+        .toArray();
+
+    return rows.map(({ _id, ...rest }) => ({
+        id: String(_id),
+        ...rest
+    }));
+}
+
+module.exports = {
+    insertUser,
+    modifyUser,
+    deleteUser,
+    getUsers,
+    COLLECTION_USERS
+};
+

+ 184 - 0
test/server/modules/users.spec.js

@@ -0,0 +1,184 @@
+/* eslint-disable no-unused-expressions */
+
+const { expect } = require('chai');
+
+const mongoMocker = require('mongo-mocker');
+
+const {
+    insertUser,
+    modifyUser,
+    deleteUser,
+    getUsers,
+    COLLECTION_USERS
+} = require('server/modules/users');
+
+describe('API - Users module', () => {
+    let db = null;
+
+    const testUsers = [
+        { name: 'John Doe', email: 'john.doe@mubaloo.com' },
+        { name: 'Jill Doe', email: 'jill.doe@mubaloo.com' }
+    ];
+
+    before(() => {
+        db = mongoMocker('mongoDriver/path', {
+            [COLLECTION_USERS]: testUsers
+        });
+    });
+
+    beforeEach(async () => {
+        const users = await db.collection(COLLECTION_USERS);
+
+        await Promise.all(testUsers.map(user => users.insert(user)));
+    });
+
+    afterEach(() => {
+        db.mock.clearAll();
+    });
+
+    describe('insertUser', () => {
+        it('should insert an employee into the users collection', async () => {
+            const users = await db.collection(COLLECTION_USERS);
+
+            const testUser = {
+                name: 'Jack Doe',
+                email: 'jack.doe@mubaloo.com'
+            };
+
+            const rowsBefore = await users.find(testUser)
+                .toArray();
+
+            expect(rowsBefore).to.be.an('array').of.length(0);
+
+            const newUserResult = await insertUser(db, testUser);
+
+            const rowsAfter = await users.find(testUser)
+                .toArray();
+
+            expect(rowsAfter).to.be.an('array').of.length(1);
+
+            expect(rowsAfter[0]).to.have.property('name', 'Jack Doe');
+            expect(rowsAfter[0]).to.have.property('email', 'jack.doe@mubaloo.com');
+            expect(rowsAfter[0]).to.have.property('_id');
+            expect(rowsAfter[0]._id).to.be.an('object');
+
+            expect(newUserResult).to.be.an('object');
+
+            expect(newUserResult).to.have.property('name', 'Jack Doe');
+            expect(newUserResult).to.have.property('email', 'jack.doe@mubaloo.com');
+        });
+
+        const badUsers = [
+            {},
+            { name: 'Some name' },
+            { email: 'some.email@mubaloo.com' },
+            { name: 'Some name', email: 'notanemail' }
+        ];
+
+        badUsers.forEach((user, index) => {
+            it(`should throw an error if information is missing or invalid (${index + 1}/${badUsers.length})`, async () => {
+
+                let errorThrown = false;
+
+                try {
+                    await insertUser(db, user);
+                } catch (err) {
+                    errorThrown = true;
+                }
+
+                if (!errorThrown) {
+                    throw new Error('Did not throw an error');
+                }
+            });
+        });
+    });
+
+    describe('modifyUser', () => {
+        it('should modify a specific user', async () => {
+            const users = await db.collection(COLLECTION_USERS);
+
+            const user = await users.findOne({ name: 'John Doe' });
+
+            const id = String(user._id);
+
+            await modifyUser(db, id, {
+                name: 'Jack Doe'
+            });
+
+            const modifiedUser = await users.findOne({ _id: user._id });
+
+            expect(modifiedUser).to.have.property('name', 'Jack Doe');
+            expect(modifiedUser).to.have.property('email', 'john.doe@mubaloo.com');
+        });
+
+        const badUsers = [
+            { email: 'notanemail' }
+        ];
+
+        badUsers.forEach((user, index) => {
+            it(`should throw an error if information is invalid (${index + 1}/${badUsers.length})`, async () => {
+
+                const users = await db.collection(COLLECTION_USERS);
+
+                const userResult = await users.findOne({ name: 'John Doe' });
+
+                const id = String(userResult._id);
+
+                let errorThrown = false;
+
+                try {
+                    await modifyUser(db, id, user);
+                } catch (err) {
+                    errorThrown = true;
+                }
+
+                if (!errorThrown) {
+                    throw new Error('Did not throw an error');
+                }
+            });
+        });
+    });
+
+    describe('deleteUser', () => {
+        it('should delete a user', async () => {
+            const users = await db.collection(COLLECTION_USERS);
+
+            const existingUser = await users.findOne({ name: 'John Doe' });
+
+            expect(existingUser).to.be.an('object');
+
+            const id = String(existingUser._id);
+
+            await deleteUser(db, id);
+
+            const newQueryResult = await users.findOne({ name: 'John Doe' });
+
+            expect(newQueryResult).to.be.undefined;
+        });
+    });
+
+    describe('getUsers', () => {
+        it('should get a complete list of users (employees) from the database', async () => {
+            const result = await getUsers(db);
+
+            expect(result).to.be.an('array').of.length(2);
+
+            expect(result[0]).to.deep.include({
+                name: 'John Doe',
+                email: 'john.doe@mubaloo.com'
+            });
+
+            expect(result[1]).to.deep.include({
+                name: 'Jill Doe',
+                email: 'jill.doe@mubaloo.com'
+            });
+
+            expect(result[0]).to.have.property('id');
+            expect(result[1]).to.have.property('id');
+
+            expect(result[0].id).to.be.a('string');
+            expect(result[1].id).to.be.a('string');
+        });
+    });
+});
+