|
1 | 1 | import {expect} from 'chai' |
2 | 2 | import sinon from 'sinon' |
3 | | -import {Server} from 'node:net'; |
| 3 | +import {Server} from 'node:net' |
| 4 | +import {EventEmitter} from 'node:events' |
| 5 | +import {PassThrough} from 'node:stream' |
4 | 6 |
|
5 | 7 | import { ux } from '@oclif/core' |
6 | 8 | import { utils } from '@heroku/heroku-cli-util' |
7 | | -import {parseExclusions, prepare, maybeTunnel, connArgs} from '../../../../src/lib/pg/push_pull.js' |
| 9 | +import {parseExclusions, prepare, maybeTunnel, connArgs, spawnPipe} from '../../../../src/lib/pg/push_pull.js' |
8 | 10 |
|
9 | 11 | describe('push_pull', function () { |
10 | 12 | describe('parseExclusions', function () { |
@@ -191,4 +193,82 @@ describe('push_pull', function () { |
191 | 193 | expect(actual).to.eql(expected) |
192 | 194 | }) |
193 | 195 | }) |
| 196 | + |
| 197 | + describe('spawnPipe', function () { |
| 198 | + it('resolves when both pgDump and pgRestore close successfully', async function () { |
| 199 | + const pgDump = new EventEmitter() as EventEmitter & {stdout: PassThrough} |
| 200 | + const pgRestore = new EventEmitter() as EventEmitter & {stdin: PassThrough} |
| 201 | + pgDump.stdout = new PassThrough() |
| 202 | + pgRestore.stdin = new PassThrough() |
| 203 | + |
| 204 | + const promise = spawnPipe(pgDump as any, pgRestore as any) |
| 205 | + |
| 206 | + pgDump.emit('close', 0) |
| 207 | + pgRestore.emit('close', 0) |
| 208 | + |
| 209 | + await expect(promise).to.eventually.be.fulfilled |
| 210 | + }) |
| 211 | + |
| 212 | + it('rejects with pg_dump error when pgDump closes with non-zero code', async function () { |
| 213 | + const pgDump = new EventEmitter() as EventEmitter & { stdout: PassThrough } |
| 214 | + const pgRestore = new EventEmitter() as EventEmitter & { stdin: PassThrough } |
| 215 | + pgDump.stdout = new PassThrough() |
| 216 | + pgRestore.stdin = new PassThrough() |
| 217 | + |
| 218 | + const promise = spawnPipe(pgDump as any, pgRestore as any) |
| 219 | + |
| 220 | + pgDump.emit('close', 1) |
| 221 | + |
| 222 | + await expect(promise).to.eventually.be.rejectedWith('pg_dump errored with 1') |
| 223 | + }) |
| 224 | + |
| 225 | + it('rejects with pg_restore error when pgRestore closes with non-zero code', async function () { |
| 226 | + const pgDump = new EventEmitter() as EventEmitter & { stdout: PassThrough } |
| 227 | + const pgRestore = new EventEmitter() as EventEmitter & { stdin: PassThrough } |
| 228 | + pgDump.stdout = new PassThrough() |
| 229 | + pgRestore.stdin = new PassThrough() |
| 230 | + |
| 231 | + const promise = spawnPipe(pgDump as any, pgRestore as any) |
| 232 | + |
| 233 | + pgDump.emit('close', 0) |
| 234 | + pgRestore.emit('close', 1) |
| 235 | + |
| 236 | + await expect(promise).to.eventually.be.rejectedWith('pg_restore errored with 1') |
| 237 | + }) |
| 238 | + |
| 239 | + it('pipes pgDump stdout to pgRestore stdin', async function () { |
| 240 | + const pgDump = new EventEmitter() as EventEmitter & { stdout: PassThrough } |
| 241 | + const pgRestore = new EventEmitter() as EventEmitter & { stdin: PassThrough } |
| 242 | + pgDump.stdout = new PassThrough() |
| 243 | + pgRestore.stdin = new PassThrough() |
| 244 | + |
| 245 | + const chunks: Buffer[] = [] |
| 246 | + pgRestore.stdin.on('data', (chunk: Buffer) => chunks.push(chunk)) |
| 247 | + |
| 248 | + spawnPipe(pgDump as any, pgRestore as any) |
| 249 | + |
| 250 | + pgDump.stdout.write('test data') |
| 251 | + pgDump.emit('close', 0) |
| 252 | + pgRestore.emit('close', 0) |
| 253 | + |
| 254 | + expect(Buffer.concat(chunks).toString()).to.equal('test data') |
| 255 | + }) |
| 256 | + |
| 257 | + it('ends pgRestore stdin when pgDump closes successfully', async function () { |
| 258 | + const pgDump = new EventEmitter() as EventEmitter & { stdout: PassThrough } |
| 259 | + const pgRestore = new EventEmitter() as EventEmitter & { stdin: PassThrough } |
| 260 | + pgDump.stdout = new PassThrough() |
| 261 | + pgRestore.stdin = new PassThrough() |
| 262 | + |
| 263 | + const endSpy = sinon.spy(pgRestore.stdin, 'end') |
| 264 | + |
| 265 | + spawnPipe(pgDump as any, pgRestore as any) |
| 266 | + |
| 267 | + pgDump.emit('close', 0) |
| 268 | + pgRestore.emit('close', 0) |
| 269 | + |
| 270 | + expect(endSpy.calledOnce).to.be.true |
| 271 | + }) |
| 272 | + }) |
194 | 273 | }) |
| 274 | + |
0 commit comments