diff --git a/Gruntfile.js b/Gruntfile.js index 3175b10491..c406983066 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -11,7 +11,6 @@ var path = require('path'), _ = require('lodash'), buildDirectory = path.resolve(process.cwd(), '.build'), distDirectory = path.resolve(process.cwd(), '.dist'), - bootstrap = require('./core/bootstrap'), // ## Build File Patterns // A list of files and patterns to include when creating a release zip. @@ -160,7 +159,9 @@ var path = require('path'), // #### All Unit tests unit: { - src: ['core/test/unit/**/*_spec.js'] + src: [ + 'core/test/unit/**/*_spec.js' + ] }, // ##### Groups of unit tests @@ -208,7 +209,9 @@ var path = require('path'), // #### All Route tests routes: { - src: ['core/test/functional/routes/**/*_test.js'] + src: [ + 'core/test/functional/routes/**/*_test.js' + ] } }, @@ -537,12 +540,13 @@ var path = require('path'), cfg.express.test.options.node_env = process.env.NODE_ENV; }); - // #### Load Config *(Utility Task)* + // #### Ensure Config *(Utility Task)* // Make sure that we have a `config.js` file when running tests // Ghost requires a `config.js` file to specify the database settings etc. Ghost comes with an example file: // `config.example.js` which is copied and renamed to `config.js` by the bootstrap process - grunt.registerTask('loadConfig', function () { - var done = this.async(); + grunt.registerTask('ensureConfig', function () { + var bootstrap = require('./core/bootstrap'), + done = this.async(); bootstrap().then(function () { done(); }).catch(function (err) { @@ -607,7 +611,7 @@ var path = require('path'), // Unit tests do **not** touch the database. // A coverage report can be generated for these tests using the `grunt test-coverage` task. grunt.registerTask('test-unit', 'Run unit tests (mocha)', - ['clean:test', 'setTestEnv', 'loadConfig', 'mochacli:unit']); + ['clean:test', 'setTestEnv', 'ensureConfig', 'mochacli:unit']); // ### Integration tests *(sub task)* // `grunt test-integration` will run just the integration tests @@ -636,7 +640,7 @@ var path = require('path'), // // A coverage report can be generated for these tests using the `grunt test-coverage` task. grunt.registerTask('test-integration', 'Run integration tests (mocha + db access)', - ['clean:test', 'setTestEnv', 'loadConfig', 'mochacli:integration']); + ['clean:test', 'setTestEnv', 'ensureConfig', 'mochacli:integration']); // ### Route tests *(sub task)* // `grunt test-routes` will run just the route tests @@ -657,7 +661,7 @@ var path = require('path'), // are working as expected, including checking the headers and status codes received. It is very easy and // quick to test many permutations of routes / urls in the system. grunt.registerTask('test-routes', 'Run functional route tests (mocha)', - ['clean:test', 'setTestEnv', 'loadConfig', 'mochacli:routes']); + ['clean:test', 'setTestEnv', 'ensureConfig', 'mochacli:routes']); // ### Functional tests for the setup process // `grunt test-functional-setup will run just the functional tests for the setup page. @@ -665,7 +669,7 @@ var path = require('path'), // Setup only works with a brand new database, so it needs to run isolated from the rest of // the functional tests. grunt.registerTask('test-functional-setup', 'Run functional tests for setup', - ['clean:test', 'setTestEnv', 'loadConfig', 'cleanDatabase', 'express:test', + ['clean:test', 'setTestEnv', 'ensureConfig', 'cleanDatabase', 'express:test', 'spawnCasperJS:setup', 'express:test:stop'] ); @@ -688,7 +692,7 @@ var path = require('path'), // The purpose of the functional tests is to ensure that Ghost is working as is expected from a user perspective // including buttons and other important interactions in the admin UI. grunt.registerTask('test-functional', 'Run functional interface tests (CasperJS)', - ['clean:test', 'setTestEnv', 'loadConfig', 'cleanDatabase', 'express:test', 'spawnCasperJS', 'express:test:stop', + ['clean:test', 'setTestEnv', 'ensureConfig', 'cleanDatabase', 'express:test', 'spawnCasperJS', 'express:test:stop', 'test-functional-setup'] ); @@ -702,7 +706,7 @@ var path = require('path'), // // Key areas for coverage are: helpers and theme elements, apps / GDK, the api and model layers. grunt.registerTask('test-coverage', 'Generate unit and integration (mocha) tests coverage report', - ['clean:test', 'setTestEnv', 'loadConfig', 'shell:coverage']); + ['clean:test', 'setTestEnv', 'ensureConfig', 'shell:coverage']); // ## Building assets diff --git a/core/bootstrap.js b/core/bootstrap.js index 296818e0c6..8e0737f06d 100644 --- a/core/bootstrap.js +++ b/core/bootstrap.js @@ -11,8 +11,8 @@ var fs = require('fs'), errors = require('./server/errors'), config = require('./server/config'), - appRoot = config().paths.appRoot, - configExample = config().paths.configExample, + appRoot = config.paths.appRoot, + configExample = config.paths.configExample, configFile; function readConfigFile(envVal) { @@ -122,7 +122,7 @@ function loadConfig(configFilePath) { // Allow config file path to be taken from, in order of importance: // environment process, passed in value, default location - configFile = process.env.GHOST_CONFIG || configFilePath || config().paths.config; + configFile = process.env.GHOST_CONFIG || configFilePath || config.paths.config; /* Check for config file and copy from config.example.js if one doesn't exist. After that, start the server. */ diff --git a/core/server/api/authentication.js b/core/server/api/authentication.js index 2338454543..02cd5a12da 100644 --- a/core/server/api/authentication.js +++ b/core/server/api/authentication.js @@ -45,7 +45,7 @@ authentication = { var dbHash = response.settings[0].value; return dataProvider.User.generateResetToken(email, expires, dbHash); }).then(function (resetToken) { - var baseUrl = config().forceAdminSSL ? (config().urlSSL || config().url) : config().url, + var baseUrl = config.forceAdminSSL ? (config.urlSSL || config.url) : config.url, siteLink = '' + baseUrl + '', resetUrl = baseUrl.replace(/\/$/, '') + '/ghost/reset/' + resetToken + '/', resetLink = '' + resetUrl + '', @@ -216,7 +216,7 @@ authentication = { to: setupUser.email, subject: 'Your New Ghost Blog', html: '

Hello!

' + - '

Good news! You\'ve successfully created a brand new Ghost blog over on ' + config().url + '

' + + '

Good news! You\'ve successfully created a brand new Ghost blog over on ' + config.url + '

' + '

You can log in to your admin account with the following details:

' + '

Email Address: ' + setupUser.email + '
' + 'Password: The password you chose when you signed up

' + diff --git a/core/server/api/mail.js b/core/server/api/mail.js index f14f50255b..40b4d02413 100644 --- a/core/server/api/mail.js +++ b/core/server/api/mail.js @@ -56,7 +56,7 @@ mail = { sendTest: function (object, options) { var html = '

Hello there!

' + '

Excellent!' + - ' You\'ve successfully setup your email config for your Ghost blog over on ' + config().url + '

' + + ' You\'ve successfully setup your email config for your Ghost blog over on ' + config.url + '

' + '

If you hadn\'t, you wouldn\'t be reading this email, but you are, so it looks like all is well :)

' + '

xoxo

' + '

