diff --git a/server/api/routes_auth.js b/server/api/routes_auth.js index 87f4a3a..59c8e37 100644 --- a/server/api/routes_auth.js +++ b/server/api/routes_auth.js @@ -100,7 +100,7 @@ module.exports = (router) => { } else { req.decoded = decoded; res.send({ - message: 'your token is vaild.', status: 'inf' + message: 'your token is vaild.', status: 'info' }); } }); diff --git a/server/api/routes_settings.js b/server/api/routes_settings.js index 988b04c..2f8a818 100644 --- a/server/api/routes_settings.js +++ b/server/api/routes_settings.js @@ -140,6 +140,10 @@ module.exports = (router) => { securityTokenHash.update(new Date().getTime().toString()); process.env.STJORNACONFIG_PASSWORD_SECRECT = securityTokenHash.digest('hex'); req.body.config.installed = true; + + // check if data folder exists, if not create it + fileHelper.createFolder(process.env.STJORNA_SERVER_STORAGE); + // create initial config file fileHelper.saveConfigFile(req.body.config, (err, config) => { if (err) { diff --git a/server/lib/env_default.js b/server/lib/env_default.js index 41f12c5..928cf63 100644 --- a/server/lib/env_default.js +++ b/server/lib/env_default.js @@ -4,6 +4,7 @@ module.exports = { process.env['STJORNA_SERVER_PORT'] = process.env.STJORNA_SERVER_PORT || 3000; process.env['STJORNA_SERVER_MAX_UPLOAD'] = process.env.STJORNA_SERVER_MAX_UPLOAD || '5mb'; process.env['STJORNA_LOGLEVEL'] = process.env.STJORNA_LOGLEVEL || 'info'; + process.env['STJORNA_REQUEST_LOG'] = process.env.STJORNA_REQUEST_LOG || ''; process.env['STJORNA_CRON_CLEANUP_INTERVAL'] = process.env.STJORNA_CRON_CLEANUP_INTERVAL || '00 3 * * *'; process.env['STJORNA_SERVER_STORAGE'] = process.env.STJORNA_SERVER_STORAGE || `${process.cwd()}/data`; diff --git a/server/lib/export/excel.js b/server/lib/export/excel.js index 9d0e785..5705744 100644 --- a/server/lib/export/excel.js +++ b/server/lib/export/excel.js @@ -1,6 +1,4 @@ -const path = require('path'); -const fs = require('fs'); -var xl = require('excel4node'); +const xl = require('excel4node'); const dbHelper = require('../database_helper.js'); diff --git a/server/lib/file_helper.js b/server/lib/file_helper.js index 16b2a77..ea54568 100644 --- a/server/lib/file_helper.js +++ b/server/lib/file_helper.js @@ -7,7 +7,7 @@ const exportJson = require('./export/json.js'); module.exports = { getFolderContent: (route, cb) => { - // build a complete file tree of all things within a route, base route is /data/uploads/ + // build a complete file tree of all things within a route, base route is //uploads/ let files = fs.readdir(route, 'utf8', (err, files) => { let fileList = []; if (files) { @@ -55,6 +55,11 @@ module.exports = { } }); }, + createFolder: (dir) => { + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir); + } + }, loadConfigFile: (cb) => { // load config file and call the cb fs.readFile(`${process.env.STJORNA_SERVER_STORAGE}/config.json`, 'utf8', cb); diff --git a/server/lib/image_helper.js b/server/lib/image_helper.js index 1e6f2a4..e058c12 100644 --- a/server/lib/image_helper.js +++ b/server/lib/image_helper.js @@ -12,7 +12,7 @@ module.exports = { let buff = Buffer.from(data, 'base64'); // generate new hash for imagename let hash = crypto.createHash('sha1').update(`${data}-${new Date().getTime()}`, 'utf8').digest('hex'); - let imagePath = `/data/uploads/${userid}${additionalPath}/${hash}.jpeg`; + let imagePath = `/uploads/${userid}${additionalPath}/${hash}.jpeg`; fileHelper.loadConfigFile((err, config) => { if (!err) { config = JSON.parse(config); @@ -20,7 +20,7 @@ module.exports = { Jimp.read(buff).then((image) => { image.resize(config.image_dimension, config.image_dimension) .quality(config.image_quality) - .write(`${__dirname}/..${imagePath}`, (err) => { + .write(`${process.env.STJORNA_SERVER_STORAGE}/${imagePath}`, (err) => { if (err) { logger.error(`image - error occured while writing image to filesystem: ${err.message}`); } diff --git a/server/lib/logging_helper.js b/server/lib/logging_helper.js index b5d9dc1..597cc64 100644 --- a/server/lib/logging_helper.js +++ b/server/lib/logging_helper.js @@ -1,6 +1,25 @@ const winston = require('winston'); const expressWinston = require('express-winston'); +// build pattern for stjorna log format +const buildStjornaLogFormat = (info) => { + const padding = info.level.length <= 7 ? 7 : 17; + // detect component, if not write nothing + let component = info.message.replace(/\s\-\s.*$/g, ''); + if (component !== info.message) { + info.message = info.message.replace(`${component} - `, ''); + component = `[${component}] `; + } else { + component = ''; + } + return `${info.timestamp} [${info.label}] [${info.level.padEnd(padding, '')}] ${component}${info.message}`; +}; + +// build pattern for stjorna request log format +const buildStjornaRequestLogFormat = (req) => { + return `${req.timestamp} [${req.label}] ${req.message}`; +}; + // set logging format for application logs const stjornaLogFormat = winston.format.combine( winston.format.colorize({ @@ -8,18 +27,7 @@ const stjornaLogFormat = winston.format.combine( }), winston.format.label({ label: 'stjorna' }), winston.format.timestamp(), - winston.format.printf((info) => { - const padding = info.level.length <= 7 ? 7 : 17; - // detect component, if not write nothing - let component = info.message.replace(/\s\-\s.*$/g, ''); - if (component !== info.message) { - info.message = info.message.replace(`${component} - `, ''); - component = `[${component}] `; - } else { - component = ''; - } - return `${info.timestamp} [${info.label}] [${info.level.padEnd(padding, '')}] ${component}${info.message}`; - }) + winston.format.printf(buildStjornaLogFormat) ); // set logging format for request logs @@ -29,37 +37,61 @@ const stjornaRequestLogFormat = winston.format.combine( }), winston.format.label({ label: 'stjorna-req' }), winston.format.timestamp(), - winston.format.printf((req) => { - return `${req.timestamp} [${req.label}] ${req.message}`; - }) + winston.format.printf(buildStjornaRequestLogFormat) ); +const isSlientLogActivated = (logType) => { + switch (logType) { + case 'application': + if (process.env.STJORNA_LOGLEVEL === 'slient') { + return true; + } + break; + case 'request': + if (process.env.STJORNA_REQUEST_LOG === 'slient') { + return true; + } + break; + } + return false; +}; + +// classic logger configuration (console.log stuff) +const logger = winston.createLogger({ + level: process.env.STJORNA_LOGLEVEL, + transports: [ + new (winston.transports.Console)({ + format: winston.format.combine(winston.format.colorize(), stjornaLogFormat), + silent: isSlientLogActivated('application') + }) + // new winston.transports.File({ filename: 'combined.log' }) + ] +}); + +// express logger configuration +const configureExpressLogging = expressWinston.logger({ + transports: [ + new winston.transports.Console({ + format: winston.format.combine(winston.format.colorize(), stjornaRequestLogFormat) + }) + ], + level: (req, res) => { + let level = ''; + if (res.statusCode >= 100) { level = 'info'; } + if (res.statusCode >= 400) { level = 'warn'; } + if (res.statusCode >= 500) { level = 'error'; } + return level; + }, + statusLevels: false, + expressFormat: true, + colorize: true, + ignoreRoute: (req, res) => { return isSlientLogActivated('request'); } +}); + module.exports = { - // classic logger configuration (console.log stuff) - logger: winston.createLogger({ - level: process.env.STJORNA_LOGLEVEL, - transports: [ - new (winston.transports.Console)({ - format: winston.format.combine(winston.format.colorize(), stjornaLogFormat) - }) - // new winston.transports.File({ filename: 'combined.log' }) - ] - }), - // express logger configuration - configureExpressLogging: expressWinston.logger({ - transports: [ - new winston.transports.Console({ - format: winston.format.combine(winston.format.colorize(), stjornaRequestLogFormat) - }) - ], - level: (req, res) => { - let level = ''; - if (res.statusCode >= 100) { level = 'info'; } - if (res.statusCode >= 400) { level = 'warn'; } - if (res.statusCode >= 500) { level = 'error'; } - return level; - }, - statusLevels: false, - expressFormat: true - }) + logger, + configureExpressLogging, + _buildStjornaLogFormat: buildStjornaLogFormat, + _buildStjornaRequestLogFormat: buildStjornaRequestLogFormat, + _isSlientLogActivated: isSlientLogActivated }; \ No newline at end of file diff --git a/server/package.json b/server/package.json index 7194401..ba68194 100644 --- a/server/package.json +++ b/server/package.json @@ -7,9 +7,9 @@ "start": "nodemon --ignore data/ server.js", "apidoc:private": "apidoc --private true -i ./api -o ./apidoc-private/", "apidoc:public": "apidoc --private false -i ./api -o ./apidoc/", - "test": "rimraf data & rimraf reports & npm run test:coverage", - "test:coverage": "nyc --reporter html --reporter lcovonly --report-dir reports/coverage --temp-directory reports/coverage/.nyc_output --reporter text npm run test:api", - "test:api": "mocha --reporter mocha-multi-reporters --reporter-options configFile=test/_config.json --timeout 5000 --full-trace --exit", + "test": "rimraf test/testdata & rimraf reports & npm run test:coverage", + "test:coverage": "nyc --reporter html --reporter lcovonly --report-dir reports/coverage --temp-dir reports/coverage/.nyc_output --reporter text npm run test:api", + "test:api": "mocha test/**/*.spec.js --reporter mocha-multi-reporters --reporter-options configFile=test/_config.json --timeout 5000 --full-trace --exit", "test:sendcoverage": "cat reports/coverage/lcov.info | codacy-coverage" }, "author": "matthias.baldi@secanis.ch", diff --git a/server/test/_initializeSetup.js b/server/test/_initializeSetup.js index e808f8d..53f27fc 100644 --- a/server/test/_initializeSetup.js +++ b/server/test/_initializeSetup.js @@ -1,70 +1,156 @@ -let chai = require('chai'); -let chaiHttp = require('chai-http'); -let server; -let should = chai.should(); -let rimraf = require('rimraf'); - -const dbHelper = require('../lib/database_helper.js'); +const chai = require('chai'); +const chaiHttp = require('chai-http'); +const should = chai.should(); +const rimraf = require('rimraf'); -let apiUrl = '/api/v1'; +const apiUrl = '/api/v1'; +let server; +let dbHelper; process.env.NODE_ENV = 'test'; process.env.STJORNA_SECURITY = 'none'; -process.env.STJORNA_LOGLEVEL = 'error'; +process.env.STJORNA_LOGLEVEL = 'slient'; +process.env.STJORNA_REQUEST_LOG = 'slient'; process.env.STJORNACONFIG_IMAGE_DIMENSION = 255; process.env.STJORNACONFIG_IMAGE_QUALITY = 50; process.env.STJORNACONFIG_PASSWORD_SECRECT = 'testpwsecret'; +process.env.STJORNA_SERVER_STORAGE = `${process.cwd()}/testdata`; -const user = { - username: 'admin', - email: 'admin@domain.com', - password: 'admin4test' -}; +const init = () => { + const config = { + image_dimension: process.env.STJORNACONFIG_IMAGE_DIMENSION - 0, + image_quality: process.env.STJORNACONFIG_IMAGE_QUALITY - 0, + allow_remote_access: true + }; + + chai.use(chaiHttp); + server = require('../server.js'); + dbHelper = require('../lib/database_helper.js'); + + before((done) => { + // wait for database + let dbState; + let iv = setInterval(() => { + try { + dbState = dbHelper.db.getState(); + if (dbState) { + clearInterval(iv); + chai.request(server) + .post(`${apiUrl}/setup`) + .send({ + config: config, + user: getDefaultUser() + }) + .end((err, res) => { + if (err) { + throw err; + } + res.should.have.status(200); + res.body.should.be.a('object'); + res.body.message.config_status.errors.length.should.be.eql(0); + res.body.message.user_status.errors.length.should.be.eql(0); + done(); + }); + } + } catch { + console.error(`couldn't start database/database connection.`) + } + }, 100); + }); + + after((done) => { + rimraf(`${process.env.STJORNA_SERVER_STORAGE}`, () => { + done(); + }); + }); +} -const config = { - image_dimension: process.env.STJORNACONFIG_IMAGE_DIMENSION - 0, - image_quality: process.env.STJORNACONFIG_IMAGE_QUALITY - 0, - allow_remote_access: true -}; +// test helper methods +const getDefaultUser = () => { + return { + username: 'admin', + email: 'admin@domain.com', + password: 'admin4test' + }; +} -chai.use(chaiHttp); +const generateIds = () => { + return { + userId: dbHelper.generateId(), + categoryId: dbHelper.generateId(), + productId: dbHelper.generateId() + }; +} -before((done) => { - server = require('../server.js'); +const generateUserObject = (userId) => { + return { + _id: userId, + username: `dummyUsername-${userId}`, + password: '', + email: `${userId}@email.com`, + apikey: '', + language: 'en', + created: new Date().getTime(), + updated: new Date().getTime() + }; +} + +const generateCategoryObject = (userId, categoryId) => { + return { + _id: categoryId, + name: `dummyCategory-${categoryId}`, + description: '', + active: true, + image: '', + imageUrl: dbHelper.generateId(), + created: new Date().getTime(), + createdUser: userId, + updated: new Date().getTime(), + updatedUser: null + }; +} + +const generateProductObject = (userId, categoryId, productId) => { + return { + _id: productId, + name: `dummyProduct-${productId}`, + category: categoryId, + price: 34.2, + description: '', + active: true, + image: '', + imageUrl: '', + created: new Date().getTime(), + createdUser: userId, + updated: new Date().getTime(), + updatedUser: null + }; +}; + +const getServer = () => { + return server; +} - // wait for database - let dbState; - let iv = setInterval(() => { - try { - dbState = dbHelper.db.getState(); - if (dbState) { - clearInterval(iv); - chai.request(server) - .post(`${apiUrl}/setup`) - .send({ - config: config, - user: user - }) - .end((err, res) => { - if (err) { - throw err; - } - res.should.have.status(200); - res.body.should.be.a('object'); - res.body.message.config_status.errors.length.should.be.eql(0); - res.body.message.user_status.errors.length.should.be.eql(0); - done(); - }); +const searchObjectByProperty = (arr, search, property)=> { + let retVal = null; + for (let i = 0; i < arr.length; i++) { + let item = arr[i]; + if (item.hasOwnProperty(property)) { + if (item[property].toLowerCase() === search.toLowerCase()) { + return item; } - } catch { - console.error(`couldn't start database/database connection.`) } - }, 100); -}); + }; + return retVal; +}; -after((done) => { - rimraf(`${process.env.STJORNA_SERVER_STORAGE}`, () => { - console.info('\n> stjorna test: deleting data folder done'); - done(); - }); -}); \ No newline at end of file +module.exports = { + init, + getDefaultUser, + generateIds, + generateUserObject, + generateProductObject, + generateCategoryObject, + getServer, + searchObjectByProperty +}; \ No newline at end of file diff --git a/server/test/api/auth.spec.js b/server/test/api/auth.spec.js new file mode 100644 index 0000000..43714da --- /dev/null +++ b/server/test/api/auth.spec.js @@ -0,0 +1,132 @@ +const chai = require('chai'); +const expect = require('chai').expect; + +const apiUrl = '/api/v1'; + +const testHelper = require('../_initializeSetup.js'); +testHelper.init(); + +describe('Authentication', () => { + it('with body token', (done) => { + chai.request(testHelper.getServer()) + .post(`${apiUrl}/authenticate`) + .send(testHelper.getDefaultUser()) + .end((err, res) => { + res.should.have.status(200); + expect(err).to.be.null; + const userLogin = res.body; + userLogin.should.be.a('object'); + userLogin.should.have.property('email').eql(testHelper.getDefaultUser().email); + userLogin.should.have.property('username').eql(testHelper.getDefaultUser().username); + userLogin.should.have.property('message').eql('have fun and enjoy life...'); + userLogin.should.have.property('status').eql('successful'); + userLogin.should.have.property('_id'); + userLogin.should.have.property('token'); + // verify login + chai.request(testHelper.getServer()) + .post(`${apiUrl}/authenticate/verify`) + .send({ + token: userLogin.token + }) + .end((err, res) => { + res.should.have.status(200); + res.body.should.be.a('object'); + res.body.should.have.property('status').eql('info'); + res.body.should.have.property('message').eql('your token is vaild.'); + done(); + }); + }); + }); + + it('with query token', (done) => { + chai.request(testHelper.getServer()) + .post(`${apiUrl}/authenticate`) + .send(testHelper.getDefaultUser()) + .end((err, res) => { + res.should.have.status(200); + expect(err).to.be.null; + const userLogin = res.body; + userLogin.should.be.a('object'); + userLogin.should.have.property('email').eql(testHelper.getDefaultUser().email); + userLogin.should.have.property('username').eql(testHelper.getDefaultUser().username); + userLogin.should.have.property('message').eql('have fun and enjoy life...'); + userLogin.should.have.property('status').eql('successful'); + userLogin.should.have.property('_id'); + userLogin.should.have.property('token'); + // verify login + chai.request(testHelper.getServer()) + .post(`${apiUrl}/authenticate/verify?token=${userLogin.token}`) + .end((err, res) => { + res.should.have.status(200); + res.body.should.be.a('object'); + res.body.should.have.property('status').eql('info'); + res.body.should.have.property('message').eql('your token is vaild.'); + done(); + }); + }); + }); + + it('with header token', (done) => { + chai.request(testHelper.getServer()) + .post(`${apiUrl}/authenticate`) + .send(testHelper.getDefaultUser()) + .end((err, res) => { + res.should.have.status(200); + expect(err).to.be.null; + const userLogin = res.body; + userLogin.should.be.a('object'); + userLogin.should.have.property('email').eql(testHelper.getDefaultUser().email); + userLogin.should.have.property('username').eql(testHelper.getDefaultUser().username); + userLogin.should.have.property('message').eql('have fun and enjoy life...'); + userLogin.should.have.property('status').eql('successful'); + userLogin.should.have.property('_id'); + userLogin.should.have.property('token'); + // verify login + chai.request(testHelper.getServer()) + .post(`${apiUrl}/authenticate/verify`) + .set('x-stjorna-access-token', userLogin.token) + .end((err, res) => { + res.should.have.status(200); + res.body.should.be.a('object'); + res.body.should.have.property('status').eql('info'); + res.body.should.have.property('message').eql('your token is vaild.'); + done(); + }); + }); + }); + + it('bad auth creds - wrong password', (done) => { + let userObj = testHelper.getDefaultUser(); + userObj.password = 'dummyPassword'; + chai.request(testHelper.getServer()) + .post(`${apiUrl}/authenticate`) + .send(userObj) + .end((err, res) => { + expect(err).to.be.null; + res.should.have.status(401); + res.body.should.be.a('object'); + res.body.should.have.property('username').eql(testHelper.getDefaultUser().username); + res.body.should.have.property('message').eql('E101: invalid login credentials'); + res.body.should.have.property('status').eql('failed'); + done(); + }); + }); + + it('bad auth creds - emtpy object', (done) => { + let userObj = testHelper.getDefaultUser(); + userObj.password = ''; + userObj.username = ''; + chai.request(testHelper.getServer()) + .post(`${apiUrl}/authenticate`) + .send(userObj) + .end((err, res) => { + expect(err).to.be.null; + res.should.have.status(401); + res.body.should.be.a('object'); + res.body.should.have.property('username').eql(''); + res.body.should.have.property('message').eql('E101: invalid login credentials'); + res.body.should.have.property('status').eql('failed'); + done(); + }); + }); +}); diff --git a/server/test/data.spec.js b/server/test/api/data.spec.js similarity index 91% rename from server/test/data.spec.js rename to server/test/api/data.spec.js index 1f425f1..a406a0d 100644 --- a/server/test/data.spec.js +++ b/server/test/api/data.spec.js @@ -1,9 +1,6 @@ -process.env.STJORNA_LOGLEVEL = 'error'; +const chai = require('chai'); -let chai = require('chai'); -let server = require('../server.js'); - -let apiUrl = '/api/v1'; +const apiUrl = '/api/v1'; const user = { username: 'admin', @@ -11,12 +8,6 @@ const user = { password: 'admin4test' }; -const config = { - image_dimension: process.env.STJORNACONFIG_IMAGE_DIMENSION - 0, - image_quality: process.env.STJORNACONFIG_IMAGE_QUALITY - 0, - allow_remote_access: true -}; - const product = { name: 'foo', category: 'catid', @@ -59,11 +50,12 @@ const category_2 = { const exampleImage = 'data:image/jpeg;base64,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'; -require('./_initializeSetup.js'); +const testHelper = require('../_initializeSetup.js'); +testHelper.init(); describe('Products/Categories', () => { it('get products', (done) => { - chai.request(server) + chai.request(testHelper.getServer()) .get(`${apiUrl}/products`) .end((err, res) => { res.should.have.status(200); @@ -76,7 +68,7 @@ describe('Products/Categories', () => { // create, alter, get, delete product it('crud product', (done) => { let productId; - chai.request(server) + chai.request(testHelper.getServer()) .put(`${apiUrl}/products`) .send(product) .end((err, res) => { @@ -92,14 +84,14 @@ describe('Products/Categories', () => { res.body.should.have.property('created'); res.body.should.have.property('updated'); productId = res.body._id; - chai.request(server) + chai.request(testHelper.getServer()) .post(`${apiUrl}/products/${productId}`) .send(product_2) .end((err, res) => { res.should.have.status(200); res.body.should.be.a('object'); res.body.should.have.property('_id').eql(productId); - chai.request(server) + chai.request(testHelper.getServer()) .get(`${apiUrl}/products/${productId}`) .end((err, res) => { res.body.should.have.property('name').eql(product_2.name); @@ -111,7 +103,7 @@ describe('Products/Categories', () => { res.body.should.have.property('updatedUser').eql(user.username) res.body.should.have.property('created'); res.body.should.have.property('updated'); - chai.request(server) + chai.request(testHelper.getServer()) .delete(`${apiUrl}/products/${productId}`) .end((err, res) => { res.should.have.status(200); @@ -125,7 +117,7 @@ describe('Products/Categories', () => { }); it('get categories', (done) => { - chai.request(server) + chai.request(testHelper.getServer()) .get(`${apiUrl}/categories`) .end((err, res) => { res.should.have.status(200); @@ -138,7 +130,7 @@ describe('Products/Categories', () => { // create, alter, get, delete category it('crud category', (done) => { let categoryId; - chai.request(server) + chai.request(testHelper.getServer()) .put(`${apiUrl}/categories`) .send(category) .end((err, res) => { @@ -152,14 +144,14 @@ describe('Products/Categories', () => { res.body.should.have.property('created'); res.body.should.have.property('updated'); categoryId = res.body._id; - chai.request(server) + chai.request(testHelper.getServer()) .post(`${apiUrl}/categories/${categoryId}`) .send(category_2) .end((err, res) => { res.should.have.status(200); res.body.should.be.a('object'); res.body.should.have.property('_id').eql(categoryId); - chai.request(server) + chai.request(testHelper.getServer()) .get(`${apiUrl}/categories/${categoryId}`) .end((err, res) => { res.body.should.have.property('name').eql(category_2.name); @@ -169,7 +161,7 @@ describe('Products/Categories', () => { res.body.should.have.property('updatedUser').eql(user.username) res.body.should.have.property('created'); res.body.should.have.property('updated'); - chai.request(server) + chai.request(testHelper.getServer()) .delete(`${apiUrl}/categories/${categoryId}`) .end((err, res) => { res.should.have.status(200); @@ -184,7 +176,7 @@ describe('Products/Categories', () => { it('get products by category', (done) => { let categoryId; - chai.request(server) + chai.request(testHelper.getServer()) .put(`${apiUrl}/categories`) .send(category_2) .end((err, res) => { @@ -198,7 +190,7 @@ describe('Products/Categories', () => { let p2 = product_2; p2.category = categoryId; // product with "wrong" categoryID - chai.request(server) + chai.request(testHelper.getServer()) .put(`${apiUrl}/products`) .send({ name: 'fooBarProd', @@ -210,7 +202,7 @@ describe('Products/Categories', () => { res.body.should.have.property('name').eql('fooBarProd'); res.body.should.have.property('category').eql('fooBarCat'); // add product with matching categoryID - chai.request(server) + chai.request(testHelper.getServer()) .put(`${apiUrl}/products`) .send(p1) .end((err, res) => { @@ -219,7 +211,7 @@ describe('Products/Categories', () => { res.body.should.have.property('name').eql(p1.name); res.body.should.have.property('category').eql(categoryId); // add product with matching categoryID - chai.request(server) + chai.request(testHelper.getServer()) .put(`${apiUrl}/products`) .send(p2) .end((err, res) => { @@ -228,7 +220,7 @@ describe('Products/Categories', () => { res.body.should.have.property('name').eql(p2.name); res.body.should.have.property('category').eql(categoryId); // test if we can get both products by categoryID - chai.request(server) + chai.request(testHelper.getServer()) .get(`${apiUrl}/categories/${categoryId}/products`) .end((err, res) => { res.should.have.status(200); @@ -245,7 +237,7 @@ describe('Products/Categories', () => { it('product with image', (done) => { let p1 = product; p1.image = exampleImage; - chai.request(server) + chai.request(testHelper.getServer()) .put(`${apiUrl}/products`) .send(p1) .end((err, res) => { @@ -254,14 +246,14 @@ describe('Products/Categories', () => { res.body.should.have.property('name').eql(p1.name); res.body.should.have.property('imageUrl'); // test if we can load the image list - chai.request(server) + chai.request(testHelper.getServer()) .get(`/api/data/uploads/undefined/products?userid=undefined`) .end((err, res) => { res.should.have.status(200); res.body.should.be.a('array'); res.body.length.should.be.eql(1); let filename = res.body[0].name; - chai.request(server) + chai.request(testHelper.getServer()) .get(`/api/data/uploads/undefined/products/${filename}?userid=undefined`) .end((err, res) => { res.should.have.status(200); diff --git a/server/test/api/info.spec.js b/server/test/api/info.spec.js new file mode 100644 index 0000000..54e8561 --- /dev/null +++ b/server/test/api/info.spec.js @@ -0,0 +1,54 @@ +const chai = require('chai'); +const expect = require('chai').expect; + +const app = require('../../package.json'); + +const apiUrl = '/api/v1'; + +const testHelper = require('../_initializeSetup.js'); +testHelper.init(); + +describe('Info', () => { + it('get server info', (done) => { + chai.request(testHelper.getServer()) + .get(`${apiUrl}/info/server`) + .end((err, res) => { + res.should.have.status(200); + res.body.should.be.a('object'); + res.body.should.have.property('hostname'); + res.body.should.have.property('api_port').eql('3000'); + res.body.should.have.property('os'); + res.body.should.have.property('arch'); + res.body.should.have.property('mem_total'); + res.body.should.have.property('mem_free'); + res.body.should.have.property('cpu'); + res.body.should.have.property('loadavg'); + res.body.should.have.property('app_version').eql(`${app.name}:${app.version}`); + done(); + }); + }); + + it('get environment', (done) => { + chai.request(testHelper.getServer()) + .get(`${apiUrl}/info/config`) + .end((err, res) => { + res.should.have.status(200); + res.body.should.be.a('array'); + + let obj = testHelper.searchObjectByProperty(res.body, 'STJORNA_CRON_CLEANUP_INTERVAL', 'name'); + expect(obj.value).to.be.equal('00 3 * * *', 'check STJORNA_CRON_CLEANUP_INTERVAL env'); + + obj = testHelper.searchObjectByProperty(res.body, 'STJORNA_LOGLEVEL', 'name'); + expect(obj.value).to.be.equal('slient', 'check STJORNA_LOGLEVEL env'); + + // security is just during tests set to 'none', per default this ENV is not set + obj = testHelper.searchObjectByProperty(res.body, 'STJORNA_SECURITY', 'name'); + expect(obj.value).to.be.equal('none', 'check STJORNA_SECURITY env'); + + obj = testHelper.searchObjectByProperty(res.body, 'STJORNA_SERVER_STORAGE', 'name'); + expect(obj.value).to.be.equal(process.env.STJORNA_SERVER_STORAGE, 'check STJORNA_SERVER_STORAGE env'); + + done(); + }); + }); +}); diff --git a/server/test/settings.spec.js b/server/test/api/settings.spec.js similarity index 86% rename from server/test/settings.spec.js rename to server/test/api/settings.spec.js index 89ed9b5..c587388 100644 --- a/server/test/settings.spec.js +++ b/server/test/api/settings.spec.js @@ -1,15 +1,6 @@ -process.env.STJORNA_LOGLEVEL = 'error'; +const chai = require('chai'); -let chai = require('chai'); -let server = require('../server.js'); - -let apiUrl = '/api/v1'; - -const user = { - username: 'admin', - email: 'admin@domain.com', - password: 'admin4test' -}; +const apiUrl = '/api/v1'; const config = { image_dimension: process.env.STJORNACONFIG_IMAGE_DIMENSION - 0, @@ -17,11 +8,12 @@ const config = { allow_remote_access: true }; -require('./_initializeSetup.js'); +const testHelper = require('../_initializeSetup.js'); +testHelper.init(); describe('Setup/Settings', () => { it('get setup status', (done) => { - chai.request(server) + chai.request(testHelper.getServer()) .get(`${apiUrl}/setup`) .end((err, res) => { res.should.have.status(200); @@ -34,7 +26,7 @@ describe('Setup/Settings', () => { }); it('get settings', (done) => { - chai.request(server) + chai.request(testHelper.getServer()) .get(`${apiUrl}/settings`) .end((err, res) => { res.should.have.status(200); @@ -49,7 +41,7 @@ describe('Setup/Settings', () => { }); it('update settings', (done) => { - chai.request(server) + chai.request(testHelper.getServer()) .post(`${apiUrl}/settings`) .send({ allow_remote_access: false @@ -58,7 +50,7 @@ describe('Setup/Settings', () => { res.should.have.status(200); res.body.should.be.a('object'); res.body.should.have.property('message').eql('configuration successfully saved'); - chai.request(server) + chai.request(testHelper.getServer()) .get(`${apiUrl}/settings`) .end((err, res) => { res.should.have.status(200); diff --git a/server/test/user.spec.js b/server/test/api/user.spec.js similarity index 87% rename from server/test/user.spec.js rename to server/test/api/user.spec.js index 157f5d5..50c4895 100644 --- a/server/test/user.spec.js +++ b/server/test/api/user.spec.js @@ -1,9 +1,6 @@ -process.env.STJORNA_LOGLEVEL = 'error'; +const chai = require('chai'); -let chai = require('chai'); -let server = require('../server.js'); - -let apiUrl = '/api/v1'; +const apiUrl = '/api/v1'; const user_2 = { username: 'admin', @@ -19,17 +16,18 @@ const user_3 = { password: 'admin4test' }; -require('./_initializeSetup.js'); +const testHelper = require('../_initializeSetup.js'); +testHelper.init(); describe('User/Auth', () => { it('update user', (done) => { - chai.request(server) + chai.request(testHelper.getServer()) .get(`${apiUrl}/users`) .end((err, res) => { res.should.have.status(200); res.body.should.be.a('array'); let userId = res.body[0]._id; - chai.request(server) + chai.request(testHelper.getServer()) .post(`${apiUrl}/users/${userId}`) .send(user_2) .end((err, res) => { @@ -43,7 +41,7 @@ describe('User/Auth', () => { }); it('auth user', (done) => { - chai.request(server) + chai.request(testHelper.getServer()) .put(`${apiUrl}/users`) .send(user_3) .end((err, res) => { @@ -52,7 +50,7 @@ describe('User/Auth', () => { res.body.should.have.property('_id'); res.body.should.have.property('username').eql(user_3.username); res.body.should.have.property('email').eql(user_3.email); - chai.request(server) + chai.request(testHelper.getServer()) .post(`${apiUrl}/authenticate`) .send(user_3) .end((err, res) => { diff --git a/server/test/lib/export.spec.js b/server/test/lib/export.spec.js new file mode 100644 index 0000000..f5c363d --- /dev/null +++ b/server/test/lib/export.spec.js @@ -0,0 +1,58 @@ +const expect = require('chai').expect; +const exportJson = require('../../lib/export/json.js'); +const exportExcel = require('../../lib/export/excel.js'); +const dbHelper = require('../../lib/database_helper.js'); + +const testHelper = require('../_initializeSetup.js'); +testHelper.init(); + +describe('Export', () => { + it('generate json', (done) => { + exportJson.generateExport((err, result) => { + expect(err).to.be.null; + result.should.be.a('object'); + result.should.have.property('contentType').eql('application/json'); + result.should.have.property('file'); + result.should.have.property('fileSuffix').eql('json'); + done(); + }); + }); + + it('generate excel - bad case', (done) => { + dbHelper.getAllDataSets = () => { + return { + categories: [], + products: [], + users: [] + }; + } + + exportExcel.generateExport((err, result) => { + expect(result).to.be.null; + err.should.be.a('object'); + err.should.have.property('message').eql('got an empty data set'); + err.should.have.property('status').eql('error'); + done(); + }); + }); + + it('generate excel', (done) => { + const ids = testHelper.generateIds(); + dbHelper.getAllDataSets = () => { + return { + categories: [testHelper.generateCategoryObject(ids.userId, ids.categoryId)], + products: [testHelper.generateProductObject(ids.userId, ids.categoryId, ids.productId)], + users: [testHelper.generateUserObject(ids.userId)] + }; + } + + exportExcel.generateExport((err, result) => { + expect(err).to.be.null; + result.should.be.a('object'); + result.should.have.property('contentType').eql('application/vnd.ms-excel'); + result.should.have.property('file'); + result.should.have.property('fileSuffix').eql('xlsx'); + done(); + }); + }); +}).timeout(10000); diff --git a/server/test/lib/logging.spec.js b/server/test/lib/logging.spec.js new file mode 100644 index 0000000..a3fa40c --- /dev/null +++ b/server/test/lib/logging.spec.js @@ -0,0 +1,56 @@ +const expect = require('chai').expect; + +const loggingHelper = require('../../lib/logging_helper.js'); + +const testHelper = require('../_initializeSetup.js'); +testHelper.init(); + +const timestampNow = new Date().toUTCString(); + +describe('Logging', () => { + it('application log format', () => { + const info = { + message: 'dummyComponent - dummyTestMesage', + level: 'warn', + label: 'stjrona', + timestamp: timestampNow + }; + + const result = loggingHelper._buildStjornaLogFormat(info); + + result.should.be.a('string'); + expect(result).to.be.equal(`${timestampNow} [stjrona] [warn] [dummyComponent] dummyTestMesage`, 'application log'); + }); + + it('request log format', () => { + const info = { + message: 'POST /foo/bar/url?with=params', + level: 'warn', + label: 'stjorna-req', + timestamp: timestampNow + }; + + const result = loggingHelper._buildStjornaRequestLogFormat(info); + + result.should.be.a('string'); + expect(result).to.be.equal(`${timestampNow} [stjorna-req] POST /foo/bar/url?with=params`, 'request log'); + }); + + it('check slient logs', () => { + let result = loggingHelper._isSlientLogActivated('application'); + result.should.be.a('boolean'); + expect(result).to.be.equal(true, 'slient application log'); + + result = loggingHelper._isSlientLogActivated('request'); + result.should.be.a('boolean'); + expect(result).to.be.equal(true, 'slient request log'); + + result = loggingHelper._isSlientLogActivated('dummy'); + result.should.be.a('boolean'); + expect(result).to.be.equal(false, 'dummy param slient log'); + + result = loggingHelper._isSlientLogActivated(); + result.should.be.a('boolean'); + expect(result).to.be.equal(false, 'empty param slient log'); + }); +});