diff --git a/test/02-teardown.test.js b/test/02-teardown.test.js index 13213d04f..591970098 100644 --- a/test/02-teardown.test.js +++ b/test/02-teardown.test.js @@ -1,149 +1,135 @@ 'use strict' +const { isWindows } = require('which-runtime') const test = require('brittle') const path = require('bare-path') +const os = require('bare-os') const hypercoreid = require('hypercore-id-encoding') const Helper = require('./helper') -const harness = path.join(Helper.localDir, 'test', 'fixtures', 'harness') +const workerTeardownDir = path.join(Helper.localDir, 'test', 'fixtures', 'worker-teardown') +const workerTeardownOsKillDir = path.join(Helper.localDir, 'test', 'fixtures', 'worker-teardown-os-kill') +const workerTeardownExitCodeDir = path.join(Helper.localDir, 'test', 'fixtures', 'worker-teardown-exit-code') -test('teardown', async function ({ is, ok, plan, comment, teardown, timeout }) { +test('teardown on pipe end', { skip: isWindows }, async function ({ ok, is, plan, comment, teardown, timeout }) { timeout(180000) + plan(4) - plan(5) - - const stager = new Helper() - teardown(() => stager.close(), { order: Infinity }) - await stager.ready() + const dir = workerTeardownDir - const dir = harness + const helper = new Helper() + teardown(() => helper.close(), { order: Infinity }) + await helper.ready() const id = Math.floor(Math.random() * 10000) comment('staging') - const stage = stager.stage({ channel: `test-${id}`, name: `test-${id}`, dir, dryRun: false, bare: true }) - const final = await Helper.pick(stage, { tag: 'final' }) - ok(final.success, 'stage succeeded') + const staging = helper.stage({ channel: `test-${id}`, name: `test-${id}`, dir, dryRun: false, bare: true }) + teardown(() => Helper.teardownStream(staging)) + const staged = await Helper.pick(staging, { tag: 'final' }) + ok(staged.success, 'stage succeeded') comment('seeding') - const seeder = new Helper() - teardown(() => seeder.close(), { order: Infinity }) - await seeder.ready() - const seed = seeder.seed({ channel: `test-${id}`, name: `test-${id}`, dir }) - teardown(() => Helper.teardownStream(seed)) - const until = await Helper.pick(seed, [{ tag: 'key' }, { tag: 'announced' }]) - const key = await until.key + const seeding = helper.seed({ channel: `test-${id}`, name: `test-${id}`, dir, key: null, cmdArgs: [] }) + teardown(() => Helper.teardownStream(seeding)) + const until = await Helper.pick(seeding, [{ tag: 'key' }, { tag: 'announced' }]) const announced = await until.announced - - ok(hypercoreid.isValid(key), 'app key is valid') ok(announced, 'seeding is announced') - comment('running') - const link = 'pear://' + key - const running = await Helper.open(link, { tags: ['teardown', 'exit'] }) - - await running.inspector.evaluate('Pear.teardown(() => console.log(\'teardown\'))') - await running.inspector.evaluate('Pear.shutdown()') - await running.inspector.close() + const key = await until.key + ok(hypercoreid.isValid(key), 'app key is valid') - const td = await running.until.teardown - is(td, 'teardown', 'teardown has been triggered') + const link = `pear://${key}` + const run = await Helper.run({ link }) + const { pipe } = run - const { code } = await running.until.exit - is(code, 0, 'exit code is 0') + const td = await Helper.untilResult(pipe, 5000, () => pipe.end()) + is(td, 'teardown', 'teardown executed') }) -test('teardown during teardown', async function ({ is, ok, plan, comment, teardown, timeout }) { +test('teardown on os kill', { skip: isWindows }, async function ({ ok, is, plan, comment, teardown, timeout }) { timeout(180000) plan(5) - const stager = new Helper() - teardown(() => stager.close(), { order: Infinity }) - await stager.ready() + const dir = workerTeardownOsKillDir - const dir = harness + const helper = new Helper() + teardown(() => helper.close(), { order: Infinity }) + await helper.ready() const id = Math.floor(Math.random() * 10000) comment('staging') - const stage = stager.stage({ channel: `test-${id}`, name: `test-${id}`, dir, dryRun: false, bare: true }) - const final = await Helper.pick(stage, { tag: 'final' }) - ok(final.success, 'stage succeeded') + const staging = helper.stage({ channel: `test-${id}`, name: `test-${id}`, dir, dryRun: false, bare: true }) + teardown(() => Helper.teardownStream(staging)) + const staged = await Helper.pick(staging, { tag: 'final' }) + ok(staged.success, 'stage succeeded') comment('seeding') - const seeder = new Helper() - teardown(() => seeder.close(), { order: Infinity }) - await seeder.ready() - const seed = seeder.seed({ channel: `test-${id}`, name: `test-${id}`, dir }) - teardown(() => Helper.teardownStream(seed)) - const until = await Helper.pick(seed, [{ tag: 'key' }, { tag: 'announced' }]) - const key = await until.key + const seeding = helper.seed({ channel: `test-${id}`, name: `test-${id}`, dir, key: null, cmdArgs: [] }) + teardown(() => Helper.teardownStream(seeding)) + const until = await Helper.pick(seeding, [{ tag: 'key' }, { tag: 'announced' }]) const announced = await until.announced - - ok(hypercoreid.isValid(key), 'app key is valid') ok(announced, 'seeding is announced') - comment('running') - const link = 'pear://' + key - const running = await Helper.open(link, { tags: ['teardown', 'exit'] }) - - await running.inspector.evaluate( - `(() => { - const { teardown } = Pear - const a = () => { b() } - const b = () => { teardown(() => console.log('teardown from b')) } - teardown( () => a() ) - })()`) + const key = await until.key + ok(hypercoreid.isValid(key), 'app key is valid') - await running.inspector.evaluate('Pear.shutdown()') - await running.inspector.close() + const link = `pear://${key}` + const run = await Helper.run({ link }) + const { pipe } = run - const td = await running.until.teardown - is(td, 'teardown from b', 'teardown from b has been triggered') + const pid = +(await Helper.untilResult(pipe)) + ok(pid > 0, 'worker pid is valid') - const { code } = await running.until.exit - is(code, 0, 'exit code is 0') + const td = await Helper.untilResult(pipe, 5000, () => os.kill(pid)) + ok(td, 'teardown executed') }) -// TODO: fixme -test.skip('exit with non-zero code in teardown', async function ({ is, ok, plan, comment, teardown }) { - plan(4) +test('teardown on os kill with exit code', { skip: isWindows }, async function ({ ok, is, plan, comment, teardown, timeout }) { + timeout(180000) + plan(6) - const stager = new Helper() - teardown(() => stager.close(), { order: Infinity }) - await stager.ready() + const dir = workerTeardownExitCodeDir - const dir = harness + const helper = new Helper() + teardown(() => helper.close(), { order: Infinity }) + await helper.ready() const id = Math.floor(Math.random() * 10000) comment('staging') - const stage = stager.stage({ channel: `test-${id}`, name: `test-${id}`, dir, dryRun: false, bare: true }) - const final = await Helper.pick(stage, { tag: 'final' }) - ok(final.success, 'stage succeeded') + const staging = helper.stage({ channel: `test-${id}`, name: `test-${id}`, dir, dryRun: false, bare: true }) + teardown(() => Helper.teardownStream(staging)) + const staged = await Helper.pick(staging, { tag: 'final' }) + ok(staged.success, 'stage succeeded') comment('seeding') - const seeder = new Helper() - teardown(() => seeder.close()) - teardown(() => seeder.close(), { order: Infinity }) - await seeder.ready() - const seed = seeder.seed({ channel: `test-${id}`, name: `test-${id}`, dir }) - teardown(() => Helper.teardownStream(seed)) - const until = await Helper.pick(seed, [{ tag: 'key' }, { tag: 'announced' }]) - const key = await until.key + const seeding = helper.seed({ channel: `test-${id}`, name: `test-${id}`, dir, key: null, cmdArgs: [] }) + teardown(() => Helper.teardownStream(seeding)) + const until = await Helper.pick(seeding, [{ tag: 'key' }, { tag: 'announced' }]) const announced = await until.announced + ok(announced, 'seeding is announced') + const key = await until.key ok(hypercoreid.isValid(key), 'app key is valid') - ok(announced, 'seeding is announced') - comment('running') - const link = 'pear://' + key - const running = await Helper.open(link, { tags: ['teardown', 'exit'] }) + const link = `pear://${key}` + const run = await Helper.run({ link }) + const { pipe } = run + + const pid = +(await Helper.untilResult(pipe)) + ok(pid > 0, 'worker pid is valid') - await running.inspector.evaluate('Pear.teardown(() => Pear.exit(124))') + const exitCodePromise = new Promise((resolve, reject) => { + const timeoutId = setTimeout(() => reject(new Error('timed out')), 5000) + pipe.on('crash', (data) => { + clearTimeout(timeoutId) + resolve(data.exitCode) + }) + }) - await running.inspector.evaluate('__PEAR_TEST__.close()') - await running.inspector.close() - // running.subprocess.kill('SIGINT') <-- this was forcing the exit code, which false-positives the test + const td = await Helper.untilResult(pipe, 5000, () => os.kill(pid)) + ok(td, 'teardown executed') - const { code } = await running.until.exit - is(code, 124, 'exit code is 124') + const exitCode = await exitCodePromise + is(exitCode, 124, 'exit code is 124') }) diff --git a/test/fixtures/worker-teardown-exit-code/index.js b/test/fixtures/worker-teardown-exit-code/index.js new file mode 100644 index 000000000..04a351c33 --- /dev/null +++ b/test/fixtures/worker-teardown-exit-code/index.js @@ -0,0 +1,9 @@ +const pipe = Pear.worker.pipe() +pipe.on('data', () => pipe.write(`${Bare.pid}`)) + +Pear.teardown(async () => { + await new Promise((resolve) => { + pipe.write('teardown', resolve) + }) + Pear.exit(124) +}) diff --git a/test/fixtures/worker-teardown-exit-code/package.json b/test/fixtures/worker-teardown-exit-code/package.json new file mode 100644 index 000000000..b0a017614 --- /dev/null +++ b/test/fixtures/worker-teardown-exit-code/package.json @@ -0,0 +1,8 @@ +{ + "name": "worker-teardown-exit-code", + "main": "index.js", + "pear": { + "name": "worker-teardown-exit-code", + "type": "terminal" + } +} diff --git a/test/fixtures/worker-teardown-os-kill/index.js b/test/fixtures/worker-teardown-os-kill/index.js new file mode 100644 index 000000000..3df111bec --- /dev/null +++ b/test/fixtures/worker-teardown-os-kill/index.js @@ -0,0 +1,8 @@ +const pipe = Pear.worker.pipe() +pipe.on('data', () => pipe.write(`${Bare.pid}`)) + +Pear.teardown(async () => { + await new Promise((resolve) => { + pipe.write('teardown', resolve) + }) +}) diff --git a/test/fixtures/worker-teardown-os-kill/package.json b/test/fixtures/worker-teardown-os-kill/package.json new file mode 100644 index 000000000..110c14a67 --- /dev/null +++ b/test/fixtures/worker-teardown-os-kill/package.json @@ -0,0 +1,8 @@ +{ + "name": "worker-teardown-os-kill", + "main": "index.js", + "pear": { + "name": "worker-teardown-os-kill", + "type": "terminal" + } +} diff --git a/test/fixtures/worker-teardown/index.js b/test/fixtures/worker-teardown/index.js new file mode 100644 index 000000000..ef1f86798 --- /dev/null +++ b/test/fixtures/worker-teardown/index.js @@ -0,0 +1,7 @@ +const pipe = Pear.worker.pipe() + +Pear.teardown(async () => { + await new Promise((resolve) => { + pipe.write('teardown', resolve) + }) +}) diff --git a/test/fixtures/worker-teardown/package.json b/test/fixtures/worker-teardown/package.json new file mode 100644 index 000000000..22b52079e --- /dev/null +++ b/test/fixtures/worker-teardown/package.json @@ -0,0 +1,8 @@ +{ + "name": "worker-teardown", + "main": "index.js", + "pear": { + "name": "worker-teardown", + "type": "terminal" + } +} diff --git a/test/helper.js b/test/helper.js index ced0ed876..aef6abfdd 100644 --- a/test/helper.js +++ b/test/helper.js @@ -157,7 +157,7 @@ class Helper extends IPC { return { pipe } } - static async untilResult (pipe, timeout = 5000) { + static async untilResult (pipe, timeout = 5000, runFn) { const res = new Promise((resolve, reject) => { const timeoutId = setTimeout(() => reject(new Error('timed out')), timeout) pipe.on('data', (data) => { @@ -173,7 +173,11 @@ class Helper extends IPC { reject(new Error('unexpected ended')) }) }) - pipe.write('start') + if (runFn) { + await runFn() + } else { + pipe.write('start') + } return res }