Team Ghost
' + diff --git a/core/server/api/settings.js b/core/server/api/settings.js index 9b6eb09f6b..732e4b0eed 100644 --- a/core/server/api/settings.js +++ b/core/server/api/settings.js @@ -135,11 +135,11 @@ readSettingsResult = function (settingsModels) { return memo; }, {}), - themes = config().paths.availableThemes, - apps = config().paths.availableApps, + themes = config.paths.availableThemes, + apps = config.paths.availableApps, res; - if (settings.activeTheme) { + if (settings.activeTheme && themes) { res = filterPaths(themes, settings.activeTheme.value); settings.availableThemes = { @@ -149,7 +149,7 @@ readSettingsResult = function (settingsModels) { }; } - if (settings.activeApps) { + if (settings.activeApps && apps) { res = filterPaths(apps, JSON.parse(settings.activeApps.value)); settings.availableApps = { @@ -201,7 +201,7 @@ populateDefaultSetting = function (key) { // Add to the settings cache return updateSettingsCache(readResult).then(function () { // Update theme with the new settings - return config.theme.update(settings, config().url); + return config.theme.update(settings, config.url); }).then(function () { // Get the result from the cache with permission checks return defaultSetting; @@ -379,7 +379,7 @@ settings = { var readResult = readSettingsResult(result); return updateSettingsCache(readResult).then(function () { - return config.theme.update(settings, config().url); + return config.theme.update(settings, config.url); }).then(function () { return settingsResult(readResult, type); }); diff --git a/core/server/api/themes.js b/core/server/api/themes.js index 5f7b472f8a..f007cab45e 100644 --- a/core/server/api/themes.js +++ b/core/server/api/themes.js @@ -27,7 +27,7 @@ themes = { return canThis(options.context).browse.theme().then(function () { return when.all([ settings.read({key: 'activeTheme', context: {internal: true}}), - config().paths.availableThemes + config.paths.availableThemes ]).then(function (result) { var activeTheme = result[0].settings[0].value, availableThemes = result[1], diff --git a/core/server/api/users.js b/core/server/api/users.js index 5d1a24a623..9489c6a83b 100644 --- a/core/server/api/users.js +++ b/core/server/api/users.js @@ -176,7 +176,7 @@ users = { dbHash = response.settings[0].value; return dataProvider.User.generateResetToken(user.email, expires, dbHash); }).then(function (resetToken) { - var baseUrl = config().forceAdminSSL ? (config().urlSSL || config().url) : config().url, + var baseUrl = config.forceAdminSSL ? (config.urlSSL || config.url) : config.url, siteLink = '' + baseUrl + '', resetUrl = baseUrl.replace(/\/$/, '') + '/ghost/signup/' + resetToken + '/', resetLink = '' + resetUrl + '', diff --git a/core/server/apps/loader.js b/core/server/apps/loader.js index d84c16cc63..60af307faa 100644 --- a/core/server/apps/loader.js +++ b/core/server/apps/loader.js @@ -11,7 +11,7 @@ var path = require('path'), // Get the full path to an app by name function getAppAbsolutePath(name) { - return path.join(config().paths.appPath, name); + return path.join(config.paths.appPath, name); } // Get a relative path to the given apps root, defaults diff --git a/core/server/config/index.js b/core/server/config/index.js index 960981cad0..a76c547260 100644 --- a/core/server/config/index.js +++ b/core/server/config/index.js @@ -14,6 +14,8 @@ var path = require('path'), ghostConfig = {}, appRoot = path.resolve(__dirname, '../../../'), corePath = path.resolve(appRoot, 'core/'), + testingEnvs = ['testing', 'testing-mysql', 'testing-pg'], + defaultConfig = {}, knexInstance; // Are we using sockets? Custom socket or the default? @@ -25,7 +27,7 @@ function getSocket() { } function updateConfig(config) { - var localPath, + var localPath = '', contentPath, subdir; @@ -53,7 +55,7 @@ function updateConfig(config) { // Otherwise default to default content path location contentPath = ghostConfig.paths.contentPath || path.resolve(appRoot, 'content'); - if (!knexInstance && ghostConfig.database) { + if (!knexInstance && ghostConfig.database && ghostConfig.database.client) { knexInstance = knex(ghostConfig.database); } @@ -80,8 +82,8 @@ function updateConfig(config) { 'lang': path.join(corePath, '/shared/lang/'), 'debugPath': subdir + '/ghost/debug/', - 'availableThemes': ghostConfig.paths.availableThemes || [], - 'availableApps': ghostConfig.paths.availableApps || [], + 'availableThemes': ghostConfig.paths.availableThemes || {}, + 'availableApps': ghostConfig.paths.availableApps || {}, 'builtScriptPath': path.join(corePath, 'built/scripts/') } }); @@ -90,8 +92,6 @@ function updateConfig(config) { // configUrl object to maintain // clean depedency tree configUrl.setConfig(ghostConfig); - - return ghostConfig; } function initConfig(rawConfig) { @@ -99,7 +99,7 @@ function initConfig(rawConfig) { // object so we can later refer to it. // Note: this is not the entirety of config.js, // just the object appropriate for this NODE_ENV - ghostConfig = updateConfig(rawConfig); + updateConfig(rawConfig); return when.all([requireTree(ghostConfig.paths.themePath), requireTree(ghostConfig.paths.appPath)]).then(function (paths) { ghostConfig.paths.availableThemes = paths[0]; @@ -108,23 +108,14 @@ function initConfig(rawConfig) { }); } -// Returns NODE_ENV config object -function config() { - // @TODO: get rid of require statement. - // This is currently needed for tests to load config file - // successfully. While running application we should never - // have to directly delegate to the config.js file. - if (_.isEmpty(ghostConfig)) { - try { - ghostConfig = require(path.resolve(__dirname, '../../../', 'config.js'))[process.env.NODE_ENV] || {}; - } catch (ignore) {/*jslint strict: true */} - ghostConfig = updateConfig(ghostConfig); - } - - return ghostConfig; +if (testingEnvs.indexOf(process.env.NODE_ENV) > -1) { + defaultConfig = require('../../../config.example')[process.env.NODE_ENV]; } -module.exports = config; +// Init config +updateConfig(defaultConfig); + +module.exports = ghostConfig; module.exports.init = initConfig; module.exports.theme = theme; module.exports.getSocket = getSocket; diff --git a/core/server/controllers/frontend.js b/core/server/controllers/frontend.js index 3b43deea2d..9c961dd905 100644 --- a/core/server/controllers/frontend.js +++ b/core/server/controllers/frontend.js @@ -91,14 +91,14 @@ frontendControllers = { // No negative pages, or page 1 if (isNaN(pageParam) || pageParam < 1 || (pageParam === 1 && req.route.path === '/page/:page/')) { - return res.redirect(config().paths.subdir + '/'); + return res.redirect(config.paths.subdir + '/'); } return getPostPage(options).then(function (page) { // If page is greater than number of pages we have, redirect to last page if (pageParam > page.meta.pagination.pages) { - return res.redirect(page.meta.pagination.pages === 1 ? config().paths.subdir + '/' : (config().paths.subdir + '/page/' + page.meta.pagination.pages + '/')); + return res.redirect(page.meta.pagination.pages === 1 ? config.paths.subdir + '/' : (config.paths.subdir + '/page/' + page.meta.pagination.pages + '/')); } setReqCtx(req, page.posts); @@ -119,7 +119,7 @@ frontendControllers = { // Get url for tag page function tagUrl(tag, page) { - var url = config().paths.subdir + '/tag/' + tag + '/'; + var url = config.paths.subdir + '/tag/' + tag + '/'; if (page && page > 1) { url += 'page/' + page + '/'; @@ -148,7 +148,7 @@ frontendControllers = { filters.doFilter('prePostsRender', page.posts).then(function (posts) { api.settings.read({key: 'activeTheme', context: {internal: true}}).then(function (response) { var activeTheme = response.settings[0], - paths = config().paths.availableThemes[activeTheme.value], + paths = config.paths.availableThemes[activeTheme.value], view = paths.hasOwnProperty('tag.hbs') ? 'tag' : 'index', // Format data for template @@ -277,7 +277,7 @@ frontendControllers = { function render() { // If we're ready to render the page but the last param is 'edit' then we'll send you to the edit page. if (params.edit === 'edit') { - return res.redirect(config().paths.subdir + '/ghost/editor/' + post.id + '/'); + return res.redirect(config.paths.subdir + '/ghost/editor/' + post.id + '/'); } else if (params.edit !== undefined) { // reject with type: 'NotFound' return when.reject(new errors.NotFoundError()); @@ -288,7 +288,7 @@ frontendControllers = { filters.doFilter('prePostsRender', post).then(function (post) { api.settings.read({key: 'activeTheme', context: {internal: true}}).then(function (response) { var activeTheme = response.settings[0], - paths = config().paths.availableThemes[activeTheme.value], + paths = config.paths.availableThemes[activeTheme.value], view = template.getThemeViewForPost(paths, post); res.render(view, {post: post}); @@ -358,9 +358,9 @@ frontendControllers = { if (isNaN(pageParam) || pageParam < 1 || (pageParam === 1 && (req.route.path === '/rss/:page/' || req.route.path === '/tag/:slug/rss/:page/'))) { if (tagParam !== undefined) { - return res.redirect(config().paths.subdir + '/tag/' + tagParam + '/rss/'); + return res.redirect(config.paths.subdir + '/tag/' + tagParam + '/rss/'); } else { - return res.redirect(config().paths.subdir + '/rss/'); + return res.redirect(config.paths.subdir + '/rss/'); } } @@ -410,9 +410,9 @@ frontendControllers = { // If page is greater than number of pages we have, redirect to last page if (pageParam > maxPage) { if (tagParam) { - return res.redirect(config().paths.subdir + '/tag/' + tagParam + '/rss/' + maxPage + '/'); + return res.redirect(config.paths.subdir + '/tag/' + tagParam + '/rss/' + maxPage + '/'); } else { - return res.redirect(config().paths.subdir + '/rss/' + maxPage + '/'); + return res.redirect(config.paths.subdir + '/rss/' + maxPage + '/'); } } diff --git a/core/server/data/export/index.js b/core/server/data/export/index.js index e893755f64..a3b77e1318 100644 --- a/core/server/data/export/index.js +++ b/core/server/data/export/index.js @@ -33,7 +33,7 @@ exporter = function () { tables = results[1], selectOps = _.map(tables, function (name) { if (excludedTables.indexOf(name) < 0) { - return config().database.knex(name).select(); + return config.database.knex(name).select(); } }); diff --git a/core/server/data/migration/index.js b/core/server/data/migration/index.js index bfdbc2f788..470e245371 100644 --- a/core/server/data/migration/index.js +++ b/core/server/data/migration/index.js @@ -6,7 +6,7 @@ var _ = require('lodash'), errors = require('../../errors'), sequence = require('when/sequence'), - commands = require('./commands'), + commands = require('./commands'), versioning = require('../versioning'), models = require('../../models'), fixtures = require('../fixtures'), @@ -44,7 +44,7 @@ backupDatabase = function backupDatabase() { logInfo('Creating database backup'); return dataExport().then(function (exportedData) { // Save the exported data to the file system for download - var fileName = path.resolve(config().paths.contentPath + '/data/' + dataExport.fileName()); + var fileName = path.resolve(config.paths.contentPath + '/data/' + dataExport.fileName()); return nodefn.call(fs.writeFile, fileName, JSON.stringify(exportedData)).then(function () { logInfo('Database backup written to: ' + fileName); diff --git a/core/server/data/utils/clients/mysql.js b/core/server/data/utils/clients/mysql.js index 94b5453845..c882b0c2d7 100644 --- a/core/server/data/utils/clients/mysql.js +++ b/core/server/data/utils/clients/mysql.js @@ -12,7 +12,7 @@ var _ = require('lodash'), checkPostTable; doRawAndFlatten = function doRaw(query, flattenFn) { - return config().database.knex.raw(query).then(function (response) { + return config.database.knex.raw(query).then(function (response) { return _.flatten(flattenFn(response)); }); }; @@ -40,10 +40,10 @@ getColumns = function getColumns(table) { // data type text instead of mediumtext. // For details see: https://github.com/TryGhost/Ghost/issues/1947 checkPostTable = function checkPostTable() { - return config().database.knex.raw('SHOW FIELDS FROM posts where Field ="html" OR Field = "markdown"').then(function (response) { + return config.database.knex.raw('SHOW FIELDS FROM posts where Field ="html" OR Field = "markdown"').then(function (response) { return _.flatten(_.map(response[0], function (entry) { if (entry.Type.toLowerCase() !== 'mediumtext') { - return config().database.knex.raw('ALTER TABLE posts MODIFY ' + entry.Field + ' MEDIUMTEXT').then(function () { + return config.database.knex.raw('ALTER TABLE posts MODIFY ' + entry.Field + ' MEDIUMTEXT').then(function () { return when.resolve(); }); } diff --git a/core/server/data/utils/clients/pgsql.js b/core/server/data/utils/clients/pgsql.js index 1639a25e2a..bbc87363bd 100644 --- a/core/server/data/utils/clients/pgsql.js +++ b/core/server/data/utils/clients/pgsql.js @@ -11,7 +11,7 @@ var _ = require('lodash'), doRawFlattenAndPluck = function doRaw(query, name) { - return config().database.knex.raw(query).then(function (response) { + return config.database.knex.raw(query).then(function (response) { return _.flatten(_.pluck(response.rows, name)); }); }; diff --git a/core/server/data/utils/clients/sqlite3.js b/core/server/data/utils/clients/sqlite3.js index d0ada875e0..b0a12ff773 100644 --- a/core/server/data/utils/clients/sqlite3.js +++ b/core/server/data/utils/clients/sqlite3.js @@ -11,7 +11,7 @@ var _ = require('lodash'), doRaw = function doRaw(query, fn) { - return config().database.knex.raw(query).then(function (response) { + return config.database.knex.raw(query).then(function (response) { return fn(response); }); }; diff --git a/core/server/data/utils/index.js b/core/server/data/utils/index.js index af09783684..64c2a0d99f 100644 --- a/core/server/data/utils/index.js +++ b/core/server/data/utils/index.js @@ -44,28 +44,28 @@ function addTableColumn(tablename, table, columnname) { } function addColumn(table, column) { - dbConfig = dbConfig || config().database; + dbConfig = dbConfig || config.database; return dbConfig.knex.schema.table(table, function (t) { addTableColumn(table, t, column); }); } function addUnique(table, column) { - dbConfig = dbConfig || config().database; + dbConfig = dbConfig || config.database; return dbConfig.knex.schema.table(table, function (table) { table.unique(column); }); } function dropUnique(table, column) { - dbConfig = dbConfig || config().database; + dbConfig = dbConfig || config.database; return dbConfig.knex.schema.table(table, function (table) { table.dropUnique(column); }); } function createTable(table) { - dbConfig = dbConfig || config().database; + dbConfig = dbConfig || config.database; return dbConfig.knex.schema.createTable(table, function (t) { var columnKeys = _.keys(schema[table]); _.each(columnKeys, function (column) { @@ -75,12 +75,12 @@ function createTable(table) { } function deleteTable(table) { - dbConfig = dbConfig || config().database; + dbConfig = dbConfig || config.database; return dbConfig.knex.schema.dropTableIfExists(table); } function getTables() { - dbConfig = dbConfig || config().database; + dbConfig = dbConfig || config.database; var client = dbConfig.client; if (_.contains(_.keys(clients), client)) { @@ -91,7 +91,7 @@ function getTables() { } function getIndexes(table) { - dbConfig = dbConfig || config().database; + dbConfig = dbConfig || config.database; var client = dbConfig.client; if (_.contains(_.keys(clients), client)) { @@ -102,7 +102,7 @@ function getIndexes(table) { } function getColumns(table) { - dbConfig = dbConfig || config().database; + dbConfig = dbConfig || config.database; var client = dbConfig.client; if (_.contains(_.keys(clients), client)) { @@ -113,7 +113,7 @@ function getColumns(table) { } function checkTables() { - dbConfig = dbConfig || config().database; + dbConfig = dbConfig || config.database; var client = dbConfig.client; if (client === 'mysql') { @@ -131,4 +131,4 @@ module.exports = { dropUnique: dropUnique, addColumn: addColumn, getColumns: getColumns -}; +}; \ No newline at end of file diff --git a/core/server/data/validation/index.js b/core/server/data/validation/index.js index 85f57f2d4c..b733500e77 100644 --- a/core/server/data/validation/index.js +++ b/core/server/data/validation/index.js @@ -101,13 +101,13 @@ validateSettings = function (defaultSettings, model) { // A Promise that will resolve to an object with a property for each installed theme. // This is necessary because certain configuration data is only available while Ghost // is running and at times the validations are used when it's not (e.g. tests) -availableThemes = requireTree(config().paths.themePath); +availableThemes = requireTree(config.paths.themePath); validateActiveTheme = function (themeName) { // If Ghost is running and its availableThemes collection exists // give it priority. - if (Object.keys(config().paths.availableThemes).length > 0) { - availableThemes = when(config().paths.availableThemes); + if (config.paths.availableThemes && Object.keys(config.paths.availableThemes).length > 0) { + availableThemes = when(config.paths.availableThemes); } return availableThemes.then(function (themes) { diff --git a/core/server/data/versioning/index.js b/core/server/data/versioning/index.js index 0ad0d28e43..eb4a88420b 100644 --- a/core/server/data/versioning/index.js +++ b/core/server/data/versioning/index.js @@ -23,7 +23,7 @@ function getDefaultDatabaseVersion() { // The migration version number according to the database // This is what the database is currently at and may need to be updated function getDatabaseVersion() { - var knex = config().database.knex; + var knex = config.database.knex; return knex.schema.hasTable('settings').then(function (exists) { // Check for the current version from the settings table @@ -54,7 +54,7 @@ function getDatabaseVersion() { } function setDatabaseVersion() { - return config().database.knex('settings') + return config.database.knex('settings') .where('key', 'databaseVersion') .update({ 'value': defaultDatabaseVersion }); } diff --git a/core/server/errors/index.js b/core/server/errors/index.js index 1e3d68d1a0..9ed4e8789b 100644 --- a/core/server/errors/index.js +++ b/core/server/errors/index.js @@ -17,7 +17,7 @@ var _ = require('lodash'), errors, // Paths for views - defaultErrorTemplatePath = path.resolve(config().paths.adminViews, 'user-error.hbs'), + defaultErrorTemplatePath = path.resolve(config.paths.adminViews, 'user-error.hbs'), userErrorTemplateExists = false; // This is not useful but required for jshint @@ -28,7 +28,7 @@ colors.setTheme({silly: 'rainbow'}); */ errors = { updateActiveTheme: function (activeTheme) { - userErrorTemplateExists = config().paths.availableThemes[activeTheme].hasOwnProperty('error.hbs'); + userErrorTemplateExists = config.paths.availableThemes[activeTheme].hasOwnProperty('error.hbs'); }, throwError: function (err) { diff --git a/core/server/helpers/index.js b/core/server/helpers/index.js index 4f82709cd0..ea5308d622 100644 --- a/core/server/helpers/index.js +++ b/core/server/helpers/index.js @@ -100,7 +100,7 @@ coreHelpers.encode = function (context, str) { // coreHelpers.page_url = function (context, block) { /*jshint unused:false*/ - var url = config().paths.subdir; + var url = config.paths.subdir; if (this.tagSlug !== undefined) { url += '/tag/' + this.tagSlug; @@ -173,7 +173,7 @@ coreHelpers.asset = function (context, options) { var output = '', isAdmin = options && options.hash && options.hash.ghost; - output += config().paths.subdir + '/'; + output += config.paths.subdir + '/'; if (!context.match(/^favicon\.ico$/) && !context.match(/^shared/) && !context.match(/^asset/)) { if (isAdmin) { @@ -349,8 +349,8 @@ coreHelpers.excerpt = function (options) { // Returns the config value for fileStorage. coreHelpers.file_storage = function (context, options) { /*jshint unused:false*/ - if (config().hasOwnProperty('fileStorage')) { - return config().fileStorage.toString(); + if (config.hasOwnProperty('fileStorage')) { + return config.fileStorage.toString(); } return 'true'; }; @@ -363,8 +363,8 @@ coreHelpers.file_storage = function (context, options) { // Returns the config value for apps. coreHelpers.apps = function (context, options) { /*jshint unused:false*/ - if (config().hasOwnProperty('apps')) { - return config().apps.toString(); + if (config.hasOwnProperty('apps')) { + return config.apps.toString(); } return 'false'; }; @@ -374,7 +374,7 @@ coreHelpers.ghost_script_tags = function () { scriptList = _.map(scriptList, function (fileName) { return scriptTemplate({ - source: config().paths.subdir + '/ghost/scripts/' + fileName, + source: config.paths.subdir + '/ghost/scripts/' + fileName, version: coreHelpers.assetHash }); }); @@ -416,7 +416,7 @@ coreHelpers.body_class = function (options) { return api.settings.read({context: {internal: true}, key: 'activeTheme'}).then(function (response) { var activeTheme = response.settings[0], - paths = config().paths.availableThemes[activeTheme.value], + paths = config.paths.availableThemes[activeTheme.value], view; if (post) { @@ -494,7 +494,7 @@ coreHelpers.ghost_foot = function (options) { foot = []; foot.push(scriptTemplate({ - source: config().paths.subdir + '/public/' + jquery, + source: config.paths.subdir + '/public/' + jquery, version: coreHelpers.assetHash })); diff --git a/core/server/index.js b/core/server/index.js index d7c898e917..f19d1de70c 100644 --- a/core/server/index.js +++ b/core/server/index.js @@ -41,7 +41,7 @@ function doFirstRun() { 'environment.', 'Your URL is set to', - '' + config().url + '.', + '' + config.url + '.', 'See http://docs.ghost.org for instructions.' ]; @@ -76,7 +76,7 @@ function initDbHashAndFirstRun() { // any are missing. function builtFilesExist() { var deferreds = [], - location = config().paths.builtScriptPath, + location = config.paths.builtScriptPath, fileNames = helpers.scriptFiles.ember; @@ -127,7 +127,7 @@ function ghostStartMessages() { console.log( "Ghost is running...".green, "\nYour blog is now available on", - config().url, + config.url, "\nCtrl+C to shut down".grey ); @@ -143,9 +143,9 @@ function ghostStartMessages() { console.log( ("Ghost is running in " + process.env.NODE_ENV + "...").green, "\nListening on", - config.getSocket() || config().server.host + ':' + config().server.port, + config.getSocket() || config.server.host + ':' + config.server.port, "\nUrl configured as:", - config().url, + config.url, "\nCtrl+C to shut down".grey ); // ensure that Ghost exits correctly on Ctrl+C @@ -231,7 +231,7 @@ function init(server) { }).then(function () { // We must pass the api.settings object // into this method due to circular dependencies. - return config.theme.update(api.settings, config().url); + return config.theme.update(api.settings, config.url); }).then(function () { return when.join( // Check for or initialise a dbHash. @@ -253,7 +253,7 @@ function init(server) { express['static'].mime.define({'application/font-woff': ['woff']}); // enabled gzip compression by default - if (config().server.compress !== false) { + if (config.server.compress !== false) { server.use(compress()); } @@ -271,11 +271,11 @@ function init(server) { middleware(server, dbHash); // Log all theme errors and warnings - _.each(config().paths.availableThemes._messages.errors, function (error) { + _.each(config.paths.availableThemes._messages.errors, function (error) { errors.logError(error.message, error.context, error.help); }); - _.each(config().paths.availableThemes._messages.warns, function (warn) { + _.each(config.paths.availableThemes._messages.warns, function (warn) { errors.logWarn(warn.message, warn.context, warn.help); }); @@ -295,8 +295,8 @@ function init(server) { } else { httpServer = server.listen( - config().server.port, - config().server.host + config.server.port, + config.server.host ); } diff --git a/core/server/mail.js b/core/server/mail.js index ff66a69b55..0c76217ef5 100644 --- a/core/server/mail.js +++ b/core/server/mail.js @@ -15,7 +15,7 @@ function GhostMailer(opts) { GhostMailer.prototype.init = function () { var self = this; self.state = {}; - if (config().mail && config().mail.transport) { + if (config.mail && config.mail.transport) { this.createTransport(); return when.resolve(); } @@ -53,17 +53,17 @@ GhostMailer.prototype.detectSendmail = function () { }; GhostMailer.prototype.createTransport = function () { - this.transport = nodemailer.createTransport(config().mail.transport, _.clone(config().mail.options) || {}); + this.transport = nodemailer.createTransport(config.mail.transport, _.clone(config.mail.options) || {}); }; GhostMailer.prototype.fromAddress = function () { - var from = config().mail && config().mail.fromaddress, + var from = config.mail && config.mail.fromaddress, domain; if (!from) { // Extract the domain name from url set in config.js - domain = config().url.match(new RegExp("^https?://([^/:?#]+)(?:[/:?#]|$)", "i")); + domain = config.url.match(new RegExp("^https?://([^/:?#]+)(?:[/:?#]|$)", "i")); domain = domain && domain[1]; // Default to ghost@[blog.url] diff --git a/core/server/middleware/index.js b/core/server/middleware/index.js index d7401dd0d2..ee4862347d 100644 --- a/core/server/middleware/index.js +++ b/core/server/middleware/index.js @@ -42,17 +42,17 @@ function ghostLocals(req, res, next) { res.locals = res.locals || {}; res.locals.version = packageInfo.version; // relative path from the URL, not including subdir - res.locals.relativeUrl = req.path.replace(config().paths.subdir, ''); + res.locals.relativeUrl = req.path.replace(config.paths.subdir, ''); next(); } function initThemeData(secure) { var themeConfig = config.theme(); - if (secure && config().urlSSL) { + if (secure && config.urlSSL) { // For secure requests override .url property with the SSL version themeConfig = _.clone(themeConfig); - themeConfig.url = config().urlSSL.replace(/\/$/, ''); + themeConfig.url = config.urlSSL.replace(/\/$/, ''); } return themeConfig; } @@ -61,13 +61,13 @@ function initThemeData(secure) { // Helper for manageAdminAndTheme function activateTheme(activeTheme) { var hbsOptions, - themePartials = path.join(config().paths.themePath, activeTheme, 'partials'); + themePartials = path.join(config.paths.themePath, activeTheme, 'partials'); // clear the view cache expressServer.cache = {}; // set view engine - hbsOptions = { partialsDir: [ config().paths.helperTemplates ] }; + hbsOptions = { partialsDir: [ config.paths.helperTemplates ] }; fs.stat(themePartials, function (err, stats) { // Check that the theme has a partials directory before trying to use it @@ -89,18 +89,18 @@ function activateTheme(activeTheme) { // Uses the URL to detect whether this response should be an admin response // This is used to ensure the right content is served, and is not for security purposes function decideContext(req, res, next) { - res.isAdmin = req.url.lastIndexOf(config().paths.subdir + '/ghost/', 0) === 0; + res.isAdmin = req.url.lastIndexOf(config.paths.subdir + '/ghost/', 0) === 0; if (res.isAdmin) { expressServer.enable('admin'); expressServer.engine('hbs', expressServer.get('admin view engine')); - expressServer.set('views', config().paths.adminViews); + expressServer.set('views', config.paths.adminViews); } else { expressServer.disable('admin'); var themeData = initThemeData(req.secure); hbs.updateTemplateOptions({ data: {blog: themeData} }); expressServer.engine('hbs', expressServer.get('theme view engine')); - expressServer.set('views', path.join(config().paths.themePath, expressServer.get('activeTheme'))); + expressServer.set('views', path.join(config.paths.themePath, expressServer.get('activeTheme'))); } // Pass 'secure' flag to the view engine @@ -120,7 +120,7 @@ function updateActiveTheme(req, res, next) { // Check if the theme changed if (activeTheme.value !== expressServer.get('activeTheme')) { // Change theme - if (!config().paths.availableThemes.hasOwnProperty(activeTheme.value)) { + if (!config.paths.availableThemes.hasOwnProperty(activeTheme.value)) { if (!res.isAdmin) { // Throw an error if the theme is not available, but not on the admin UI return errors.throwError('The currently active theme ' + activeTheme.value + ' is missing.'); @@ -144,7 +144,7 @@ function redirectToSetup(req, res, next) { api.authentication.isSetup().then(function (exists) { if (!exists.setup[0].status && !req.path.match(/\/ghost\/setup\//)) { - return res.redirect(config().paths.subdir + '/ghost/setup/'); + return res.redirect(config.paths.subdir + '/ghost/setup/'); } next(); }).otherwise(function (err) { @@ -153,8 +153,8 @@ function redirectToSetup(req, res, next) { } function isSSLrequired(isAdmin) { - var forceSSL = url.parse(config().url).protocol === 'https:' ? true : false, - forceAdminSSL = (isAdmin && config().forceAdminSSL); + var forceSSL = url.parse(config.url).protocol === 'https:' ? true : false, + forceAdminSSL = (isAdmin && config.forceAdminSSL); if (forceSSL || forceAdminSSL) { return true; } @@ -166,7 +166,7 @@ function isSSLrequired(isAdmin) { function checkSSL(req, res, next) { if (isSSLrequired(res.isAdmin)) { if (!req.secure) { - var forceAdminSSL = config().forceAdminSSL, + var forceAdminSSL = config.forceAdminSSL, redirectUrl; // Check if forceAdminSSL: { redirect: false } is set, which means @@ -175,7 +175,7 @@ function checkSSL(req, res, next) { return res.send(403); } - redirectUrl = url.parse(config().urlSSL || config().url); + redirectUrl = url.parse(config.urlSSL || config.url); return res.redirect(301, url.format({ protocol: 'https:', hostname: redirectUrl.hostname, @@ -192,7 +192,7 @@ function checkSSL(req, res, next) { // Handle requests to robots.txt and cache file function robots() { var content, // file cache - filePath = path.join(config().paths.corePath, '/shared/robots.txt'); + filePath = path.join(config.paths.corePath, '/shared/robots.txt'); return function robots(req, res, next) { if ('/robots.txt' === req.url) { @@ -224,9 +224,9 @@ function robots() { } setupMiddleware = function (server) { - var logging = config().logging, - subdir = config().paths.subdir, - corePath = config().paths.corePath, + var logging = config.logging, + subdir = config.paths.subdir, + corePath = config.paths.corePath, oauthServer = oauth2orize.createServer(); // silence JSHint without disabling unused check for the whole file diff --git a/core/server/middleware/middleware.js b/core/server/middleware/middleware.js index 403f4a029a..ee8dcec14c 100644 --- a/core/server/middleware/middleware.js +++ b/core/server/middleware/middleware.js @@ -43,7 +43,7 @@ var middleware = { // SubPath is the url path starting after any default subdirectories // it is stripped of anything after the two levels `/ghost/.*?/` as the reset link has an argument - path = req.path.substring(config().paths.subdir.length); + path = req.path.substring(config.paths.subdir.length); /*jslint regexp:true, unparam:true*/ subPath = path.replace(/^(\/.*?\/.*?\/)(.*)?/, function (match, a) { return a; @@ -130,7 +130,7 @@ var middleware = { api.settings.read({context: {internal: true}, key: 'activeTheme'}).then(function (response) { var activeTheme = response.settings[0]; - express['static'](path.join(config().paths.themePath, activeTheme.value), {maxAge: ONE_YEAR_MS})(req, res, next); + express['static'](path.join(config.paths.themePath, activeTheme.value), {maxAge: ONE_YEAR_MS})(req, res, next); }); }, diff --git a/core/server/models/base.js b/core/server/models/base.js index 3b14e9aa5c..c96c85f62e 100644 --- a/core/server/models/base.js +++ b/core/server/models/base.js @@ -21,7 +21,7 @@ var bookshelf = require('bookshelf'), // ### ghostBookshelf // Initializes a new Bookshelf instance called ghostBookshelf, for reference elsewhere in Ghost. -ghostBookshelf = bookshelf(config().database.knex); +ghostBookshelf = bookshelf(config.database.knex); // Load the registry plugin, which helps us avoid circular dependencies ghostBookshelf.plugin('registry'); diff --git a/core/server/routes/admin.js b/core/server/routes/admin.js index ad77146308..e0fff8f58a 100644 --- a/core/server/routes/admin.js +++ b/core/server/routes/admin.js @@ -9,7 +9,7 @@ var admin = require('../controllers/admin'), adminRoutes = function (middleware) { var router = express.Router(), - subdir = config().paths.subdir; + subdir = config.paths.subdir; // ### Admin routes router.get(/^\/(logout|signout)\/$/, function redirect(req, res) { diff --git a/core/server/routes/frontend.js b/core/server/routes/frontend.js index fe5612f464..0a86f9dfb4 100644 --- a/core/server/routes/frontend.js +++ b/core/server/routes/frontend.js @@ -9,7 +9,7 @@ var frontend = require('../controllers/frontend'), frontendRoutes = function () { var router = express.Router(), - subdir = config().paths.subdir; + subdir = config.paths.subdir; // ### Frontend routes router.get('/rss/', frontend.rss); diff --git a/core/server/storage/localfilesystem.js b/core/server/storage/localfilesystem.js index b2877de436..e227a645ff 100644 --- a/core/server/storage/localfilesystem.js +++ b/core/server/storage/localfilesystem.js @@ -20,7 +20,7 @@ localFileStore = _.extend(baseStore, { // - returns a promise which ultimately returns the full url to the uploaded image 'save': function (image) { var saved = when.defer(), - targetDir = this.getTargetDir(config().paths.imagesPath), + targetDir = this.getTargetDir(config.paths.imagesPath), targetFilename; this.getUniqueFileName(this, image, targetDir).then(function (filename) { @@ -31,7 +31,7 @@ localFileStore = _.extend(baseStore, { }).then(function () { // The src for the image must be in URI format, not a file system path, which in Windows uses \ // For local file system storage can use relative path so add a slash - var fullUrl = (config().paths.subdir + '/' + config().paths.imagesRelPath + '/' + path.relative(config().paths.imagesPath, targetFilename)).replace(new RegExp('\\' + path.sep, 'g'), '/'); + var fullUrl = (config.paths.subdir + '/' + config.paths.imagesRelPath + '/' + path.relative(config.paths.imagesPath, targetFilename)).replace(new RegExp('\\' + path.sep, 'g'), '/'); return saved.resolve(fullUrl); }).otherwise(function (e) { errors.logError(e); @@ -58,7 +58,7 @@ localFileStore = _.extend(baseStore, { ONE_YEAR_MS = 365 * 24 * ONE_HOUR_MS; // For some reason send divides the max age number by 1000 - return express['static'](config().paths.imagesPath, {maxAge: ONE_YEAR_MS}); + return express['static'](config.paths.imagesPath, {maxAge: ONE_YEAR_MS}); } }); diff --git a/core/server/update-check.js b/core/server/update-check.js index b64ee15af8..9cc6a29235 100644 --- a/core/server/update-check.js +++ b/core/server/update-check.js @@ -49,7 +49,7 @@ function updateCheckError(error) { function updateCheckData() { var data = {}, ops = [], - mailConfig = config().mail; + mailConfig = config.mail; ops.push(api.settings.read(_.extend(internal, {key: 'dbHash'})).otherwise(errors.rejectError)); ops.push(api.settings.read(_.extend(internal, {key: 'activeTheme'})).otherwise(errors.rejectError)); @@ -71,7 +71,7 @@ function updateCheckData() { data.ghost_version = currentVersion; data.node_version = process.versions.node; data.env = process.env.NODE_ENV; - data.database_type = config().database.client; + data.database_type = config.database.client; data.email_transport = mailConfig && (mailConfig.options && mailConfig.options.service ? mailConfig.options.service : mailConfig.transport); return when.settle(ops).then(function (descriptors) { @@ -81,7 +81,7 @@ function updateCheckData() { posts = descriptors[3].value, users = descriptors[4].value, npm = descriptors[5].value, - blogUrl = url.parse(config().url), + blogUrl = url.parse(config.url), blogId = blogUrl.hostname + blogUrl.pathname.replace(/\//, '') + hash.value; data.blog_id = crypto.createHash('md5').update(blogId).digest('hex'); @@ -175,7 +175,7 @@ function updateCheck() { // 1. updateCheck is defined as false in config.js // 2. we've already done a check this session // 3. we're not in production or development mode - if (config().updateCheck === false || _.indexOf(allowedCheckEnvironments, process.env.NODE_ENV) === -1) { + if (config.updateCheck === false || _.indexOf(allowedCheckEnvironments, process.env.NODE_ENV) === -1) { // No update check deferred.resolve(); } else { diff --git a/core/shared/lang/i18n.js b/core/shared/lang/i18n.js index 394a82b85d..4f3617d6ad 100644 --- a/core/shared/lang/i18n.js +++ b/core/shared/lang/i18n.js @@ -10,7 +10,7 @@ I18n = function (ghost) { // TODO: validate var lang = ghost.settings('defaultLang'), - path = config().paths.lang, + path = config.paths.lang, langFilePath = path + lang + '.json'; return function (req, res, next) { diff --git a/core/test/integration/api/api_themes_spec.js b/core/test/integration/api/api_themes_spec.js index 15e86404cc..b148ef9b3c 100644 --- a/core/test/integration/api/api_themes_spec.js +++ b/core/test/integration/api/api_themes_spec.js @@ -14,6 +14,7 @@ var _ = require('lodash'), describe('Themes API', function () { var configStub, + config, sandbox, settingsReadStub; @@ -42,7 +43,7 @@ describe('Themes API', function () { return when({ settings: [{value: 'casper'}] }); }); - configStub = sandbox.stub().returns({ + configStub = { 'paths': { 'subdir': '', 'availableThemes': { @@ -54,7 +55,10 @@ describe('Themes API', function () { } } } - }); + }; + + config = ThemeAPI.__get__('config'); + _.extend(config, configStub); done(); }).catch(done); @@ -63,12 +67,6 @@ describe('Themes API', function () { should.exist(ThemeAPI); it('can browse', function (done) { - var config; - - config = ThemeAPI.__get__('config'); - _.extend(configStub, config); - ThemeAPI.__set__('config', configStub); - ThemeAPI.browse({context: {user: 1}}).then(function (result) { should.exist(result); result.themes.length.should.be.above(0); @@ -80,12 +78,6 @@ describe('Themes API', function () { }); it('can edit', function (done) { - var config; - - config = ThemeAPI.__get__('config'); - _.extend(configStub, config); - ThemeAPI.__set__('config', configStub); - ThemeAPI.edit({themes: [{uuid: 'casper', active: true }]}, {context: {user: 1}}).then(function (result) { should.exist(result); should.exist(result.themes); diff --git a/core/test/integration/model/model_settings_spec.js b/core/test/integration/model/model_settings_spec.js index 30a98aeaeb..c58fcf6277 100644 --- a/core/test/integration/model/model_settings_spec.js +++ b/core/test/integration/model/model_settings_spec.js @@ -164,7 +164,7 @@ describe('Settings Model', function () { describe('populating defaults from settings.json', function () { beforeEach(function (done) { - config().database.knex('settings').truncate().then(function () { + config.database.knex('settings').truncate().then(function () { done(); }); }); diff --git a/core/test/integration/update_check_spec.js b/core/test/integration/update_check_spec.js index 43eae81fa9..14c40b971d 100644 --- a/core/test/integration/update_check_spec.js +++ b/core/test/integration/update_check_spec.js @@ -1,11 +1,14 @@ -var should = require('should'); -var when = require('when'); -var rewire = require('rewire'); -var packageInfo = require('../../../package'); -var ghost = require('../../../core'); -var permissions = require('../../server/permissions'); -var testUtils = require('../utils'); -var updateCheck = rewire('../../server/update-check'); +/*globals describe, before, beforeEach, afterEach, after, it */ +var should = require('should'), + rewire = require('rewire'), + _ = require('lodash'), + packageInfo = require('../../../package'), + ghost = require('../../../core'), + config = rewire('../../../core/server/config'), + defaultConfig = require('../../../config.example')[process.env.NODE_ENV], + permissions = require('../../server/permissions'), + testUtils = require('../utils'), + updateCheck = rewire('../../server/update-check'); describe('Update Check', function () { var environmentsOrig; @@ -14,7 +17,9 @@ describe('Update Check', function () { environmentsOrig = updateCheck.__get__('allowedCheckEnvironments'); updateCheck.__set__('allowedCheckEnvironments', ['development', 'production', 'testing']); - ghost().then(function () { + _.extend(config, defaultConfig); + + ghost({config: config.paths.config}).then(function () { return testUtils.clearData(); }).then(function () { done(); diff --git a/core/test/unit/bootstrap_spec.js b/core/test/unit/bootstrap_spec.js index fa241cdb94..97ad68b620 100644 --- a/core/test/unit/bootstrap_spec.js +++ b/core/test/unit/bootstrap_spec.js @@ -35,7 +35,7 @@ describe('Bootstrap', function () { // the test infrastructure is setup so that there is always config present, // but we want to overwrite the test to actually load config.example.js, so that any local changes // don't break the tests - bootstrap.__set__('configFile', path.join(config().paths.appRoot, 'config.example.js')); + bootstrap.__set__('configFile', path.join(config.paths.appRoot, 'config.example.js')); bootstrap().then(function (config) { config.url.should.equal(defaultConfig.url); @@ -49,7 +49,7 @@ describe('Bootstrap', function () { }); it('uses the passed in config file location', function (done) { - bootstrap(path.join(config().paths.appRoot, 'config.example.js')).then(function (config) { + bootstrap(path.join(config.paths.appRoot, 'config.example.js')).then(function (config) { config.url.should.equal(defaultConfig.url); config.database.client.should.equal(defaultConfig.database.client); config.database.connection.should.eql(defaultConfig.database.connection); diff --git a/core/test/unit/config_spec.js b/core/test/unit/config_spec.js index b209c8b49a..e0066cff64 100644 --- a/core/test/unit/config_spec.js +++ b/core/test/unit/config_spec.js @@ -1,4 +1,4 @@ -/*globals describe, it, beforeEach, afterEach */ +/*globals describe, it, before, beforeEach, afterEach */ /*jshint expr:true*/ var should = require('should'), sinon = require('sinon'), @@ -71,17 +71,16 @@ describe('Config', function () { }); describe('Index', function () { - // Make a copy of the default config file - // so we can restore it after every test. - // Using _.merge to recursively apply every property. - var defaultConfigFile = _.merge({}, config()); afterEach(function () { - configUpdate(defaultConfigFile); + // Make a copy of the default config file + // so we can restore it after every test. + // Using _.merge to recursively apply every property. + configUpdate(_.merge({}, config)); }); it('should have exactly the right keys', function () { - var pathConfig = config().paths; + var pathConfig = config.paths; // This will fail if there are any extra keys pathConfig.should.have.keys( @@ -107,7 +106,7 @@ describe('Config', function () { }); it('should have the correct values for each key', function () { - var pathConfig = config().paths, + var pathConfig = config.paths, appRoot = path.resolve(__dirname, '../../../'); pathConfig.should.have.property('appRoot', appRoot); @@ -116,28 +115,28 @@ describe('Config', function () { it('should not return a slash for subdir', function () { configUpdate({url: 'http://my-ghost-blog.com'}); - config().paths.should.have.property('subdir', ''); + config.paths.should.have.property('subdir', ''); configUpdate({url: 'http://my-ghost-blog.com/'}); - config().paths.should.have.property('subdir', ''); + config.paths.should.have.property('subdir', ''); }); it('should handle subdirectories properly', function () { configUpdate({url: 'http://my-ghost-blog.com/blog'}); - config().paths.should.have.property('subdir', '/blog'); + config.paths.should.have.property('subdir', '/blog'); configUpdate({url: 'http://my-ghost-blog.com/blog/'}); - config().paths.should.have.property('subdir', '/blog'); + config.paths.should.have.property('subdir', '/blog'); configUpdate({url: 'http://my-ghost-blog.com/my/blog'}); - config().paths.should.have.property('subdir', '/my/blog'); + config.paths.should.have.property('subdir', '/my/blog'); configUpdate({url: 'http://my-ghost-blog.com/my/blog/'}); - config().paths.should.have.property('subdir', '/my/blog'); + config.paths.should.have.property('subdir', '/my/blog'); }); it('should allow specific properties to be user defined', function () { - var contentPath = path.join(config().paths.appRoot, 'otherContent', '/'), + var contentPath = path.join(config.paths.appRoot, 'otherContent', '/'), configFile = 'configFileDanceParty.js'; configUpdate({ @@ -147,16 +146,20 @@ describe('Config', function () { } }); - config().should.have.property('config', configFile); - config().paths.should.have.property('contentPath', contentPath); - config().paths.should.have.property('themePath', contentPath + 'themes'); - config().paths.should.have.property('appPath', contentPath + 'apps'); - config().paths.should.have.property('imagesPath', contentPath + 'images'); + config.should.have.property('config', configFile); + config.paths.should.have.property('contentPath', contentPath); + config.paths.should.have.property('themePath', contentPath + 'themes'); + config.paths.should.have.property('appPath', contentPath + 'apps'); + config.paths.should.have.property('imagesPath', contentPath + 'images'); }); }); describe('urlFor', function () { + before(function () { + configUpdate(_.merge({}, defaultConfig)); + }); + afterEach(function () { configUpdate({url: defaultConfig.url}); }); diff --git a/core/test/unit/errorHandling_spec.js b/core/test/unit/errorHandling_spec.js index dd16656000..8e393712a3 100644 --- a/core/test/unit/errorHandling_spec.js +++ b/core/test/unit/errorHandling_spec.js @@ -215,24 +215,22 @@ describe('Error handling', function () { before(function () { originalConfig = errors.__get__('config'); - errors.__set__('config', function () { - return { - 'paths': { - 'themePath': '/content/themes', - 'availableThemes': { - 'casper': { - 'assets': null, - 'default.hbs': '/content/themes/casper/default.hbs', - 'index.hbs': '/content/themes/casper/index.hbs', - 'page.hbs': '/content/themes/casper/page.hbs', - 'tag.hbs': '/content/themes/casper/tag.hbs' - }, - 'theme-with-error': { - 'error.hbs':'' - } + errors.__set__('config', { + 'paths': { + 'themePath': '/content/themes', + 'availableThemes': { + 'casper': { + 'assets': null, + 'default.hbs': '/content/themes/casper/default.hbs', + 'index.hbs': '/content/themes/casper/index.hbs', + 'page.hbs': '/content/themes/casper/page.hbs', + 'tag.hbs': '/content/themes/casper/tag.hbs' + }, + 'theme-with-error': { + 'error.hbs': '' } } - }; + } }); errors.updateActiveTheme('casper'); }); diff --git a/core/test/unit/frontend_spec.js b/core/test/unit/frontend_spec.js index c0acdc3202..6490bf9aad 100644 --- a/core/test/unit/frontend_spec.js +++ b/core/test/unit/frontend_spec.js @@ -10,6 +10,7 @@ var assert = require('assert'), // Stuff we are testing api = require('../../server/api'), + config = rewire('../../server/config'), frontend = rewire('../../server/controllers/frontend'); // To stop jshint complaining @@ -88,10 +89,8 @@ describe('Frontend Controller', function () { }); it('Redirects to home if page number is 0 with subdirectory', function () { - frontend.__set__('config', function() { - return { - paths: {subdir: '/blog'} - }; + frontend.__set__('config', { + paths: {subdir: '/blog'} }); var req = {params: {page: 0}, route: {path: '/page/:page/'}}; @@ -104,10 +103,8 @@ describe('Frontend Controller', function () { }); it('Redirects to home if page number is 1 with subdirectory', function () { - frontend.__set__('config', function() { - return { - paths: {subdir: '/blog'} - }; + frontend.__set__('config', { + paths: {subdir: '/blog'} }); var req = {params: {page: 1}, route: {path: '/page/:page/'}}; @@ -131,10 +128,8 @@ describe('Frontend Controller', function () { }); it('Redirects to last page if page number too big with subdirectory', function (done) { - frontend.__set__('config', function() { - return { - paths: {subdir: '/blog'} - }; + frontend.__set__('config', { + paths: {subdir: '/blog'} }); var req = {params: {page: 4}, route: {path: '/page/:page/'}}; @@ -217,7 +212,7 @@ describe('Frontend Controller', function () { }] })); - frontend.__set__('config', sandbox.stub().returns({ + frontend.__set__('config', { 'paths': { 'subdir': '', 'availableThemes': { @@ -230,7 +225,7 @@ describe('Frontend Controller', function () { } } } - })); + }); }); describe('custom tag template', function () { @@ -317,10 +312,8 @@ describe('Frontend Controller', function () { }); it('Redirects to base tag page if page number is 0 with subdirectory', function () { - frontend.__set__('config', function() { - return { - paths: {subdir: '/blog'} - }; + frontend.__set__('config', { + paths: {subdir: '/blog'} }); var req = {params: {page: 0, slug: 'pumpkin'}}; @@ -333,10 +326,8 @@ describe('Frontend Controller', function () { }); it('Redirects to base tag page if page number is 1 with subdirectory', function () { - frontend.__set__('config', function() { - return { - paths: {subdir: '/blog'} - }; + frontend.__set__('config', { + paths: {subdir: '/blog'} }); var req = {params: {page: 1, slug: 'pumpkin'}}; @@ -360,10 +351,8 @@ describe('Frontend Controller', function () { }); it('Redirects to last page if page number too big with subdirectory', function (done) { - frontend.__set__('config', function() { - return { - paths: {subdir: '/blog'} - }; + frontend.__set__('config', { + paths: {subdir: '/blog'} }); var req = {params: {page: 4, slug: 'pumpkin'}}; @@ -435,7 +424,7 @@ describe('Frontend Controller', function () { }] })); - frontend.__set__('config', sandbox.stub().returns({ + frontend.__set__('config', { 'paths': { 'subdir': '', 'availableThemes': { @@ -449,7 +438,7 @@ describe('Frontend Controller', function () { } } } - })); + }); }); describe('static pages', function () { @@ -895,13 +884,10 @@ describe('Frontend Controller', function () { describe('rss redirects', function () { var res, apiUsersStub, - overwriteConfig = function(newConfig) { + configUpdate = config.__get__('updateConfig'), + overwriteConfig = function (newConfig) { var existingConfig = frontend.__get__('config'); - var newConfigModule = function() { - return newConfig; - }; - newConfigModule.urlFor = existingConfig.urlFor; - frontend.__set__('config', newConfigModule); + configUpdate(_.extend(existingConfig, newConfig)); }; beforeEach(function () { @@ -995,6 +981,8 @@ describe('Frontend Controller', function () { }); it('Redirects to last page if page number too big', function (done) { + overwriteConfig({paths: {subdir: ''}}); + var req = {params: {page: 4}, route: {path: '/rss/:page/'}}; frontend.rss(req, res, done).then(function () { diff --git a/core/test/unit/import_spec.js b/core/test/unit/import_spec.js index 6cb019ce90..0395567348 100644 --- a/core/test/unit/import_spec.js +++ b/core/test/unit/import_spec.js @@ -6,10 +6,13 @@ var testUtils = require('../utils'), when = require('when'), assert = require('assert'), _ = require('lodash'), + rewire = require('rewire'), // Stuff we are testing - config = require('../../server/config'), - migration = require('../../server/data/migration'), + config = rewire('../../server/config'), + configUpdate = config.__get__('updateConfig'), + defaultConfig = rewire('../../../config.example')[process.env.NODE_ENV], + migration = rewire('../../server/data/migration'), versioning = require('../../server/data/versioning'), exporter = require('../../server/data/export'), importer = require('../../server/data/import'), @@ -23,10 +26,14 @@ describe('Import', function () { should.exist(exporter); should.exist(importer); - var sandbox, - knex = config().database.knex; + var sandbox, knex; beforeEach(function (done) { + var newConfig = _.extend(config, defaultConfig); + migration.__get__('config', newConfig); + configUpdate(newConfig); + knex = config.database.knex; + sandbox = sinon.sandbox.create(); // clear database... we need to initialise it manually for each test testUtils.clearData().then(function () { diff --git a/core/test/unit/mail_spec.js b/core/test/unit/mail_spec.js index 85ec41bdac..c527fc3a42 100644 --- a/core/test/unit/mail_spec.js +++ b/core/test/unit/mail_spec.js @@ -14,8 +14,7 @@ var should = require('should'), fakeConfig, fakeSettings, fakeSendmail, - sandbox = sinon.sandbox.create(), - config; + sandbox = sinon.sandbox.create(); // Mock SMTP config SMTP = { @@ -39,9 +38,11 @@ SENDMAIL = { describe('Mail', function () { var overrideConfig = function (newConfig) { - mailer.__set__('config', sandbox.stub().returns( - _.extend({}, defaultConfig, newConfig) - )); + var config = rewire('../../server/config'), + configUpdate = config.__get__('updateConfig'), + existingConfig = mailer.__get__('config'); + + configUpdate(_.extend(existingConfig, newConfig)); }; beforeEach(function () { @@ -53,7 +54,7 @@ describe('Mail', function () { }; fakeSendmail = '/fake/bin/sendmail'; - config = sinon.stub().returns(fakeConfig); + overrideConfig(fakeConfig); sandbox.stub(mailer, 'isWindows', function () { return false; diff --git a/core/test/unit/server_helpers_index_spec.js b/core/test/unit/server_helpers_index_spec.js index 700b6af40e..cb7edc5e6b 100644 --- a/core/test/unit/server_helpers_index_spec.js +++ b/core/test/unit/server_helpers_index_spec.js @@ -9,7 +9,6 @@ var should = require('should'), Polyglot = require('node-polyglot'), api = require('../../server/api'), hbs = require('express-hbs'), - packageInfo = require('../../../package'), // Stuff we are testing handlebars = hbs.handlebars, @@ -21,15 +20,14 @@ describe('Core Helpers', function () { var sandbox, apiStub, - configStub, overrideConfig = function (newConfig) { - helpers.__set__('config', function() { - return newConfig; - }); + var existingConfig = helpers.__get__('config'); + configUpdate(_.extend(existingConfig, newConfig)); }; beforeEach(function (done) { - var adminHbs = hbs.create(); + var adminHbs = hbs.create(), + existingConfig = helpers.__get__('config'); helpers = rewire('../../server/helpers'); sandbox = sinon.sandbox.create(); apiStub = sandbox.stub(api.settings, 'read', function () { @@ -38,8 +36,7 @@ describe('Core Helpers', function () { }); }); - config = helpers.__get__('config'); - configStub = sandbox.stub().returns({ + overrideConfig({ 'paths': { 'subdir': '', 'availableThemes': { @@ -54,17 +51,16 @@ describe('Core Helpers', function () { } } }); - _.extend(configStub, config); - configStub.theme = sandbox.stub().returns({ + + existingConfig.theme = sandbox.stub().returns({ title: 'Ghost', description: 'Just a blogging platform.', url: 'http://testurl.com' }); - helpers.__set__('config', configStub); helpers.loadCoreHelpers(adminHbs); // Load template helpers in handlebars - hbs.express3({ partialsDir: [config().paths.helperTemplates] }); + hbs.express3({ partialsDir: [config.paths.helperTemplates] }); hbs.cachePartials(function () { done(); }); @@ -400,7 +396,7 @@ describe('Core Helpers', function () { describe('ghost_head Helper', function () { // TODO: these tests should be easier to do! - var configUrl = config().url; + var configUrl = config.url; afterEach(function () { configUpdate({url: configUrl}); @@ -625,10 +621,8 @@ describe('Core Helpers', function () { }); it('can return a valid url with subdirectory', function () { - helpers.__set__('config', function() { - return { - paths: {'subdir': '/blog'} - }; + _.extend(helpers.__get__('config'), { + paths: {'subdir': '/blog'} }); helpers.page_url(1).should.equal('/blog/'); helpers.page_url(2).should.equal('/blog/page/2/'); @@ -645,10 +639,8 @@ describe('Core Helpers', function () { }); it('can return a valid url for tag pages with subdirectory', function () { - helpers.__set__('config', function() { - return { - paths: {'subdir': '/blog'} - }; + _.extend(helpers.__get__('config'), { + paths: {'subdir': '/blog'} }); var tagContext = { tagSlug: 'pumpkin' @@ -671,10 +663,8 @@ describe('Core Helpers', function () { }); it('can return a valid url with subdirectory', function () { - helpers.__set__('config', function() { - return { - paths: {'subdir': '/blog'} - }; + _.extend(helpers.__get__('config'), { + paths: {'subdir': '/blog'} }); helpers.pageUrl(1).should.equal('/blog/'); helpers.pageUrl(2).should.equal('/blog/page/2/'); @@ -691,10 +681,8 @@ describe('Core Helpers', function () { }); it('can return a valid url for tag pages with subdirectory', function () { - helpers.__set__('config', function() { - return { - paths: {'subdir': '/blog'} - }; + _.extend(helpers.__get__('config'), { + paths: {'subdir': '/blog'} }); var tagContext = { tagSlug: 'pumpkin' @@ -1430,7 +1418,7 @@ describe('Core Helpers', function () { describe('adminUrl', function () { var rendered, - configUrl = config().url; + configUrl = config.url; afterEach(function () { configUpdate({url: configUrl}); @@ -1519,19 +1507,15 @@ describe('Core Helpers', function () { fileStorage.should.equal('true'); }); - it('should return the config().fileStorage value when it exists', function () { + it('should return the config.fileStorage value when it exists', function () { var setting = 'file storage value', cfg = helpers.__get__('config'), fileStorage; - configStub = sandbox.stub().returns({ + _.extend(cfg, { fileStorage: setting }); - _.extend(cfg, configStub); - - helpers.__set__('config', cfg); - fileStorage = helpers.file_storage(); should.exist(fileStorage); @@ -1552,19 +1536,15 @@ describe('Core Helpers', function () { apps.should.equal('false'); }); - it('should return the config().apps value when it exists', function () { + it('should return the config.apps value when it exists', function () { var setting = 'app value', cfg = helpers.__get__('config'), apps; - configStub = sandbox.stub().returns({ + _.extend(cfg, { apps: setting }); - _.extend(cfg, configStub); - - helpers.__set__('config', cfg); - apps = helpers.apps(); should.exist(apps); diff --git a/core/test/unit/storage_localfilesystem_spec.js b/core/test/unit/storage_localfilesystem_spec.js index f59002cf63..61b728d17e 100644 --- a/core/test/unit/storage_localfilesystem_spec.js +++ b/core/test/unit/storage_localfilesystem_spec.js @@ -3,18 +3,29 @@ var fs = require('fs-extra'), path = require('path'), should = require('should'), - config = require('../../server/config'), sinon = require('sinon'), - localfilesystem = require('../../server/storage/localfilesystem'); + rewire = require('rewire'), + _ = require('lodash'), + config = rewire('../../server/config'), + configUpdate = config.__get__('updateConfig'), + localfilesystem = rewire('../../server/storage/localfilesystem'); // To stop jshint complaining should.equal(true, true); describe('Local File System Storage', function () { - var image; + var image, + overrideConfig = function (newConfig) { + var existingConfig = localfilesystem.__get__('config'), + updatedConfig = _.extend(existingConfig, newConfig); + configUpdate(updatedConfig); + localfilesystem.__set__('config', updatedConfig); + }; beforeEach(function () { + overrideConfig(config); + sinon.stub(fs, 'mkdirs').yields(); sinon.stub(fs, 'copy').yields(); sinon.stub(fs, 'exists').yields(false); @@ -121,17 +132,17 @@ describe('Local File System Storage', function () { }); describe('when a custom content path is used', function () { - var origContentPath = config().paths.contentPath; - var origImagesPath = config().paths.imagesPath; + var origContentPath = config.paths.contentPath; + var origImagesPath = config.paths.imagesPath; beforeEach(function () { - config().paths.contentPath = config().paths.appRoot + '/var/ghostcms'; - config().paths.imagesPath = config().paths.appRoot + '/var/ghostcms/' + config().paths.imagesRelPath; + config.paths.contentPath = config.paths.appRoot + '/var/ghostcms'; + config.paths.imagesPath = config.paths.appRoot + '/var/ghostcms/' + config.paths.imagesRelPath; }); afterEach(function () { - config().paths.contentPath = origContentPath; - config().paths.imagesPath = origImagesPath; + config.paths.contentPath = origContentPath; + config.paths.imagesPath = origImagesPath; }); it('should send the correct path to image', function (done) { diff --git a/core/test/utils/fork.js b/core/test/utils/fork.js index 2d0825ae15..6113fe477c 100644 --- a/core/test/utils/fork.js +++ b/core/test/utils/fork.js @@ -36,8 +36,8 @@ function findFreePort(port) { // passing to forkGhost() method function forkConfig() { // require caches values, and we want to read it fresh from the file - delete require.cache[config().paths.config]; - return _.cloneDeep(require(config().paths.config)[process.env.NODE_ENV]); + delete require.cache[config.paths.config]; + return _.cloneDeep(require(config.paths.config)[process.env.NODE_ENV]); } // Creates a new fork of Ghost process with a given config @@ -51,7 +51,7 @@ function forkGhost(newConfig, envName) { newConfig.server.port = port; newConfig.url = url.format(_.extend(url.parse(newConfig.url), {port: port, host: null})); - var newConfigFile = path.join(config().paths.appRoot, 'config.test' + port + '.js'); + var newConfigFile = path.join(config.paths.appRoot, 'config.test' + port + '.js'); fs.writeFile(newConfigFile, 'module.exports = {' + envName + ': ' + JSON.stringify(newConfig) + '}', function(err) { if (err) throw err; @@ -59,7 +59,7 @@ function forkGhost(newConfig, envName) { var env = _.clone(process.env); env['GHOST_CONFIG'] = newConfigFile; env['NODE_ENV'] = envName; - var child = cp.fork(path.join(config().paths.appRoot, 'index.js'), {env: env}); + var child = cp.fork(path.join(config.paths.appRoot, 'index.js'), {env: env}); var pingTries = 0; var pingCheck; diff --git a/core/test/utils/index.js b/core/test/utils/index.js index c55dea9815..754d9014d7 100644 --- a/core/test/utils/index.js +++ b/core/test/utils/index.js @@ -1,5 +1,4 @@ -var knex = require('../../server/models/base').knex, - when = require('when'), +var when = require('when'), sequence = require('when/sequence'), nodefn = require('when/node'), _ = require('lodash'), @@ -9,6 +8,7 @@ var knex = require('../../server/models/base').knex, DataGenerator = require('./fixtures/data-generator'), API = require('./api'), fork = require('./fork'), + config = require('../../server/config'), teardown; @@ -22,6 +22,7 @@ function clearData() { } function insertPosts() { + var knex = config.database.knex; // ToDo: Get rid of pyramid of doom return when(knex('posts').insert(DataGenerator.forKnex.posts).then(function () { return knex('tags').insert(DataGenerator.forKnex.tags).then(function () { @@ -34,7 +35,8 @@ function insertMorePosts(max) { var lang, status, posts = [], - i, j, k = 0; + i, j, k = 0, + knex = config.database.knex; max = max || 50; @@ -57,6 +59,7 @@ function insertMorePosts(max) { function insertMorePostsTags(max) { max = max || 50; + var knex = config.database.knex; return when.all([ // PostgreSQL can return results in any order @@ -79,8 +82,8 @@ function insertMorePostsTags(max) { promises.push(DataGenerator.forKnex.createPostsTags(posts[i], injectionTagId)); } - return sequence(_.times(promises.length, function(index) { - return function() { + return sequence(_.times(promises.length, function (index) { + return function () { return knex('posts_tags').insert(promises[index]); }; })); @@ -88,7 +91,8 @@ function insertMorePostsTags(max) { } function insertDefaultUser() { - var user; + var user, + knex = config.database.knex; user = DataGenerator.forKnex.createUser(DataGenerator.Content.users[0]); @@ -99,7 +103,8 @@ function insertDefaultUser() { function insertAdminUser() { var users = [], - userRoles = []; + userRoles = [], + knex = config.database.knex; users.push(DataGenerator.forKnex.createUser(DataGenerator.Content.users[1])); userRoles.push(DataGenerator.forKnex.createUserRole(2, 2)); @@ -112,7 +117,8 @@ function insertAdminUser() { function insertEditorUser() { var users = [], - userRoles = []; + userRoles = [], + knex = config.database.knex; users.push(DataGenerator.forKnex.createUser(DataGenerator.Content.users[2])); userRoles.push(DataGenerator.forKnex.createUserRole(3, 2)); @@ -125,7 +131,8 @@ function insertEditorUser() { function insertAuthorUser() { var users = [], - userRoles = []; + userRoles = [], + knex = config.database.knex; users.push(DataGenerator.forKnex.createUser(DataGenerator.Content.users[3])); userRoles.push(DataGenerator.forKnex.createUserRole(4, 3)); @@ -137,7 +144,8 @@ function insertAuthorUser() { } function insertDefaultApp() { - var apps = []; + var apps = [], + knex = config.database.knex; apps.push(DataGenerator.forKnex.createApp(DataGenerator.Content.apps[0])); @@ -164,13 +172,16 @@ function insertDefaultApp() { } function insertApps() { + var knex = config.database.knex; return knex('apps').insert(DataGenerator.forKnex.apps).then(function () { return knex('app_fields').insert(DataGenerator.forKnex.app_fields); }); } function insertAppWithSettings() { - var apps = [], app_settings = []; + var apps = [], + app_settings = [], + knex = config.database.knex; apps.push(DataGenerator.forKnex.createApp(DataGenerator.Content.apps[0])); app_settings.push(DataGenerator.forKnex.createAppSetting(DataGenerator.Content.app_settings[0])); @@ -178,9 +189,9 @@ function insertAppWithSettings() { return knex('apps').insert(apps, 'id') .then(function (results) { - var appId = results[0]; + var appId = results[0], i; - for (var i = 0; i < app_settings.length; i++) { + for (i = 0; i < app_settings.length; i++) { app_settings[i].app_id = appId; } @@ -188,7 +199,9 @@ function insertAppWithSettings() { }); } function insertAppWithFields() { - var apps = [], app_fields = []; + var apps = [], + app_fields = [], + knex = config.database.knex; apps.push(DataGenerator.forKnex.createApp(DataGenerator.Content.apps[0])); app_fields.push(DataGenerator.forKnex.createAppField(DataGenerator.Content.app_fields[0]));