diff --git a/test/parallel/test-readable-from-iterator-closing.js b/test/parallel/test-readable-from-iterator-closing.js index ef7e54821ace..0ddd222756a1 100644 --- a/test/parallel/test-readable-from-iterator-closing.js +++ b/test/parallel/test-readable-from-iterator-closing.js @@ -101,6 +101,40 @@ async function syncRejectedSupport() { } } +async function syncRejectedAfterResolvedSupport() { + const expectedError = new Error('later rejection'); + const finallyMustCall = mustCall(); + const bodyMustCall = mustCall((chunk) => { + assert.strictEqual(chunk, 'a'); + }); + const catchMustCall = mustCall((error) => { + assert.strictEqual(error, expectedError); + }); + const thirdNextMustNotCall = mustNotCall(); + + function* generate() { + try { + yield Promise.resolve('a'); + yield Promise.reject(expectedError); + thirdNextMustNotCall(); + } finally { + finallyMustCall(); + } + } + + const stream = Readable.from(generate()); + + try { + for await (const chunk of stream) { + bodyMustCall(chunk); + } + } catch (error) { + catchMustCall(error); + } + + assert.strictEqual(stream.destroyed, true); +} + async function noReturnAfterThrow() { const returnMustNotCall = mustNotCall(); const bodyMustNotCall = mustNotCall(); @@ -187,6 +221,7 @@ Promise.all([ syncSupport(), syncPromiseSupport(), syncRejectedSupport(), + syncRejectedAfterResolvedSupport(), noReturnAfterThrow(), closeStreamWhileNextIsPending(), closeAfterNullYielded(), diff --git a/test/parallel/test-stream-readable-next-no-null.js b/test/parallel/test-stream-readable-next-no-null.js index 7599e386ca70..b3348c2b9794 100644 --- a/test/parallel/test-stream-readable-next-no-null.js +++ b/test/parallel/test-stream-readable-next-no-null.js @@ -1,19 +1,87 @@ 'use strict'; -const { mustNotCall, expectsError } = require('../common'); +const common = require('../common'); +const assert = require('assert'); const { Readable } = require('stream'); +const { finished } = require('stream/promises'); -async function* generate() { - yield null; -} - -const stream = Readable.from(generate()); - -stream.on('error', expectsError({ +const expectedError = { code: 'ERR_STREAM_NULL_VALUES', name: 'TypeError', message: 'May not write null values to stream' -})); +}; + +async function rejectsNull(iterable, expectedChunks = []) { + const stream = Readable.from(iterable); + const chunks = []; + const completion = finished(stream); + + stream.on('data', (chunk) => chunks.push(chunk)); + + await assert.rejects(completion, expectedError); + assert.deepStrictEqual(chunks, expectedChunks); + assert.strictEqual(stream.destroyed, true); +} + +async function asyncIteratorYieldsNull() { + const cleanup = common.mustCall(); + + async function* generate() { + try { + yield null; + } finally { + cleanup(); + } + } + + await rejectsNull(generate()); +} -stream.on('data', mustNotCall()); +async function syncIteratorYieldsNull() { + const cleanup = common.mustCall(); + + function* generate() { + try { + yield null; + } finally { + cleanup(); + } + } + + await rejectsNull(generate()); +} + +async function firstSyncValueResolvesToNull() { + const cleanup = common.mustCall(); + + function* generate() { + try { + yield Promise.resolve(null); + } finally { + cleanup(); + } + } + + await rejectsNull(generate()); +} + +async function laterSyncValueResolvesToNull() { + const cleanup = common.mustCall(); + + function* generate() { + try { + yield Promise.resolve('first'); + yield Promise.resolve(null); + } finally { + cleanup(); + } + } + + await rejectsNull(generate(), ['first']); +} -stream.on('end', mustNotCall()); +Promise.all([ + asyncIteratorYieldsNull(), + syncIteratorYieldsNull(), + firstSyncValueResolvesToNull(), + laterSyncValueResolvesToNull(), +]).then(common.mustCall());