From 63edf6b32807aebac1809404e547ab0ab3b49ede Mon Sep 17 00:00:00 2001 From: Daniel Schep Date: Wed, 19 Sep 2018 12:22:38 -0400 Subject: [PATCH 1/3] simpler test cleanup precursor to JS version since i want it as simple as possible before port --- test.bats | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/test.bats b/test.bats index 12343bcd..9c581112 100755 --- a/test.bats +++ b/test.bats @@ -18,12 +18,9 @@ setup() { teardown() { rm -rf puck puck2 puck3 node_modules .serverless .requirements.zip .requirements-cache \ - foobar package-lock.json serverless-python-requirements-*.tgz - if [ -f serverless.yml.bak ]; then mv serverless.yml.bak serverless.yml; fi - if [ -f slimPatterns.yml ]; then rm -f slimPatterns.yml; fi - if [ -d "${USR_CACHE_DIR}" ] ; then - rm -Rf "${USR_CACHE_DIR}" - fi + foobar package-lock.json serverless.yml.bak slimPatterns.yml "${USR_CACHE_DIR}" + serverless-python-requirements-*.tgz + git checkout serverless.yml cd ../.. if [ -d "tests/base with a space" ] ; then rm -Rf "tests/base with a space" From eb53a715868bb988e33b89590ceaca018847ce9a Mon Sep 17 00:00:00 2001 From: Daniel Schep Date: Wed, 19 Sep 2018 12:23:44 -0400 Subject: [PATCH 2/3] move 1st test down.. probably too much but its not the first/primary test --- test.bats | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test.bats b/test.bats index 9c581112..9b8812dc 100755 --- a/test.bats +++ b/test.bats @@ -27,18 +27,6 @@ teardown() { fi } -@test "py3.6 supports custom file name with fileName option" { - cd tests/base - npm i $(npm pack ../..) - docker &> /dev/null || skip "docker not present" - ! uname -sm|grep Linux || groups|grep docker || id -u|egrep '^0$' || skip "can't dockerize on linux if not root & not in docker group" - perl -p -i'.bak' -e 's/(pythonRequirements:$)/\1\n fileName: puck/' serverless.yml - echo "requests" > puck - sls package - ls .serverless/requirements/requests - ! ls .serverless/requirements/flask -} - @test "py3.6 can package flask with default options" { cd tests/base npm i $(npm pack ../..) @@ -588,3 +576,15 @@ teardown() { unzip .serverless/sls-py-req-test.zip -d puck ls puck/flask } + +@test "py3.6 supports custom file name with fileName option" { + cd tests/base + npm i $(npm pack ../..) + docker &> /dev/null || skip "docker not present" + ! uname -sm|grep Linux || groups|grep docker || id -u|egrep '^0$' || skip "can't dockerize on linux if not root & not in docker group" + perl -p -i'.bak' -e 's/(pythonRequirements:$)/\1\n fileName: puck/' serverless.yml + echo "requests" > puck + sls package + ls .serverless/requirements/requests + ! ls .serverless/requirements/flask +} From eb8cc1134786c5acf4eff3a77be1cccaa5ae9eb7 Mon Sep 17 00:00:00 2001 From: Daniel Schep Date: Thu, 20 Sep 2018 13:09:09 -0400 Subject: [PATCH 3/3] First pass at tape tests instead of bats tests Main advantage is that these can run on windows more easily, thus fixing #249 --- appveyor.yml | 23 +++---- index.js | 5 +- lib/pip.js | 2 +- package.json | 9 ++- test.js | 136 ++++++++++++++++++++++++++++++++++++++++ tests/base/package.json | 2 +- 6 files changed, 157 insertions(+), 20 deletions(-) create mode 100644 test.js diff --git a/appveyor.yml b/appveyor.yml index 7431e08b..abc3badb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,14 +1,9 @@ -version: '{build}' -init: -- ps: >- - Install-Product node 6 - - npm i -g serverless -build: off -test_script: -- cmd: >- - cd example - - npm i - - sls package --pythonBin=c:/python36/python.exe \ No newline at end of file +version: '{build}' +init: +- ps: npm i -g serverless +build: off +test_script: +- cmd: >- + npm i + + node test.js diff --git a/index.js b/index.js index ff73b1f7..155897ce 100644 --- a/index.js +++ b/index.js @@ -34,7 +34,10 @@ class ServerlessPythonRequirements { invalidateCaches: false, fileName: 'requirements.txt', usePipenv: true, - pythonBin: this.serverless.service.provider.runtime || 'python', + pythonBin: + process.platform === 'win32' + ? 'python.exe' + : this.serverless.service.provider.runtime || 'python', dockerizePip: false, dockerSsh: false, dockerImage: null, diff --git a/lib/pip.js b/lib/pip.js index bc84b4d4..aed9c03f 100644 --- a/lib/pip.js +++ b/lib/pip.js @@ -274,7 +274,7 @@ function installRequirements(targetFolder, serverless, options) { * @return {string} */ function dockerPathForWin(options, path) { - if (process.platform === 'win32' && options.dockerizePip) { + if (process.platform === 'win32') { return `"${path.replace(/\\/g, '/')}"`; } else if (process.platform === 'win32' && !options.dockerizePip) { return path; diff --git a/package.json b/package.json index d332c158..8be5cffa 100644 --- a/package.json +++ b/package.json @@ -38,13 +38,16 @@ "main": "index.js", "bin": {}, "scripts": { - "test": "bats test.bats", + "test": "node test.js && bats test.bats", "lint": "eslint *.js lib/*.js && prettier -l index.js lib/*.js || (echo need formatting ; exit 1)", - "format": "prettier --write index.js lib/*.js" + "format": "prettier --write index.js lib/*.js test.js" }, "devDependencies": { "eslint": "*", - "prettier": "*" + "prettier": "*", + "cross-spawn": "*", + "deasync-promise": "*", + "tape": "*" }, "dependencies": { "appdirectory": "^0.1.0", diff --git a/test.js b/test.js new file mode 100644 index 00000000..ff5d90a4 --- /dev/null +++ b/test.js @@ -0,0 +1,136 @@ +const crossSpawn = require('cross-spawn'); +const deasync = require('deasync-promise'); +const glob = require('glob-all'); +const JSZip = require('jszip'); +const tape = require('tape'); +const { removeSync, readFileSync } = require('fs-extra'); +const { sep } = require('path'); + +const { getUserCachePath } = require('./lib/shared'); + +const initialWorkingDir = process.cwd(); + +const mkCommand = cmd => (args, options = {}) => { + const { error, stdout, stderr, status } = crossSpawn.sync( + cmd, + args, + Object.assign( + { + env: Object.assign( + process.env, + { SLS_DEBUG: 't' }, + process.env.CI ? { LC_ALL: 'C.UTF-8', LANG: 'C.UTF-8' } : {} + ) + }, + options + ) + ); + if (error) throw error; + if (status) { + console.error(stdout.toString()); // eslint-disable-line no-console + console.error(stderr.toString()); // eslint-disable-line no-console + throw new Error(`${cmd} failed with status code ${status}`); + } + return stdout && stdout.toString().trim(); +}; +const sls = mkCommand('sls'); +const git = mkCommand('git'); +const npm = mkCommand('npm'); + +const setup = () => { + removeSync(getUserCachePath()); +}; + +const teardown = () => { + [ + 'puck', + 'puck2', + 'puck3', + 'node_modules', + '.serverless', + '.requirements.zip', + '.requirements-cache', + 'foobar', + 'package-lock.json', + 'slimPatterns.yml', + 'serverless.yml.bak', + getUserCachePath(), + ...glob.sync('serverless-python-requirements-*.tgz') + ].map(path => removeSync(path)); + git(['checkout', 'serverless.yml']); + process.chdir(initialWorkingDir); + removeSync('tests/base with a space'); +}; + +const test = (desc, func) => + tape.test(desc, t => { + setup(); + try { + func(t); + } finally { + teardown(); + } + }); + +const getPythonBin = (version = 3) => { + if (![2, 3].includes(version)) throw new Error('version must be 2 or 3'); + if (process.platform === 'win32') + return `c:/python${version === 2 ? '27' : '36'}-x64/python.exe`; + else return version === 2 ? 'python2.7' : 'python3.6'; +}; + +const listZipFiles = filename => + Object.keys(deasync(new JSZip().loadAsync(readFileSync(filename))).files); + +test('default pythonBin can package flask with default options', t => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package']); + const zipfiles = listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.end(); +}); + +test('py3.6 can package flask with default options', t => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls([`--pythonBin=${getPythonBin(3)}`, 'package']); + const zipfiles = listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.end(); +}); + +test('py3.6 can package flask with zip option', t => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls([`--pythonBin=${getPythonBin(3)}`, '--zip=true', 'package']); + const zipfiles = listZipFiles('.serverless/sls-py-req-test.zip'); + t.true( + zipfiles.includes('.requirements.zip'), + 'zipped requirements are packaged' + ); + t.true(zipfiles.includes(`unzip_requirements.py`), 'unzip util is packaged'); + t.false( + zipfiles.includes(`flask${sep}__init__.py`), + "flask isn't packaged on its own" + ); + t.end(); +}); + +test('py3.6 can package flask with slim option', t => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls([`--pythonBin=${getPythonBin(3)}`, '--slim=true', 'package']); + const zipfiles = listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.deepEqual( + zipfiles.filter(filename => filename.endsWith('.pyc')), + [], + 'no pyc files packaged' + ); + t.end(); +}); diff --git a/tests/base/package.json b/tests/base/package.json index f75ba960..d37ade00 100644 --- a/tests/base/package.json +++ b/tests/base/package.json @@ -9,6 +9,6 @@ "author": "", "license": "ISC", "dependencies": { - "serverless-python-requirements": "file:serverless-python-requirements-4.2.1.tgz" + "serverless-python-requirements": "file:serverless-python-requirements-4.2.4.tgz" } }