'use strict'; /** * Database Mock - wrapper for database.js, makes system use separate test db, instead of production * ATTENTION: testing db is flushed before every use! */ require('../../require-main'); const path = require('path'); const nconf = require('nconf'); const url = require('url'); const util = require('util'); process.env.NODE_ENV = process.env.TEST_ENV || 'production'; global.env = process.env.NODE_ENV || 'production'; const winston = require('winston'); const packageInfo = require('../../package.json'); winston.add(new winston.transports.Console({ format: winston.format.combine( winston.format.splat(), winston.format.simple() ), })); try { const fs = require('fs'); const configJSON = fs.readFileSync(path.join(__dirname, '../../config.json'), 'utf-8'); winston.info('configJSON'); winston.info(configJSON); } catch (err) { console.error(err.stack); throw err; } nconf.file({ file: path.join(__dirname, '../../config.json') }); nconf.defaults({ base_dir: path.join(__dirname, '../..'), themes_path: path.join(__dirname, '../../node_modules'), upload_path: 'test/uploads', views_dir: path.join(__dirname, '../../build/public/templates'), relative_path: '', }); const urlObject = url.parse(nconf.get('url')); const relativePath = urlObject.pathname !== '/' ? urlObject.pathname : ''; nconf.set('relative_path', relativePath); nconf.set('asset_base_url', `${relativePath}/assets`); nconf.set('upload_path', path.join(nconf.get('base_dir'), nconf.get('upload_path'))); nconf.set('upload_url', '/assets/uploads'); nconf.set('url_parsed', urlObject); nconf.set('base_url', `${urlObject.protocol}//${urlObject.host}`); nconf.set('secure', urlObject.protocol === 'https:'); nconf.set('use_port', !!urlObject.port); nconf.set('port', urlObject.port || nconf.get('port') || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567); // cookies don't provide isolation by port: http://stackoverflow.com/a/16328399/122353 const domain = nconf.get('cookieDomain') || urlObject.hostname; const origins = nconf.get('socket.io:origins') || `${urlObject.protocol}//${domain}:*`; nconf.set('socket.io:origins', origins); if (nconf.get('isCluster') === undefined) { nconf.set('isPrimary', true); nconf.set('isCluster', false); nconf.set('singleHostCluster', false); } const dbType = nconf.get('database'); const testDbConfig = nconf.get('test_database'); const productionDbConfig = nconf.get(dbType); if (!testDbConfig) { const errorText = 'test_database is not defined'; winston.info( '\n===========================================================\n' + 'Please, add parameters for test database in config.json\n' + 'For example (redis):\n' + '"test_database": {\n' + ' "host": "127.0.0.1",\n' + ' "port": "6379",\n' + ' "password": "",\n' + ' "database": "1"\n' + '}\n' + ' or (mongo):\n' + '"test_database": {\n' + ' "host": "127.0.0.1",\n' + ' "port": "27017",\n' + ' "password": "",\n' + ' "database": "1"\n' + '}\n' + ' or (mongo) in a replicaset\n' + '"test_database": {\n' + ' "host": "127.0.0.1,127.0.0.1,127.0.0.1",\n' + ' "port": "27017,27018,27019",\n' + ' "username": "",\n' + ' "password": "",\n' + ' "database": "nodebb_test"\n' + '}\n' + ' or (postgres):\n' + '"test_database": {\n' + ' "host": "127.0.0.1",\n' + ' "port": "5432",\n' + ' "username": "postgres",\n' + ' "password": "",\n' + ' "database": "nodebb_test"\n' + '}\n' + '===========================================================' ); winston.error(errorText); throw new Error(errorText); } if (testDbConfig.database === productionDbConfig.database && testDbConfig.host === productionDbConfig.host && testDbConfig.port === productionDbConfig.port) { const errorText = 'test_database has the same config as production db'; winston.error(errorText); throw new Error(errorText); } nconf.set(dbType, testDbConfig); winston.info('database config %s', dbType, testDbConfig); winston.info(`environment ${global.env}`); const db = require('../../src/database'); module.exports = db; before(async function () { this.timeout(30000); // Parse out the relative_url and other goodies from the configured URL const urlObject = url.parse(nconf.get('url')); nconf.set('core_templates_path', path.join(__dirname, '../../src/views')); nconf.set('base_templates_path', path.join(nconf.get('themes_path'), 'nodebb-theme-persona/templates')); nconf.set('theme_config', path.join(nconf.get('themes_path'), 'nodebb-theme-persona', 'theme.json')); nconf.set('bcrypt_rounds', 1); nconf.set('socket.io:origins', '*:*'); nconf.set('version', packageInfo.version); nconf.set('runJobs', false); nconf.set('jobsDisabled', false); await db.init(); if (db.hasOwnProperty('createIndices')) { await db.createIndices(); } await setupMockDefaults(); await db.initSessionStore(); const meta = require('../../src/meta'); nconf.set('theme_templates_path', meta.config['theme:templates'] ? path.join(nconf.get('themes_path'), meta.config['theme:id'], meta.config['theme:templates']) : nconf.get('base_templates_path')); // nconf defaults, if not set in config if (!nconf.get('sessionKey')) { nconf.set('sessionKey', 'express.sid'); } await meta.dependencies.check(); const webserver = require('../../src/webserver'); const sockets = require('../../src/socket.io'); await sockets.init(webserver.server); require('../../src/notifications').startJobs(); require('../../src/user').startJobs(); await webserver.listen(); // Iterate over all of the test suites/contexts this.test.parent.suites.forEach((suite) => { // Attach an afterAll listener that resets the defaults suite.afterAll(async () => { await setupMockDefaults(); }); }); }); async function setupMockDefaults() { const meta = require('../../src/meta'); await db.emptydb(); winston.info('test_database flushed'); await setupDefaultConfigs(meta); await meta.configs.init(); meta.config.postDelay = 0; meta.config.initialPostDelay = 0; meta.config.newbiePostDelay = 0; meta.config.autoDetectLang = 0; require('../../src/groups').cache.reset(); require('../../src/posts/cache').reset(); require('../../src/cache').reset(); require('../../src/middleware/uploads').clearCache(); // privileges must be given after cache reset await giveDefaultGlobalPrivileges(); await enableDefaultPlugins(); await meta.themes.set({ type: 'local', id: 'nodebb-theme-persona', }); const fs = require('fs'); await fs.promises.rm('test/uploads', { recursive: true, force: true }); const { mkdirp } = require('mkdirp'); const folders = [ 'test/uploads', 'test/uploads/category', 'test/uploads/files', 'test/uploads/system', 'test/uploads/profile', ]; for (const folder of folders) { /* eslint-disable no-await-in-loop */ await mkdirp(folder); } } db.setupMockDefaults = setupMockDefaults; async function setupDefaultConfigs(meta) { winston.info('Populating database with default configs, if not already set...\n'); const defaults = require(path.join(nconf.get('base_dir'), 'install/data/defaults.json')); defaults.eventLoopCheckEnabled = 0; defaults.minimumPasswordStrength = 0; await meta.configs.setOnEmpty(defaults); } async function giveDefaultGlobalPrivileges() { winston.info('Giving default global privileges...\n'); const privileges = require('../../src/privileges'); await privileges.global.give([ 'groups:chat', 'groups:upload:post:image', 'groups:signature', 'groups:search:content', 'groups:search:users', 'groups:search:tags', 'groups:local:login', 'groups:view:users', 'groups:view:tags', 'groups:view:groups', ], 'registered-users'); await privileges.global.give([ 'groups:view:users', 'groups:view:tags', 'groups:view:groups', ], 'guests'); } async function enableDefaultPlugins() { winston.info('Enabling default plugins\n'); const testPlugins = Array.isArray(nconf.get('test_plugins')) ? nconf.get('test_plugins') : []; const defaultEnabled = [ 'nodebb-plugin-dbsearch', 'nodebb-widget-essentials', 'nodebb-plugin-composer-default', ].concat(testPlugins); winston.info('[install/enableDefaultPlugins] activating default plugins', defaultEnabled); await db.sortedSetAdd('plugins:active', Object.keys(defaultEnabled), defaultEnabled); }