diff --git a/.jshint.airbnb.json b/.jshint.airbnb.json index 60ce80a5..4ec00310 100644 --- a/.jshint.airbnb.json +++ b/.jshint.airbnb.json @@ -8,7 +8,7 @@ "eqeqeq": true, "indent": 2, "latedef": true, - "maxlen": 80, + "maxlen": 120, "newcap": true, "quotmark": "single", "strict": true, diff --git a/.travis.yml b/.travis.yml index 5f0328b4..85d71384 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ sudo: false node_js: - '0.12' - '4.2' - - '5.3' + - '5.7' cache: directories: - "$(npm root -g)" diff --git a/src/.npmignore b/src/.npmignore index b2d892dc..a917fce7 100644 --- a/src/.npmignore +++ b/src/.npmignore @@ -2,3 +2,4 @@ test tests /lib .gitignore +coverage \ No newline at end of file diff --git a/src/bin/commands/assets/migration_tpl.txt b/src/bin/commands/assets/migration_tpl.txt new file mode 100644 index 00000000..73ecfea6 --- /dev/null +++ b/src/bin/commands/assets/migration_tpl.txt @@ -0,0 +1,20 @@ +/** + * Generated by deepify v{version} + * + * {date} + */ + +'use strict'; + +module.exports = { + up: function(db, cb) { + // Your code goes here ... + + cb(); + }, + down: function(db, cb) { // OPTIONAL + // Your code goes here ... + + cb(); + }, +}; diff --git a/src/bin/commands/build-frontend.js b/src/bin/commands/build-frontend.js index 58c6c5ef..fec8c297 100644 --- a/src/bin/commands/build-frontend.js +++ b/src/bin/commands/build-frontend.js @@ -14,7 +14,7 @@ module.exports = function(mainPath) { var Config = require('deep-package-manager').Property_Config; var Exec = require('../../lib.compiled/Helpers/Exec').Exec; - if (mainPath.indexOf('/') !== 0) { + if (mainPath.indexOf(path.sep) !== 0) { mainPath = path.join(process.cwd(), mainPath); } diff --git a/src/bin/commands/compile-es6.js b/src/bin/commands/compile-es6.js index e85f38ce..b69553f5 100644 --- a/src/bin/commands/compile-es6.js +++ b/src/bin/commands/compile-es6.js @@ -7,9 +7,14 @@ module.exports = function(mainPath) { var path = require('path'); + var os = require('os'); var Exec = require('../../lib.compiled/Helpers/Exec').Exec; - if (mainPath.indexOf('/') !== 0) { + var regExp = /(`\s*pwd\s*`|\$\(\s*pwd\s*\))/ig; + + mainPath = mainPath.replace(regExp, process.cwd()); + + if (mainPath.indexOf(path.sep) !== 0 && os.type() !== 'Windows_NT') { mainPath = path.join(process.cwd(), mainPath); } diff --git a/src/bin/commands/compile-prod.js b/src/bin/commands/compile-prod.js index 6e929f0f..75d040d4 100644 --- a/src/bin/commands/compile-prod.js +++ b/src/bin/commands/compile-prod.js @@ -11,11 +11,11 @@ module.exports = function(mainPath) { var fs = require('fs'); var Exec = require('../../lib.compiled/Helpers/Exec').Exec; var LambdaExtractor = require('../../lib.compiled/Helpers/LambdasExtractor').LambdasExtractor; + var ValidationSchemasSync = require('../../lib.compiled/Helpers/ValidationSchemasSync').ValidationSchemasSync; var DepsTreeOptimizer = require('../../lib.compiled/NodeJS/DepsTreeOptimizer').DepsTreeOptimizer; var NpmInstall = require('../../lib.compiled/NodeJS/NpmInstall').NpmInstall; var NpmInstallLibs = require('../../lib.compiled/NodeJS/NpmInstallLibs').NpmInstallLibs; var NpmPrune = require('../../lib.compiled/NodeJS/NpmPrune').NpmPrune; - var NpmDedupe = require('../../lib.compiled/NodeJS/NpmDedupe').NpmDedupe; var NpmRun = require('../../lib.compiled/NodeJS/NpmRun').NpmRun; var NpmChain = require('../../lib.compiled/NodeJS/NpmChain').NpmChain; var Bin = require('../../lib.compiled/NodeJS/Bin').Bin; @@ -29,14 +29,13 @@ module.exports = function(mainPath) { var installSdk = this.opts.locate('aws-sdk').exists; var microservicesToDeploy = this.opts.locate('partial').value; - if (mainPath.indexOf('/') !== 0) { + if (mainPath.indexOf(path.sep) !== 0) { mainPath = path.join(process.cwd(), mainPath); } var property = new Property(mainPath); property.microservicesToUpdate = getMicroservicesToDeploy(); - var microservices = property.workingMicroservices; var lambdas = { path: [], tmpPath: [], @@ -44,6 +43,10 @@ module.exports = function(mainPath) { lambdas.path = arrayUnique(new LambdaExtractor(property).extractWorking(LambdaExtractor.NPM_PACKAGE_FILTER)); + console.log('Sync validation schemas into ' + lambdas.path.length + ' Lambdas'); + + new ValidationSchemasSync(property).syncWorking(ValidationSchemasSync.NPM_PACKAGE_FILTER); + for (var i in lambdas.path) { if (!lambdas.path.hasOwnProperty(i)) { continue; @@ -96,17 +99,8 @@ module.exports = function(mainPath) { }.bind(this), lambdas); function prepareSources(cb, lambdas) { - var wait = new WaitFor(); - var remaining = lambdas.path.length; - console.log(lambdas.path.length + ' Lambdas sources are going to be copied...'); - wait.push(function() { - return remaining <= 0; - }.bind(this)); - - wait.ready(cb); - for (var i in lambdas.path) { if (!lambdas.path.hasOwnProperty(i)) { continue; @@ -121,22 +115,22 @@ module.exports = function(mainPath) { fse.removeSync(lambdaTmpPath); } - fse.copy(lambdaPath, lambdaTmpPath, function(lambdaTmpPath, i, error) { - if (error) { - console.error(error); + try { + fse.copySync(lambdaPath, lambdaTmpPath); - lambdas.splice(i, 1); - } else { - var nodeModules = path.join(lambdaTmpPath, 'node_modules'); + var nodeModules = path.join(lambdaTmpPath, 'node_modules'); - if (fs.existsSync(nodeModules)) { - fse.removeSync(nodeModules); - } + if (fs.existsSync(nodeModules)) { + fse.removeSync(nodeModules); } + } catch (error) { + console.error(error); - remaining--; - }.bind(this, lambdaTmpPath, i)); + lambdas.splice(i, 1); + } } + + cb(); } function optimize(cb, lambdas, final) { @@ -182,19 +176,34 @@ module.exports = function(mainPath) { } function optimizeDeps(cb, lambdas) { + if (lambdas.tmpPath.length <= 0) { + cb(); + return; + } + + _optimizeDepsChunk( + NpmInstall._chunkArray(lambdas.tmpPath, NpmInstall.DEFAULT_CHUNK_SIZE), + cb, + lambdas + ); + } + + function _optimizeDepsChunk(chunks, cb, lambdas) { + var chunk = chunks.shift(); + var wait = new WaitFor(); - var remaining = lambdas.tmpPath.length; + var remaining = chunk.length; wait.push(function() { return remaining <= 0; }.bind(this)); - for (var i in lambdas.tmpPath) { - if (!lambdas.tmpPath.hasOwnProperty(i)) { + for (var i in chunk) { + if (!chunk.hasOwnProperty(i)) { continue; } - var lambdaTmpPath = lambdas.tmpPath[i]; + var lambdaTmpPath = chunk[i]; console.log('Optimizing Lambda dependencies in ' + lambdaTmpPath); @@ -210,7 +219,11 @@ module.exports = function(mainPath) { } wait.ready(function() { - optimize.bind(this)(cb, lambdas, true); + if (chunks.length <= 0) { + optimize.bind(this)(cb, lambdas, true); + } else { + _optimizeDepsChunk(chunks, cb, lambdas); + } }.bind(this)); } @@ -278,6 +291,11 @@ module.exports = function(mainPath) { path.basename(lambdaPath) + '.zip' ); + if (fs.existsSync(outputFile)) { + console.log('Removing old Lambda build ' + outputFile); + fse.removeSync(outputFile); + } + console.log('Packing Lambda code into ' + outputFile + ' (' + lambdaTmpPath + ')'); // @todo: replace this with a node native @@ -316,7 +334,9 @@ module.exports = function(mainPath) { function arrayUnique(a) { return a.reduce(function(p, c) { - if (p.indexOf(c) < 0) p.push(c); + if (p.indexOf(c) < 0) { + p.push(c); + } return p; }, []); } diff --git a/src/bin/commands/create-migration.js b/src/bin/commands/create-migration.js new file mode 100644 index 00000000..123f33d4 --- /dev/null +++ b/src/bin/commands/create-migration.js @@ -0,0 +1,32 @@ +#!/usr/bin/env node +/** + * Created by AlexanderC on 8/4/15. + */ + +'use strict'; + +module.exports = function(microservicePath) { + var path = require('path'); + var fs = require('fs'); + var fse = require('fs-extra'); + var Microservice = require('deep-package-manager').Microservice_Instance; + + if (microservicePath.indexOf(path.sep) !== 0) { + microservicePath = path.join(process.cwd(), microservicePath); + } + + var ms = Microservice.create(microservicePath); + var migrationsPath = ms.autoload.migration; + + var migrationTpl = fs.readFileSync(path.join(__dirname, 'assets', 'migration_tpl.txt')).toString(); + var migrationFile = path.join(migrationsPath, 'Version' + (new Date()).getTime() + '.js'); + + console.log('Creating migration in ' + migrationFile); + + fse.outputFileSync( + migrationFile, + migrationTpl + .replace('{version}', this.version) + .replace('{date}', new Date().toLocaleString()) + ); +}; diff --git a/src/bin/commands/deploy.js b/src/bin/commands/deploy.js index 946e5d1a..e8e4bff1 100644 --- a/src/bin/commands/deploy.js +++ b/src/bin/commands/deploy.js @@ -14,23 +14,26 @@ module.exports = function(mainPath) { var Bin = require('../../lib.compiled/NodeJS/Bin').Bin; var Prompt = require('../../lib.compiled/Terminal/Prompt').Prompt; var Property = require('deep-package-manager').Property_Instance; + var SharedAwsConfig = require('deep-package-manager').Helpers_SharedAwsConfig; var Config = require('deep-package-manager').Property_Config; var S3Service = require('deep-package-manager').Provisioning_Service_S3Service; var ProvisioningCollisionsDetectedException = require('deep-package-manager').Property_Exception_ProvisioningCollisionsDetectedException; + var DeployConfig = require('deep-package-manager').Property_DeployConfig; + var isProd = this.opts.locate('prod').exists; var installSdk = this.opts.locate('aws-sdk').exists; var localOnly = this.opts.locate('dry-run').exists; var fastDeploy = this.opts.locate('fast').exists; var dumpCodePath = this.opts.locate('dump-local').value; var cfgBucket = this.opts.locate('cfg-bucket').value; - var hasToPullDeps = this.opts.locate('pull-deps').exists; + var appEnv = isProd ? 'prod' : this.opts.locate('env').value; var microservicesToDeploy = this.opts.locate('partial').value; - if (mainPath.indexOf('/') !== 0) { + if (mainPath.indexOf(path.sep) !== 0) { mainPath = path.join(process.cwd(), mainPath); } - if (dumpCodePath && dumpCodePath.indexOf('/') !== 0) { + if (dumpCodePath && dumpCodePath.indexOf(path.sep) !== 0) { dumpCodePath = path.join(process.cwd(), dumpCodePath); } @@ -38,12 +41,32 @@ module.exports = function(mainPath) { var configExists = fs.existsSync(configFile); var config = null; + appEnv = appEnv ? appEnv.toLowerCase() : null; + + if (appEnv && DeployConfig.AVAILABLE_ENV.indexOf(appEnv) === -1) { + console.error( + 'Invalid environment "' + appEnv + '". Available environments: ' + + DeployConfig.AVAILABLE_ENV.join(', ') + ); + this.exit(1); + } + if (!configExists) { config = Config.generate(); + if (appEnv) { + config.env = appEnv; + } + fse.outputJsonSync(configFile, config); } else { config = fse.readJsonSync(configFile); + + if (appEnv) { + config.env = appEnv; + + fse.outputJsonSync(configFile, config); + } } var propertyInstance; @@ -108,111 +131,77 @@ module.exports = function(mainPath) { }.bind(this)); } - function startDeploy() { - propertyInstance = new Property(tmpPropertyPath, Config.DEFAULT_FILENAME); - - propertyInstance.assureFrontendEngine(function(error) { - if (error) { - console.error('Error while assuring frontend engine: ' + error); + function ensureAWSProdKeys(cb) { + (new SharedAwsConfig()).refillPropertyConfigIfNeeded(config, function(refilled) { + if (refilled) { + fse.outputJsonSync(configFile, config); } - propertyInstance.runInitMsHooks(function() { - var deployCb = function() { - prepareProduction.bind(this)(propertyInstance.path, doDeploy.bind(this)); - }; - - hasToPullDeps ? pullDeps.bind(this)(deployCb) : deployCb.bind(this)(); - }.bind(this)); - }.bind(this)); + cb(); + }); } - function getConfigFromS3(propertyInstance, cb) { - console.log('Trying to retrieve .cfg.deeploy.json from S3 ' + cfgBucket); - - var s3 = new propertyInstance.AWS.S3(); + function startDeploy() { + ensureAWSProdKeys(function() { + propertyInstance = new Property(tmpPropertyPath, Config.DEFAULT_FILENAME); - var payload = { - Bucket: cfgBucket, - Key: '.cfg.deeploy.json', - }; + propertyInstance.assureFrontendEngine(function(error) { + if (error) { + console.error('Error while assuring frontend engine: ' + error); + } - s3.getObject(payload, function(error, data) { - cb.bind(this)(error, data); + propertyInstance.runInitMsHooks(function() { + prepareProduction.bind(this)(propertyInstance.path, doDeploy.bind(this)); + }.bind(this)); + }.bind(this)); }.bind(this)); } - function dumpConfigToS3(propertyInstance, cb) { - if (!propertyInstance.config || !propertyInstance.config.provisioning) { - cb && cb.bind(this)(); - return; - } - - var plainConfig = JSON.stringify(propertyInstance.config); - var s3 = new propertyInstance.AWS.S3(); - - var payload = { - Bucket: propertyInstance.config.provisioning.s3.buckets[S3Service.SYSTEM_BUCKET].name, - Key: '.cfg.deeploy.json', - Body: plainConfig, - }; + function dumpConfig(propertyInstance, cb) { + propertyInstance.configObj.completeDump(function() { + if (!fastDeploy) { + var configFile = propertyInstance.configObj.configFile; - s3.putObject(payload, function(error, data) { - if (error) { - console.error('Error persisting config to S3', error); - this.exit(1); + fse.copySync(configFile, path.join(mainPath, path.basename(configFile))); } - cb && cb.bind(this)(); + cb.bind(this)(); }.bind(this)); } - function dumpConfig(propertyInstance, cb) { - if (!propertyInstance.config) { - cb && cb.bind(this)(); - return; - } + function doCompileProd(propertyPath, cb) { + console.log('Start preparing for production'); - var deepConfigFile = path.join(mainPath, '.cfg.deeploy.json'); - var plainConfig = JSON.stringify(propertyInstance.config); + var cmd = new Exec( + Bin.node, + this.scriptPath, + 'compile-prod', + propertyPath + ); - console.log('Dumping config into ' + deepConfigFile); + !fastDeploy && cmd.addArg('--remove-source'); + microservicesToDeploy && cmd.addArg('--partial ' + microservicesToDeploy); + installSdk && cmd.addArg('--aws-sdk'); - fs.writeFile(deepConfigFile, plainConfig, function(error) { - if (error) { - console.error('Error while dumping config into ' + deepConfigFile + ': ' + error); + cmd.run(function(result) { + if (result.failed) { + console.error('Backend production preparations failed: ' + result.error); + this.exit(1); } - dumpConfigToS3.bind(this)(propertyInstance, cb); - }.bind(this)); + cb(); + }.bind(this), true); } function prepareProduction(propertyPath, cb) { - if (!localOnly) { + if (isProd) { + doCompileProd.bind(this)(propertyPath, cb); + } else if (!localOnly) { var prompt = new Prompt('Prepare for production?'); prompt.readConfirm(function(result) { if (result) { - console.log('Start preparing for production'); - - var cmd = new Exec( - Bin.node, - this.scriptPath, - 'compile-prod', - propertyPath - ); - - !fastDeploy && cmd.addArg('--remove-source'); - microservicesToDeploy && cmd.addArg('--partial ' + microservicesToDeploy); - installSdk && cmd.addArg('--aws-sdk'); - - cmd.run(function(result) { - if (result.failed) { - console.error('Backend production preparations failed: ' + result.error); - this.exit(1); - } - - cb(); - }.bind(this), true); + doCompileProd.bind(this)(propertyPath, cb); return; } @@ -221,18 +210,14 @@ module.exports = function(mainPath) { cb(); }.bind(this)); - - return; + } else { + cb(); } - - cb(); } function doDeploy() { propertyInstance.localDeploy = localOnly; - var deepConfigFile = path.join(mainPath, '.cfg.deeploy.json'); - // @todo: improve it! // Gracefully teardown... (function() { @@ -276,64 +261,27 @@ module.exports = function(mainPath) { }.bind(this)); }.bind(this))(); - var updateCfg = fs.existsSync(deepConfigFile) ? JSON.parse(fs.readFileSync(deepConfigFile)) : null; + propertyInstance.configObj.tryLoadConfig(function() { + if (propertyInstance.configObj.configExists) { + propertyInstance.update(function() { + console.log('CloudFront (CDN) domain: ' + getCfDomain(propertyInstance)); + console.log('Website address: ' + getPublicWebsite(propertyInstance)); - // @todo: rewrite this section! - if (!updateCfg) { - if (!cfgBucket) { + dumpConfig.bind(this)(propertyInstance, dumpCode); + }.bind(this), null, getMicroservicesToDeploy()); + } else { if (microservicesToDeploy) { console.warn(' Partial deploy option is useless during first deploy...'); } - console.log('Installing web app ' + config.appIdentifier); - propertyInstance.install(function() { console.log('CloudFront (CDN) domain: ' + getCfDomain(propertyInstance)); console.log('Website address: ' + getPublicWebsite(propertyInstance)); dumpConfig.bind(this)(propertyInstance, dumpCode); }.bind(this)); - } else { - getConfigFromS3.bind(this)(propertyInstance, function(error, updateCfg) { - if (error) { - console.error('Error fetching config from AWS S3 bucket ' + cfgBucket, error); - } - - if (!error) { - console.log('Updating web app ' + config.appIdentifier); - - propertyInstance.update(JSON.parse(updateCfg.Body.toString()), function() { - console.log('CloudFront (CDN) domain: ' + getCfDomain(propertyInstance)); - console.log('Website address: ' + getPublicWebsite(propertyInstance)); - - dumpConfig.bind(this)(propertyInstance, dumpCode); - }.bind(this), getMicroservicesToDeploy()); - } else { - if (microservicesToDeploy) { - console.warn(' Partial deploy option is useless during first deploy...'); - } - - console.log('Installing web app ' + config.appIdentifier); - - propertyInstance.install(function() { - console.log('CloudFront (CDN) domain: ' + getCfDomain(propertyInstance)); - console.log('Website address: ' + getPublicWebsite(propertyInstance)); - - dumpConfig.bind(this)(propertyInstance, dumpCode); - }.bind(this)); - } - }.bind(this)); } - } else { - console.log('Updating web app ' + config.appIdentifier); - - propertyInstance.update(updateCfg, function() { - console.log('CloudFront (CDN) domain: ' + getCfDomain(propertyInstance)); - console.log('Website address: ' + getPublicWebsite(propertyInstance)); - - dumpConfig.bind(this)(propertyInstance, dumpCode); - }.bind(this), getMicroservicesToDeploy()); - } + }.bind(this), cfgBucket); } function arrayUnique(a) { @@ -358,26 +306,6 @@ module.exports = function(mainPath) { return typeof msIdentifiers === 'string' ? [msIdentifiers] : msIdentifiers; } - function pullDeps(cb) { - console.log('Resolving dependencies in ' + tmpPropertyPath); - - new Exec( - Bin.node, - this.scriptPath, - 'pull-deps', - tmpPropertyPath - ) - .avoidBufferOverflow() - .run(function(result) { - if (result.failed) { - console.error('Error while pulling dependencies in ' + tmpPropertyPath + ': ' + result.error); - this.exit(1); - } - - cb.bind(this)(); - }.bind(this)); - } - function dumpCode() { if (!dumpCodePath) { return; @@ -516,7 +444,8 @@ module.exports = function(mainPath) { function getPublicWebsite(propertyInstance) { var config = propertyInstance.config; var bucketName = config.provisioning.s3.buckets[S3Service.PUBLIC_BUCKET].name; + var bucketRegion = propertyInstance.provisioning.s3.config.region; - return 'http://' + bucketName + '.s3-website-' + config.awsRegion + '.amazonaws.com'; + return 'http://' + bucketName + '.s3-website-' + bucketRegion + '.amazonaws.com'; } }; diff --git a/src/bin/commands/disable-ssl.js b/src/bin/commands/disable-ssl.js new file mode 100644 index 00000000..2306f201 --- /dev/null +++ b/src/bin/commands/disable-ssl.js @@ -0,0 +1,81 @@ +#!/usr/bin/env node +/** + * Created by AlexanderC on 8/4/15. + */ + +'use strict'; + +module.exports = function(mainPath) { + var path = require('path'); + var fs = require('fs'); + var Property = require('deep-package-manager').Property_Instance; + var ACMService = require('deep-package-manager').Provisioning_Service_ACMService; + var CloudFrontService = require('deep-package-manager').Provisioning_Service_CloudFrontService; + var Config = require('deep-package-manager').Property_Config; + + if (mainPath.indexOf(path.sep) !== 0) { + mainPath = path.join(process.cwd(), mainPath); + } + + var propertyConfigFile = path.join(mainPath, Config.DEFAULT_FILENAME); + + if (!fs.existsSync(propertyConfigFile)) { + console.error('You should have the application configured'); + this.exit(1); + } + + var property = new Property(mainPath); + + property.configObj.tryLoadConfig(function() { + if (!property.configObj.configExists) { + console.error('You should have the application deployed'); + this.exit(1); + } + + var domain = property.config.domain; + + if (!domain) { + console.error('Please add a domain to \'' + Config.DEFAULT_FILENAME + '\' config file in order to deactivate SSL!'); + } + + var acmService = property.provisioning.services.find(ACMService); + var cfService = property.provisioning.services.find(CloudFrontService); + + console.log('Looking for ACM certificate available of the domain \'' + domain + '\''); + + acmService.getDomainCertificateArn(domain, function(certArn) { + if (!certArn) { + console.error('There is no certificate available for the domain \'' + domain + '\''); + this.exit(1); + } + + var configChanges = { + DefaultCacheBehavior: { + ViewerProtocolPolicy: 'allow-all', + }, + ViewerCertificate: { + Certificate: null, + CertificateSource: 'cloudfront', + CloudFrontDefaultCertificate: true, + }, + Aliases: { + Quantity: 0, + Items: null, + }, + }; + + console.log('Deactivating ACM certificate \'' + certArn + '\' for domain \'' + domain + '\''); + + cfService.updateDistribution(configChanges, function(error) { + if (error) { + console.error(error); + this.exit(1); + } + + console.log( + 'Certificate \'' + certArn + '\' have been successfully unassigned from the CloudFront distribution' + ); + }.bind(this)); + }.bind(this)); + }.bind(this)); +}; diff --git a/src/bin/commands/enable-ssl.js b/src/bin/commands/enable-ssl.js new file mode 100644 index 00000000..0ae4572e --- /dev/null +++ b/src/bin/commands/enable-ssl.js @@ -0,0 +1,112 @@ +#!/usr/bin/env node +/** + * Created by AlexanderC on 8/4/15. + */ + +'use strict'; + +module.exports = function(mainPath) { + var path = require('path'); + var fse = require('fs-extra'); + var fs = require('fs'); + var Property = require('deep-package-manager').Property_Instance; + var ACMService = require('deep-package-manager').Provisioning_Service_ACMService; + var CloudFrontService = require('deep-package-manager').Provisioning_Service_CloudFrontService; + var Config = require('deep-package-manager').Property_Config; + + var domain = this.opts.locate('domain').value || null; + + if (mainPath.indexOf(path.sep) !== 0) { + mainPath = path.join(process.cwd(), mainPath); + } + + var propertyConfigFile = path.join(mainPath, Config.DEFAULT_FILENAME); + + if (!fs.existsSync(propertyConfigFile)) { + console.error('You should have the application configured'); + this.exit(1); + } + + if (domain) { + var config = null; + + config = fse.readJsonSync(propertyConfigFile); + config.domain = domain; + + fse.outputJsonSync(propertyConfigFile, config); + } + + var property = new Property(mainPath); + + domain = property.config.domain; + + if (!domain) { + console.error('Please add a domain to \'' + Config.DEFAULT_FILENAME + '\' config file in order to activate SSL!'); + console.log('You may add \'--domain\' option to add it to the config automatically.'); + } + + property.configObj.tryLoadConfig(function() { + if (!property.configObj.configExists) { + console.error('You should have the application deployed'); + this.exit(1); + } + + property.config.domain = domain; + + var acmService = property.provisioning.services.find(ACMService); + var cfService = property.provisioning.services.find(CloudFrontService); + + console.log('Ensure ACM certificate available for domain \'' + domain + '\''); + + acmService.ensureCertificate(domain, function (error, certArn) { + if (error) { + console.error(error); + this.exit(1); + } + + console.log('Ensure ACM certificate \'' + certArn + '\' for domain \'' + domain + '\' have been activated'); + + acmService.isCertificateIssued(certArn, function(error, isIssued) { + if (error) { + console.error(error); + this.exit(1); + } else if(!isIssued) { + console.error( + 'You must validate the certificate \'' + certArn + + '\' by accessing the link from the mail sent by AWS ' + + '(Subject: "Certificate approval for ' + domain + '")' + ); + this.exit(1); + } + + var configChanges = { + DefaultCacheBehavior: { + ViewerProtocolPolicy: 'redirect-to-https', + }, + ViewerCertificate: { + Certificate: certArn, + CertificateSource: 'acm', + CloudFrontDefaultCertificate: false, + }, + Aliases: { + Quantity: 1, + Items: [domain], + }, + }; + + console.log('Activating ACM certificate \'' + certArn + '\' for domain \'' + domain + '\''); + + cfService.updateDistribution(configChanges, function(error) { + if (error) { + console.error(error); + this.exit(1); + } + + console.log( + 'The ACM certificate \'' + certArn + '\' have been successfully assigned to the CloudFront distribution' + ); + }.bind(this)); + }.bind(this)); + }.bind(this)); + }.bind(this)); +}; diff --git a/src/bin/commands/helloworld.js b/src/bin/commands/helloworld.js index 5def3214..bb16d748 100644 --- a/src/bin/commands/helloworld.js +++ b/src/bin/commands/helloworld.js @@ -6,25 +6,32 @@ 'use strict'; module.exports = function(dumpPath) { - - // @todo: put it anywhere in a config - var helloWorldRepoUrl = 'https://github.com/MitocGroup/deep-microservices-helloworld.git'; - + var path = require('path'); + var fse = require('fs-extra'); var Exec = require('../../lib.compiled/Helpers/Exec').Exec; var Bin = require('../../lib.compiled/NodeJS/Bin').Bin; + if (dumpPath.indexOf(path.sep) !== 0) { + dumpPath = path.join(process.cwd(), dumpPath); + } + var cmd = new Exec( Bin.node, this.scriptPath, 'install', - helloWorldRepoUrl, - dumpPath + 'github://MitocGroup/deep-microservices-helloworld', + '--init', + '--skip-github-deps' ); + fse.ensureDirSync(dumpPath); + + cmd.cwd = dumpPath; + cmd.run(function(result) { if (result.failed) { console.error(result.error); this.exit(1); } - }, true); + }.bind(this), true); }; diff --git a/src/bin/commands/init-backend.js b/src/bin/commands/init-backend.js index 97444528..f026212e 100644 --- a/src/bin/commands/init-backend.js +++ b/src/bin/commands/init-backend.js @@ -7,9 +7,7 @@ module.exports = function(mainPath) { var path = require('path'); - var Autoload = require('deep-package-manager').Microservice_Metadata_Autoload; var Property = require('deep-package-manager').Property_Instance; - var WaitFor = require('deep-package-manager').Helpers_WaitFor; var Config = require('deep-package-manager').Property_Config; var fs = require('fs'); var fse = require('fs-extra'); @@ -21,8 +19,9 @@ module.exports = function(mainPath) { var LambdaExtractor = require('../../lib.compiled/Helpers/LambdasExtractor').LambdasExtractor; var microservicesToInit = this.opts.locate('partial').value; + var useProd = this.opts.locate('prod').exists; - if (mainPath.indexOf('/') !== 0) { + if (mainPath.indexOf(path.sep) !== 0) { mainPath = path.join(process.cwd(), mainPath); } @@ -68,14 +67,17 @@ module.exports = function(mainPath) { var lambdaPaths = new LambdaExtractor(property).extractWorking(LambdaExtractor.NPM_PACKAGE_FILTER); var chain = new NpmChain(); - - chain.add( - new NpmInstall(lambdaPaths) - .addExtraArg( - '--loglevel silent' - ) + var installCmd = new NpmInstall(lambdaPaths) + .addExtraArg( + '--loglevel silent' ); + if (useProd) { + installCmd.addExtraArg('--prod'); + } + + chain.add(installCmd); + var linkCmd = new NpmLink(lambdaPaths); linkCmd.libs = 'aws-sdk'; diff --git a/src/bin/commands/install.js b/src/bin/commands/install.js index 65345e98..a19e6828 100644 --- a/src/bin/commands/install.js +++ b/src/bin/commands/install.js @@ -5,174 +5,216 @@ 'use strict'; -module.exports = function(microserviceRepo, dumpPath) { - var fs = require('fs'); - var tmp = require('tmp'); - var path = require('path'); - var fse = require('fs-extra'); - var Exec = require('../../lib.compiled/Helpers/Exec').Exec; +module.exports = function(dependency) { + + // @todo: move it in some json config? + var DEFAULT_REGISTRY_BASE_HOST = 'https://deep.mg'; + + var GitHubDependency = require('deep-package-manager').Registry_GitHub_Dependency; + var AuthToken = require('../../lib.compiled/Registry/AuthToken').AuthToken; + var RegistryConfig = require('../../lib.compiled/Registry/Config').Config; + var Property = require('deep-package-manager').Property_Instance; + var PropertyConfig = require('deep-package-manager').Property_Config; + var Registry = require('deep-package-manager').Registry_Registry; + var RegistryAuthorizer = require('deep-package-manager').Registry_Storage_Driver_Helpers_Api_Auth_Authorizer; var Bin = require('../../lib.compiled/NodeJS/Bin').Bin; - var Prompt = require('../../lib.compiled/Terminal/Prompt').Prompt; + var Exec = require('../../lib.compiled/Helpers/Exec').Exec; + var Microservice = require('deep-package-manager').Microservice_Instance; + var path = require('path'); - if (dumpPath.indexOf('/') !== 0) { - dumpPath = path.join(process.cwd(), dumpPath); - } + var registryBaseHost = this.opts.locate('registry').value || + RegistryConfig.create().refresh('registry').read('registry') || + DEFAULT_REGISTRY_BASE_HOST; - gitFetch(microserviceRepo, function(error) { - if (error) { - this.exit(1); - return; - } + var workingDirectory = process.cwd(); + var skipGitHubDeps = this.opts.locate('skip-github-deps').exists; + var gitHubAuthPair = this.opts.locate('github-auth').value; + var initApp = this.opts.locate('init').exists; + var depParts = parseDep(); + var depName = depParts[0]; + var depVersion = depParts[1]; - npmInstall('"babel@^5.x.x"', function(error) { + if (depName) { + var fetcher = GitHubDependency.isGitHubDependency(depName) ? fetchGitHub : fetchRepository; + + fetcher.bind(this)(function(error) { if (error) { - console.error('Error while installing babel: ' + error); + console.error(error); + this.exit(1); } - var prompt = new Prompt('Initialize backend?'); - - prompt.readConfirm(function(result) { - if (result) { - console.log('Start initializing backend...'); - - var cmd = new Exec( - Bin.node, - this.scriptPath, - 'init-backend', - dumpPath - ); + console.log('The microservice \'' + depName + '\' has been successfully installed'); - cmd.run(function(result) { - if (result.failed) { - console.error(result.error); - } - - console.log('The web application was successfully installed.'); - }, true); - - return; + initBackend.bind(this)(); + }.bind(this)); + } else { + createRegistry.bind(this)(function(registry) { + console.log('Installing web app dependencies'); + + registry.install(createProperty(), function(error) { + if (error) { + console.error(error); + this.exit(1); } - console.log('The web application was successfully installed.'); + console.log('Wep app dependencies have been successfully installed'); + + initBackend.bind(this)(); }.bind(this)); }.bind(this)); - }.bind(this)); + } - function npmInstall(repo, cb) { - console.log('Installing ' + repo + ' via NPM globally'); + function createProperty() { + return Property.create(workingDirectory, PropertyConfig.DEFAULT_FILENAME); + } - new Exec('npm list -g --depth 0 ' + repo + ' || npm install -g ' + repo) - .avoidBufferOverflow() - .run(function(result) { - if (result.failed) { - console.error('Error installing ' + repo + ' globally: ' + result.error); + function getRegistryToken() { + return (new AuthToken()).refresh().toString(); + } - cb(result.error); - return; - } + function createRegistry(cb) { + console.log('Initializing remote registry'); - cb(null); - }.bind(this)); + Registry.createApiRegistry(registryBaseHost, function(error, registry) { + if (error) { + console.error(error); + this.exit(1); + } + + registry.storage.driver.authorizer = RegistryAuthorizer.createHeaderToken(getRegistryToken()); + + cb(registry); + }.bind(this), true); } - function gitFetch(repo, cb, copyFiles) { - copyFiles = copyFiles || {}; + function parseDep() { + var parts = (dependency || '').split('@'); - var tmpFolder = tmp.dirSync().name; + if (parts.length <= 1) { + return [parts[0], '*']; + } - console.log('Cloning the ' + repo + ' into ' + tmpFolder); + return [parts[0], parts[1]]; + } - var cmd = new Exec('git', 'clone', '--depth=1', repo, '.'); - cmd.cwd = tmpFolder; + function initBackend() { + if (!initApp) { + return; + } - cmd - .avoidBufferOverflow() - .run(function(result) { - if (result.failed) { - console.error('Error cloning ' + repo + ' repository into ' + tmpFolder + ': ' + result.error); + console.log('Start initializing backend'); - fse.removeSync(tmpFolder); - cb(result.error); - return; - } + npmInstall('"babel@^5.x.x"', function(error) { + if (error) { + console.error('Error while installing babel: ' + error); + this.exit(1); + } - var srcDir = path.join(tmpFolder, 'src'); + //@todo - temporary workaround for FATAL ERROR- JS Allocation failed – process out of memory + if(/^win/.test(process.platform)) { + console.warn('The web application was successfully installed on Windows!\n'); + console.info('To initialize backend use "deepify init-backend path/to" command'); + console.info('To run local development server use "deepify server path/to" command'); + return; + } - if (!fs.existsSync(srcDir)) { - var error = 'Missing "src" directory in ' + tmpFolder; + var cmd = new Exec( + Bin.node, + this.scriptPath, + 'init-backend', + workingDirectory + ); - fse.removeSync(tmpFolder); - cb(new Error(error)); - return; + cmd.run(function(result) { + if (result.failed) { + console.error(result.error); + this.exit(1); } - cleanupDir(srcDir); + console.log('Wep app dependencies have been successfully initialized'); + }.bind(this), true); + }.bind(this)); + } + + function fetchGitHub(cb) { + console.log('Fetching microservice from GitHub'); - try { - var subdir = getSubdirName(srcDir); - } catch (e) { - fse.removeSync(srcDir); - cb(e); - return; - } + var depObj = new GitHubDependency(depName, depVersion); - var sourcePath = path.join(srcDir, subdir); - var targetPath = path.join(dumpPath, subdir); + // @todo: move logic into GitHubDependency? + if (gitHubAuthPair) { + var gitHubCred = gitHubAuthPair.split(':'); - fse.copySync(sourcePath, targetPath, {clobber: true}); + if (gitHubCred.length === 1) { - var copyFilesKeys = Object.keys(copyFiles); + // @todo: read git user followed by this fallback? + gitHubCred.unshift(depObj.repositoryUser); + } - for (var i in copyFilesKeys) { - if (!copyFilesKeys.hasOwnProperty(i)) { - continue; - } + depObj.auth(gitHubCred[0], gitHubCred[1]); + } - var fSrc = copyFilesKeys[i]; - var fDes = copyFiles[fSrc]; + var dumpPath = path.join(workingDirectory, depObj.shortDependencyName); - fse.copySync(path.join(tmpFolder, fSrc), fDes, {clobber: true}); + depObj.extract( + dumpPath, + function(error) { + if (error) { + console.error(error); + this.exit(1); } - fse.removeSync(tmpFolder); + if (skipGitHubDeps) { + cb.bind(this)(); + return; + } - cb(null); - }.bind(this)); - } + var microservice = Microservice.create(dumpPath); - /** - * @param {String} dir - */ - function cleanupDir(dir) { - new Exec('find', dir, '-type d -name ".git" -print0 | xargs -0 rm -rf') - .avoidBufferOverflow() - .runSync(); - } + createRegistry.bind(this)(function(registry) { + console.log('Installing \'' + depObj.shortDependencyName + '\' dependencies'); - /** - * @param {String} dir - * @returns {String} - */ - function getSubdirName(dir) { - var dirFiles = fs.readdirSync(dir); + registry.install(createProperty(), function(error) { + if (error) { + console.error(error); + this.exit(1); + } - if (dirFiles.length <= 0) { - throw new Error('No sub directories in ' + dir); - } + cb.bind(this)(); + }.bind(this), [microservice.identifier]); + }.bind(this)); + }.bind(this) + ); + } - for (var i in dirFiles) { - if (!dirFiles.hasOwnProperty(i)) { - continue; - } + function fetchRepository(cb) { + createRegistry.bind(this)(function(registry) { + console.log('Fetching microservice from DEEP repository'); + + registry.installModule( + depName, + depVersion, + workingDirectory, + cb.bind(this), + createProperty.bind(this)() + ); + }.bind(this)); + } - var file = dirFiles[i]; - var filePath = path.join(dir, file); + function npmInstall(repo, cb) { + console.log('Installing ' + repo + ' via NPM globally'); - if (fs.lstatSync(filePath).isDirectory()) { - return file; - } - } + new Exec('npm list -g --depth 0 ' + repo + ' || npm install -g ' + repo) + .avoidBufferOverflow() + .run(function(result) { + if (result.failed) { + console.error('Error installing ' + repo + ' globally: ' + result.error); - throw new Error('There is no directory in ' + dir); + cb(result.error); + return; + } + + cb(null); + }.bind(this)); } }; diff --git a/src/bin/commands/publish.js b/src/bin/commands/publish.js new file mode 100644 index 00000000..9535e199 --- /dev/null +++ b/src/bin/commands/publish.js @@ -0,0 +1,121 @@ +#!/usr/bin/env node +/** + * Created by AlexanderC on 8/4/15. + */ + +'use strict'; + +module.exports = function(microservicePath) { + + // @todo: move it in some json config? + var DEFAULT_REGISTRY_BASE_HOST = 'https://deep.mg'; + var SAMPLE_PROPERTY_CONFIG = { + appIdentifier: 'appIdentifier', + env: 'prod', + awsAccountId: '0000000000', + aws: { + accessKeyId: 'accessKeyId', + secretAccessKey: 'secretAccessKey', + region: 'us-east-1', + }, + }; + + var path = require('path'); + var fse = require('fs-extra'); + var tmp = require('tmp'); + var AuthToken = require('../../lib.compiled/Registry/AuthToken').AuthToken; + var RegistryConfig = require('../../lib.compiled/Registry/Config').Config; + var Registry = require('deep-package-manager').Registry_Registry; + var RegistryAuthorizer = require('deep-package-manager').Registry_Storage_Driver_Helpers_Api_Auth_Authorizer; + var Microservice = require('deep-package-manager').Microservice_Instance; + var LambdaExtractor = require('../../lib.compiled/Helpers/LambdasExtractor').LambdasExtractor; + var Property = require('deep-package-manager').Property_Instance; + var Config = require('deep-package-manager').Property_Config; + var Lambda = require('deep-package-manager').Property_Lambda; + var Core = require('deep-core'); + + var registryBaseHost = this.opts.locate('registry').value || + RegistryConfig.create().refresh('registry').read('registry') || + DEFAULT_REGISTRY_BASE_HOST; + + if (microservicePath.indexOf(path.sep) !== 0) { + microservicePath = path.join(process.cwd(), microservicePath); + } + + var tmpDirObj = tmp.dirSync(); + var tmpPropertyPath = tmpDirObj.name; + var tmpMicroservicePath = path.join(tmpPropertyPath, path.basename(microservicePath)); + + // Gracefully teardown... + (function() { + process.on('uncaughtException', function(error) { + console.error(error); + fse.removeSync(tmpPropertyPath); + this.exit(1); + }.bind(this)); + + process.on('SIGINT', function() { + console.log('Gracefully shutting down from SIGINT (Ctrl-C)...'); + fse.removeSync(tmpPropertyPath); + this.exit(1); + }.bind(this)); + }.bind(this))(); + + console.log('Copying microservice sources into ' + tmpMicroservicePath); + fse.copySync(microservicePath, tmpMicroservicePath); + + var propertyConfigFile = path.join(tmpPropertyPath, Config.DEFAULT_FILENAME); + console.log('Persisting temporary property config in ' + propertyConfigFile); + fse.outputJsonSync(propertyConfigFile, SAMPLE_PROPERTY_CONFIG); + + createRegistry.bind(this)(function(registry) { + registry.publishModule(tmpMicroservicePath, function(error) { + fse.removeSync(tmpPropertyPath); + + if (error) { + console.error('Error publishing microservice: ' + error); + this.exit(1); + } + + console.log('Microservice has been successfully published'); + }.bind(this)); + }.bind(this)); + + function getRegistryToken() { + return (new AuthToken()).refresh().toString(); + } + + var property = new Property(tmpPropertyPath); + + console.log('Cleaning up microservice'); + + console.log('Remove custom parameters and tests'); + fse.removeSync(path.join(microservicePath, Microservice.PARAMS_FILE)); + fse.removeSync(path.join(microservicePath, 'Tests')); + + (new LambdaExtractor(property)) + .extractWorking(LambdaExtractor.NPM_PACKAGE_FILTER) + .forEach(function(lambdaPath) { + console.log('Cleaning up backend in ' + lambdaPath); + + fse.removeSync(path.join(lambdaPath, 'node_modules')); + fse.removeSync(path.join(lambdaPath, Core.AWS.Lambda.Runtime.VALIDATION_SCHEMAS_DIR)); + fse.removeSync(path.join(lambdaPath, Lambda.CONFIG_FILE)); + }.bind(this)); + + function createRegistry(cb) { + console.log('Initializing remote registry'); + + Registry.createApiRegistry(registryBaseHost, function(error, registry) { + if (error) { + console.error(error); + fse.removeSync(tmpPropertyPath); + this.exit(1); + } + + registry.storage.driver.authorizer = RegistryAuthorizer.createHeaderToken(getRegistryToken()); + + cb(registry); + }.bind(this), true); + } +}; diff --git a/src/bin/commands/pull-deps.js b/src/bin/commands/pull-deps.js deleted file mode 100644 index 3fe4dee9..00000000 --- a/src/bin/commands/pull-deps.js +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env node -/** - * Created by AlexanderC on 6/19/15. - */ - -'use strict'; - -module.exports = function(mainPath) { - var aws = require('aws-sdk'); - var path = require('path'); - var fs = require('fs'); - var fse = require('fs-extra'); - var Manager = require('deep-package-manager').Dependencies_Manager; - var S3Driver = require('deep-package-manager').Dependencies_Driver_S3StdDriver; - var Config = require('deep-package-manager').Property_Config; - - var dryRun = this.opts.locate('dry-run').exists; - - var microservicesStack = []; - - var semicolonIndex = mainPath.lastIndexOf(':'); - if (-1 !== semicolonIndex) { - var microservicesRaw = mainPath.substr(semicolonIndex + 1); - - microservicesRaw.split(',').forEach(function(subPath) { - microservicesStack.push(subPath.trim()); - }); - - mainPath = mainPath.substr(0, semicolonIndex); - } - - if (mainPath.indexOf('/') !== 0) { - mainPath = path.join(process.cwd(), mainPath); - } - - var configFile = path.join(mainPath, Config.DEFAULT_FILENAME); - var configExists = fs.existsSync(configFile); - - if (!configExists) { - console.error('Missing ' + Config.DEFAULT_FILENAME + ' configuration file in ' + mainPath); - this.exit(1); - } - - var config = fse.readJsonSync(configFile); - - if (!config.dependencies - || typeof config.dependencies.bucket === 'undefined') { - - console.error('Missing dependencies or bucket properties'); - this.exit(1); - } - - var s3Bucket = config.dependencies.bucket; - var s3Prefix = config.dependencies.prefix || ''; - - aws.config.update(config.dependencies.aws || config.aws); - - var driver = new S3Driver(aws, s3Bucket); - driver.basePath = mainPath; - driver.prefix = s3Prefix; - - var mg = new Manager(driver); - mg.driver.dryRun = dryRun; - - console.log( - 'Resolving dependencies from ' + mainPath + - ' using \'s3://' + s3Bucket + '/' + s3Prefix + ' \' repository' - ); - - if (microservicesStack.length > 0) { - console.log('---> ', microservicesStack); - - mg.pullBatch(microservicesStack, function() { - console.log('Done!'); - }); - } else { - mg.pull(function() { - console.log('Done!'); - }); - } -}; diff --git a/src/bin/commands/push-deps.js b/src/bin/commands/push-deps.js deleted file mode 100644 index 18493b1a..00000000 --- a/src/bin/commands/push-deps.js +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env node -/** - * Created by AlexanderC on 6/19/15. - */ - -'use strict'; - -module.exports = function(mainPath) { - var aws = require('aws-sdk'); - var path = require('path'); - var fs = require('fs'); - var fse = require('fs-extra'); - var Manager = require('deep-package-manager').Dependencies_Manager; - var S3Driver = require('deep-package-manager').Dependencies_Driver_S3StdDriver; - var Config = require('deep-package-manager').Property_Config; - - var dryRun = this.opts.locate('dry-run').exists; - - var microservicesStack = []; - - var semicolonIndex = mainPath.lastIndexOf(':'); - if (-1 !== semicolonIndex) { - var microservicesRaw = mainPath.substr(semicolonIndex + 1); - - microservicesRaw.split(',').forEach(function(subPath) { - microservicesStack.push(subPath.trim()); - }); - - mainPath = mainPath.substr(0, semicolonIndex); - } - - if (mainPath.indexOf('/') !== 0) { - mainPath = path.join(process.cwd(), mainPath); - } - - var configFile = path.join(mainPath, Config.DEFAULT_FILENAME); - var configExists = fs.existsSync(configFile); - - if (!configExists) { - console.error('Missing ' + Config.DEFAULT_FILENAME + ' configuration file in ' + mainPath); - this.exit(1); - } - - var config = fse.readJsonSync(configFile); - - if (!config.dependencies - || typeof config.dependencies.bucket === 'undefined') { - - console.error('Missing dependencies or bucket properties'); - this.exit(1); - } - - var s3Bucket = config.dependencies.bucket; - var s3Prefix = config.dependencies.prefix || ''; - - aws.config.update(config.dependencies.aws || config.aws); - - var driver = new S3Driver(aws, s3Bucket); - driver.basePath = mainPath; - driver.prefix = s3Prefix; - - var mg = new Manager(driver); - mg.driver.dryRun = dryRun; - - console.log('Uploading dependencies from ' + mainPath + ' into s3://' + s3Bucket + '/' + s3Prefix); - - if (microservicesStack.length > 0) { - console.log('---> ', microservicesStack); - - mg.pushBatch(microservicesStack, function() { - console.log('Done!'); - }); - } else { - mg.push(function() { - console.log('Done!'); - }); - } -}; diff --git a/src/bin/commands/registry-cfg.js b/src/bin/commands/registry-cfg.js new file mode 100644 index 00000000..2e3bf309 --- /dev/null +++ b/src/bin/commands/registry-cfg.js @@ -0,0 +1,43 @@ +#!/usr/bin/env node +/** + * Created by AlexanderC on 8/4/15. + */ + +'use strict'; + +module.exports = function(parameter) { + var RegistryConfig = require('../../lib.compiled/Registry/Config').Config; + + parameter = parameter || 'unknown'; + var newValue = this.opts.locate('set').value; + var printAvailable = this.opts.locate('print').exists; + var config = RegistryConfig.create().refresh(parameter); + + if (printAvailable) { + resetDeepLog.bind(this)(); + console.log(Object.keys((new RegistryConfig()).varsMapper.MAPPING).join(', ')); + } else if (newValue) { + console.log('Setting new value of parameter "' + parameter + '"'); + + config.add(parameter, newValue); + + try { + config.persist(); + } catch (error) { + console.error('Failed to set new value of parameter "' + parameter + '": ' + error); + this.exit(1); + } + } else { + if (!config.has(parameter)) { + console.error('Missing parameter "' + parameter + '" in config'); + this.exit(1); + } + + resetDeepLog.bind(this)(); + console.log(config.read(parameter)); + } + + function resetDeepLog() { + this.constructor._logDriver.overrideJsConsole(false); + } +}; diff --git a/src/bin/commands/run-lambda.js b/src/bin/commands/run-lambda.js index fcbcffbf..f61d94af 100644 --- a/src/bin/commands/run-lambda.js +++ b/src/bin/commands/run-lambda.js @@ -14,7 +14,6 @@ module.exports = function(lambdaPath) { var os = require('os'); var Autoload = require('deep-package-manager').Microservice_Metadata_Autoload; var NpmInstallLibs = require('../../lib.compiled/NodeJS/NpmInstallLibs').NpmInstallLibs; - var NpmLink = require('../../lib.compiled/NodeJS/NpmLink').NpmLink; var Bin = require('../../lib.compiled/NodeJS/Bin').Bin; var dbServer = this.opts.locate('db-server').value || 'LocalDynamo'; @@ -26,7 +25,7 @@ module.exports = function(lambdaPath) { Autoload._skipBuild(); } - if (lambdaPath.indexOf('/') !== 0) { + if (lambdaPath.indexOf(path.sep) !== 0) { lambdaPath = path.join(process.cwd(), lambdaPath); } @@ -60,8 +59,6 @@ module.exports = function(lambdaPath) { console.log('AWS configuration found in ' + awsConfigFile); } - var mainPath = path.dirname(lambdaPath); - console.log('Linking aws-sdk library'); if (!Bin.npmModuleInstalled('aws-sdk', true)) { diff --git a/src/bin/commands/server.js b/src/bin/commands/server.js index 6680af0c..f22729cc 100644 --- a/src/bin/commands/server.js +++ b/src/bin/commands/server.js @@ -35,11 +35,11 @@ module.exports = function(mainPath) { Autoload._skipBuild(); } - if (mainPath.indexOf('/') !== 0) { + if (mainPath.indexOf(path.sep) !== 0) { mainPath = path.join(process.cwd(), mainPath); } - if (buildPath && buildPath.indexOf('/') !== 0) { + if (buildPath && buildPath.indexOf(path.sep) !== 0) { buildPath = path.join(process.cwd(), buildPath); } diff --git a/src/bin/commands/undeploy.js b/src/bin/commands/undeploy.js index 89c5df14..71643182 100644 --- a/src/bin/commands/undeploy.js +++ b/src/bin/commands/undeploy.js @@ -34,7 +34,7 @@ module.exports = function(mainPath) { } } - if (mainPath.indexOf('/') !== 0) { + if (mainPath.indexOf(path.sep) !== 0) { mainPath = path.join(process.cwd(), mainPath); } @@ -68,6 +68,8 @@ module.exports = function(mainPath) { } if (backupConfig) { + console.log('Create configuration backup in ' + this.fileNameBck); + matcher.bckConfigFile(function(error) { if (error) { console.error(error); diff --git a/src/bin/manifest.js b/src/bin/manifest.js index 24d6f907..7c98ca85 100644 --- a/src/bin/manifest.js +++ b/src/bin/manifest.js @@ -26,19 +26,35 @@ module.exports = { }, }, }, - 'install': { - example: 'deepify install https://github.com/MitocGroup/deep-microservices-todo-app.git path/to/web_app', - description: 'Install an microservice from remote git repository', + install: { + example: 'deepify install github://MitocGroup/deep-microservices-todo-app', + description: 'Install the web app or a single microservice from the registry or GitHub', opts: { + init: { + alias: 'i', + description: 'Initialize deep web app', + required: false, + }, + registry: { + alias: 'r', + description: 'Custom registry url (ex. https://deep.mg)', + required: false, + }, + 'github-auth': { + alias: 'a', + description: 'GitHub credentials pair used for Basic authentication (ex. "user:token" or simply "token")', + required: false, + }, + 'skip-github-deps': { + alias: 's', + description: 'Skip fetching dependencies when working with a GitHub hosted microservice', + required: false, + }, }, args: { - repository: { - description: 'The remote microservice git repository', - required: true, - }, - path: { - description: 'The path to dump microservice into', - required: true, + dependency: { + description: 'The dependency you want to fetch (ex. "deep.ng.todo@^0.0.x")', + required: false, }, }, }, @@ -93,6 +109,15 @@ module.exports = { example: 'deepify deploy path/to/web_app', description: 'Deploy an web app', opts: { + prod: { + description: 'Prepare web app for production and ensure prod env is used', + required: false, + }, + env: { + alias: 'e', + description: 'Web app environment to be used (default fetched from deploy config)', + required: false, + }, 'cfg-bucket': { alias: 'b', description: 'AWS S3 system bucket name where the deploy config was persisted (ex. deep.prod.system.db0c09cc)', @@ -108,11 +133,6 @@ module.exports = { description: 'Dump built web app locally into the specified directory', required: false, }, - 'pull-deps': { - alias: 'p', - description: 'Pull dependencies from the remote repository', - required: false, - }, partial: { alias: 'm', description: 'Partial deploy (one or several comma separated microservices identifiers)', @@ -163,6 +183,74 @@ module.exports = { }, }, }, + publish: { + example: 'deepify publish ./sample-microservice', + description: 'Publish microservice (may require manual approval before getting public)', + opts: { + registry: { + alias: 'r', + description: 'Custom registry url (ex. https://deep.mg)', + required: false, + }, + }, + args: { + path: { + description: 'The path to the microservice you want to publish', + required: true, + }, + }, + }, + 'registry-cfg': { + example: 'deepify registry-cfg token --set "some_custom_auth_token"', + description: 'Read/Set the registry configuration value (read unless called with --set)', + opts: { + set: { + alias: 's', + description: 'Set the registry parameter to the value given', + required: false, + }, + print: { + alias: 'p', + description: 'Print available registry parameters', + required: false, + }, + }, + args: { + parameter: { + description: 'Registry configuration parameter name', + required: false, + }, + }, + }, + 'enable-ssl': { + example: 'deepify enable-ssl path/to/web_app', + description: 'Enables SSL on a deployed web app', + opts: { + domain: { + alias: 'd', + description: 'The domain to create the certificate for (overrides the "deeploy.json" value)', + required: false, + }, + }, + args: { + path: { + description: 'The path to the web app', + required: true, + }, + }, + }, + 'disable-ssl': { + example: 'deepify disable-ssl path/to/web_app', + description: 'Disable activated SSL on a deployed web app', + opts: { + }, + args: { + path: { + description: 'The path to the web app', + required: true, + }, + }, + }, 'build-frontend': { example: 'deepify build-frontend path/to/web_app', description: 'Build frontend of a web app', @@ -219,6 +307,18 @@ module.exports = { }, }, }, + 'create-migration': { + example: 'deepify create-migration path/to/microservice', + description: 'Create empty migration for a certain microservice', + opts: { + }, + args: { + path: { + description: 'The path to the microservice', + required: true, + }, + }, + }, 'init-backend': { example: 'deepify init-backend path/to/web_app', description: 'Initialize backend', @@ -228,6 +328,11 @@ module.exports = { description: 'Partial init (one or several comma separated microservices identifiers)', required: false, }, + prod: { + alias: 'p', + description: 'Run npm install with --prod flag', + required: false, + }, }, args: { path: { @@ -262,40 +367,6 @@ module.exports = { required: true, }, }, - }, - 'pull-deps': { - example: 'deepify pull-deps path/to/web_app', - description: 'Pull web app microservices\' dependencies', - opts: { - 'dry-run': { - alias: 'd', - description: 'Work locally, without pulling dependencies from the remote repository', - required: false, - }, - }, - args: { - path: { - description: 'The path to the web app', - required: true, - }, - }, - }, - 'push-deps': { - example: 'deepify push-deps path/to/web_app', - description: 'Publish microservices from within the given web app', - opts: { - 'dry-run': { - alias: 'd', - description: 'Work locally, without pushing microservices to the remote repository', - required: false, - }, - }, - args: { - path: { - description: 'The path to the web app', - required: true, - }, - }, - }, + } }, }; diff --git a/src/bin/registry-server.js b/src/bin/registry-server.js new file mode 100755 index 00000000..55bdfbd2 --- /dev/null +++ b/src/bin/registry-server.js @@ -0,0 +1,43 @@ +#!/usr/bin/env node +/** + * Created by AlexanderC on 2/23/16. + */ + +var input = process.argv; +input.shift(); +input.shift(); + +var RegistryServer = require('deep-package-manager').Registry_Local_Server; +var path = require('path'); + +var serverPath = path.join(process.cwd(), '.deepRegistry'); + +if (input.length > 0) { + serverPath = input[0]; + + if (serverPath.indexOf(path.sep) !== 0) { + serverPath = path.join(process.cwd(), serverPath); + } +} + +var server = new RegistryServer(serverPath); + +server.start(function(error) { + if (error) { + console.error(error); + process.exit(1); + } + + console.log('--->', RegistryServer.DEFAULT_REGISTRY_HOST); + console.info('\nPress Ctrl-C to stop the server'); +}); + +process.on('uncaughtException', function(error) { + server.stop(); + console.error(error); + process.exit(1); +}); + +process.on('SIGINT', function() { + server.stop(); +}); diff --git a/src/hooks/autocomplete_deepify.sh b/src/hooks/autocomplete_deepify.sh index 6967a784..ade95d0d 100755 --- a/src/hooks/autocomplete_deepify.sh +++ b/src/hooks/autocomplete_deepify.sh @@ -5,6 +5,46 @@ BASH_PROFILE="$HOME/.bash_profile" COMPLETION_ADD=". $SCRIPT_PATH/deepify_comp.sh" IS_MAC=$(uname -a | grep Darwin) CURRENT_USER=$(whoami) +NODE_VERSION=$(node -v) +NODE_VERSION="${NODE_VERSION/v/}" + +do_version_check() { + + [ "$1" == "$2" ] && return 2 + + ver1front=`echo $1 | cut -d "." -f -1` + ver1back=`echo $1 | cut -d "." -f 2-` + + ver2front=`echo $2 | cut -d "." -f -1` + ver2back=`echo $2 | cut -d "." -f 2-` + + if [ "$ver1front" != "$1" ] || [ "$ver2front" != "$2" ]; then + [ "$ver1front" -gt "$ver2front" ] && return 3 + [ "$ver1front" -lt "$ver2front" ] && return 1 + + [ "$ver1front" == "$1" ] || [ -z "$ver1back" ] && ver1back=0 + [ "$ver2front" == "$2" ] || [ -z "$ver2back" ] && ver2back=0 + do_version_check "$ver1back" "$ver2back" + return $? + else + [ "$1" -gt "$2" ] && return 3 || return 1 + fi +} + +do_version_check "${NODE_VERSION}" "4.2.4" + +VERSION_STATUS=$? + +if ([ "$OSTYPE" == "win32" ] || [ "$OSTYPE" == "win64" ] || [ "$OSTYPE" == "msys" ]) && [ $VERSION_STATUS -lt 2 ]; then + echo "Node ${NODE_VERSION} on Windows doesn’t support prompt! Do you want to use in no interaction mode?[Y/N]" + read answer + if echo "$answer" | grep -iq "^y" ;then + export DEEP_NO_INTERACTION=1 + echo "Added environment variable DEEP_NO_INTERACTION=1 for Windows" + else + echo "Please update your node version on Windows to minimum 4.2.4" + fi +fi if [ "$IS_MAC" = "" ]; then BASH_PROFILE="$HOME/.bashrc" diff --git a/src/lib/Helpers/ValidationSchemasSync.js b/src/lib/Helpers/ValidationSchemasSync.js new file mode 100644 index 00000000..ebece279 --- /dev/null +++ b/src/lib/Helpers/ValidationSchemasSync.js @@ -0,0 +1,127 @@ +/** + * Created by AlexanderC on 1/26/16. + */ + +'use strict'; + +import path from 'path'; +import fs from 'fs'; +import fse from 'fs-extra'; +import Core from 'deep-core'; +import {Microservice_Metadata_Action as Action} from 'deep-package-manager'; +import {Property_ValidationSchema as ValidationSchema} from 'deep-package-manager'; + +export class ValidationSchemasSync { + /** + * @param {Property} property + */ + constructor(property) { + this._property = property; + } + + /** + * @param {Server} server + * @returns {ValidationSchemasSync} + */ + static createFromServer(server) { + return new this(server.property); + } + + /** + * @returns {Property} + */ + get property() { + return this._property; + } + + /** + * @param {Function|null} filter + * @returns {ValidationSchemasSync} + */ + syncWorking(filter = null) { + return ValidationSchemasSync._sync( + this._property.workingMicroservices, + filter + ); + } + + /** + * @param {Function|null} filter + * @returns {ValidationSchemasSync} + */ + sync(filter = null) { + return ValidationSchemasSync._sync( + this._property.microservices, + filter + ); + } + + /** + * @param {Object} microservices + * @param {Function|null} filter + * @returns {ValidationSchemasSync} + */ + static _sync(microservices, filter = null) { + let lambdas = []; + let validationSchemasDirs = []; + + for (let i in microservices) { + if (!microservices.hasOwnProperty(i)) { + continue; + } + + let microservice = microservices[i]; + + validationSchemasDirs.push(microservice.autoload.validation); + + for (let j in microservice.resources.actions) { + if (!microservice.resources.actions.hasOwnProperty(j)) { + continue; + } + + let microserviceRoute = microservice.resources.actions[j]; + + if (microserviceRoute.type === Action.LAMBDA) { + let lambdaPath = path.join( + microservice.autoload.backend, + microserviceRoute.source + ); + + lambdas.push(lambdaPath); + } + } + } + + let validationSchemas = ValidationSchema.create(...validationSchemasDirs); + + lambdas = filter + ? lambdas.filter(filter) + : lambdas; + + lambdas.forEach((lambdaPath) => { + let schemasPath = path.join(lambdaPath, Core.AWS.Lambda.Runtime.VALIDATION_SCHEMAS_DIR); + + if (fs.existsSync(schemasPath)) { + fse.removeSync(schemasPath); + } + + validationSchemas.forEach((schema) => { + let schemaPath = schema.schemaPath; + let destinationSchemaPath = path.join(schemasPath, `${schema.name}.js`); + + fse.copySync(schemaPath, destinationSchemaPath); + }); + }); + + return this; + } + + /** + * @returns {Function} + */ + static get NPM_PACKAGE_FILTER() { + return (lambdaPath) => { + return fs.existsSync(path.join(lambdaPath, 'package.json')); + }; + } +} diff --git a/src/lib/Lambda/Runtime.js b/src/lib/Lambda/Runtime.js index dabe8ef8..7fddc150 100644 --- a/src/lib/Lambda/Runtime.js +++ b/src/lib/Lambda/Runtime.js @@ -96,6 +96,13 @@ export class Runtime { return this._name; } + /** + * @returns {String} + */ + get arnName() { + return this._name.replace(/\-\d+$/i, ''); + } + /** * @param {String} name */ @@ -177,7 +184,7 @@ export class Runtime { invokeAsync: function (localPath, data, callback) { this.invoke(localPath, data, (error, result) => { if (error) { - _this._log(`Lambda ${data.lambda} async execution fail: ${error.message}`); + _this._log(`Lambda ${data.lambda} async execution fail`, error); return; } @@ -276,12 +283,12 @@ export class Runtime { /** make the context Lambda alike */ awsRequestId: '6bde10dc-a329-11e5-8f4d-55470b0a5783', invokeid: '6bde10dc-a329-11e5-8f4d-55470b0a5783', - logGroupName: `/aws/lambda/${this._name}`, + logGroupName: `/aws/lambda/${this.arnName}`, logStreamName: `${logStreamDate}/${logStreamDate}[$LATEST]e680b516b0ea402eb3ff38f10b40a264`, - functionName: this._name, + functionName: this.arnName, memoryLimitInMB: '128', functionVersion: '$LATEST', - invokedFunctionArn: `arn:aws:lambda:::function:${this._name}`, + invokedFunctionArn: `arn:aws:lambda:::function:${this.arnName}`, clientContext: { EnvironmentName: Runtime.ENVIRONMENT, diff --git a/src/lib/Lambda/thread_wrapper.js b/src/lib/Lambda/thread_wrapper.js index c2a44eca..78ab3819 100644 --- a/src/lib/Lambda/thread_wrapper.js +++ b/src/lib/Lambda/thread_wrapper.js @@ -53,6 +53,12 @@ runtime.name = rawRuntime._name; runtime.fail = (error) => { if (assureContextNotSent(error)) { + if (typeof error !== 'string') { + try { + error = JSON.parse(error); + } catch (e) {} + } + process.send({ state: 'fail', args: [error], diff --git a/src/lib/NodeJS/Bin.js b/src/lib/NodeJS/Bin.js index 50a0c478..08ee7d41 100644 --- a/src/lib/NodeJS/Bin.js +++ b/src/lib/NodeJS/Bin.js @@ -48,6 +48,9 @@ export class Bin { Bin._npm = Bin.resolve('npm'); + //@todo - temporary solution for windows -> node exists globally so we can use without full path + Bin._npm = (Bin._isWin) ? 'npm': Bin._npm; + return Bin._npm; } @@ -56,6 +59,7 @@ export class Bin { */ static get node() { if (Bin._node) { + return Bin._node; } @@ -67,6 +71,9 @@ export class Bin { Bin._node = Bin.resolve('node'); } + //@todo - temporary solution for windows -> node exists globally so we can use without full path + Bin._node = (Bin._isWin) ? 'node': Bin._node; + return Bin._node; } @@ -75,11 +82,8 @@ export class Bin { * @returns {String} */ static resolve(bin) { - if (Bin._isWin) { - throw new Error('Unable to resolve a binary on win* platform'); - } - - let cmd = new Exec('which', bin).runSync(); + let locatorCmd = (Bin._isWin) ? 'where': 'which'; + let cmd = new Exec(locatorCmd, bin).runSync(); if (cmd.failed) { throw cmd.error; diff --git a/src/lib/NodeJS/NpmDependency.js b/src/lib/NodeJS/NpmDependency.js index 1b8f3057..e5a417f6 100644 --- a/src/lib/NodeJS/NpmDependency.js +++ b/src/lib/NodeJS/NpmDependency.js @@ -142,7 +142,7 @@ export class NpmDependency { /** * @param {String} dependencyName * @param {String|RegExp|null} version - * @returns {NpmDependency|null} + * @returns {NpmDependency|null|*} */ find(dependencyName, version = null) { let depObj = NpmDependency._resolveFullDepName(dependencyName); @@ -216,6 +216,8 @@ export class NpmDependency { * @returns {NpmDependency} */ removeUndefined() { + let childrenCopy = []; + for (let i in this._children) { if (!this._children.hasOwnProperty(i)) { continue; @@ -224,13 +226,15 @@ export class NpmDependency { let childDep = this._children[i]; if (!childDep.version || childDep.version === 'undefined') { - delete this._children[i]; continue; } + childrenCopy.push(childDep); childDep.removeUndefined(); } + this._children = childrenCopy; + return this; } @@ -287,6 +291,10 @@ export class NpmDependency { addChild(child) { this._children.push(child); + if (child.parent !== this) { + child.parent = this; + } + return this; } @@ -309,6 +317,10 @@ export class NpmDependency { */ set parent(parent) { this._parent = parent; + + if (this._parent.children.indexOf(this) === -1) { + this._parent.addChild(this); + } } /** @@ -348,7 +360,6 @@ export class NpmDependency { depData.name = depName; let dep = NpmDependency.createFromRawObject(depData, isMain); - dep.parent = mainDep; mainDep.addChild(dep); } @@ -370,7 +381,7 @@ export class NpmDependency { */ toString(noHeader = false) { let str = ''; - let pad = ' '.repeat(this._getParentsDepth()); + let pad = new Array(this._getParentsDepth()).join(' '); if (!noHeader) { str += `- ${pad}${this.fullName}${os.EOL}`; diff --git a/src/lib/NodeJS/NpmInstall.js b/src/lib/NodeJS/NpmInstall.js index ee09a5cf..b5e92505 100644 --- a/src/lib/NodeJS/NpmInstall.js +++ b/src/lib/NodeJS/NpmInstall.js @@ -53,31 +53,39 @@ export class NpmInstall { * @returns {NpmInstall} */ runChunk(cb, chunkSize = NpmInstall.DEFAULT_CHUNK_SIZE, silent = NpmInstall.DEFAULT_SILENT_STATE) { - let wait = new WaitFor(); - let chunks = NpmInstall._chunkArray(this._dirs, chunkSize); - let remaining = chunks.length; - - wait.push(() => { - return remaining <= 0; - }); + if (this._dirs.length <= 0) { + cb(); + return this; + } - for (let i in chunks) { - if (!chunks.hasOwnProperty(i)) { - continue; - } + this._runChunkItem( + NpmInstall._chunkArray(this._dirs, chunkSize), + silent, + cb + ); - let chunk = chunks[i]; + return this; + } - let instance = this._newInstance(...chunk); + /** + * @param {Array} chunks + * @param {Boolean} silent + * @param {Function} cb + * @private + */ + _runChunkItem(chunks, silent, cb) { + let chunk = chunks.shift(); - instance.run(() => { - remaining--; - }, silent); - } + let instance = this._newInstance(...chunk); - wait.ready(cb); + instance.run(() => { + if (chunks.length <= 0) { + cb(); + return; + } - return this; + this._runChunkItem(chunks, silent, cb); + }, silent); } /** @@ -184,6 +192,6 @@ export class NpmInstall { * @returns {Number} */ static get DEFAULT_CHUNK_SIZE() { - return 4; + return 3; } } diff --git a/src/lib/NodeJS/UndefinedDepsResolver.js b/src/lib/NodeJS/UndefinedDepsResolver.js index d7b3f5e1..fd0f609e 100644 --- a/src/lib/NodeJS/UndefinedDepsResolver.js +++ b/src/lib/NodeJS/UndefinedDepsResolver.js @@ -137,7 +137,6 @@ export class UndefinedDepsResolver { this._cloneChildrenStack(childDep, clonedDep); parentDep.addChild(clonedDep); - clonedDep.parent = parentDep; if (!clonedDep.version || clonedDep.version === 'undefined') { let shadowKey = `${clonedDep.name}@${clonedDep.requestedVersion}`; diff --git a/src/lib/Registry/AuthToken.js b/src/lib/Registry/AuthToken.js new file mode 100644 index 00000000..924b2082 --- /dev/null +++ b/src/lib/Registry/AuthToken.js @@ -0,0 +1,49 @@ +/** + * Created by AlexanderC on 2/19/16. + */ + +'use strict'; + +import {Config} from './Config'; + +export class AuthToken { + /** + * @param {String|null} token + */ + constructor(token = null) { + this._token = token; + } + + /** + * @returns {String|null|*} + */ + get token() { + return this._token; + } + + /** + * @returns {AuthToken} + */ + refresh() { + this._token = Config + .create() + .refresh(AuthToken.TOKEN_VAR_NAME) + .read(AuthToken.TOKEN_VAR_NAME) || 'ANON'; + + return this; + } + + /** + * @returns {String} + */ + static get TOKEN_VAR_NAME() { + return 'DEEP_REGISTRY_AUTH_TOKEN'; + } + + /** + * @returns {String} + */ + toString() { + return this._token.toString(); + } +} diff --git a/src/lib/Registry/Config.js b/src/lib/Registry/Config.js new file mode 100644 index 00000000..f069cdce --- /dev/null +++ b/src/lib/Registry/Config.js @@ -0,0 +1,185 @@ +/** + * Created by AlexanderC on 2/19/16. + */ + +'use strict'; + +import os from 'os'; +import path from 'path'; +import fse from 'fs-extra'; +import fs from 'fs'; +import {ConfigVars} from './ConfigVars'; +import {_extend as extend} from 'util'; +import {Registry_Storage_Driver_Helpers_Api_RegistryAutoDiscovery as RegistryAutoDiscovery} from 'deep-package-manager'; + +export class Config { + /** + * @param {Object} config + */ + constructor(config = {}) { + this._config = config; + this._varsMapper = ConfigVars; + } + + /** + * @param {String[]|String|*} readFromGlobalsNames + * @returns {Config} + */ + refresh(...readFromGlobalsNames) { + let envConfig = this._config; + let fileConfig = {}; + + readFromGlobalsNames.forEach((name) => { + let value = this._readVarFromEnvVar(name) || + this._readVarFromGlobal(name); + + if (value) { + envConfig[this._varsMapper.map(name)] = value; + } + }); + + let configFile = Config.CONFIG_FILE; + + if (fs.existsSync(configFile)) { + fileConfig = fse.readJsonSync(configFile); + } + + this._config = extend(this._config, envConfig, fileConfig); + + return this; + } + + /** + * @returns {Config} + */ + persist() { + fse.outputJsonSync(Config.CONFIG_FILE, this._config); + + return this; + } + + /** + * @param {String} name + * @param {*} value + * @returns {Config} + */ + add(name, value) { + this._config[this._varsMapper.map(name)] = value; + + return this; + } + + /** + * @param {String} name + * @returns {Boolean} + */ + has(name) { + return this._config.hasOwnProperty(this._varsMapper.map(name)); + } + + /** + * @returns {*} + */ + read(name) { + return this._config[this._varsMapper.map(name)]; + } + + /** + * @param {String} name + * @returns {Config} + */ + unset(name) { + delete this._config[this._varsMapper.map(name)]; + + return this; + } + + /** + * @param {String} name + * @returns {*} + * @private + */ + _readVarFromEnvVar(name) { + return process.env[this._varsMapper.unMap(name)]; + } + + /** + * @param {String} name + * @returns {*} + * @private + */ + _readVarFromGlobal(name) { + return global[this._varsMapper.unMap(name)]; + } + + /** + * @returns {ConfigVars|Object} + */ + get varsMapper() { + return this._varsMapper; + } + + /** + * @param {ConfigVars|Object} mapper + */ + set varsMapper(mapper) { + this._varsMapper = mapper; + } + + /** + * @returns {Object} + */ + get rawConfig() { + return this._config; + } + + /** + * @returns {Object} + */ + get config() { + let conf = {}; + + for (let name in this._config) { + if (!this._config.hasOwnProperty(name)) { + continue; + } + + conf[this._varsMapper.unMap(name)] = this._config[name]; + } + + return conf; + } + + /** + * @param {Boolean} createIfMissing + * @returns {Config} + */ + static create(createIfMissing = true) { + let configFile = Config.CONFIG_FILE; + let config = {}; + + if (!fs.existsSync(configFile)) { + if (createIfMissing) { + fse.outputJsonSync(configFile, config); + } + } else { + config = fse.readJsonSync(configFile); + } + + return new Config(config); + } + + /** + * @returns {String} + */ + static get CONFIG_FILE() { + return path.join(RegistryAutoDiscovery.DEFAULT_CONFIG_DIR, Config.CONFIG_FILE_NAME); + } + + /** + * @returns {String} + */ + static get CONFIG_FILE_NAME() { + return 'deepify.config.json'; + } +} diff --git a/src/lib/Registry/ConfigVars.js b/src/lib/Registry/ConfigVars.js new file mode 100644 index 00000000..cd10c82a --- /dev/null +++ b/src/lib/Registry/ConfigVars.js @@ -0,0 +1,54 @@ +/** + * Created by AlexanderC on 2/22/16. + */ + +'use strict'; + +import {AuthToken} from './AuthToken'; + +export class ConfigVars { + + /** + * @param {String} name + * @returns {String} + */ + static map(name) { + return ConfigVars.REVERSED_MAPPING[name] || name; + } + + /** + * @param {String} name + * @returns {String} + */ + static unMap(name) { + return ConfigVars.MAPPING[name] || name; + } + + /** + * @returns {Object} + */ + static get REVERSED_MAPPING() { + let mapping = ConfigVars.MAPPING; + let obj = {}; + + for (let name in mapping) { + if (!mapping.hasOwnProperty(name)) { + continue; + } + + obj[mapping[name]] = name; + } + + return obj; + } + + /** + * @returns {Object} + */ + static get MAPPING() { + return { + token: AuthToken.TOKEN_VAR_NAME, // DEEP_REGISTRY_AUTH_TOKEN + registry: 'DEEP_REGISTRY_BASE_HOST', + }; + } +} diff --git a/src/lib/Server/Instance.js b/src/lib/Server/Instance.js index 60a73aa2..f1c52128 100644 --- a/src/lib/Server/Instance.js +++ b/src/lib/Server/Instance.js @@ -21,6 +21,7 @@ import {Property_Frontend as Frontend} from 'deep-package-manager'; import {Microservice_Metadata_Action as Action} from 'deep-package-manager'; import {Property_Config as Config} from 'deep-package-manager'; import DeepDB from 'deep-db'; +import DeepFS from 'deep-fs'; import {Hook} from './Hook'; export class Instance { @@ -28,7 +29,7 @@ export class Instance { * @param {Property} property */ constructor(property) { - if (!property instanceof Property) { + if (!(property instanceof Property)) { throw new PropertyObjectRequiredException(); } @@ -37,6 +38,8 @@ export class Instance { }; this._property = property; this._server = null; + this._fs = null; + this._host = null; this._localId = 0; @@ -54,6 +57,13 @@ export class Instance { this._setup(); } + /** + * @returns {DeepFS} + */ + get fs() { + return this._fs; + } + /** * @returns {String} */ @@ -235,6 +245,41 @@ export class Instance { return new Instance(new Property(mainPath, configFile)); } + /** + * @returns {Object} + * @private + */ + get _kernelMock() { + let lambdasArns = Object.keys(this._defaultLambdasConfig); + + let kernel = { + config: { + buckets: { + system: { + name: '' + }, + public: { + name: '' + }, + temp: { + name: '' + } + }, + }, + microservice: () => { + return { + identifier: '', + }; + }, + }; + + if (lambdasArns.length > 0) { + kernel.config = this._defaultLambdasConfig[lambdasArns[0]]; + } + + return kernel; + } + /** * @param {Number} port * @param {String} dbServer @@ -245,56 +290,67 @@ export class Instance { let hook = new Hook(this); hook.runBefore(() => { - this._log(`Creating server on port ${port}`); - - this._server = Http.createServer((...args) => { - this._handler(...args); - }); + this._log('Booting local FS'); - var localDbInstance = null; + this._fs = new DeepFS(); + this._fs.localBackend = true; - this._server.listen(port, (error) => { - if (error) { - throw new FailedToStartServerException(port, error); - } + this._fs.boot(this._kernelMock, () => { + this._log(`Linking custom validation schemas`); - this._host = `http://localhost:${port}`; + Frontend.dumpValidationSchemas(this._property.config, this._fs.public._rootFolder, true); - this._log('HTTP Server is up and running!'); - - if (!dbServer) { - hook.runAfter(() => { - callback(this); - }); + this._log(`Creating server on port ${port}`); - return; - } + this._server = Http.createServer((...args) => { + this._handler(...args); + }); - this._log(`Creating local DynamoDB instance on port ${DeepDB.LOCAL_DB_PORT}`); + var localDbInstance = null; - localDbInstance = DeepDB.startLocalDynamoDBServer((error) => { + this._server.listen(port, (error) => { if (error) { throw new FailedToStartServerException(port, error); } - this._log(`You can access DynamoDB Console via http://localhost:${DeepDB.LOCAL_DB_PORT}/shell`); + this._host = `http://localhost:${port}`; - hook.runAfter(() => { - callback(this); - }); - }, dbServer); - }); + this._log('HTTP Server is up and running!'); - // @todo: move it in destructor? - process.on('exit', () => { - this.stop(() => { - if (localDbInstance) { - localDbInstance.stop(() => { - process.exit(0); + if (!dbServer) { + hook.runAfter(() => { + callback(this); }); - } else { - process.exit(0); + + return; } + + this._log(`Creating local DynamoDB instance on port ${DeepDB.LOCAL_DB_PORT}`); + + localDbInstance = DeepDB.startLocalDynamoDBServer((error) => { + if (error) { + throw new FailedToStartServerException(port, error); + } + + this._log(`You can access DynamoDB Console via http://localhost:${DeepDB.LOCAL_DB_PORT}/shell`); + + hook.runAfter(() => { + callback(this); + }); + }, dbServer); + }); + + // @todo: move it in destructor? + process.on('exit', () => { + this.stop(() => { + if (localDbInstance) { + localDbInstance.stop(() => { + process.exit(0); + }); + } else { + process.exit(0); + } + }); }); }); }); @@ -334,7 +390,7 @@ export class Instance { lambda.name = `${lambdaConfig.name}-${this.localId}`; - lambda.succeed = (result) => { + let successCb = (result) => { let plainResult = JSON.stringify(result); if (!asyncMode) { @@ -345,12 +401,29 @@ export class Instance { } }; + lambda.succeed = successCb; + lambda.fail = (error) => { + let errorObj = error; + + if (typeof error === 'object' && error instanceof Error) { + errorObj = { + errorType: error.name, + errorMessage: error.message, + errorStack: error.stack + }; + + if (error.validationErrors) { + errorObj.validationErrors = error.validationErrors; + } + } + if (!asyncMode) { - this._log(`Lambda ${lambdaConfig.name} execution fail: ${error.message}`); - this._send500(response, error); + this._log(`Lambda ${lambdaConfig.name} execution fail`, errorObj.errorMessage); + + successCb(errorObj); } else { - this._log(`Lambda ${lambdaConfig.name} async execution fail: ${error.message}`); + this._log(`Lambda ${lambdaConfig.name} async execution fail`, errorObj.errorMessage); } }; @@ -449,14 +522,21 @@ export class Instance { return; } - FileSystem.exists(filename, function(exists) { + FileSystem.exists(filename, (exists) => { if (!exists) { - this._log(`File ${filename} not found`); - this._send404(response); - return; + if (this._fs.public.existsSync(uri)) { + filename = Path.join(this._fs.public._rootFolder, uri); + + this._log(`{LFS} ${uri} resolved into ${filename}`); + } else { + this._log(`File ${filename} not found`); + this._send404(response); + + return; + } } - FileSystem.stat(filename, function(error, stats) { + FileSystem.stat(filename, (error, stats) => { if (error) { this._log(`Unable to stat file ${filename}: ${error}`); this._send500(response, error); @@ -469,7 +549,7 @@ export class Instance { filename = Path.join(filename, 'index.html'); } - FileSystem.readFile(filename, 'binary', function(error, file) { + FileSystem.readFile(filename, 'binary', (error, file) => { if (error) { this._log(`Unable to read file ${filename}: ${error}`); this._send500(response, error); @@ -480,9 +560,9 @@ export class Instance { this._log(`Serving file ${filename} of type ${mimeType}`); this._send(response, file, 200, mimeType, true); - }.bind(this)); - }.bind(this)); - }.bind(this)); + }); + }); + }); } /** @@ -511,7 +591,7 @@ export class Instance { * @private */ _resolveMicroservice(uri) { - let parts = uri.replace(/^\/(.+)$/, '$1').split(Path.sep); + let parts = uri.replace(/^\/(.+)$/, '$1').split('/'); if (parts.length > 0) { for (let identifier in this._microservices) { diff --git a/src/lib/Terminal/Arguments.js b/src/lib/Terminal/Arguments.js index 95507180..37205968 100644 --- a/src/lib/Terminal/Arguments.js +++ b/src/lib/Terminal/Arguments.js @@ -39,7 +39,7 @@ export class Arguments { * @returns {Arguments} */ merge(sibling) { - if (!sibling instanceof Arguments) { + if (!(sibling instanceof Arguments)) { throw new ArgumentsObjectRequiredException(); } @@ -141,7 +141,7 @@ export class Arguments { * @returns {Arguments} */ add(argument) { - if (!argument instanceof Argument) { + if (!(argument instanceof Argument)) { throw new ArgumentObjectRequiredException(); } diff --git a/src/lib/Terminal/Exception/MissingArgumentException.js b/src/lib/Terminal/Exception/MissingArgumentException.js index 0b958e79..cef24b8a 100644 --- a/src/lib/Terminal/Exception/MissingArgumentException.js +++ b/src/lib/Terminal/Exception/MissingArgumentException.js @@ -13,7 +13,7 @@ export class MissingArgumentException extends ValidationException { * @param {Argument} argument */ constructor(argument) { - if (!argument instanceof Argument) { + if (!(argument instanceof Argument)) { throw new ArgumentObjectRequiredException(); } diff --git a/src/lib/Terminal/Exception/MissingOptionException.js b/src/lib/Terminal/Exception/MissingOptionException.js index 4c37723a..dfec3aea 100644 --- a/src/lib/Terminal/Exception/MissingOptionException.js +++ b/src/lib/Terminal/Exception/MissingOptionException.js @@ -13,7 +13,7 @@ export class MissingOptionException extends ValidationException { * @param {Option} option */ constructor(option) { - if (!option instanceof Option) { + if (!(option instanceof Option)) { throw new OptionObjectRequiredException(); } diff --git a/src/lib/Terminal/Help.js b/src/lib/Terminal/Help.js index e35491c5..3b91ee82 100644 --- a/src/lib/Terminal/Help.js +++ b/src/lib/Terminal/Help.js @@ -15,7 +15,7 @@ export class Help { constructor(program) { this._program = program; - if (!program instanceof Program) { + if (!(program instanceof Program)) { throw new ProgramInstanceRequiredException(); } } diff --git a/src/lib/Terminal/Options.js b/src/lib/Terminal/Options.js index bffc142f..93851fd4 100644 --- a/src/lib/Terminal/Options.js +++ b/src/lib/Terminal/Options.js @@ -38,7 +38,7 @@ export class Options { * @returns {Options} */ merge(sibling) { - if (!sibling instanceof Options) { + if (!(sibling instanceof Options)) { throw new OptionsObjectRequiredException(); } @@ -119,7 +119,7 @@ export class Options { * @returns {Options} */ add(option) { - if (!option instanceof Option) { + if (!(option instanceof Option)) { throw new OptionObjectRequiredException(); } diff --git a/src/lib/Terminal/Program.js b/src/lib/Terminal/Program.js index 41092e59..f702ccd9 100644 --- a/src/lib/Terminal/Program.js +++ b/src/lib/Terminal/Program.js @@ -299,7 +299,7 @@ export class Program { * @returns {Program} */ addCommand(subProgram) { - if (!subProgram instanceof Program) { + if (!(subProgram instanceof Program)) { throw new ProgramInstanceRequiredException(); } diff --git a/src/node-bin/compile.sh b/src/node-bin/compile.sh new file mode 100644 index 00000000..d65c99b2 --- /dev/null +++ b/src/node-bin/compile.sh @@ -0,0 +1,9 @@ +if [ "$TRAVIS" == "true" ]; then + echo "Skipping code transpiling to ES5 becuase we are in travis" +elif [ -d 'lib/' ] && [ "$OSTYPE" != "win32" ] && [ "$OSTYPE" != "win64" ]; then + BABEL_ENV=production babel lib/ --out-dir lib.compiled/; +elif [ "$OSTYPE" == "win32" ] || [ "$OSTYPE" == "win64" ]; then + echo "You should have installed and configured http://git-scm.com/ and run all bash command by using git-bash.exe" +else + echo "Skipping code transpiling to ES5..." +fi diff --git a/src/node-bin/obfuscate-compiled.sh b/src/node-bin/obfuscate-compiled.sh new file mode 100644 index 00000000..824f0dbb --- /dev/null +++ b/src/node-bin/obfuscate-compiled.sh @@ -0,0 +1,9 @@ +if [ "$TRAVIS" == "true" ]; then + echo "Skipping running uglify becuase we are in travis" +elif [ -d 'lib/' ] && [ "$OSTYPE" != "msys" ] && [ "$OSTYPE" != "win32" ] && [ "$OSTYPE" != "win64" ]; then + `which uglify` || npm install uglify -g; for f in $(find lib.compiled -type f -name *.js); do uglify -s ${f} -o ${f}; done; +elif [ -d 'lib/' ] && ([ "$OSTYPE" == "win32" ] || [ "$OSTYPE" == "win64" ]); then + echo "You should have installed and configured http://git-scm.com/ and run all bash command by using git-bash.exe" +elif [ -d 'lib/' ] && [ "$OSTYPE" == "msys" ]; then + echo "Running obfuscate-compiled from git-bash without results" +fi diff --git a/src/node-bin/test.sh b/src/node-bin/test.sh new file mode 100644 index 00000000..f30be637 --- /dev/null +++ b/src/node-bin/test.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +if [ "$OSTYPE" != "msys" ] && [ "$OSTYPE" != "win32" ] && [ "$OSTYPE" != "win64" ]; then + babel-node `which isparta` cover --include 'lib/**/*.js' `which _mocha` -- 'test/**/*.spec.js' --reporter spec --ui tdd --recursive --timeout 20s +elif [ "$OSTYPE" == "win32" ] || [ "$OSTYPE" == "win64" ]; then + echo "You should have installed and configured http://git-scm.com/ and run all bash command by using git-bash.exe" +else + echo "Running from git-bash without gathering coverage" + babel-node `which _mocha` --ui tdd --recursive --reporter spec +fi diff --git a/src/package.json b/src/package.json index ea9aa9c1..9f6d5bd0 100644 --- a/src/package.json +++ b/src/package.json @@ -1,6 +1,6 @@ { "name": "deepify", - "version": "1.5.0", + "version": "1.7.0", "description": "DEEP Development Tools", "keywords": [ "Digital Enterprise End-To-End Platform", @@ -48,15 +48,16 @@ }, "scripts": { "preinstall": "npm run compile", - "postinstall": "hooks/autocomplete_deepify.sh", - "test": "babel-node `which isparta` cover --include 'lib/**/*.js' _mocha -- 'test/**/*.js' --reporter spec --ui tdd", - "compile": "if [ -d 'lib/' ]; then BABEL_ENV=production babel lib/ --out-dir lib.compiled/; fi", - "obfuscate-compiled": "if [ -d 'lib/' ]; then npm list -g --depth 0 uglify > /dev/null 2>&1 || npm install uglify -g; for f in $(find lib.compiled -type f -name *.js); do uglify -s ${f} -o ${f}; done; fi", + "postinstall": "bash hooks/autocomplete_deepify.sh", + "test": "bash node-bin/test.sh", + "compile": "bash node-bin/compile.sh", + "obfuscate-compiled": "bash node-bin/obfuscate-compiled.sh", "prepublish": "npm run obfuscate-compiled" }, "dependencies": { "deep-package-manager": "^1.x.x", "deep-log": "^1.x.x", + "deep-fs": "^1.x.x", "deep-db": "^1.x.x", "deep-core": "^1.x.x", "proxyquire": "^1.5.x", @@ -78,9 +79,6 @@ "node": ">=0.12 <5.0", "npm": ">=2.10 <3.0" }, - "os": [ - "!win32" - ], "preferGlobal": true, "analyze": true } diff --git a/src/test/Helpers/Exec.spec.js b/src/test/Helpers/Exec.spec.js new file mode 100644 index 00000000..121bdd22 --- /dev/null +++ b/src/test/Helpers/Exec.spec.js @@ -0,0 +1,10 @@ +'use strict'; + +import chai from 'chai'; +import {Exec} from '../../lib/Helpers/Exec'; + +suite('Helpers/Exec', () => { + test('Class Exec exists in Helpers/Exec', () => { + chai.expect(Exec).to.be.an('function'); + }); +}); diff --git a/src/test/Helpers/LambdasExtractor.spec.js b/src/test/Helpers/LambdasExtractor.spec.js new file mode 100644 index 00000000..d98dcfe8 --- /dev/null +++ b/src/test/Helpers/LambdasExtractor.spec.js @@ -0,0 +1,53 @@ +'use strict'; + +import chai from 'chai'; +import {LambdasExtractor} from '../../lib/Helpers/LambdasExtractor'; +import {Property_Instance as PropertyInstance} from 'deep-package-manager'; + +suite('Helpers/LambdasExtractor', () => { + let propertyInstance = null; + let lambdasExtractor = null; + + test('Class LambdasExtractor exists in Helpers/LambdasExtractor', () => { + chai.expect(LambdasExtractor).to.be.an('function'); + }); + + test('Check _extract()', () => { + propertyInstance = new PropertyInstance('./test/TestMaterials/Property2', 'deeploy.test.json'); + + lambdasExtractor = new LambdasExtractor(propertyInstance); + + chai.expect(propertyInstance).to.be.an.instanceOf(PropertyInstance); + chai.expect(lambdasExtractor).to.be.an.instanceOf(LambdasExtractor); + chai.expect(lambdasExtractor.property).to.equal(propertyInstance); + }); + + test('Check _extract()', () => { + let microservices = { + 'hello.world.example': { + isRoot: false, + parameters: {}, + resources: { + sample: { + 'say-hello': { + type: 'lambda', + methods: [ + 'POST', + ], + forceUserIdentity: true, + region: 'us-west-2', + source: { + api: 'https://1zf47jpvxd.execute-api.us-west-2.amazonaws.com/dev/hello-world-example/sample/say-hello', + original: 'arn:aws:lambda:us-west-2:389615756922:function:DeepDevSampleSayHello64232f3705a', + }, + }, + }, + }, + }, + }; + + let actualResult = LambdasExtractor._extract(microservices); + + chai.expect(actualResult).to.be.eql([]); + }); +}); diff --git a/src/test/Helpers/ValidationSchemasSync.spec.js b/src/test/Helpers/ValidationSchemasSync.spec.js new file mode 100644 index 00000000..300057ff --- /dev/null +++ b/src/test/Helpers/ValidationSchemasSync.spec.js @@ -0,0 +1,13 @@ +// THIS TEST WAS GENERATED AUTOMATICALLY ON Wed Feb 03 2016 11:01:21 GMT+0200 (EET) + +'use strict'; + +import chai from 'chai'; +import {ValidationSchemasSync} from '../../lib/Helpers/ValidationSchemasSync'; + +// @todo: Add more advanced tests +suite('Helpers/ValidationSchemasSync', function() { + test('Class ValidationSchemasSync exists in Helpers/ValidationSchemasSync', () => { + chai.expect(ValidationSchemasSync).to.be.an('function'); + }); +}); diff --git a/src/test/Lambda/ForksManager.spec.js b/src/test/Lambda/ForksManager.spec.js new file mode 100644 index 00000000..874d1303 --- /dev/null +++ b/src/test/Lambda/ForksManager.spec.js @@ -0,0 +1,72 @@ +'use strict'; + +import chai from 'chai'; +import {ForksManager} from '../../lib/Lambda/ForksManager'; +import ChildProcess from 'child_process'; +import path from 'path'; + +suite('Lambda/ForksManager', function () { + let forkPath = path.join(__dirname + './../TestMaterials/PathToForked/child'); + + test('Class ForksManager exists in Lambda/ForksManager', function () { + chai.expect(typeof ForksManager).to.equal('function'); + }); + + test('Check STORAGE_KEY static getter', function () { + chai.expect(ForksManager.STORAGE_KEY).to.equal('_deep_fm_forks_'); + }); + + test('Check SIGKILL_KEY static getter', function () { + chai.expect(ForksManager.SIGKILL_KEY).to.equal('_deep_fm_sigkill_'); + }); + + test('Check _isManaged static getter return false', function () { + chai.expect(ForksManager._isManaged, '_isManaged returns false').to.equal(false); + }); + + test('Check _wasMainProcessKilled static getter return false', function () { + chai.expect(ForksManager._wasMainProcessKilled, '_wasMainProcessKilled returns false').to.equal(false); + }); + + test('Check _isForksStackEmpty static getter return true', function () { + chai.expect(ForksManager._isForksStackEmpty, '_isForksStackEmpty returns false').to.equal(true); + }); + + test('Check manage() for !ForksManager._isManaged', function () { + let fork = ChildProcess.fork(forkPath); + + let actualResult = ForksManager.manage(fork); + + chai.expect(actualResult, 'returns undefined').to.equal(undefined); + chai.expect(ForksManager._isManaged, '_isManaged returns false').to.equal(false); + chai.expect(ForksManager._wasMainProcessKilled, '_wasMainProcessKilled returns false').to.equal(false); + chai.expect(ForksManager._isForksStackEmpty, '_isForksStackEmpty returns false').to.equal(true); + }); + + test('Check registerListener() for !ForksManager._isManaged', function () { + ForksManager.registerListener(); + + chai.expect(ForksManager._isManaged, '_isManaged returns true').to.equal(true); + chai.expect(ForksManager._isForksStackEmpty, '_isForksStackEmpty returns true').to.equal(true); + chai.expect(ForksManager._wasMainProcessKilled, '_wasMainProcessKilled returns false').to.equal(false); + }); + + test('Check manage() > _addForkToStack() ', function() { + let fork = ChildProcess.fork(forkPath); + + ForksManager.manage(fork); + + chai.expect(ForksManager._isManaged, '_isManaged returns true').to.equal(true); + chai.expect(ForksManager._isForksStackEmpty, '_isForksStackEmpty returns false').to.equal(false); + chai.expect(ForksManager._wasMainProcessKilled, '_wasMainProcessKilled returns false').to.equal(false); + }); + + test('Check registerListener() for ForksManager._isManaged', function () { + let actualResult = ForksManager.registerListener(); + + chai.expect(actualResult, 'returns undefined').to.equal(undefined); + chai.expect(ForksManager._isManaged, '_isManaged returns true').to.equal(true); + chai.expect(ForksManager._isForksStackEmpty, '_isForksStackEmpty returns false').to.equal(false); + chai.expect(ForksManager._wasMainProcessKilled, '_wasMainProcessKilled returns false').to.equal(false); + }); +}); diff --git a/src/test/Lambda/Runtime.js b/src/test/Lambda/Runtime.js deleted file mode 100644 index 0c817860..00000000 --- a/src/test/Lambda/Runtime.js +++ /dev/null @@ -1,13 +0,0 @@ -// THIS TEST WAS GENERATED AUTOMATICALLY ON Mon Sep 14 2015 16:15:41 GMT+0300 (EEST) - -'use strict'; - -import chai from 'chai'; -import {Runtime} from '../../lib/Lambda/Runtime'; - -// @todo: Add more advanced tests -suite('Lambda/Runtime', function() { - test('Class Runtime exists in Lambda/Runtime', function() { - chai.expect(typeof Runtime).to.equal('function'); - }); -}); diff --git a/src/test/Lambda/Runtime.spec.js b/src/test/Lambda/Runtime.spec.js new file mode 100644 index 00000000..e2c46cdd --- /dev/null +++ b/src/test/Lambda/Runtime.spec.js @@ -0,0 +1,229 @@ +'use strict'; + +import chai from 'chai'; +import path from 'path'; +import {Runtime} from '../../lib/Lambda/Runtime'; +import {Timer} from '../../lib/Lambda/Timer'; + +suite('Lambda/Runtime', () => { + let runtime = null; + let lambda = { + name: 'testLambda', + handler: () => {}, + }; + let lambdaPath = path.join(__dirname + './../TestMaterials/Lambda/lambda'); + + test('Class Runtime exists in Lambda/Runtime', () => { + chai.expect(Runtime).to.be.an('function'); + }); + + test('Check constructor sets _lambda', () => { + runtime = new Runtime(lambda); + + chai.expect(runtime).to.be.an.instanceOf(Runtime); + chai.expect(runtime.lambda).to.eql(lambda); + }); + + test('Check constructor sets _measureTime = false', () => { + chai.expect(runtime.measureTime).to.equal(false); + }); + + test('Check constructor sets _timer = null', () => { + chai.expect(runtime._timer).to.equal(null); + }); + + test('Check constructor sets _silent = false', () => { + chai.expect(runtime.silent).to.equal(false); + }); + + test('Check constructor sets _name ', () => { + chai.expect(runtime.name).to.not.equal(''); + }); + + test('Check constructor sets _lambdaPath = null', () => { + chai.expect(runtime.lambdaPath).to.equal(null); + }); + + test('Check constructor sets _awsConfigFile = null', () => { + chai.expect(runtime._awsConfigFile).to.equal(null); + }); + + test('Check ENVIRONMENT static getter', () => { + chai.expect(Runtime.ENVIRONMENT).to.equal('local'); + }); + + test('Check SIBLING_EXEC_WRAPPER_NAME static getter', () => { + chai.expect(Runtime.SIBLING_EXEC_WRAPPER_NAME).to.equal('_deep_lambda_exec_'); + }); + + test('Check createLambda()', () => { + let actualResult = Runtime.createLambda(lambdaPath); + + chai.expect(actualResult).to.be.an.instanceOf(Runtime); + chai.expect(actualResult.awsConfigFile).to.be.equal(null); + }); + + test('Check complete setter/getter', () => { + let cb = () => { + return 'test cb'; + }; + let complete = runtime.complete; + + runtime.complete = cb; + chai.expect(runtime.complete).to.equal(cb); + + runtime.complete = null; + chai.expect(runtime.complete).to.equal(null); + + runtime.complete = complete; + chai.expect(runtime.complete).to.equal(complete); + }); + + test('Check fail setter/getter', () => { + let cb = () => { + return 'test cb fail'; + }; + let fail = runtime.fail; + + runtime.fail = cb; + chai.expect(runtime.fail).to.equal(cb); + + runtime.fail = null; + chai.expect(runtime.fail).to.equal(null); + + runtime.fail = fail; + chai.expect(runtime.fail).to.equal(fail); + }); + + test('Check succeed setter/getter', () => { + let cb = () => { + return 'test cb succeed'; + }; + let succeed = runtime.succeed; + + runtime.succeed = cb; + chai.expect(runtime.succeed).to.equal(cb); + + runtime.succeed = null; + chai.expect(runtime.succeed).to.equal(null); + + runtime.succeed = succeed; + chai.expect(runtime.succeed).to.equal(succeed); + }); + + test('Check succeed setter/getter', () => { + let measureTime = runtime.measureTime; + + runtime.measureTime = true; + chai.expect(runtime.measureTime).to.equal(true); + chai.expect(runtime._timer).to.be.an.instanceOf(Timer); + + runtime.measureTime = false; + chai.expect(runtime.measureTime).to.equal(false); + chai.expect(runtime._timer).to.be.an.instanceOf(Timer); + + runtime.succeed = measureTime; + chai.expect(runtime.measureTime).to.equal(measureTime); + }); + + test('Check name setter/getter', () => { + let name = runtime.name; + let lambdaName = 'new test name'; + + runtime.name = lambdaName; + chai.expect(runtime.name).to.equal(lambdaName); + + runtime.name = null; + chai.expect(runtime.name).to.equal(null); + + runtime.name = name; + chai.expect(runtime.name).to.equal(name); + }); + + test('Check silent setter/getter', () => { + let silent = runtime.silent; + + runtime.silent = true; + chai.expect(runtime.silent).to.equal(true); + + runtime.silent = false; + chai.expect(runtime.silent).to.equal(false); + + runtime.silent = null; + chai.expect(runtime.silent).to.equal(null); + + runtime.silent = silent; + chai.expect(runtime.silent).to.equal(silent); + }); + + test('Check context getter', () => { + let expectedContextKeys = [ + 'awsRequestId', + 'fail', + 'functionName', + 'functionVersion', + 'invokedFunctionArn', + 'invokeid', + 'logGroupName', + 'logStreamName', + 'memoryLimitInMB', + 'succeed', + ]; + let actualResult = Object.keys(runtime.context); + + expectedContextKeys.forEach((value) => { + chai.expect(actualResult).to.include(value); + }); + chai.expect(runtime.context.succeed).to.be.an('function'); + chai.expect(runtime.context.fail).to.be.an('function'); + }); + + test('Check _injectSiblingExecutionWrapper', () => { + let actualResult = runtime._injectSiblingExecutionWrapper(); + chai.expect(global[Runtime.SIBLING_EXEC_WRAPPER_NAME]).to.be.an('object'); + chai.expect(global[Runtime.SIBLING_EXEC_WRAPPER_NAME].invoke).to.be.an('function'); + chai.expect(global[Runtime.SIBLING_EXEC_WRAPPER_NAME].invokeAsync).to.be.an('function'); + }); + + test('Check run for _measureTime = undefined', () => { + let event = { + firstInputArg: 'test', + secondInputArg: 2, + thirdInputArg: false, + fourthInputArg: null, + }; + + let actualResult = runtime.run(event); + + chai.expect(actualResult.lambda).to.be.an('object'); + chai.expect(actualResult.lambda.handler).to.be.an('function'); + chai.expect(actualResult.lambda.name).to.equal(lambda.name); + }); + + test('Check run for _measureTime != undefined', () => { + let event = { + firstInputArg: 'test', + secondInputArg: 2, + thirdInputArg: false, + fourthInputArg: null, + }; + + let actualResult = runtime.run(event, true); + + chai.expect(actualResult.lambda).to.be.an('object'); + chai.expect(actualResult.measureTime).to.equal(true); + chai.expect(actualResult._timer).to.be.an.instanceOf(Timer); + chai.expect(actualResult.lambda.handler).to.be.an('function'); + chai.expect(actualResult.lambda.name).to.equal(lambda.name); + }); + + test('Check _logCallback()', () => { + runtime.silent = true; + let type = 'number'; + + let actualResult = runtime._logCallback(type); + + chai.expect(actualResult).to.be.an('function'); + chai.expect(actualResult()).to.be.equal(undefined); + }); +}); diff --git a/src/test/Lambda/Thread.js b/src/test/Lambda/Thread.js deleted file mode 100644 index 7e4b1dbb..00000000 --- a/src/test/Lambda/Thread.js +++ /dev/null @@ -1,13 +0,0 @@ -// THIS TEST WAS GENERATED AUTOMATICALLY ON Mon Sep 14 2015 16:15:41 GMT+0300 (EEST) - -'use strict'; - -import chai from 'chai'; -import {Thread} from '../../lib/Lambda/Thread'; - -// @todo: Add more advanced tests -suite('Lambda/Thread', function() { - test('Class Thread exists in Lambda/Thread', function() { - chai.expect(typeof Thread).to.equal('function'); - }); -}); diff --git a/src/test/Lambda/Thread.spec.js b/src/test/Lambda/Thread.spec.js new file mode 100644 index 00000000..e139f35a --- /dev/null +++ b/src/test/Lambda/Thread.spec.js @@ -0,0 +1,69 @@ +'use strict'; + +import chai from 'chai'; +import path from 'path'; +import {Thread} from '../../lib/Lambda/Thread'; +import {Runtime} from '../../lib/Lambda/Runtime'; + +suite('Lambda/Thread', () => { + let lambda = { + name: 'testLambda', + handler: () => {}, + }; + let lambdaPath = path.join(__dirname + './../TestMaterials/Lambda/lambda'); + let runtime = new Runtime(lambda, lambdaPath); + let thread = null; + + test('Class Thread exists in Lambda/Thread', () => { + chai.expect(Thread).to.be.an('function'); + }); + + test('Check constructor sets _lambda', () => { + thread = new Thread(runtime); + + chai.expect(thread).to.be.an.instanceOf(Thread); + chai.expect(thread.runtime).to.be.an.instanceOf(Runtime); + chai.expect(thread.process).to.equal(null); + }); + + test('Check WRAPPER static getter', () => { + chai.expect(Thread.WRAPPER).to.contains('thread_wrapper.js'); + }); + + test('Check _cleanup()', () => { + let actualResult = thread._cleanup(); + + chai.expect(actualResult).to.be.an.instanceOf(Thread); + chai.expect(thread.process).to.equal(null); + }); + + test('Check run()', () => { + runtime.silent = true; + let event = { + firstInputArg: 'test', + secondInputArg: 2, + thirdInputArg: false, + fourthInputArg: null, + }; + + //'spawnargs', '_eventsCount' do not exist in node 0.12 + let expectedResultArray = [ + 'domain', '_events', '_maxListeners', + '_closesNeeded', '_closesGot', 'connected', 'signalCode', + 'exitCode', 'killed', 'spawnfile', '_handle', + 'pid', 'stdin', 'stdout', 'stderr', + 'stdio', '_channel', '_handleQueue', 'send', '_send', + 'disconnect', '_disconnect', + ]; + + let actualResult = thread.run(event, false); + let processKeys = Object.keys(thread.process); + + chai.expect(actualResult).to.be.an.instanceOf(Thread); + + expectedResultArray.forEach((value) => { + chai.expect(processKeys).to.include(value); + }); + }); + +}); diff --git a/src/test/Lambda/Timer.spec.js b/src/test/Lambda/Timer.spec.js new file mode 100644 index 00000000..cee304cb --- /dev/null +++ b/src/test/Lambda/Timer.spec.js @@ -0,0 +1,87 @@ +'use strict'; + +import chai from 'chai'; +import {Timer} from '../../lib/Lambda/Timer'; + +suite('Lambda/Timer', () => { + let name = 'testName'; + let timer = null; + let approxStartTime = null; + let approxStoptTime = null; + + test('Class Timer exists in Lambda/Timer', () => { + chai.expect(Timer).to.be.an('function'); + }); + + test('Check constructor sets _name, _startTime, _stopTime', () => { + timer = new Timer(name); + + chai.expect(timer).to.be.an.instanceOf(Timer); + chai.expect(timer.name).to.equal(name); + chai.expect(timer.startTime).to.equal(null); + chai.expect(timer.startTimeSec).to.equal(null); + chai.expect(timer.stopTime).to.equal(null); + chai.expect(timer.stopTimeSec).to.equal(null); + }); + + test('Check start()', () => { + approxStartTime = new Date().getTime(); + + let actualResult = timer.start(); + + chai.expect(timer).to.be.an.instanceOf(Timer); + chai.expect(actualResult.startTime).to.be.at.least(approxStartTime); + chai.expect(actualResult.startTimeSec).to.be.at.least( + parseFloat((approxStartTime / 1000).toFixed(2)) + ); + }); + + test('Check stop()', () => { + let actualResult = timer.stop(); + + approxStoptTime = new Date().getTime(); + + chai.expect(timer).to.be.an.instanceOf(Timer); + chai.expect(approxStoptTime).to.be.at.least(actualResult.stopTime); + chai.expect( + parseFloat((approxStoptTime / 1000).toFixed(2)) + ).to.be.at.least(actualResult.stopTimeSec); + }); + + test('Check time getter', () => { + let expectedResult = approxStoptTime - approxStartTime; + + chai.expect(expectedResult).to.be.at.least(timer.time); + chai.expect( + parseFloat((expectedResult / 1000).toFixed(2)) + ).to.be.at.least(timer.timeSec); + }); + + test('Check toString() for sec', () => { + chai.expect(timer.toString()).to.equal( + `Timing for ${name}: ${timer.timeSec} seconds` + ); + }); + + test('Check toString() for !sec', () => { + chai.expect(timer.toString(false)).to.equal( + `Timing for ${name}: ${timer.time} miliseconds` + ); + }); + + test('Check time for !startTime', () => { + //arrange + approxStartTime = new Date().getTime(); + + timer = new Timer(name); + + //act + let actualResult = timer.time; + + approxStoptTime = new Date().getTime(); + let expectedResult = approxStoptTime - approxStartTime; + + //assert + chai.expect(expectedResult).to.be.at.least(actualResult); + }); +}); diff --git a/src/test/NodeJS/Bin.spec.js b/src/test/NodeJS/Bin.spec.js new file mode 100644 index 00000000..c5686c4c --- /dev/null +++ b/src/test/NodeJS/Bin.spec.js @@ -0,0 +1,40 @@ +'use strict'; + +import chai from 'chai'; +import {Bin} from '../../lib/NodeJS/Bin'; + +suite('NodeJS/Bin', () => { + test('Class Bin exists in NodeJS/Bin', () => { + chai.expect(Bin).to.be.an('function'); + }); + + test('Check npmModuleInstalled() returns true for global mocha', () => { + chai.expect(Bin.npmModuleInstalled('mocha', true)).to.equal(true); + }); + + test('Check npmModuleInstalled() returns true for local fs-extra', () => { + chai.expect(Bin.npmModuleInstalled('fs-extra', false)).to.equal(true); + }); + + test('Check npmMajorVersion returns version above than 1', () => { + chai.expect(Bin.npmMajorVersion).to.be.above(1); + }); + + test('Check nodeMajorVersion returns version above than -1', () => { + chai.expect(Bin.nodeMajorVersion).to.be.above(-1); + }); + + test('Check node returns valid string', () => { + chai.expect(Bin.node).to.contain('node'); + + //check when Bin._node already setted + chai.expect(Bin.node).to.contain('node'); + }); + + test('Check npm returns valid string', () => { + chai.expect(Bin.npm).to.contain('npm'); + + //check when Bin._node already setted + chai.expect(Bin.npm).to.contain('npm'); + }); +}); diff --git a/src/test/NodeJS/DepsTreeOptimizer.spec.js b/src/test/NodeJS/DepsTreeOptimizer.spec.js new file mode 100644 index 00000000..03ca5682 --- /dev/null +++ b/src/test/NodeJS/DepsTreeOptimizer.spec.js @@ -0,0 +1,10 @@ +'use strict'; + +import chai from 'chai'; +import {DepsTreeOptimizer} from '../../lib/NodeJS/DepsTreeOptimizer'; + +suite('NodeJS/DepsTreeOptimizer', () => { + test('Class DepsTreeOptimizer exists in NodeJS/DepsTreeOptimizer', () => { + chai.expect(DepsTreeOptimizer).to.be.an('function'); + }); +}); diff --git a/src/test/NodeJS/NpmChain.spec.js b/src/test/NodeJS/NpmChain.spec.js new file mode 100644 index 00000000..b3476c23 --- /dev/null +++ b/src/test/NodeJS/NpmChain.spec.js @@ -0,0 +1,35 @@ +'use strict'; + +import chai from 'chai'; +import {NpmChain} from '../../lib/NodeJS/NpmChain'; +import {NpmInstall} from '../../lib/NodeJS/NpmInstall'; +import {NpmLink} from '../../lib/NodeJS/NpmLink'; +import {NpmUpdate} from '../../lib/NodeJS/NpmUpdate'; + +suite('NodeJS/NpmChain', () => { + let npmLink = null; + let npmUpdate = new NpmUpdate(); + let npmChain = null; + + test('Class NpmChain exists in NodeJS/NpmChain', () => { + chai.expect(NpmChain).to.be.an('function'); + }); + + test('Check constructor sets _commands', () => { + let libs = 'mocha isparta'; + npmLink = new NpmLink(); + npmLink.libs = libs; + + npmChain = new NpmChain(npmLink); + chai.expect(npmChain).to.be.an.instanceOf(NpmChain); + chai.expect(npmChain.commands[0]).to.be.an.instanceOf(NpmInstall); + chai.expect(npmChain.commands).to.include(npmLink); + }); + + test('Check add() adds command', () => { + npmChain.add(npmUpdate); + chai.expect(npmChain).to.be.an.instanceOf(NpmChain); + chai.expect(npmChain.commands).to.include(npmLink); + chai.expect(npmChain.commands).to.include(npmUpdate); + }); +}); diff --git a/src/test/NodeJS/NpmDedupe.spec.js b/src/test/NodeJS/NpmDedupe.spec.js new file mode 100644 index 00000000..81cfcac6 --- /dev/null +++ b/src/test/NodeJS/NpmDedupe.spec.js @@ -0,0 +1,32 @@ +'use strict'; + +import chai from 'chai'; +import {NpmDedupe} from '../../lib/NodeJS/NpmDedupe'; +import {NpmInstall} from '../../lib/NodeJS/NpmInstall'; + +suite('NodeJS/NpmDedupe', () => { + let npmDedupe = null; + + test('Class NpmDedupe exists in NodeJS/NpmDedupe', () => { + chai.expect(NpmDedupe).to.be.an('function'); + }); + + test('Check constructor sets _cmd = null', () => { + npmDedupe = new NpmDedupe(); + chai.expect(npmDedupe).to.be.an.instanceOf(NpmDedupe); + }); + + test('Check _newInstance() returns new NpmInstall instance with dirs.length = 3', () => { + let args = ['mocha', '-g', 'logLevel=debug']; + + let actualResult = npmDedupe._newInstance(args); + + chai.expect(actualResult).to.be.an.instanceOf(NpmInstall); + chai.expect(actualResult.extraArgs).to.eql([]); + chai.expect(actualResult.dirs).to.eql(args); + }); + + test('Check _mainCmd getter returns valid string', () => { + chai.expect(npmDedupe._mainCmd).to.includes('npm dedupe'); + }); +}); diff --git a/src/test/NodeJS/NpmDependency.spec.js b/src/test/NodeJS/NpmDependency.spec.js new file mode 100644 index 00000000..7f7bf268 --- /dev/null +++ b/src/test/NodeJS/NpmDependency.spec.js @@ -0,0 +1,51 @@ +'use strict'; + +import chai from 'chai'; +import {NpmDependency} from '../../lib/NodeJS/NpmDependency'; + +suite('NodeJS/NpmDependency', () => { + let name = 'mocha'; + let version = '2.3.4'; + let npmDependency = new NpmDependency(name, version); + + test('Class NpmDependency exists in NodeJS/NpmDependency', () => { + chai.expect(NpmDependency).to.be.an('function'); + }); + + test(`Check constructor sets _name = ${name}`, () => { + chai.expect(npmDependency).to.be.an.instanceOf(NpmDependency); + chai.expect(npmDependency.name).to.equal(name); + }); + + test(`Check constructor sets _version = ${version}`, () => { + chai.expect(npmDependency.version).to.equal(version); + }); + + test(`Check constructor sets _requestedVersion = ${version}`, () => { + chai.expect(npmDependency.requestedVersion).to.equal(version); + }); + + test('Check constructor sets _parent = null', () => { + chai.expect(npmDependency.parent).to.equal(null); + }); + + test('Check constructor sets _children = []', () => { + chai.expect(npmDependency.children).to.eql([]); + }); + + test('Check constructor sets isMain = false by default', () => { + chai.expect(npmDependency.isMain).to.eql(false); + }); + + test('Check constructor sets _defaultRootPath = ""', () => { + chai.expect(npmDependency.defaultRootPath).to.eql(''); + }); + + test(`Check fullName returns ${name}@${version}`, () => { + chai.expect(npmDependency.fullName).to.eql(`${name}@${version}`); + }); + + test('Check NODE_MODULES_DIR', () => { + chai.expect(NpmDependency.NODE_MODULES_DIR).to.equal('node_modules'); + }); +}); diff --git a/src/test/NodeJS/NpmInstall.spec.js b/src/test/NodeJS/NpmInstall.spec.js new file mode 100644 index 00000000..413fa712 --- /dev/null +++ b/src/test/NodeJS/NpmInstall.spec.js @@ -0,0 +1,82 @@ +'use strict'; + +import chai from 'chai'; +import {NpmInstall} from '../../lib/NodeJS/NpmInstall'; + +suite('NodeJS/NpmInstall', () => { + let npmInstall = new NpmInstall(); + + test('Class NpmInstall exists in NodeJS/NpmInstall', () => { + chai.expect(NpmInstall).to.be.an('function'); + }); + + test('Check DEFAULT_SILENT_STATE getter returns false', () => { + chai.expect(NpmInstall.DEFAULT_SILENT_STATE).to.equal(false); + }); + + test('Check DEFAULT_CHUNK_SIZE getter returns value above than 0', () => { + chai.expect(NpmInstall.DEFAULT_CHUNK_SIZE).to.above(0); + }); + + test('Check _mainCmd getter returns valid string', () => { + chai.expect(npmInstall._mainCmd).to.includes('npm install'); + }); + + test('Check constructor sets valid value for _dirs', () => { + chai.expect(npmInstall).to.be.an.instanceOf(NpmInstall); + chai.expect(npmInstall.dirs).to.be.eql([]); + }); + + test('Check constructor sets valid value for _extraArgs', () => { + chai.expect(Array.isArray(npmInstall.extraArgs)).to.be.equal(true); + chai.expect(npmInstall.extraArgs).to.eql([]); + }); + + test('Check _execArgs getter', () => { + chai.expect(Array.isArray(npmInstall._execArgs)).to.be.equal(true); + chai.expect(npmInstall._execArgs[0]).to.includes('npm install'); + }); + + test('Check addExtraArg()', () => { + let length = npmInstall.extraArgs.length; + let newArg = 'logLevel=debug'; + + let actualResult = npmInstall.addExtraArg(newArg); + + chai.expect(actualResult).to.be.an.instanceOf(NpmInstall); + chai.expect(actualResult.extraArgs.length).to.equal(++length); + chai.expect(actualResult.extraArgs.pop()).to.equal(newArg); + }); + + test('Check _chunkArray() returns valid array', () => { + let toChunkArray = ['one', 'two', 'three', 'four']; + let expectedResult = [['one', 'two', 'three'], ['four']]; + let size = 3; + + let actualResult = NpmInstall._chunkArray(toChunkArray, size); + + chai.expect(actualResult).to.be.an.instanceOf(Array); + chai.expect(actualResult).to.eql(expectedResult); + }); + + test('Check _newInstance() returns new NpmInstall instance with dirs.length = 3', () => { + let args = ['mocha', '-g', 'logLevel=debug']; + + let actualResult = npmInstall._newInstance(args); + + chai.expect(actualResult).to.be.an.instanceOf(NpmInstall); + chai.expect(actualResult.extraArgs).to.eql([]); + chai.expect(actualResult.dirs).to.eql(args); + }); + + test('Check _newInstance() returns new NpmInstall instance with dirs.length = 3', () => { + let args = ['mocha']; + + let actualResult = npmInstall._newInstance(args); + + chai.expect(actualResult).to.be.an.instanceOf(NpmInstall); + chai.expect(actualResult.extraArgs).to.eql([]); + chai.expect(actualResult.dirs).to.eql(args); + }); + +}); diff --git a/src/test/NodeJS/NpmInstallLibs.spec.js b/src/test/NodeJS/NpmInstallLibs.spec.js new file mode 100644 index 00000000..de7fec73 --- /dev/null +++ b/src/test/NodeJS/NpmInstallLibs.spec.js @@ -0,0 +1,52 @@ +'use strict'; + +import chai from 'chai'; +import {NpmInstallLibs} from '../../lib/NodeJS/NpmInstallLibs'; +import {NpmInstall} from '../../lib/NodeJS/NpmInstall'; + +suite('NodeJS/NpmInstallLibs', () => { + let libs = 'mocha isparta'; + let libsArray = ['codacy-coverage', 'istanbul']; + let npmInstallLibs = null; + + test('Class NpmInstallLibs exists in NodeJS/NpmInstallLibs', () => { + chai.expect(NpmInstallLibs).to.be.an('function'); + }); + + test('Check constructor sets _libsPlain = null, global = false', () => { + npmInstallLibs = new NpmInstallLibs(); + chai.expect(npmInstallLibs).to.be.an.instanceOf(NpmInstallLibs); + chai.expect(npmInstallLibs.libsPlain).to.equal(null); + chai.expect(npmInstallLibs.global).to.equal(false); + }); + + test('Check _libsPlain setter', () => { + npmInstallLibs.libs = libsArray; + chai.expect(npmInstallLibs.libsPlain).to.equal(libsArray.join(' ')); + + npmInstallLibs.libs = libs; + chai.expect(npmInstallLibs.libsPlain).to.equal(libs); + }); + + test('Check global setter', () => { + npmInstallLibs.global = false; + chai.expect(npmInstallLibs.global).to.equal(false); + + npmInstallLibs.global = true; + chai.expect(npmInstallLibs.global).to.equal(true); + chai.expect(npmInstallLibs.dirs).to.eql([process.cwd()]); + }); + + test('Check _newInstance() returns new NpmInstall instance with dirs.length = 3', () => { + let args = ['mocha', '-g', 'logLevel=debug']; + + let actualResult = npmInstallLibs._newInstance(args); + + chai.expect(actualResult).to.be.an.instanceOf(NpmInstall); + chai.expect(actualResult.libsPlain).to.equal(libs); + }); + + test('Check _mainCmd getter returns valid string', () => { + chai.expect(npmInstallLibs._mainCmd).to.includes(`npm install ${libs}`); + }); +}); diff --git a/src/test/NodeJS/NpmLink.spec.js b/src/test/NodeJS/NpmLink.spec.js new file mode 100644 index 00000000..596e2945 --- /dev/null +++ b/src/test/NodeJS/NpmLink.spec.js @@ -0,0 +1,42 @@ +'use strict'; + +import chai from 'chai'; +import {NpmLink} from '../../lib/NodeJS/NpmLink'; +import {NpmInstall} from '../../lib/NodeJS/NpmInstall'; + +suite('NodeJS/NpmLink', () => { + let libs = 'mocha isparta'; + let libsArray = ['codacy-coverage', 'istanbul']; + let npmLink = null; + + test('Class NpmLink exists in NodeJS/NpmLink', () => { + chai.expect(NpmLink).to.be.an('function'); + }); + + test('Check constructor sets _libsPlain = null', () => { + npmLink = new NpmLink(); + chai.expect(npmLink).to.be.an.instanceOf(NpmLink); + chai.expect(npmLink.libsPlain).to.equal(null); + }); + + test('Check _libsPlain setter', () => { + npmLink.libs = libsArray; + chai.expect(npmLink.libsPlain).to.equal(libsArray.join(' ')); + + npmLink.libs = libs; + chai.expect(npmLink.libsPlain).to.equal(libs); + }); + + test('Check _newInstance() returns new NpmInstall instance with dirs.length = 3', () => { + let args = ['mocha', '-g', 'logLevel=debug']; + + let actualResult = npmLink._newInstance(args); + + chai.expect(actualResult).to.be.an.instanceOf(NpmInstall); + chai.expect(actualResult.libsPlain).to.equal(libs); + }); + + test('Check _mainCmd getter returns valid string', () => { + chai.expect(npmLink._mainCmd).to.includes(`npm link ${libs}`); + }); +}); diff --git a/src/test/NodeJS/NpmListDependencies.spec.js b/src/test/NodeJS/NpmListDependencies.spec.js new file mode 100644 index 00000000..b0b1415c --- /dev/null +++ b/src/test/NodeJS/NpmListDependencies.spec.js @@ -0,0 +1,41 @@ +'use strict'; + +import chai from 'chai'; +import {NpmListDependencies} from '../../lib/NodeJS/NpmListDependencies'; +import {NpmDependency} from '../../lib/NodeJS/NpmDependency'; +import {NpmDepsListException} from '../../lib/NodeJS/Exception/NpmDepsListException'; + +suite('NodeJS/NpmListDependencies', () => { + let npmListDependencies = null + + test('Class NpmListDependencies exists in NodeJS/NpmListDependencies', () => { + chai.expect(NpmListDependencies).to.be.an('function'); + }); + + test('Check constructor sets _path', () => { + let currentDirectory = __dirname; + npmListDependencies = new NpmListDependencies(currentDirectory); + chai.expect(npmListDependencies).to.be.an.instanceOf(NpmListDependencies); + chai.expect(npmListDependencies.path).to.equal(currentDirectory); + }); + + test('Check list(0)', () => { + let actualResult = npmListDependencies.list(0); + + chai.expect(actualResult).to.be.an.instanceOf(NpmDependency); + }); + + test('Check list() throws NpmDepsListException', () => { + let error = null; + + npmListDependencies = new NpmListDependencies('invalidPath'); + + try { + npmListDependencies.list(0); + } catch (e) { + error = e; + } + + chai.expect(error).to.be.an.instanceOf(NpmDepsListException); + }); +}); diff --git a/src/test/NodeJS/NpmPrune.spec.js b/src/test/NodeJS/NpmPrune.spec.js new file mode 100644 index 00000000..d84596ce --- /dev/null +++ b/src/test/NodeJS/NpmPrune.spec.js @@ -0,0 +1,32 @@ +'use strict'; + +import chai from 'chai'; +import {NpmPrune} from '../../lib/NodeJS/NpmPrune'; +import {NpmInstall} from '../../lib/NodeJS/NpmInstall'; + +suite('NodeJS/NpmPrune', () => { + let npmPrune = null; + + test('Class NpmPrune exists in NodeJS/NpmPrune', () => { + chai.expect(NpmPrune).to.be.an('function'); + }); + + test('Check constructor sets _cmd = null', () => { + npmPrune = new NpmPrune(); + chai.expect(npmPrune).to.be.an.instanceOf(NpmPrune); + }); + + test('Check _newInstance() returns new NpmInstall instance with dirs.length = 3', () => { + let args = ['mocha', '-g', 'logLevel=debug']; + + let actualResult = npmPrune._newInstance(args); + + chai.expect(actualResult).to.be.an.instanceOf(NpmInstall); + chai.expect(actualResult.extraArgs).to.eql([]); + chai.expect(actualResult.dirs).to.eql(args); + }); + + test('Check _mainCmd getter returns valid string', () => { + chai.expect(npmPrune._mainCmd).to.includes('npm prune'); + }); +}); diff --git a/src/test/NodeJS/NpmRun.spec.js b/src/test/NodeJS/NpmRun.spec.js new file mode 100644 index 00000000..630f460c --- /dev/null +++ b/src/test/NodeJS/NpmRun.spec.js @@ -0,0 +1,40 @@ +'use strict'; + +import chai from 'chai'; +import {NpmRun} from '../../lib/NodeJS/NpmRun'; +import {NpmInstall} from '../../lib/NodeJS/NpmInstall'; + +suite('NodeJS/NpmRun', () => { + let npmRun = null; + let cmd = 'test'; + + test('Class NpmRun exists in NodeJS/NpmRun', () => { + chai.expect(NpmRun).to.be.an('function'); + }); + + test('Check constructor sets _cmd = null', () => { + npmRun = new NpmRun(); + chai.expect(npmRun).to.be.an.instanceOf(NpmRun); + chai.expect(npmRun.cmd).to.equal(null); + }); + + test('Check _newInstance() returns new NpmInstall instance with dirs.length = 3', () => { + let args = ['mocha', '-g', 'logLevel=debug']; + + let actualResult = npmRun._newInstance(args); + + chai.expect(actualResult).to.be.an.instanceOf(NpmInstall); + chai.expect(actualResult.extraArgs).to.eql([]); + chai.expect(actualResult.dirs).to.eql(args); + }); + + test('Check cmd setter', () => { + npmRun.cmd = cmd; + + chai.expect(npmRun.cmd).to.equal(cmd); + }); + + test('Check _mainCmd getter returns valid string', () => { + chai.expect(npmRun._mainCmd).to.includes(`npm run ${cmd}`); + }); +}); diff --git a/src/test/NodeJS/NpmShrinkwrap.spec.js b/src/test/NodeJS/NpmShrinkwrap.spec.js new file mode 100644 index 00000000..834763dc --- /dev/null +++ b/src/test/NodeJS/NpmShrinkwrap.spec.js @@ -0,0 +1,32 @@ +'use strict'; + +import chai from 'chai'; +import {NpmShrinkwrap} from '../../lib/NodeJS/NpmShrinkwrap'; +import {NpmInstall} from '../../lib/NodeJS/NpmInstall'; + +suite('NodeJS/NpmShrinkwrap', () => { + let npmShrinkwrap = null; + + test('Class NpmShrinkwrap exists in NodeJS/NpmShrinkwrap', () => { + chai.expect(NpmShrinkwrap).to.be.an('function'); + }); + + test('Check constructor sets _cmd = null', () => { + npmShrinkwrap = new NpmShrinkwrap(); + chai.expect(npmShrinkwrap).to.be.an.instanceOf(NpmShrinkwrap); + }); + + test('Check _newInstance() returns new NpmInstall instance with dirs.length = 3', () => { + let args = ['mocha', '-g', 'logLevel=debug']; + + let actualResult = npmShrinkwrap._newInstance(args); + + chai.expect(actualResult).to.be.an.instanceOf(NpmInstall); + chai.expect(actualResult.extraArgs).to.eql([]); + chai.expect(actualResult.dirs).to.eql(args); + }); + + test('Check _mainCmd getter returns valid string', () => { + chai.expect(npmShrinkwrap._mainCmd).to.includes('npm shrinkwrap'); + }); +}); diff --git a/src/test/NodeJS/NpmUninstallLibs.spec.js b/src/test/NodeJS/NpmUninstallLibs.spec.js new file mode 100644 index 00000000..b8c16281 --- /dev/null +++ b/src/test/NodeJS/NpmUninstallLibs.spec.js @@ -0,0 +1,42 @@ +'use strict'; + +import chai from 'chai'; +import {NpmUninstallLibs} from '../../lib/NodeJS/NpmUninstallLibs'; +import {NpmInstall} from '../../lib/NodeJS/NpmInstall'; + +suite('NodeJS/NpmUninstallLibs', () => { + let libs = 'mocha isparta'; + let libsArray = ['codacy-coverage', 'istanbul']; + let npmUninstallLibs = null; + + test('Class NpmUninstallLibs exists in NodeJS/NpmUninstallLibs', () => { + chai.expect(NpmUninstallLibs).to.be.an('function'); + }); + + test('Check constructor sets _libsPlain = null', () => { + npmUninstallLibs = new NpmUninstallLibs(); + chai.expect(npmUninstallLibs).to.be.an.instanceOf(NpmUninstallLibs); + chai.expect(npmUninstallLibs.libsPlain).to.equal(null); + }); + + test('Check _libsPlain setter', () => { + npmUninstallLibs.libs = libsArray; + chai.expect(npmUninstallLibs.libsPlain).to.equal(libsArray.join(' ')); + + npmUninstallLibs.libs = libs; + chai.expect(npmUninstallLibs.libsPlain).to.equal(libs); + }); + + test('Check _newInstance() returns new NpmInstall instance with dirs.length = 3', () => { + let args = ['mocha', '-g', 'logLevel=debug']; + + let actualResult = npmUninstallLibs._newInstance(args); + + chai.expect(actualResult).to.be.an.instanceOf(NpmInstall); + chai.expect(actualResult.libsPlain).to.equal(libs); + }); + + test('Check _mainCmd getter returns valid string', () => { + chai.expect(npmUninstallLibs._mainCmd).to.includes(`npm uninstall ${libs}`); + }); +}); diff --git a/src/test/NodeJS/NpmUpdate.spec.js b/src/test/NodeJS/NpmUpdate.spec.js new file mode 100644 index 00000000..c9788272 --- /dev/null +++ b/src/test/NodeJS/NpmUpdate.spec.js @@ -0,0 +1,32 @@ +'use strict'; + +import chai from 'chai'; +import {NpmUpdate} from '../../lib/NodeJS/NpmUpdate'; +import {NpmInstall} from '../../lib/NodeJS/NpmInstall'; + +suite('NodeJS/NpmUpdate', () => { + let npmUpdate = null; + + test('Class NpmUpdate exists in NodeJS/NpmUpdate', () => { + chai.expect(NpmUpdate).to.be.an('function'); + }); + + test('Check constructor sets _cmd = null', () => { + npmUpdate = new NpmUpdate(); + chai.expect(npmUpdate).to.be.an.instanceOf(NpmUpdate); + }); + + test('Check _newInstance() returns new NpmInstall instance with dirs.length = 3', () => { + let args = ['mocha', '-g', 'logLevel=debug']; + + let actualResult = npmUpdate._newInstance(args); + + chai.expect(actualResult).to.be.an.instanceOf(NpmInstall); + chai.expect(actualResult.extraArgs).to.eql([]); + chai.expect(actualResult.dirs).to.eql(args); + }); + + test('Check _mainCmd getter returns valid string', () => { + chai.expect(npmUpdate._mainCmd).to.includes('npm update'); + }); +}); diff --git a/src/test/NodeJS/PackageVersionResolver.spec.js b/src/test/NodeJS/PackageVersionResolver.spec.js new file mode 100644 index 00000000..89ba8f84 --- /dev/null +++ b/src/test/NodeJS/PackageVersionResolver.spec.js @@ -0,0 +1,47 @@ +'use strict'; + +import chai from 'chai'; +import {PackageVersionResolver} from '../../lib/NodeJS/PackageVersionResolver'; + +suite('NodeJS/PackageVersionResolver', () => { + let name = 'mocha'; + let version = '2.3.4'; + let nameWithVersion = 'mocha@2.3.4'; + let packagePath = './packagePath'; + let packageVersionResolver = null; + + + test('Class PackageVersionResolver exists in NodeJS/PackageVersionResolver', () => { + chai.expect(PackageVersionResolver).to.be.an('function'); + }); + + test('Check constructor for !version', () => { + packageVersionResolver = new PackageVersionResolver(packagePath, nameWithVersion, null); + + chai.expect(packageVersionResolver).to.be.an.instanceOf(PackageVersionResolver); + + chai.expect(packageVersionResolver.packagePath).to.equal(packagePath); + chai.expect(packageVersionResolver.name).to.equal(name); + chai.expect(packageVersionResolver.version).to.equal(version); + }); + + test('Check constructor for version', () => { + packageVersionResolver = new PackageVersionResolver(packagePath, name, version); + + chai.expect(packageVersionResolver).to.be.an.instanceOf(PackageVersionResolver); + + chai.expect(packageVersionResolver.packagePath).to.equal(packagePath); + chai.expect(packageVersionResolver.name).to.equal(name); + chai.expect(packageVersionResolver.version).to.equal(version); + }); + + test('Check _command() returns valid string', () => { + chai.expect(packageVersionResolver._command).to.contain( + `npm ls --loglevel silent --json ${packageVersionResolver._fullName}` + ); + }); + + test('Check _fullName() returns valid string', () => { + chai.expect(packageVersionResolver._fullName).to.equal(`${name}@'${version}'`); + }); +}); diff --git a/src/test/NodeJS/UndefinedDepsResolver.spec.js b/src/test/NodeJS/UndefinedDepsResolver.spec.js new file mode 100644 index 00000000..d14396e7 --- /dev/null +++ b/src/test/NodeJS/UndefinedDepsResolver.spec.js @@ -0,0 +1,63 @@ +'use strict'; + +import chai from 'chai'; +import {UndefinedDepsResolver} from '../../lib/NodeJS/UndefinedDepsResolver'; +import {NpmDependency} from '../../lib/NodeJS/NpmDependency'; + +suite('NodeJS/UndefinedDepsResolver', () => { + let name = 'mocha'; + let version = '2.3.4'; + let mainDep = new NpmDependency(name, version, true); + let undefinedDepsResolver = new UndefinedDepsResolver(mainDep); + + test('Class UndefinedDepsResolver exists in NodeJS/UndefinedDepsResolver', () => { + chai.expect(UndefinedDepsResolver).to.be.an('function'); + }); + + test('Check UndefinedDepsResolver constructor throws Error for !mainDep.isMain', () => { + let error = null; + let mainDep = new NpmDependency(name, version, false); + + try { + undefinedDepsResolver = new UndefinedDepsResolver(mainDep); + } catch (e) { + error = e; + } + + chai.expect(error, 'error is an instance of Error').to.be.an.instanceOf(Error); + chai.expect(error.message).to.equal(`Npm dependency ${mainDep.fullName} is not the deps tree root`); + }); + + test('Check UndefinedDepsResolver constructor throws Error for !mainDep.isMain', + () => { + let error = null; + let mainDep = new NpmDependency(name, null, true); + + try { + undefinedDepsResolver = new UndefinedDepsResolver(mainDep); + } catch (e) { + error = e; + } + + chai.expect(error, 'error is an instance of Error').to.be.an.instanceOf(Error); + chai.expect(error.message).to.equal(`Npm main dependency version have to be defined in ${mainDep.fullName}`); + } + ); + + test('Check constructor sets _mainDep', () => { + chai.expect(undefinedDepsResolver).to.be.an.instanceOf(UndefinedDepsResolver); + chai.expect(undefinedDepsResolver.mainDep).to.equal(mainDep); + }); + + test('Check constructor sets _undefinedStack', () => { + chai.expect(undefinedDepsResolver._undefinedStack).to.eql([]); + }); + + test('Check constructor sets _cloneShadow', () => { + chai.expect(undefinedDepsResolver._cloneShadow).to.eql({}); + }); + + test('Check constructor sets _resolvedStack', () => { + chai.expect(undefinedDepsResolver._resolvedStack).to.eql({}); + }); +}); diff --git a/src/test/Server/Hook.spec.js b/src/test/Server/Hook.spec.js new file mode 100644 index 00000000..36ca8f85 --- /dev/null +++ b/src/test/Server/Hook.spec.js @@ -0,0 +1,90 @@ +'use strict'; + +import chai from 'chai'; +import sinon from 'sinon'; +import sinonChai from 'sinon-chai'; +import {Hook} from '../../lib/Server/Hook'; +import {Instance} from '../../lib/Server/Instance'; +import {Property_Instance as PropertyInstance} from 'deep-package-manager'; + +chai.use(sinonChai); + +suite('Server/Hook', () => { + let hook = null; + let server = null; + let propertyInstance = null; + + test('Class Hook exists in Server/Hook', () => { + chai.expect(Hook).to.be.an('function'); + }); + + test('Check constructor sets correctly server', () => { + propertyInstance = new PropertyInstance('./test/TestMaterials/Property2', 'deeploy.test.json'); + + server = new Instance(propertyInstance); + + hook = new Hook(server); + + chai.expect(hook, 'is an instance of Hook').to.be.an.instanceOf(Hook); + chai.expect(hook.server, 'server is an instance of Server').to.be.an.instanceOf(Instance); + chai.expect(hook.server).to.be.equal(server); + }); + + test('Check BEFORE static getter', () => { + chai.expect(Hook.BEFORE).to.be.equal('before'); + }); + + test('Check AFTER static getter', () => { + chai.expect(Hook.AFTER).to.be.equal('after'); + }); + + test('Check FILE_NAME static getter', () => { + chai.expect(Hook.FILE_NAME).to.be.equal('hook.server.js'); + }); + + test('Check _createContext', () => { + chai.expect(hook._createContext('after').isAfter()).to.be.equal(true); + chai.expect(hook._createContext('after').isBefore()).to.be.equal(false); + chai.expect(hook._createContext('before').isAfter()).to.be.equal(false); + chai.expect(hook._createContext('before').isBefore()).to.be.equal(true); + }); + + test('Check _run for no hook files', () => { + //arrange + let spyCallback = sinon.spy(); + let type = 'before'; + + //act + let actualResult = hook._run(propertyInstance.microservices[0], type, spyCallback); + + //assert + chai.expect(spyCallback).to.have.been.calledWithExactly(); + chai.expect(actualResult, 'is an instance of Hook').to.be.an.instanceOf(Hook); + }); + + //@todo - need to clarify why cb is not executed + test('Check run()', () => { + let spyCallback = sinon.spy(); + let type = 'before'; + + let actualResult = hook.run(type, spyCallback); + + chai.expect(actualResult, 'is an instance of Hook').to.be.an.instanceOf(Hook); + }); + + test('Check runBefore()', () => { + let spyCallback = sinon.spy(); + + let actualResult = hook.runBefore(spyCallback); + + chai.expect(actualResult, 'is an instance of Hook').to.be.an.instanceOf(Hook); + }); + + test('Check runAfter()', () => { + let spyCallback = sinon.spy(); + + let actualResult = hook.runAfter(spyCallback); + + chai.expect(actualResult, 'is an instance of Hook').to.be.an.instanceOf(Hook); + }); +}); diff --git a/src/test/Server/Instance.js b/src/test/Server/Instance.js deleted file mode 100644 index 971993d9..00000000 --- a/src/test/Server/Instance.js +++ /dev/null @@ -1,13 +0,0 @@ -// THIS TEST WAS GENERATED AUTOMATICALLY ON Mon Sep 14 2015 16:15:41 GMT+0300 (EEST) - -'use strict'; - -import chai from 'chai'; -import {Instance} from '../../lib/Server/Instance'; - -// @todo: Add more advanced tests -suite('Server/Instance', function() { - test('Class Instance exists in Server/Instance', function() { - chai.expect(typeof Instance).to.equal('function'); - }); -}); diff --git a/src/test/Server/Instance.spec.js b/src/test/Server/Instance.spec.js new file mode 100644 index 00000000..df9bc91c --- /dev/null +++ b/src/test/Server/Instance.spec.js @@ -0,0 +1,154 @@ +'use strict'; + +import chai from 'chai'; +import sinon from 'sinon'; +import sinonChai from 'sinon-chai'; +import path from 'path'; +import {Instance} from '../../lib/Server/Instance'; +import {PropertyObjectRequiredException} from '../../lib/Server/Exception/PropertyObjectRequiredException'; +import {Property_Instance as PropertyInstance} from 'deep-package-manager'; +import {Property_Frontend as Frontend} from 'deep-package-manager'; + +chai.use(sinonChai); + +suite('Server/Instance', () => { + let server = null; + let propertyInstance = null; + + test('Class Instance exists in Server/Instance', () => { + chai.expect(Instance).to.be.an('function'); + }); + + test('Check constructor throws exception for invalid property', () => { + let error = null; + + try { + server = new Instance({}); + } catch(e) { + error = e; + } + + chai.expect(error).to.be.an.instanceOf(PropertyObjectRequiredException); + }); + + test('Check constructor sets correctly properties for valid property', () => { + propertyInstance = new PropertyInstance('./test/TestMaterials/Property2', 'deeploy.test.json'); + + server = new Instance(propertyInstance); + + chai.expect(server).to.be.an.instanceOf(Instance); + chai.expect(server.property).to.be.an.instanceOf(PropertyInstance); + chai.expect(server.property).to.be.equal(propertyInstance); + chai.expect(server.logger).to.be.an('function'); + chai.expect(server.nativeServer).to.be.equal(null); + chai.expect(server.fs).to.be.equal(null); + chai.expect(server.host).to.be.equal(null); + chai.expect(server._localId).to.be.equal(0); + }); + + test('Check logger getter/setter', () => { + let logger = server.logger; + let newLogger = (...args) => { + console.debug(...args); + }; + + server.logger = newLogger; + chai.expect(server.logger).to.be.equal(newLogger); + + server.logger = logger; + chai.expect(server.logger).to.be.equal(logger); + }); + + test('Check profiling getter/setter', () => { + let profiling = server.profiling; + + server.profiling = true; + chai.expect(server.profiling).to.be.equal(true); + + server.profiling = false; + chai.expect(server.profiling).to.be.equal(false); + + server.profiling = profiling; + chai.expect(server.profiling).to.be.equal(profiling); + }); + + test('Check localId getter postincrements _localId', () => { + let localId = server._localId; + let expectedResult = localId + 1; + + let actualResult = server.localId; + + chai.expect(actualResult).to.be.equal(localId); + chai.expect(server._localId).to.be.equal(expectedResult); + }); + + test('Check _setup()', () => { + server._setup(); + + chai.expect(server._rootMicroservice).to.have.all.keys('frontend', 'identifier', 'lambdas', 'path'); + chai.expect(server._defaultFrontendConfig).to.be.an('object'); + }); + + test('Check buildPath > _populateBuildConfig()', () => { + let error = null; + + try { + server.buildPath = path.join(__dirname, '../TestMaterials'); + } catch (e) { + error = e; + } + + console.log('error buildPath: ', error); + }); + + test('Check running returns false', () => { + chai.expect(server.running).to.equal(false); + }); + + test('Check stop() when !running', () => { + let spyCallback = sinon.spy(); + + let actualResult = server.stop(spyCallback); + + chai.expect(actualResult, 'is an instance of Server').to.be.an.instanceOf(Instance); + chai.expect(spyCallback).to.have.been.calledWithExactly(); + }); + + test('Check LAMBDA_URI static getter', () => { + chai.expect(Instance.LAMBDA_URI).to.equal('/_/lambda'); + }); + + test('Check LAMBDA_ASYNC_URI static getter', () => { + chai.expect(Instance.LAMBDA_ASYNC_URI).to.equal('/_/lambda-async'); + }); + + test('Check _kernelMock getter', () => { + let actualResult = server._kernelMock; + + chai.expect(actualResult).to.be.an('object'); + chai.expect(actualResult).to.have.all.keys('config', 'microservice'); + }); + + test('Check _resolveMicroservice', () => { + let uri = 'microservice2/p/a/t/h'; + + let actualResult = server._resolveMicroservice(uri); + + chai.expect(actualResult).to.contains('Microservice2'); + }); + + //@todo - need to rework + test('Check listen()', () => { + let actualResult = server.listen(); + }); + + //@todo - need to rework + //test('Check _runLambda()', () => { + // let response = {}; + // let lambdaConfig = {}; + // let payload = {}; + // let asyncMode = false; + // + // let actualResult = server._runLambda(response, lambdaConfig, payload, asyncMode); + //}); +}); diff --git a/src/test/Terminal/Argument.js b/src/test/Terminal/Argument.js deleted file mode 100644 index 112764d2..00000000 --- a/src/test/Terminal/Argument.js +++ /dev/null @@ -1,13 +0,0 @@ -// THIS TEST WAS GENERATED AUTOMATICALLY ON Mon Sep 14 2015 16:15:41 GMT+0300 (EEST) - -'use strict'; - -import chai from 'chai'; -import {Argument} from '../../lib/Terminal/Argument'; - -// @todo: Add more advanced tests -suite('Terminal/Argument', function() { - test('Class Argument exists in Terminal/Argument', function() { - chai.expect(typeof Argument).to.equal('function'); - }); -}); diff --git a/src/test/Terminal/Argument.spec.js b/src/test/Terminal/Argument.spec.js new file mode 100644 index 00000000..1675b2ff --- /dev/null +++ b/src/test/Terminal/Argument.spec.js @@ -0,0 +1,70 @@ +'use strict'; + +import chai from 'chai'; +import {Argument} from '../../lib/Terminal/Argument'; + +suite('Terminal/Argument', () => { + let name = 'server'; + let argument = null; + + test('Class Argument exists in Terminal/Argument', () => { + chai.expect(Argument).to.be.an('function'); + }); + + test('Check constructor sets correctly values by default', () => { + argument = new Argument(name); + + chai.expect(argument, 'is an instance of Argument').to.be.an.instanceOf(Argument); + chai.expect(argument.name).to.be.equal(name); + chai.expect(argument.description).to.be.equal(null); + chai.expect(argument.required).to.be.equal(false); + chai.expect(argument.hidden).to.be.equal(false); + chai.expect(argument.exists).to.be.equal(false); + chai.expect(argument.value).to.be.equal(undefined); + }); + + test('Check _matchNonOption() returns false', () => { + let actualResult = Argument._matchNonOption('-testArgument'); + + chai.expect(actualResult).to.be.equal(false); + }); + + test('Check _matchNonOption() returns true', () => { + let actualResult = Argument._matchNonOption('testArgument'); + + chai.expect(actualResult).to.be.equal(true); + }); + + test('Check collect()', () => { + let actualResult = argument.collect(['argumentHere']); + + chai.expect(actualResult).to.be.an.instanceOf(Argument); + chai.expect(actualResult.value).to.be.equal('argumentHere'); + }); + + test('Check hidden setter/getter', () => { + let hidden = argument.hidden; + + argument.hidden = true; + chai.expect(argument.hidden).to.be.equal(true); + + argument.hidden = false; + chai.expect(argument.hidden).to.be.equal(false); + + argument.hidden = hidden; + chai.expect(argument.hidden).to.be.equal(hidden); + }); + + test('Check required setter/getter', () => { + let required = argument.required; + + argument.required = true; + chai.expect(argument.required).to.be.equal(true); + + argument.required = false; + chai.expect(argument.required).to.be.equal(false); + + argument.required = required; + chai.expect(argument.required).to.be.equal(required); + }); +}); diff --git a/src/test/Terminal/Arguments.js b/src/test/Terminal/Arguments.js deleted file mode 100644 index 67603f3b..00000000 --- a/src/test/Terminal/Arguments.js +++ /dev/null @@ -1,13 +0,0 @@ -// THIS TEST WAS GENERATED AUTOMATICALLY ON Mon Sep 14 2015 16:15:41 GMT+0300 (EEST) - -'use strict'; - -import chai from 'chai'; -import {Arguments} from '../../lib/Terminal/Arguments'; - -// @todo: Add more advanced tests -suite('Terminal/Arguments', function() { - test('Class Arguments exists in Terminal/Arguments', function() { - chai.expect(typeof Arguments).to.equal('function'); - }); -}); diff --git a/src/test/Terminal/Arguments.spec.js b/src/test/Terminal/Arguments.spec.js new file mode 100644 index 00000000..c8d0627c --- /dev/null +++ b/src/test/Terminal/Arguments.spec.js @@ -0,0 +1,179 @@ +'use strict'; + +import chai from 'chai'; +import {Arguments} from '../../lib/Terminal/Arguments'; +import {Argument} from '../../lib/Terminal/Argument'; +import {ArgumentObjectRequiredException} from '../../lib/Terminal/Exception/ArgumentObjectRequiredException'; +import {ArgumentsObjectRequiredException} from '../../lib/Terminal/Exception/ArgumentsObjectRequiredException'; +import {MissingArgumentException} from '../../lib/Terminal/Exception/MissingArgumentException'; + +suite('Terminal/Arguments', () => { + let name = 'server'; + let argumentsInstance = null; + let argument = new Argument(name); + + test('Class Arguments exists in Terminal/Arguments', () => { + chai.expect(Arguments).to.be.an('function'); + }); + + test('Check constructor sets correctly values by default', () => { + argumentsInstance = new Arguments(); + + chai.expect(argumentsInstance, 'is an instance of Argument').to.be.an.instanceOf(Arguments); + chai.expect(argumentsInstance.list()).to.be.eql([]); + chai.expect(argumentsInstance.listUnmanaged()).to.be.eql([]); + }); + + test('Check add() throws ArgumentObjectRequiredException', () => { + let error = null; + + try { + argumentsInstance.add({}); + } catch (e) { + error = e; + } + + chai.expect( + error, 'is an instance of ArgumentObjectRequiredException' + ).to.be.an.instanceOf(ArgumentObjectRequiredException); + chai.expect(argumentsInstance.list()).to.be.eql([]); + }); + + test('Check add()', () => { + let actualResult = argumentsInstance.add(argument); + + chai.expect(actualResult).to.be.an.instanceOf(Arguments); + chai.expect(argumentsInstance.list()[0]).to.be.an.instanceOf(Argument); + chai.expect(argumentsInstance.list()[0].name).to.be.equal(name); + }); + + test('Check locate() returns argument by name', () => { + let actualResult = argumentsInstance.locate(name); + + chai.expect(actualResult).to.be.an.instanceOf(Argument); + chai.expect(actualResult).to.be.equal(argument); + }); + + test('Check locate() returns null', () => { + let actualResult = argumentsInstance.locate('non-existing name'); + + chai.expect(actualResult).to.be.equal(null); + }); + + test('Check listValues() for !includeUnmanaged', () => { + argument.collect(['logLevel=debug']); + + let actualResult = argumentsInstance.listValues(false); + + chai.expect(actualResult[0]).to.be.equal(argument.value); + }); + + test('Check hasUnmanaged() returns false', () => { + chai.expect(argumentsInstance.hasUnmanaged).to.be.equal(false); + }); + + test('Check remove()', () => { + argumentsInstance.remove(name); + + chai.expect(argumentsInstance.list()).to.be.eql([]); + }); + + test('Check create()', () => { + name = 'deploy'; + let actualResult = argumentsInstance.create(name); + + chai.expect(actualResult).to.be.an.instanceOf(Arguments); + chai.expect(argumentsInstance.list()[0]).to.be.an.instanceOf(Argument); + chai.expect(argumentsInstance.list()[0].name).to.be.equal(name); + }); + + test('Check merge()', () => { + let toMergeName = 'install'; + let sibling = new Arguments(toMergeName); + sibling.create(toMergeName); + + let actualResult = argumentsInstance.merge(sibling); + + chai.expect(actualResult).to.be.an.instanceOf(Arguments); + chai.expect(argumentsInstance.list()[0]).to.be.an.instanceOf(Argument); + chai.expect(argumentsInstance.list()[0].name).to.be.equal(name); + chai.expect(argumentsInstance.list()[1]).to.be.an.instanceOf(Argument); + chai.expect(argumentsInstance.list()[1].name).to.be.eql(toMergeName); + }); + + test('Check merge() throws ArgumentObjectRequiredException', () => { + let error = null; + let expectedResult = argumentsInstance.list(); + + try { + argumentsInstance.merge({}); + } catch (e) { + error = e; + } + + chai.expect( + error, 'is an instance of ArgumentsObjectRequiredException' + ).to.be.an.instanceOf(ArgumentsObjectRequiredException); + chai.expect(argumentsInstance.list()).to.be.eql(expectedResult); + }); + + test('Check populate()', () => { + let argumentItem = 'logLevel=warn'; + let args = [argumentItem]; + + let actualResult = argumentsInstance.populate(args); + + chai.expect(actualResult).to.be.an.instanceOf(Arguments); + + chai.expect(argumentsInstance.list()[0].value).to.be.equal(argumentItem); + chai.expect(argumentsInstance.list()[0].exists).to.be.equal(true); + + }); + + //@todo - clarify with Alex C if Argument[] or String[] + test('Check populateUnmanaged()', () => { + let args = ['logLevel=info', 'testArg=value']; + + let actualResult = argumentsInstance.populateUnmanaged(args); + + chai.expect(actualResult).to.be.an.instanceOf(Arguments); + chai.expect(actualResult.listUnmanaged()).to.be.eql(args); + }); + + test('Check hasUnmanaged() returns true', () => { + chai.expect(argumentsInstance.hasUnmanaged).to.be.equal(true); + }); + + test('Check validate()', () => { + let actualResult = argumentsInstance.validate(); + + chai.expect( + actualResult, 'is an instance of Arguments' + ).to.be.an.instanceOf(Arguments); + }); + + test('Check validate() throws MissingArgumentException', () => { + let error = null; + + argumentsInstance.list()[0].required = true; + argumentsInstance.list()[0]._exists = false; + + try { + argumentsInstance.validate(); + } catch (e) { + error = e; + } + + chai.expect( + error, 'is an instance of MissingArgumentException' + ).to.be.an.instanceOf(MissingArgumentException); + }); + + test('Check listvalidate() for includeUnmanaged', () => { + let expectedResult = ['logLevel=info', 'testArg=value']; + + let actualResult = argumentsInstance.listValues(true); + + chai.expect(actualResult).to.eql(expectedResult); + }); +}); diff --git a/src/test/Terminal/Help.js b/src/test/Terminal/Help.js deleted file mode 100644 index 7947b9c5..00000000 --- a/src/test/Terminal/Help.js +++ /dev/null @@ -1,13 +0,0 @@ -// THIS TEST WAS GENERATED AUTOMATICALLY ON Mon Sep 14 2015 16:15:41 GMT+0300 (EEST) - -'use strict'; - -import chai from 'chai'; -import {Help} from '../../lib/Terminal/Help'; - -// @todo: Add more advanced tests -suite('Terminal/Help', function() { - test('Class Help exists in Terminal/Help', function() { - chai.expect(typeof Help).to.equal('function'); - }); -}); diff --git a/src/test/Terminal/Help.spec.js b/src/test/Terminal/Help.spec.js new file mode 100644 index 00000000..0acca4b4 --- /dev/null +++ b/src/test/Terminal/Help.spec.js @@ -0,0 +1,94 @@ +'use strict'; + +import chai from 'chai'; +import {Help} from '../../lib/Terminal/Help'; +import {Program} from '../../lib/Terminal/Program'; +import {ProgramInstanceRequiredException} from '../../lib/Terminal/Exception/ProgramInstanceRequiredException'; + +suite('Terminal/Help', () => { + let programName = 'testProgramName'; + let programVersion = 'testProgramVersion'; + let programDescription = 'testProgramDescription'; + let programExample = 'testProgramExample'; + let program = null; + let help = null; + + test('Class Help exists in Terminal/Help', () => { + chai.expect(Help).to.be.an('function'); + }); + + test('Check constructor sets correctly values by default', () => { + program = new Program(programName, programVersion, programDescription, programExample); + help = new Help(program) + + chai.expect(help, 'is an instance of Help').to.be.an.instanceOf(Help); + chai.expect(help.program, 'is an instance of Program').to.be.an.instanceOf(Program); + chai.expect(help.program).to.be.eql(program); + }); + + test('Check constructor throws ProgramInstanceRequiredException', () => { + let error = null; + + try { + help = new Help({}); + } catch (e) { + error = e; + } + + chai.expect(error, 'is an instance of Help').to.be.an.instanceOf(ProgramInstanceRequiredException); + }); + + test('Check _stringify() returns "unknown"', () => { + let actualResult = Help._stringify(); + + chai.expect(actualResult).to.equal('unknown'); + + actualResult = Help._stringify(null); + + chai.expect(actualResult).to.equal('unknown'); + }); + + test('Check _stringify() returns "true"', () => { + let actualResult = Help._stringify(true); + + chai.expect(actualResult).to.equal('true'); + }); + + test('Check _stringify() returns "false"', () => { + let actualResult = Help._stringify(false); + + chai.expect(actualResult).to.equal('false'); + }); + + test('Check _stringify() returns "one,two"', () => { + let actualResult = Help._stringify(['one', 'two']); + + chai.expect(actualResult).to.equal('one,two'); + }); + + //@todo - need to deeply investigate how to check console.log + test('Check _printHead', () => { + let actualResult = help._printHead(); + + chai.expect(actualResult, 'is an instance of Help').to.be.an.instanceOf(Help); + }); + + test('Check _printExample', () => { + let actualResult = help._printExample(); + + chai.expect(actualResult, 'is an instance of Help').to.be.an.instanceOf(Help); + }); + + test('Check print', () => { + let actualResult = help.print(); + + chai.expect(actualResult, 'is an instance of Help').to.be.an.instanceOf(Help); + }); + + test('Check _scoreSimilarWord returns 0', () => { + + let actualResult = Help._scoreSimilarWord('testString', 'testWord'); + + chai.expect(actualResult).to.be.equal(0); + }); +}); diff --git a/src/test/Terminal/Option.js b/src/test/Terminal/Option.js deleted file mode 100644 index 8d5e026f..00000000 --- a/src/test/Terminal/Option.js +++ /dev/null @@ -1,13 +0,0 @@ -// THIS TEST WAS GENERATED AUTOMATICALLY ON Mon Sep 14 2015 16:15:41 GMT+0300 (EEST) - -'use strict'; - -import chai from 'chai'; -import {Option} from '../../lib/Terminal/Option'; - -// @todo: Add more advanced tests -suite('Terminal/Option', function() { - test('Class Option exists in Terminal/Option', function() { - chai.expect(typeof Option).to.equal('function'); - }); -}); diff --git a/src/test/Terminal/Option.spec.js b/src/test/Terminal/Option.spec.js new file mode 100644 index 00000000..fe29dfae --- /dev/null +++ b/src/test/Terminal/Option.spec.js @@ -0,0 +1,24 @@ +'use strict'; + +import chai from 'chai'; +import {Option} from '../../lib/Terminal/Option'; + +suite('Terminal/Option', () => { + let option = null; + let name = 'server' + + test('Class Option exists in Terminal/Option', () => { + chai.expect(Option).to.be.an('function'); + }); + + test('Check constructor sets correctly values by default', () => { + option = new Option(name); + + chai.expect(option, 'is an instance of Option').to.be.an.instanceOf(Option); + chai.expect(option.name).to.be.equal(name); + chai.expect(option.description).to.be.equal(null); + chai.expect(option.alias).to.be.equal(null); + chai.expect(option.required).to.be.equal(false); + chai.expect(option.hidden).to.be.equal(false); + }); +}); diff --git a/src/test/Terminal/Options.js b/src/test/Terminal/Options.js deleted file mode 100644 index 4b598c1e..00000000 --- a/src/test/Terminal/Options.js +++ /dev/null @@ -1,13 +0,0 @@ -// THIS TEST WAS GENERATED AUTOMATICALLY ON Mon Sep 14 2015 16:15:41 GMT+0300 (EEST) - -'use strict'; - -import chai from 'chai'; -import {Options} from '../../lib/Terminal/Options'; - -// @todo: Add more advanced tests -suite('Terminal/Options', function() { - test('Class Options exists in Terminal/Options', function() { - chai.expect(typeof Options).to.equal('function'); - }); -}); diff --git a/src/test/Terminal/Options.spec.js b/src/test/Terminal/Options.spec.js new file mode 100644 index 00000000..d7adb706 --- /dev/null +++ b/src/test/Terminal/Options.spec.js @@ -0,0 +1,10 @@ +'use strict'; + +import chai from 'chai'; +import {Options} from '../../lib/Terminal/Options'; + +suite('Terminal/Options', () => { + test('Class Options exists in Terminal/Options', () => { + chai.expect(Options).to.be.an('function'); + }); +}); diff --git a/src/test/Terminal/Program.js b/src/test/Terminal/Program.js deleted file mode 100644 index 1d13dd54..00000000 --- a/src/test/Terminal/Program.js +++ /dev/null @@ -1,13 +0,0 @@ -// THIS TEST WAS GENERATED AUTOMATICALLY ON Mon Sep 14 2015 16:15:41 GMT+0300 (EEST) - -'use strict'; - -import chai from 'chai'; -import {Program} from '../../lib/Terminal/Program'; - -// @todo: Add more advanced tests -suite('Terminal/Program', function() { - test('Class Program exists in Terminal/Program', function() { - chai.expect(typeof Program).to.equal('function'); - }); -}); diff --git a/src/test/Terminal/Program.spec.js b/src/test/Terminal/Program.spec.js new file mode 100644 index 00000000..3e38fa01 --- /dev/null +++ b/src/test/Terminal/Program.spec.js @@ -0,0 +1,54 @@ +'use strict'; + +import chai from 'chai'; +import {Program} from '../../lib/Terminal/Program'; +import {Options} from '../../lib/Terminal/Options'; +import {Arguments} from '../../lib/Terminal/Arguments'; + +suite('Terminal/Program', () => { + let programName = 'testProgramName'; + let programVersion = 'testProgramVersion'; + let programDescription = 'testProgramDescription'; + let programExample = 'testProgramExample'; + let program = null; + + test('Class Program exists in Terminal/Program', () => { + chai.expect(Program).to.be.an('function'); + }); + + test('Check constructor sets correctly values by default', () => { + program = new Program(); + + chai.expect(program, 'is an instance of Program').to.be.an.instanceOf(Program); + chai.expect(program.name).to.be.equal(null); + chai.expect(program.version).to.be.equal(null); + chai.expect(program.description).to.be.equal(null); + chai.expect(program.example).to.be.equal(null); + chai.expect(program.commands).to.be.eql([]); + chai.expect(program.inputParsed).to.be.equal(false); + chai.expect(program.unmanagedArgs).to.be.eql([]); + chai.expect(program.action).to.be.an('function'); + chai.expect(program.opts).to.be.an.instanceOf(Options); + chai.expect(program.args).to.be.an.instanceOf(Arguments); + chai.expect(program.nodeBinary).to.equal(Program.NODE_BINARY); + chai.expect(program.scriptPath).to.equal(null); + }); + + test('Check constructor sets name', () => { + program = new Program(programName, programVersion, programDescription, programExample); + + chai.expect(program, 'is an instance of Program').to.be.an.instanceOf(Program); + chai.expect(program.name).to.be.equal(programName); + chai.expect(program.version).to.be.equal(programVersion); + chai.expect(program.description).to.be.equal(programDescription); + chai.expect(program.example).to.be.equal(programExample); + chai.expect(program.commands).to.be.eql([]); + chai.expect(program.inputParsed).to.be.equal(false); + chai.expect(program.unmanagedArgs).to.be.eql([]); + chai.expect(program.action).to.be.an('function'); + chai.expect(program.opts).to.be.an.instanceOf(Options); + chai.expect(program.args).to.be.an.instanceOf(Arguments); + chai.expect(program.nodeBinary).to.equal(Program.NODE_BINARY); + chai.expect(program.scriptPath).to.equal(null); + }); +}); diff --git a/src/test/Terminal/Prompt.spec.js b/src/test/Terminal/Prompt.spec.js new file mode 100644 index 00000000..b3c6e3ae --- /dev/null +++ b/src/test/Terminal/Prompt.spec.js @@ -0,0 +1,20 @@ +'use strict'; + +import chai from 'chai'; +import {Prompt} from '../../lib/Terminal/Prompt'; +import {Helpers_Terminal_Prompt as MainPrompt} from 'deep-package-manager'; + +suite('Terminal/Prompt', function() { + let prompt = new Prompt(); + + test('Class Prompt exists in Terminal/Prompt', function() { + chai.expect(typeof Prompt).to.equal('function'); + }); + + test('Check constructor sets correctly values by default', () => { + prompt = new Prompt(); + + chai.expect(prompt, 'is an instance of Prompt').to.be.an.instanceOf(Prompt); + chai.expect(prompt, 'is an instance of MainPrompt').to.be.an.instanceOf(MainPrompt); + }); +}); diff --git a/src/test/TestMaterials/Lambda/lambda.js b/src/test/TestMaterials/Lambda/lambda.js new file mode 100644 index 00000000..6504231f --- /dev/null +++ b/src/test/TestMaterials/Lambda/lambda.js @@ -0,0 +1,10 @@ +/** + * Created by vcernomschi on 2/2/16. + */ + +'use strict'; + +module.exports = () => { + var lambda = { name: 'testLambda from file', handler: () => {},}; + return lambda; +}; diff --git a/src/test/TestMaterials/PathToForked/child.js b/src/test/TestMaterials/PathToForked/child.js new file mode 100644 index 00000000..6d2cd0bc --- /dev/null +++ b/src/test/TestMaterials/PathToForked/child.js @@ -0,0 +1,18 @@ +/** + * Created by vcernomschi on 2/2/16. + */ + +import chai from 'chai'; +import ChildProcess from 'child_process'; + +let exec = ChildProcess.exec; + +exec('ls -l', { + cwd: __dirname, +}, (error, stdout) => { + if (error) { + chai.assert.fail(true, true, 'Error while running "ls -l"'); + } else { + let result = stdout ? stdout.toString().trim() : null; + } +}); \ No newline at end of file diff --git a/src/test/TestMaterials/Property2/Microservice/.parameters.json b/src/test/TestMaterials/Property2/Microservice/.parameters.json new file mode 100644 index 00000000..e170716d --- /dev/null +++ b/src/test/TestMaterials/Property2/Microservice/.parameters.json @@ -0,0 +1,4 @@ +{ + "backend": {}, + "frontend": {} +} \ No newline at end of file diff --git a/src/test/TestMaterials/Property2/Microservice/Backend/resources.json b/src/test/TestMaterials/Property2/Microservice/Backend/resources.json new file mode 100644 index 00000000..f8522da5 --- /dev/null +++ b/src/test/TestMaterials/Property2/Microservice/Backend/resources.json @@ -0,0 +1,12 @@ +{ + "test-resource" : { + "test": { + "description": "Just a test resource", + "type": "lambda", + "methods": [ + "POST" + ], + "source": "src/TestResource/Test" + } + } +} \ No newline at end of file diff --git a/src/test/TestMaterials/Property2/Microservice/Backend/src/TestResource/Test/Handler.es6 b/src/test/TestMaterials/Property2/Microservice/Backend/src/TestResource/Test/Handler.es6 new file mode 100644 index 00000000..70099276 --- /dev/null +++ b/src/test/TestMaterials/Property2/Microservice/Backend/src/TestResource/Test/Handler.es6 @@ -0,0 +1,22 @@ +'use strict'; + +import DeepFramework from 'deep-framework'; + +/** + * Sample lambda runtime + */ +export default class extends DeepFramework.Core.AWS.Lambda.Runtime { + /** + * @param {Array} args + */ + constructor(...args) { + super(...args); + } + + /** + * Handle lambda execution + */ + handle(request) { + this.createResponse(request).send(); + } +} diff --git a/src/test/TestMaterials/Property2/Microservice/Backend/src/TestResource/Test/bootstrap.es6 b/src/test/TestMaterials/Property2/Microservice/Backend/src/TestResource/Test/bootstrap.es6 new file mode 100644 index 00000000..9b842547 --- /dev/null +++ b/src/test/TestMaterials/Property2/Microservice/Backend/src/TestResource/Test/bootstrap.es6 @@ -0,0 +1,14 @@ +'use strict'; + +if (typeof Symbol === 'undefined') { + require('babel-core/polyfill'); +} + +import DeepFramework from 'deep-framework'; +import Handler from './Handler'; + +exports.handler = function(event, context) { + DeepFramework.Kernel.loadFromFile('_config.json', function(deepKernel) { + new Handler(deepKernel).run(event, context); + }); +}; diff --git a/src/test/TestMaterials/Property2/Microservice/Backend/src/TestResource/Test/package.json b/src/test/TestMaterials/Property2/Microservice/Backend/src/TestResource/Test/package.json new file mode 100644 index 00000000..2787d760 --- /dev/null +++ b/src/test/TestMaterials/Property2/Microservice/Backend/src/TestResource/Test/package.json @@ -0,0 +1,34 @@ +{ + "name": "test-resource-test", + "version": "0.0.1", + "description": "Test resource", + "author": "Mitoc Group ", + "contributors": [ + { + "name": "Stefan Hariton", + "email": "shariton@mitocroup.com" + } + ], + "bugs": { + "url": "https://github.com/MitocGroup/deep-management/issues" + }, + "keywords": [ + "DEEP", + "Stripe", + "Digital Enterprise End-To-End Platform", + "Deep Core Libraries", + "Billing" + ], + "dependencies": { + "deep-framework": "*", + "babel-core": "5.5.*" + }, + "devDependencies": { + }, + "scripts": { + "postinstall": "BABEL_ENV=production babel -x \".es6\" ./ --out-dir ./" + }, + "preferGlobal": false, + "private": true, + "analyze": true +} diff --git a/src/test/TestMaterials/Property2/Microservice/Data/Models/model.json b/src/test/TestMaterials/Property2/Microservice/Data/Models/model.json new file mode 100644 index 00000000..e0b99814 --- /dev/null +++ b/src/test/TestMaterials/Property2/Microservice/Data/Models/model.json @@ -0,0 +1,11 @@ +{ + "UserId": "string", + "AccountId": "string", + "URL": "string", + "Metadata": "map", + "Size": { + "Width": "number", + "Height": "number" + }, + "Tags": "mapSet" +} \ No newline at end of file diff --git a/src/test/TestMaterials/Property2/Microservice/deepkg.json b/src/test/TestMaterials/Property2/Microservice/deepkg.json new file mode 100644 index 00000000..2d15ad43 --- /dev/null +++ b/src/test/TestMaterials/Property2/Microservice/deepkg.json @@ -0,0 +1,21 @@ +{ + "identifier": "microservice1", + "name": "microservice1", + "description": "Microservice used for testing purposes", + "version": "0.0.1", + "propertyRoot": true, + "author": { + "name": "Mitoc Group", + "email": "hello@mitocgroup.com", + "website": "http://www.mitocgroup.com" + }, + "contributors": [ + { + "name": "Vitali Cernomschi", + "email": "vcernomschi@mitocgroup.com", + "website": "http://www.mitocgroup.com" + } + ], + "dependencies": { + } +} \ No newline at end of file diff --git a/src/test/TestMaterials/Property2/Microservice2/.parameters.json b/src/test/TestMaterials/Property2/Microservice2/.parameters.json new file mode 100644 index 00000000..e170716d --- /dev/null +++ b/src/test/TestMaterials/Property2/Microservice2/.parameters.json @@ -0,0 +1,4 @@ +{ + "backend": {}, + "frontend": {} +} \ No newline at end of file diff --git a/src/test/TestMaterials/Property2/Microservice2/Backend/resources.json b/src/test/TestMaterials/Property2/Microservice2/Backend/resources.json new file mode 100644 index 00000000..f8522da5 --- /dev/null +++ b/src/test/TestMaterials/Property2/Microservice2/Backend/resources.json @@ -0,0 +1,12 @@ +{ + "test-resource" : { + "test": { + "description": "Just a test resource", + "type": "lambda", + "methods": [ + "POST" + ], + "source": "src/TestResource/Test" + } + } +} \ No newline at end of file diff --git a/src/test/TestMaterials/Property2/Microservice2/Backend/src/TestResource/Test/Handler.es6 b/src/test/TestMaterials/Property2/Microservice2/Backend/src/TestResource/Test/Handler.es6 new file mode 100644 index 00000000..70099276 --- /dev/null +++ b/src/test/TestMaterials/Property2/Microservice2/Backend/src/TestResource/Test/Handler.es6 @@ -0,0 +1,22 @@ +'use strict'; + +import DeepFramework from 'deep-framework'; + +/** + * Sample lambda runtime + */ +export default class extends DeepFramework.Core.AWS.Lambda.Runtime { + /** + * @param {Array} args + */ + constructor(...args) { + super(...args); + } + + /** + * Handle lambda execution + */ + handle(request) { + this.createResponse(request).send(); + } +} diff --git a/src/test/TestMaterials/Property2/Microservice2/Backend/src/TestResource/Test/bootstrap.es6 b/src/test/TestMaterials/Property2/Microservice2/Backend/src/TestResource/Test/bootstrap.es6 new file mode 100644 index 00000000..9b842547 --- /dev/null +++ b/src/test/TestMaterials/Property2/Microservice2/Backend/src/TestResource/Test/bootstrap.es6 @@ -0,0 +1,14 @@ +'use strict'; + +if (typeof Symbol === 'undefined') { + require('babel-core/polyfill'); +} + +import DeepFramework from 'deep-framework'; +import Handler from './Handler'; + +exports.handler = function(event, context) { + DeepFramework.Kernel.loadFromFile('_config.json', function(deepKernel) { + new Handler(deepKernel).run(event, context); + }); +}; diff --git a/src/test/TestMaterials/Property2/Microservice2/Backend/src/TestResource/Test/package.json b/src/test/TestMaterials/Property2/Microservice2/Backend/src/TestResource/Test/package.json new file mode 100644 index 00000000..2787d760 --- /dev/null +++ b/src/test/TestMaterials/Property2/Microservice2/Backend/src/TestResource/Test/package.json @@ -0,0 +1,34 @@ +{ + "name": "test-resource-test", + "version": "0.0.1", + "description": "Test resource", + "author": "Mitoc Group ", + "contributors": [ + { + "name": "Stefan Hariton", + "email": "shariton@mitocroup.com" + } + ], + "bugs": { + "url": "https://github.com/MitocGroup/deep-management/issues" + }, + "keywords": [ + "DEEP", + "Stripe", + "Digital Enterprise End-To-End Platform", + "Deep Core Libraries", + "Billing" + ], + "dependencies": { + "deep-framework": "*", + "babel-core": "5.5.*" + }, + "devDependencies": { + }, + "scripts": { + "postinstall": "BABEL_ENV=production babel -x \".es6\" ./ --out-dir ./" + }, + "preferGlobal": false, + "private": true, + "analyze": true +} diff --git a/src/test/TestMaterials/Property2/Microservice2/Data/Models/model.json b/src/test/TestMaterials/Property2/Microservice2/Data/Models/model.json new file mode 100644 index 00000000..e0b99814 --- /dev/null +++ b/src/test/TestMaterials/Property2/Microservice2/Data/Models/model.json @@ -0,0 +1,11 @@ +{ + "UserId": "string", + "AccountId": "string", + "URL": "string", + "Metadata": "map", + "Size": { + "Width": "number", + "Height": "number" + }, + "Tags": "mapSet" +} \ No newline at end of file diff --git a/src/test/TestMaterials/Property2/Microservice2/deepkg.json b/src/test/TestMaterials/Property2/Microservice2/deepkg.json new file mode 100644 index 00000000..08a106a1 --- /dev/null +++ b/src/test/TestMaterials/Property2/Microservice2/deepkg.json @@ -0,0 +1,21 @@ +{ + "identifier": "microservice2", + "name": "microservice2", + "description": "Microservice used for testing purposes", + "version": "0.0.1", + "propertyRoot": false, + "author": { + "name": "Mitoc Group", + "email": "hello@mitocgroup.com", + "website": "http://www.mitocgroup.com" + }, + "contributors": [ + { + "name": "Vitali Cernomschi", + "email": "vcernomschi@mitocgroup.com", + "website": "http://www.mitocgroup.com" + } + ], + "dependencies": { + } +} \ No newline at end of file diff --git a/src/test/TestMaterials/Property2/deeploy.test.json b/src/test/TestMaterials/Property2/deeploy.test.json new file mode 100644 index 00000000..60fdf0d1 --- /dev/null +++ b/src/test/TestMaterials/Property2/deeploy.test.json @@ -0,0 +1,10 @@ +{ + "aws": { + "accessKeyId": "to_pass_string_validation", + "region": "us-west-2", + "secretAccessKey": "to_pass_string_validation" + }, + "env": "test", + "awsAccountId": 123456789012, + "appIdentifier": "generated" +} \ No newline at end of file diff --git a/src/test/TestMaterials/_www/_config.json b/src/test/TestMaterials/_www/_config.json new file mode 100644 index 00000000..a71c310b --- /dev/null +++ b/src/test/TestMaterials/_www/_config.json @@ -0,0 +1,113 @@ +{ + "env": "dev", + "deployId": "5h56a8765e6159cdda87a4d11f710b46", + "awsRegion": "us-west-2", + "models": [ + { + "Todo": { + "Title": "string", + "Completed": "boolean", + "Id": "timeUUID" + } + } + ], + "identityPoolId": "us-east-1:5hgj4567-2ba5-4af8-b89d-d46e7496e9a7", + "identityProviders": { + "www.amazon.com": "amzn1.application.3b5k2jb53252352kjh5b23kj5hb" + }, + "microservices": { + "deep.ng.root": { + "isRoot": true, + "parameters": {}, + "resources": {} + }, + "deep.ng.todo": { + "isRoot": false, + "parameters": {}, + "resources": { + "todo": { + "create": { + "type": "lambda", + "methods": [ + "POST" + ], + "forceUserIdentity": true, + "region": "us-west-2", + "source": { + "api": "https://aass3gf5kd.execute-api.us-west-2.amazonaws.com/dev/deep-ng-todo/todo/create", + "original": "arn:aws:lambda:us-west-2:123456789123:function:DeepDevTodoCreate12354e1234ew" + } + }, + "retrieve": { + "type": "lambda", + "methods": [ + "GET" + ], + "forceUserIdentity": true, + "region": "us-west-2", + "source": { + "api": "https://aass3gf5kd.execute-api.us-west-2.amazonaws.com/dev/deep-ng-todo/todo/retrieve", + "original": "arn:aws:lambda:us-west-2:123456789123:function:DeepDevTodoRetrieve12354e1234ew" + } + }, + "delete": { + "type": "lambda", + "methods": [ + "DELETE" + ], + "forceUserIdentity": true, + "region": "us-west-2", + "source": { + "api": "https://aass3gf5kd.execute-api.us-west-2.amazonaws.com/dev/deep-ng-todo/todo/delete", + "original": "arn:aws:lambda:us-west-2:123456789123:function:DeepDevTodoDelete12354e1234ew" + } + }, + "deleteCompleted": { + "type": "lambda", + "methods": [ + "POST" + ], + "forceUserIdentity": true, + "region": "us-west-2", + "source": { + "api": "https://aass3gf5kd.execute-api.us-west-2.amazonaws.com/dev/deep-ng-todo/todo/deleteCompleted", + "original": "arn:aws:lambda:us-west-2:123456789123:function:DeepDevTodoDeleteCompleted12354e1234ew" + } + }, + "update": { + "type": "lambda", + "methods": [ + "PUT" + ], + "forceUserIdentity": true, + "region": "us-west-2", + "source": { + "api": "https://aass3gf5kd.execute-api.us-west-2.amazonaws.com/dev/deep-ng-todo/todo/update", + "original": "arn:aws:lambda:us-west-2:123456789123:function:DeepDevTodoUpdate12354e1234ew" + } + }, + "markAll": { + "type": "lambda", + "methods": [ + "POST" + ], + "forceUserIdentity": true, + "region": "us-west-2", + "source": { + "api": "https://aass3gf5kd.execute-api.us-west-2.amazonaws.com/dev/deep-ng-todo/todo/markAll", + "original": "arn:aws:lambda:us-west-2:123456789123:function:DeepDevTodoMarkAll12354e1234ew" + } + } + } + } + } + }, + "globals": { + "userProviderEndpoint": "@deep.auth:user:retrieve", + "security": { + "identityProviders": { + "www.amazon.com": "amzn1.application.3e4r2jb12345678kjh5b23kj5hb" + } + } + } +} \ No newline at end of file diff --git a/test/bin/_head.sh b/test/bin/_head.sh index a960070f..b10e47ba 100644 --- a/test/bin/_head.sh +++ b/test/bin/_head.sh @@ -24,7 +24,7 @@ function eval_or_exit() { if [[ ${RET_CODE} != 0 ]] && [[ $1 == "npm run test" ]]; then #Run DEBUG_TEST_CMD command to show error in log echo "[FAILED] $1 -> try to re-run to show error in debug mode" - local DEBUG_TEST_CMD="mocha --ui tdd --compilers js:mocha-babel --recursive --reporter spec" + local DEBUG_TEST_CMD="babel-node `which _mocha` --ui tdd --recursive --reporter spec" eval_or_exit "$DEBUG_TEST_CMD" elif [ ${RET_CODE} != 0 ]; then echo "[FAILED] $1" diff --git a/test/bin/helper/FindClasses.js b/test/bin/helper/FindClasses.js index c709a622..6ba01fe4 100644 --- a/test/bin/helper/FindClasses.js +++ b/test/bin/helper/FindClasses.js @@ -29,7 +29,8 @@ export class FindClasses { for (let filepath of classFiles) { let relativePath = filepath.substr(this._libPath.length + 1); - let testFilePath = path.join(this._testsPath, relativePath); + + let testFilePath = path.join(this._testsPath, relativePath.replace('.js', '.spec.js')); if (!fs.existsSync(testFilePath)) { let testContent = FindClasses._genTestSuite(relativePath); @@ -195,9 +196,9 @@ export class FindClasses { content.push("{import}"); content.push(""); content.push("// @todo: Add more advanced tests"); - content.push("suite(\"{fullClass}\", function() {"); - content.push(" test('Class {class} exists in {fullClass}', function() {"); - content.push(" chai.expect(typeof {class}).to.equal('function');"); + content.push("suite(\'{fullClass}\', function() {"); + content.push(" test('Class {class} exists in {fullClass}', () => {"); + content.push(" chai.expect({class}).to.be.an('function');"); content.push(" });"); content.push("});"); content.push(""); @@ -216,7 +217,7 @@ export class FindClasses { * @returns {String} */ static get REAL_LIB_DIR() { - return 'lib.compiled'; + return 'lib'; } /**