├── .github └── workflows │ ├── ci.yml │ ├── commit-if-modified.sh │ ├── copyright-year.sh │ ├── isaacs-makework.yml │ └── package-json-repo.js ├── .gitignore ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── test ├── callback-error │ ├── 00-setup.js │ ├── test-blerg.js │ ├── test-ping.js │ └── zz-teardown.js ├── env-polluted │ ├── 00-setup.js │ ├── test-blerg.js │ ├── test-ping.js │ └── zz-teardown.js ├── exit-early │ ├── 00-setup.js │ ├── test-blerg.js │ ├── test-ping.js │ └── zz-teardown.js ├── ignore-sigterm │ ├── 00-setup.js │ ├── test-blerg.js │ ├── test-ping.js │ └── zz-teardown.js ├── index.js ├── success │ ├── 00-setup.js │ ├── test-blerg.js │ ├── test-ping.js │ └── zz-teardown.js ├── two-things-one-process │ ├── 00-setup.js │ ├── test-blerg.js │ ├── test-ping.js │ └── zz-teardown.js └── two-things-two-processes │ ├── 00-setup.js │ ├── 01-setup-second.js │ ├── test-blerg.js │ ├── test-ping.js │ └── zz-teardown.js └── tup.js /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | strategy: 8 | matrix: 9 | node-version: [12.x, 14.x, 16.x, 17.x] 10 | platform: 11 | - os: ubuntu-latest 12 | shell: bash 13 | - os: macos-latest 14 | shell: bash 15 | - os: windows-latest 16 | shell: bash 17 | - os: windows-latest 18 | shell: powershell 19 | fail-fast: false 20 | 21 | runs-on: ${{ matrix.platform.os }} 22 | defaults: 23 | run: 24 | shell: ${{ matrix.platform.shell }} 25 | 26 | steps: 27 | - name: Checkout Repository 28 | uses: actions/checkout@v1.1.0 29 | 30 | - name: Use Nodejs ${{ matrix.node-version }} 31 | uses: actions/setup-node@v1 32 | with: 33 | node-version: ${{ matrix.node-version }} 34 | 35 | - name: Install dependencies 36 | run: npm install 37 | 38 | - name: Run Tests 39 | run: npm test -- -c -t0 40 | -------------------------------------------------------------------------------- /.github/workflows/commit-if-modified.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | git config --global user.email "$1" 3 | shift 4 | git config --global user.name "$1" 5 | shift 6 | message="$1" 7 | shift 8 | if [ $(git status --porcelain "$@" | egrep '^ M' | wc -l) -gt 0 ]; then 9 | git add "$@" 10 | git commit -m "$message" 11 | git push || git pull --rebase 12 | git push 13 | fi 14 | -------------------------------------------------------------------------------- /.github/workflows/copyright-year.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | dir=${1:-$PWD} 3 | dates=($(git log --date=format:%Y --pretty=format:'%ad' --reverse | sort | uniq)) 4 | if [ "${#dates[@]}" -eq 1 ]; then 5 | datestr="${dates}" 6 | else 7 | datestr="${dates}-${dates[${#dates[@]}-1]}" 8 | fi 9 | 10 | stripDate='s/^((.*)Copyright\b(.*?))((?:,\s*)?(([0-9]{4}\s*-\s*[0-9]{4})|(([0-9]{4},\s*)*[0-9]{4})))(?:,)?\s*(.*)\n$/$1$9\n/g' 11 | addDate='s/^.*Copyright(?:\s*\(c\))? /Copyright \(c\) '$datestr' /g' 12 | for l in $dir/LICENSE*; do 13 | perl -pi -e "$stripDate" $l 14 | perl -pi -e "$addDate" $l 15 | done 16 | -------------------------------------------------------------------------------- /.github/workflows/isaacs-makework.yml: -------------------------------------------------------------------------------- 1 | name: "various tidying up tasks to silence nagging" 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | jobs: 10 | makework: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | with: 15 | fetch-depth: 0 16 | - name: Use Node.js 17 | uses: actions/setup-node@v2.1.4 18 | with: 19 | node-version: 16.x 20 | - name: put repo in package.json 21 | run: node .github/workflows/package-json-repo.js 22 | - name: check in package.json if modified 23 | run: | 24 | bash -x .github/workflows/commit-if-modified.sh \ 25 | "package-json-repo-bot@example.com" \ 26 | "package.json Repo Bot" \ 27 | "chore: add repo to package.json" \ 28 | package.json package-lock.json 29 | - name: put all dates in license copyright line 30 | run: bash .github/workflows/copyright-year.sh 31 | - name: check in licenses if modified 32 | run: | 33 | bash .github/workflows/commit-if-modified.sh \ 34 | "license-year-bot@example.com" \ 35 | "License Year Bot" \ 36 | "chore: add copyright year to license" \ 37 | LICENSE* 38 | -------------------------------------------------------------------------------- /.github/workflows/package-json-repo.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const pf = require.resolve(`${process.cwd()}/package.json`) 4 | const pj = require(pf) 5 | 6 | if (!pj.repository && process.env.GITHUB_REPOSITORY) { 7 | const fs = require('fs') 8 | const server = process.env.GITHUB_SERVER_URL || 'https://github.com' 9 | const repo = `${server}/${process.env.GITHUB_REPOSITORY}` 10 | pj.repository = repo 11 | const json = fs.readFileSync(pf, 'utf8') 12 | const match = json.match(/^\s*\{[\r\n]+([ \t]*)"/) 13 | const indent = match[1] 14 | const output = JSON.stringify(pj, null, indent || 2) + '\n' 15 | fs.writeFileSync(pf, output) 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ 3 | .nyc_output/ 4 | nyc_output/ 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The ISC License 2 | 3 | Copyright (c) 2016-2023 Isaac Z. Schlueter and Contributors 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 15 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # t-up 2 | 3 | Tee up a test server in one file, tear it down in another 4 | 5 | This makes it easy to set up a server or child process in a test file 6 | called `00-setup.js` or something, and then tear it down in 7 | `zz-teardown.js`, so that you can get the benefits of running your 8 | tests in separate files, but still bang on the same server across the 9 | entire suite. 10 | 11 | ## USAGE 12 | 13 | You may have a setup file that starts a server, like so: 14 | 15 | ```javascript 16 | // 00-setup.js 17 | var tup = require('t-up') 18 | 19 | tup(function (done) { 20 | var http = require('http') 21 | http.createServer(function (req, res) { 22 | if (req.url === '/ping') { 23 | res.end('pong\n') 24 | } else { 25 | res.statusCode = 404 26 | res.end('not found\n') 27 | } 28 | }).listen(1337, function () { 29 | // standard node-style callback 30 | // if you call this with an error, it'll blow up 31 | done() 32 | }) 33 | }) 34 | ``` 35 | 36 | Then, a bunch of tests that do things to that server: 37 | 38 | ```javascript 39 | // test-ping.js 40 | var t = require('tap') 41 | var http = require('http') 42 | 43 | t.test('ping returns pong', function (t) { 44 | http.get('http://localhost:1337/ping', function (res) { 45 | t.equal(res.statusCode, 200) 46 | var pong = '' 47 | res.on('data', function (d) { pong += d }) 48 | res.on('end', function () { 49 | t.equal(pong, 'pong\n') 50 | t.end() 51 | }) 52 | }) 53 | }) 54 | ``` 55 | 56 | And of course, a negative test: 57 | 58 | ```javascript 59 | // test-blerg.js 60 | t.test('anything else 404s', function (t) { 61 | http.get('http://localhost:1337/blerg', function (res) { 62 | t.equal(res.statusCode, 404) 63 | var pong = '' 64 | res.on('data', function (d) { pong += d }) 65 | res.on('end', function () { 66 | t.equal(pong, 'not found\n') 67 | t.end() 68 | }) 69 | }) 70 | }) 71 | ``` 72 | 73 | Last but not least, tear down the server when you're done: 74 | 75 | ```javascript 76 | // zz-teardown.js 77 | var tup = require('t-up') 78 | tup.close() 79 | ``` 80 | 81 | ## CAVEATS 82 | 83 | - The module that calls `t-up` should probably not do anything else. 84 | It's going to be run a second time in a child process, so any other 85 | side-effects are bad. 86 | - You should use [tap](http://node-tap.org) for your tests, since this 87 | module will output some TAP notes about how it's going. 88 | - Definitely clean up after yourself! If you don't ever call 89 | `tup.close()`, then you'll have a bunch of node processes lying 90 | around. 91 | - The server MUST call the `done()` function when it's ready to move 92 | onto the next test. This writes a special key to standard output, 93 | and prevents a race condition where the tests fail because the 94 | server isn't up yet. Otherwise, all stdio is completely lost, 95 | because the child process is abandoned without access to that stuff. 96 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "t-up", 3 | "version": "2.0.3", 4 | "description": "Tee up a test server in one file, tear it down in another", 5 | "main": "tup.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/tapjs/t-up.git" 9 | }, 10 | "author": "Isaac Z. Schlueter (http://blog.izs.me/)", 11 | "license": "ISC", 12 | "scripts": { 13 | "test": "tap --100 test/*.js", 14 | "preversion": "npm test", 15 | "postversion": "npm publish", 16 | "postpublish": "git push origin --all; git push origin --tags" 17 | }, 18 | "dependencies": { 19 | "mkdirp": "^0.5.1", 20 | "rimraf": "^2.5.2", 21 | "tap": "^15.1.6" 22 | }, 23 | "devDependencies": { 24 | "glob": "^7.0.0" 25 | }, 26 | "files": [ 27 | "tup.js" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /test/callback-error/00-setup.js: -------------------------------------------------------------------------------- 1 | var tup = require('../..') 2 | 3 | tup(function (done) { 4 | var http = require('http') 5 | http.createServer(function (req, res) { 6 | if (req.url === '/ping') { 7 | res.end('pong\n') 8 | } else { 9 | res.statusCode = 404 10 | res.end('not found\n') 11 | } 12 | }).listen(1337, function () { 13 | done(new Error('started the server, but feeling uneasy about it')) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /test/callback-error/test-blerg.js: -------------------------------------------------------------------------------- 1 | var t = require('tap') 2 | var http = require('http') 3 | 4 | t.test('anything else 404s', function (t) { 5 | http.get('http://localhost:1337/blerg', function (res) { 6 | t.equal(res.statusCode, 404) 7 | var pong = '' 8 | res.on('data', function (d) { pong += d }) 9 | res.on('end', function () { 10 | t.equal(pong, 'not found\n') 11 | t.end() 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /test/callback-error/test-ping.js: -------------------------------------------------------------------------------- 1 | var t = require('tap') 2 | var http = require('http') 3 | 4 | t.test('ping returns pong', function (t) { 5 | http.get('http://localhost:1337/ping', function (res) { 6 | t.equal(res.statusCode, 200) 7 | var pong = '' 8 | res.on('data', function (d) { pong += d }) 9 | res.on('end', function () { 10 | t.equal(pong, 'pong\n') 11 | t.end() 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /test/callback-error/zz-teardown.js: -------------------------------------------------------------------------------- 1 | var tup = require('../..') 2 | tup.close() 3 | -------------------------------------------------------------------------------- /test/env-polluted/00-setup.js: -------------------------------------------------------------------------------- 1 | var tup = require('../..') 2 | 3 | if (!process.env.T_UP_HASH) { 4 | process.env.T_UP_HASH = 'cookie' 5 | } 6 | 7 | tup(function (done) { 8 | console.log('this is a server') 9 | var http = require('http') 10 | http.createServer(function (req, res) { 11 | if (req.url === '/ping') { 12 | res.end('pong\n') 13 | } else { 14 | res.statusCode = 404 15 | res.end('not found\n') 16 | } 17 | }).listen(1337, function () { 18 | done() 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /test/env-polluted/test-blerg.js: -------------------------------------------------------------------------------- 1 | var t = require('tap') 2 | var http = require('http') 3 | 4 | t.test('anything else 404s', function (t) { 5 | http.get('http://localhost:1337/blerg', function (res) { 6 | t.equal(res.statusCode, 404) 7 | var pong = '' 8 | res.on('data', function (d) { pong += d }) 9 | res.on('end', function () { 10 | t.equal(pong, 'not found\n') 11 | t.end() 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /test/env-polluted/test-ping.js: -------------------------------------------------------------------------------- 1 | var t = require('tap') 2 | var http = require('http') 3 | 4 | t.test('ping returns pong', function (t) { 5 | http.get('http://localhost:1337/ping', function (res) { 6 | t.equal(res.statusCode, 200) 7 | var pong = '' 8 | res.on('data', function (d) { pong += d }) 9 | res.on('end', function () { 10 | t.equal(pong, 'pong\n') 11 | t.end() 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /test/env-polluted/zz-teardown.js: -------------------------------------------------------------------------------- 1 | var tup = require('../..') 2 | tup.close() 3 | -------------------------------------------------------------------------------- /test/exit-early/00-setup.js: -------------------------------------------------------------------------------- 1 | var tup = require('../..') 2 | 3 | tup(function (done) { 4 | var http = require('http') 5 | http.createServer(function (req, res) { 6 | if (req.url === '/ping') { 7 | res.end('pong\n') 8 | } else { 9 | res.statusCode = 404 10 | res.end('not found\n') 11 | } 12 | }).listen(1337, function () { 13 | process.exit(1) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /test/exit-early/test-blerg.js: -------------------------------------------------------------------------------- 1 | var t = require('tap') 2 | var http = require('http') 3 | 4 | t.test('anything else 404s', function (t) { 5 | http.get('http://localhost:1337/blerg', function (res) { 6 | t.equal(res.statusCode, 404) 7 | var pong = '' 8 | res.on('data', function (d) { pong += d }) 9 | res.on('end', function () { 10 | t.equal(pong, 'not found\n') 11 | t.end() 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /test/exit-early/test-ping.js: -------------------------------------------------------------------------------- 1 | var t = require('tap') 2 | var http = require('http') 3 | 4 | t.test('ping returns pong', function (t) { 5 | http.get('http://localhost:1337/ping', function (res) { 6 | t.equal(res.statusCode, 200) 7 | var pong = '' 8 | res.on('data', function (d) { pong += d }) 9 | res.on('end', function () { 10 | t.equal(pong, 'pong\n') 11 | t.end() 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /test/exit-early/zz-teardown.js: -------------------------------------------------------------------------------- 1 | var tup = require('../..') 2 | tup.close() 3 | -------------------------------------------------------------------------------- /test/ignore-sigterm/00-setup.js: -------------------------------------------------------------------------------- 1 | var tup = require('../..') 2 | 3 | process.on('SIGTERM', function () { 4 | // nice suggestion, buddy 5 | }) 6 | 7 | tup(function (done) { 8 | var http = require('http') 9 | http.createServer(function (req, res) { 10 | 'change the toString to make a different hash' 11 | if (req.url === '/ping') { 12 | res.end('pong\n') 13 | } else { 14 | res.statusCode = 404 15 | res.end('not found\n') 16 | } 17 | }).listen(1337, function () { 18 | done() 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /test/ignore-sigterm/test-blerg.js: -------------------------------------------------------------------------------- 1 | var t = require('tap') 2 | var http = require('http') 3 | 4 | t.test('anything else 404s', function (t) { 5 | http.get('http://localhost:1337/blerg', function (res) { 6 | t.equal(res.statusCode, 404) 7 | var pong = '' 8 | res.on('data', function (d) { pong += d }) 9 | res.on('end', function () { 10 | t.equal(pong, 'not found\n') 11 | t.end() 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /test/ignore-sigterm/test-ping.js: -------------------------------------------------------------------------------- 1 | var t = require('tap') 2 | var http = require('http') 3 | 4 | t.test('ping returns pong', function (t) { 5 | http.get('http://localhost:1337/ping', function (res) { 6 | t.equal(res.statusCode, 200) 7 | var pong = '' 8 | res.on('data', function (d) { pong += d }) 9 | res.on('end', function () { 10 | t.equal(pong, 'pong\n') 11 | t.end() 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /test/ignore-sigterm/zz-teardown.js: -------------------------------------------------------------------------------- 1 | var tup = require('../..') 2 | tup.close() 3 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var t = require('tap') 2 | var Test = t.Test 3 | var glob = require('glob') 4 | 5 | t.test('basic success case', function (t) { 6 | runTest(t, 'success', { 7 | plan: { 8 | start: 1, 9 | end: 4 10 | }, 11 | count: 4, 12 | pass: 4, 13 | ok: true 14 | }) 15 | }) 16 | 17 | t.test('env with a pre-existing T_UP_HASH', function (t) { 18 | runTest(t, 'env-polluted', { 19 | plan: { 20 | start: 1, 21 | end: 4 22 | }, 23 | count: 4, 24 | pass: 4, 25 | ok: true 26 | }) 27 | }) 28 | 29 | t.test('ignore SIGTERM case', function (t) { 30 | runTest(t, 'ignore-sigterm', { 31 | plan: { 32 | start: 1, 33 | end: 4 34 | }, 35 | count: 4, 36 | pass: 3, 37 | ok: false, 38 | fail: 1 39 | }) 40 | }) 41 | 42 | t.test('exit early', function (t) { 43 | runTest(t, 'exit-early', { 44 | plan: { 45 | start: 1, 46 | end: 4 47 | }, 48 | count: 4, 49 | pass: 1, 50 | ok: false, 51 | fail: 3 52 | }) 53 | }) 54 | 55 | t.test('two things', function (t) { 56 | t.test('one process', function (t) { 57 | runTest(t, 'two-things-one-process', { 58 | plan: { 59 | start: 1, 60 | end: 4 61 | }, 62 | count: 4, 63 | pass: 1, 64 | ok: false, 65 | fail: 3 66 | }) 67 | }) 68 | t.test('two processes', function (t) { 69 | runTest(t, 'two-things-two-processes', { 70 | plan: { 71 | start: 1, 72 | end: 5 73 | }, 74 | count: 5, 75 | pass: 5, 76 | ok: true 77 | }) 78 | }) 79 | t.end() 80 | }) 81 | 82 | t.test('callback error', function (t) { 83 | runTest(t, 'callback-error', { 84 | plan: { 85 | start: 1, 86 | end: 4 87 | }, 88 | count: 4, 89 | pass: 1, 90 | ok: false, 91 | fail: 3 92 | }) 93 | }) 94 | 95 | function runTest (t, folder, expect) { 96 | var tt = new Test({ name: folder }) 97 | var out = '' 98 | tt.on('data', function (c) { 99 | out += c 100 | }) 101 | var set = glob.sync(__dirname + '/' + folder + '/*.js') 102 | set.forEach(function (f) { 103 | tt.spawn(process.execPath, [f], { stdio: ['pipe', 'pipe', 'ignore'] }) 104 | }) 105 | tt.end() 106 | tt.on('complete', function (results) { 107 | t.match(results, expect) 108 | t.end() 109 | }) 110 | } 111 | -------------------------------------------------------------------------------- /test/success/00-setup.js: -------------------------------------------------------------------------------- 1 | var tup = require('../..') 2 | 3 | tup(function (done) { 4 | console.log('this is a server') 5 | var http = require('http') 6 | http.createServer(function (req, res) { 7 | if (req.url === '/ping') { 8 | res.end('pong\n') 9 | } else { 10 | res.statusCode = 404 11 | res.end('not found\n') 12 | } 13 | }).listen(1337, function () { 14 | done() 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /test/success/test-blerg.js: -------------------------------------------------------------------------------- 1 | var t = require('tap') 2 | var http = require('http') 3 | 4 | t.test('anything else 404s', function (t) { 5 | http.get('http://localhost:1337/blerg', function (res) { 6 | t.equal(res.statusCode, 404) 7 | var pong = '' 8 | res.on('data', function (d) { pong += d }) 9 | res.on('end', function () { 10 | t.equal(pong, 'not found\n') 11 | t.end() 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /test/success/test-ping.js: -------------------------------------------------------------------------------- 1 | var t = require('tap') 2 | var http = require('http') 3 | 4 | t.test('ping returns pong', function (t) { 5 | http.get('http://localhost:1337/ping', function (res) { 6 | t.equal(res.statusCode, 200) 7 | var pong = '' 8 | res.on('data', function (d) { pong += d }) 9 | res.on('end', function () { 10 | t.equal(pong, 'pong\n') 11 | t.end() 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /test/success/zz-teardown.js: -------------------------------------------------------------------------------- 1 | var tup = require('../..') 2 | tup.close() 3 | -------------------------------------------------------------------------------- /test/two-things-one-process/00-setup.js: -------------------------------------------------------------------------------- 1 | var tup = require('../..') 2 | 3 | tup(function (done) { 4 | var http = require('http') 5 | http.createServer(function (req, res) { 6 | if (req.url === '/ping') { 7 | res.end('pong\n') 8 | } else { 9 | res.statusCode = 404 10 | res.end('not found\n') 11 | } 12 | }).listen(15443, function () { 13 | done() 14 | }) 15 | }) 16 | 17 | tup(function (done) { 18 | var http = require('http') 19 | http.createServer(function (req, res) { 20 | if (req.url === '/ping') { 21 | res.end('pong\n') 22 | } else { 23 | res.statusCode = 404 24 | res.end('not found\n') 25 | } 26 | }).listen(15444, function () { 27 | done() 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /test/two-things-one-process/test-blerg.js: -------------------------------------------------------------------------------- 1 | var t = require('tap') 2 | var http = require('http') 3 | 4 | t.test('anything else 404s', function (t) { 5 | http.get('http://localhost:15443/blerg', function (res) { 6 | t.equal(res.statusCode, 404) 7 | var pong = '' 8 | res.on('data', function (d) { pong += d }) 9 | res.on('end', function () { 10 | t.equal(pong, 'not found\n') 11 | t.end() 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /test/two-things-one-process/test-ping.js: -------------------------------------------------------------------------------- 1 | var t = require('tap') 2 | var http = require('http') 3 | 4 | t.test('ping returns pong', function (t) { 5 | http.get('http://localhost:15444/ping', function (res) { 6 | t.equal(res.statusCode, 200) 7 | var pong = '' 8 | res.on('data', function (d) { pong += d }) 9 | res.on('end', function () { 10 | t.equal(pong, 'pong\n') 11 | t.end() 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /test/two-things-one-process/zz-teardown.js: -------------------------------------------------------------------------------- 1 | var tup = require('../..') 2 | tup.close() 3 | -------------------------------------------------------------------------------- /test/two-things-two-processes/00-setup.js: -------------------------------------------------------------------------------- 1 | var tup = require('../..') 2 | 3 | tup(function (done) { 4 | var http = require('http') 5 | http.createServer(function (req, res) { 6 | if (req.url === '/ping') { 7 | res.end('pong\n') 8 | } else { 9 | res.statusCode = 404 10 | res.end('not found\n') 11 | } 12 | }).listen(15443, function () { 13 | done() 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /test/two-things-two-processes/01-setup-second.js: -------------------------------------------------------------------------------- 1 | 2 | var tup = require('../..') 3 | 4 | tup(function (done) { 5 | var http = require('http') 6 | http.createServer(function (req, res) { 7 | if (req.url === '/ping') { 8 | res.end('pong\n') 9 | } else { 10 | res.statusCode = 404 11 | res.end('not found\n') 12 | } 13 | }).listen(15444, function () { 14 | done() 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /test/two-things-two-processes/test-blerg.js: -------------------------------------------------------------------------------- 1 | var t = require('tap') 2 | var http = require('http') 3 | 4 | t.test('anything else 404s', function (t) { 5 | http.get('http://localhost:15443/blerg', function (res) { 6 | t.equal(res.statusCode, 404) 7 | var pong = '' 8 | res.on('data', function (d) { pong += d }) 9 | res.on('end', function () { 10 | t.equal(pong, 'not found\n') 11 | t.end() 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /test/two-things-two-processes/test-ping.js: -------------------------------------------------------------------------------- 1 | var t = require('tap') 2 | var http = require('http') 3 | 4 | t.test('ping returns pong', function (t) { 5 | http.get('http://localhost:15444/ping', function (res) { 6 | t.equal(res.statusCode, 200) 7 | var pong = '' 8 | res.on('data', function (d) { pong += d }) 9 | res.on('end', function () { 10 | t.equal(pong, 'pong\n') 11 | t.end() 12 | }) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /test/two-things-two-processes/zz-teardown.js: -------------------------------------------------------------------------------- 1 | var tup = require('../..') 2 | tup.close() 3 | -------------------------------------------------------------------------------- /tup.js: -------------------------------------------------------------------------------- 1 | module.exports = tup 2 | tup.close = close 3 | 4 | var path = require('path') 5 | var crypto = require('crypto') 6 | var spawn = require('child_process').spawn 7 | var fs = require('fs') 8 | 9 | function sha (fn) { 10 | return crypto.createHash('sha256').update(fn.toString()).digest('hex') 11 | } 12 | 13 | var called = false 14 | function tup (fn) { 15 | if (called) { 16 | throw new Error('You may only use t-up once per process') 17 | } 18 | called = true 19 | process.nextTick(function () { 20 | var hash = sha(fn) 21 | if (process.env.T_UP_HASH === hash) { 22 | // already in child process, yay! 23 | fn(function (er) { 24 | if (er) { 25 | throw er 26 | } else { 27 | console.log('\nT-UP DONE %s\n', hash) 28 | } 29 | }) 30 | } else { 31 | child(hash) 32 | } 33 | }) 34 | } 35 | 36 | function child (hash) { 37 | var cwd = process.cwd() 38 | var pidfile = path.resolve(cwd, '.t-up', hash) 39 | var t = require('tap') 40 | kill(pidfile) 41 | t.pass('killed pidfile ' + pidfile) 42 | 43 | var mkdirp = require('mkdirp') 44 | mkdirp.sync(cwd + '/.t-up/') 45 | 46 | var fd = fs.openSync(pidfile, 'wx') 47 | 48 | var args = process.execArgv.concat(process.argv.slice(1)) 49 | var env = Object.keys(process.env).reduce(function (env, k) { 50 | if (!env[k]) { 51 | env[k] = process.env[k] 52 | } 53 | return env 54 | }, { 55 | T_UP_HASH: hash 56 | }) 57 | 58 | var child = spawn(process.execPath, args, { 59 | env: env, 60 | cwd: cwd, 61 | stdio: [ 'ignore', 'pipe', 'pipe' ], 62 | detached: true 63 | }) 64 | 65 | t.pass('spawned child, awaiting stdout data') 66 | 67 | fs.writeSync(fd, child.pid + '\n') 68 | 69 | child.stderr.pipe(process.stderr) 70 | 71 | var unrefed = false 72 | var out = '' 73 | child.stdout.on('data', function (c) { 74 | out += c 75 | if (out.split(/[\r\n]+/).indexOf('T-UP DONE ' + hash) !== -1) { 76 | unrefed = true 77 | child.unref() 78 | child.stdout.unref() 79 | child.stderr.unref() 80 | } 81 | }) 82 | 83 | child.on('exit', function (exitCode, signal) { 84 | // almost certainly won't get an 'exit' event if we've already 85 | // unrefed the child, but avoid a false failure anyway. 86 | /* istanbul ignore else */ 87 | if (!unrefed) { 88 | t.fail('child process exited early', { 89 | exitCode: exitCode, 90 | signal: signal 91 | }) 92 | } 93 | }) 94 | } 95 | 96 | function kill (pidfile) { 97 | var t = require('tap') 98 | t.pass('kill(' + pidfile + ')') 99 | var rimraf = require('rimraf') 100 | try { 101 | var pid = fs.readFileSync(pidfile, 'utf8').trim() 102 | } catch (er) { 103 | t.pass('could not read pidfile, probably that is fine') 104 | return 105 | } 106 | 107 | try { 108 | process.kill(pid, 'SIGTERM') 109 | } catch (e) { 110 | // very unlikely to get some error other than ESRCH, but if so, 111 | // go ahead and blow up. 112 | /* istanbul ignore else */ 113 | if (e.code === 'ESRCH') { 114 | t.pass('process was not running') 115 | } else { 116 | t.error(e) 117 | } 118 | } 119 | 120 | rimraf.sync(pidfile) 121 | t.pass('removed pidfile') 122 | 123 | try { 124 | var piddir = path.dirname(pidfile) 125 | var files = fs.readdirSync(piddir) 126 | if (!files.length) { 127 | rimraf.sync(piddir) 128 | } 129 | } catch (e) {} 130 | 131 | // Give it 200ms, then make sure SIGTERM was enough 132 | // on windows, SIGTERM is the same as SIGKILL 133 | /* istanbul ignore else */ 134 | if (process.platform !== 'win32') { 135 | setTimeout(function () { 136 | var er 137 | try { 138 | process.kill(pid, 'SIGKILL') 139 | } catch (e) { 140 | er = e 141 | } 142 | if (!er) { 143 | t.fail('exit delayed, SIGKILL was required') 144 | } else { 145 | // very unlikely to get some error other than ESRCH, but if so, 146 | // go ahead and blow up. 147 | /* istanbul ignore else */ 148 | if (er.code === 'ESRCH') { 149 | t.pass('exited successfully with SIGTERM') 150 | } else { 151 | throw er 152 | } 153 | } 154 | }, 200) 155 | } 156 | } 157 | 158 | function close () { 159 | var dir = path.resolve(process.cwd(), '.t-up') 160 | try { 161 | var pidfiles = fs.readdirSync(dir) 162 | } catch (er) { 163 | return 164 | } 165 | 166 | pidfiles.forEach(function (pf) { 167 | kill(path.resolve(dir, pf)) 168 | }) 169 | } 170 | --------------------------------------------------------------------------------