import { expect, describe, it } from "bun:test"; import { Readable, Writable, Duplex, Transform, PassThrough } from "node:stream"; import { createReadStream } from "node:fs"; import { tmpdir } from "node:os"; import { writeFileSync } from "node:fs"; describe("Readable", () => { it("should be able to be created without _construct method defined", done => { const readable = new Readable({ read() { this.push("Hello World!\n"); this.push(null); }, }); expect(readable instanceof Readable).toBe(true); let data = ""; readable.on("data", chunk => { data += chunk.toString(); }); readable.on("end", () => { expect(data).toBe("Hello World!\n"); done(); }); }); it("should be able to be piped via .pipe", done => { const readable = new Readable({ read() { this.push("Hello World!"); this.push(null); }, }); const writable = new Writable({ write(chunk, encoding, callback) { expect(chunk.toString()).toBe("Hello World!"); callback(); done(); }, }); readable.pipe(writable); }); it("should be able to be piped via .pipe, issue #3607", done => { const path = `${tmpdir()}/${Date.now()}.testReadStreamEmptyFile.txt`; writeFileSync(path, ""); const stream = createReadStream(path); stream.on("error", err => { done(err); }); let called = false; const writable = new Writable({ write(chunk, encoding, callback) { called = true; callback(); }, }); writable.on("finish", () => { try { expect(called).toBeFalse(); } catch (err) { return done(err); } done(); }); stream.pipe(writable); }); it("should be able to be piped via .pipe, issue #3668", done => { const path = `${tmpdir()}/${Date.now()}.testReadStream.txt`; writeFileSync(path, "12345"); const stream = createReadStream(path, { start: 0, end: 4 }); const writable = new Writable({ write(chunk, encoding, callback) { try { expect(chunk.toString()).toBe("12345"); } catch (err) { done(err); return; } callback(); done(); }, }); stream.on("error", err => { done(err); }); stream.pipe(writable); }); it("should be able to be piped via .pipe, both start and end are 0", done => { const path = `${tmpdir()}/${Date.now()}.testReadStream2.txt`; writeFileSync(path, "12345"); const stream = createReadStream(path, { start: 0, end: 0 }); const writable = new Writable({ write(chunk, encoding, callback) { try { // Both start and end are inclusive and start counting at 0. expect(chunk.toString()).toBe("1"); } catch (err) { done(err); return; } callback(); done(); }, }); stream.on("error", err => { done(err); }); stream.pipe(writable); }); it("should be able to be piped via .pipe with a large file", done => { const length = 128 * 1024; const data = "B".repeat(length); const path = `${tmpdir()}/${Date.now()}.testReadStreamLargeFile.txt`; writeFileSync(path, data); const stream = createReadStream(path, { start: 0, end: length - 1 }); let res = ""; let count = 0; const writable = new Writable({ write(chunk, encoding, callback) { count += 1; res += chunk; callback(); }, }); writable.on("finish", () => { try { expect(res).toEqual(data); expect(count).toBeGreaterThan(1); } catch (err) { return done(err); } done(); }); stream.on("error", err => { done(err); }); stream.pipe(writable); }); }); describe("Duplex", () => { it("should allow subclasses to be derived via .call() on class", () => { function Subclass(opts) { if (!(this instanceof Subclass)) return new Subclass(opts); Duplex.call(this, opts); } Object.setPrototypeOf(Subclass.prototype, Duplex.prototype); Object.setPrototypeOf(Subclass, Duplex); const subclass = new Subclass(); expect(subclass instanceof Duplex).toBe(true); }); }); describe("Transform", () => { it("should allow subclasses to be derived via .call() on class", () => { function Subclass(opts) { if (!(this instanceof Subclass)) return new Subclass(opts); Transform.call(this, opts); } Object.setPrototypeOf(Subclass.prototype, Transform.prototype); Object.setPrototypeOf(Subclass, Transform); const subclass = new Subclass(); expect(subclass instanceof Transform).toBe(true); }); }); describe("PassThrough", () => { it("should allow subclasses to be derived via .call() on class", () => { function Subclass(opts) { if (!(this instanceof Subclass)) return new Subclass(opts); PassThrough.call(this, opts); } Object.setPrototypeOf(Subclass.prototype, PassThrough.prototype); Object.setPrototypeOf(Subclass, PassThrough); const subclass = new Subclass(); expect(subclass instanceof PassThrough).toBe(true); }); });