diff --git a/.travis.yml b/.travis.yml index 01caa44..60ec71b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,9 @@ before_script: script: - make build +after_script: + - yarn test-integration + before_deploy: - if [ ! -d "$HOME/google-cloud-sdk/bin" ]; then rm -rf $HOME/google-cloud-sdk; export CLOUDSDK_CORE_DISABLE_PROMPTS=1; curl https://sdk.cloud.google.com | bash >/dev/null; fi - source /home/travis/google-cloud-sdk/path.bash.inc diff --git a/package.json b/package.json index bbceb66..e08cc6a 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "private": true, "scripts": { "start": "node index.js", - "test": "jest", + "test": "jest --testPathIgnorePatterns tests/integration", + "test-integration": "jest --testPathPattern tests/integration", "lint": "eslint -c .eslintrc.js ./*.js" }, "jest": { @@ -20,7 +21,7 @@ "*.js" ], "collectCoverage": false, - "testRegex": ".*test.js$" + "testRegex": "[a-zA-Z]*\\.test\\.js$" }, "dependencies": { "email-addresses": "^3.0.3", @@ -32,12 +33,15 @@ "winston": "^3.0.0" }, "devDependencies": { + "node-docker-api": "^1.1.22", "eslint": "^5.9.0", "eslint-config-standard": "^12.0.0", "eslint-plugin-import": "^2.14.0", "eslint-plugin-node": "^8.0.0", "eslint-plugin-promise": "^4.0.1", "eslint-plugin-standard": "^4.0.0", - "jest": "^24.8.0" + "jest": "^24.8.0", + "request": "^2.88.0", + "supertest": "^4.0.2" } } diff --git a/tests/integration/docker.js b/tests/integration/docker.js new file mode 100644 index 0000000..393103b --- /dev/null +++ b/tests/integration/docker.js @@ -0,0 +1,106 @@ +const request = require('request') +const { Docker } = require('node-docker-api') + +const docker = new Docker({ socketPath: '/var/run/docker.sock' }) + +let container = null +let logstream = null + +function create() { + return docker.container.create({ + Image: 'multipl/mailgun:latest', + Env: [ + "PROGRAM_ALIAS=mailgun_integration_test", + "PORT=3000", + "LOG_FILE=false", + "LOG_LEVEL=debug", + "MAILGUN_API_KEY=api_key", + "MAILGUN_DOMAIN=mail.multipl.io", + + "http_proxy=http://127.0.0.1:3002", + "https_proxy=http://127.0.0.1:3002", + ], + HostConfig: { + PortBindings: { + "3000/tcp": [{ + "HostPort": "3000", + }], + }, + NetworkMode: "host", + }, + }) + .then(c => { + container = c + console.log('Container created') + return c.start() + }) + .then((c) => c.logs({ + follow: true, + stdout: true, + stderr: true, + })) + .then(stream => { + logstream = stream + stream.on('data', info => console.log(info.toString())) + stream.on('error', err => console.log(err.toString())) + }) + .then(() => { + console.log('Container started') + return ready("/ready") + }) + .catch(error => console.log(error)) +} + +function ready(path) { + const timeout = 1000 + let tries = 100 + + return new Promise((resolve, reject) => { + const check = () => { + console.log("Ready check") + + if (tries-- <= 0) { + reject("Max readiness tries reached.") + } + + request + .get("http://127.0.0.1:3000" + path) + .on('response', function(response) { + if (response.statusCode == 200) { + console.log("Container ready") + resolve(response) + } + else { + setTimeout(check, timeout) + } + }) + .on('error', (error) => { + setTimeout(check, timeout) + }) + } + + setTimeout(check, timeout) + }) +} + +function kill() { + // shutdown log stream + logstream.destroy() + + // kill container + if (container != null) { + return container.stop() + .then(c => c.delete()) + .catch((error) => console.log(error)) + } + else { + return Promise.resolve(true) + } +} + +module.exports = { + create, + ready, + kill, +} + diff --git a/tests/integration/integration.test.js b/tests/integration/integration.test.js new file mode 100644 index 0000000..2a612a1 --- /dev/null +++ b/tests/integration/integration.test.js @@ -0,0 +1,62 @@ +/* eslint-disable */ + +const PORT = 3000 +const ADDRESS = `localhost:${PORT}` + +const request = require('supertest')(ADDRESS) + +const { create, ready, kill } = require('./docker') + +const express = require('express') +const app = express() + +describe('api', () => { + let mailgunServer = null + + beforeAll(() => { + // setup mailgun-mock server + app.all('*', (req, res) => { + console.log('mock-mailgun : ' + req.url) + res.send('OK') + }) + mailgunServer = app.listen(3002, () => console.log(`Mock mailgun app listening on port 3002`)) + + // create the container + return create() + }, 60 * 1000) + + it('readiness probe route responds positivelly', () => { + return request.get('/ready') + .then(response => { + expect(response.statusCode).toBe(200) + }) + }) + + it('subscribes an email', () => { + const email = "user@acme.com" + + return request.get('/subscribe/' + email) + .then(response => { + expect(response.statusCode).toBe(200) + }) + }) + + it('onboard a new user', () => { + const email = "user@acme.com" + const name = "user" + + return request.get('/onboard/' + email + '/' + name) + .then(response => { + expect(response.statusCode).toBe(200) + }) + }) + + afterAll(() => { + // shutdown mock-mailgun + mailgunServer.close() + + // kill container + kill() + }) +}) + diff --git a/yarn.lock b/yarn.lock index 90118a5..63e9901 100644 --- a/yarn.lock +++ b/yarn.lock @@ -348,6 +348,14 @@ resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.12.tgz#45dd1d0638e8c8f153e87d296907659296873916" integrity sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw== +JSONStream@0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-0.10.0.tgz#74349d0d89522b71f30f0a03ff9bd20ca6f12ac0" + integrity sha1-dDSdDYlSK3HzDwoD/5vSDKbxKsA= + dependencies: + jsonparse "0.0.5" + through ">=2.2.7 <3" + abab@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.0.tgz#aba0ab4c5eee2d4c79d3487d85450fb2376ebb0f" @@ -895,6 +903,11 @@ commander@~2.20.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== +component-emitter@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + component-emitter@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" @@ -947,6 +960,11 @@ cookie@0.3.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= +cookiejar@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c" + integrity sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA== + copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" @@ -1006,7 +1024,7 @@ data-urls@^1.0.0: whatwg-mimetype "^2.2.0" whatwg-url "^7.0.0" -debug@2, debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: +debug@2, debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -1148,6 +1166,16 @@ dns-prefetch-control@0.1.0: resolved "https://registry.yarnpkg.com/dns-prefetch-control/-/dns-prefetch-control-0.1.0.tgz#60ddb457774e178f1f9415f0cabb0e85b0b300b2" integrity sha1-YN20V3dOF48flBXwyrsOhbCzALI= +docker-modem@^0.3.1: + version "0.3.7" + resolved "https://registry.yarnpkg.com/docker-modem/-/docker-modem-0.3.7.tgz#3f510d09f5d334dc2134228f92bd344671227df4" + integrity sha1-P1ENCfXTNNwhNCKPkr00RnEiffQ= + dependencies: + JSONStream "0.10.0" + debug "^2.6.0" + readable-stream "~1.0.26-4" + split-ca "^1.0.0" + doctrine@1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" @@ -1559,7 +1587,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@3, extend@~3.0.2: +extend@3, extend@^3.0.0, extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== @@ -1719,7 +1747,7 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= -form-data@^2.3.2, form-data@~2.3.2: +form-data@^2.3.1, form-data@^2.3.2, form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== @@ -1728,6 +1756,11 @@ form-data@^2.3.2, form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" +formidable@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.1.tgz#70fb7ca0290ee6ff961090415f4b3df3d2082659" + integrity sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg== + forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -2857,6 +2890,11 @@ json5@^2.1.0: dependencies: minimist "^1.2.0" +jsonparse@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-0.0.5.tgz#330542ad3f0a654665b778f3eb2d9a9fa507ac64" + integrity sha1-MwVCrT8KZUZlt3jz6y2an6UHrGQ= + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -3063,6 +3101,11 @@ mem@^4.0.0: mimic-fn "^2.0.0" p-is-promise "^2.0.0" +memorystream@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= + merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -3075,7 +3118,7 @@ merge-stream@^1.0.1: dependencies: readable-stream "^2.0.1" -methods@~1.1.2: +methods@^1.1.1, methods@^1.1.2, methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= @@ -3116,6 +3159,11 @@ mime@1.4.1: resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== +mime@^1.4.1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" @@ -3259,6 +3307,14 @@ nocache@2.0.0: resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.0.0.tgz#202b48021a0c4cbde2df80de15a17443c8b43980" integrity sha1-ICtIAhoMTL3i34DeFaF0Q8i0OYA= +node-docker-api@^1.1.22: + version "1.1.22" + resolved "https://registry.yarnpkg.com/node-docker-api/-/node-docker-api-1.1.22.tgz#230327efd309a6ec7302bf3f404ab760040a5806" + integrity sha1-IwMn79MJpuxzAr8/QEq3YAQKWAY= + dependencies: + docker-modem "^0.3.1" + memorystream "^0.3.1" + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -3835,6 +3891,11 @@ qs@6.5.2, qs@~6.5.2: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== +qs@^6.5.1: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + range-parser@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" @@ -3909,7 +3970,7 @@ readable-stream@1.1.x: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@2, readable-stream@^2.0.1, readable-stream@^2.0.6, readable-stream@^2.3.6: +readable-stream@2, readable-stream@^2.0.1, readable-stream@^2.0.6, readable-stream@^2.3.5, readable-stream@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== @@ -3922,6 +3983,16 @@ readable-stream@2, readable-stream@^2.0.1, readable-stream@^2.0.6, readable-stre string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@~1.0.26-4: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + realpath-native@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" @@ -3978,7 +4049,7 @@ request-promise-native@^1.0.5: stealthy-require "^1.1.0" tough-cookie ">=2.3.3" -request@^2.87.0: +request@^2.87.0, request@^2.88.0: version "2.88.0" resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== @@ -4382,6 +4453,11 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.2.tgz#a59efc09784c2a5bada13cfeaf5c75dd214044d2" integrity sha512-qky9CVt0lVIECkEsYbNILVnPvycuEBkXoMFLRWsREkomQLevYhtRKC+R91a5TOAQ3bCMjikRwhyaRqj1VYatYg== +split-ca@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/split-ca/-/split-ca-1.0.1.tgz#6c83aff3692fa61256e0cd197e05e9de157691a6" + integrity sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY= + split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -4515,6 +4591,30 @@ strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= +superagent@^3.8.3: + version "3.8.3" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.8.3.tgz#460ea0dbdb7d5b11bc4f78deba565f86a178e128" + integrity sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA== + dependencies: + component-emitter "^1.2.0" + cookiejar "^2.1.0" + debug "^3.1.0" + extend "^3.0.0" + form-data "^2.3.1" + formidable "^1.2.0" + methods "^1.1.1" + mime "^1.4.1" + qs "^6.5.1" + readable-stream "^2.3.5" + +supertest@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/supertest/-/supertest-4.0.2.tgz#c2234dbdd6dc79b6f15b99c8d6577b90e4ce3f36" + integrity sha512-1BAbvrOZsGA3YTCWqbmh14L0YEq0EGICX/nBnfkfVJn7SrxQV1I3pMYjSzG9y/7ZU2V9dWqyqk2POwxlb09duQ== + dependencies: + methods "^1.1.2" + superagent "^3.8.3" + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -4582,7 +4682,7 @@ throat@^4.0.0: resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" integrity sha1-iQN8vJLFarGJJua6TLsgDhVnKmo= -through@^2.3.6: +"through@>=2.2.7 <3", through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=