How to test errors when WRITING to a stream

52 Views Asked by At

I'm trying to tests errors thrown in different parts of the the app. The app pipes a stream from the request (multipart file upload) via encryption to an S3 bucket.

I AM able to create a mock exception in the _read for the encryption stream, but for the destination writable stream if I try the same approach with _write I get an unhandleed exception.

The following test works but if I switch to using _write the error isn't caught correctly

it('error in encryption stream',  async () => {

    const encryptionStream = new DuplexMock();
    encryptionStream._read = () => { 
        throw new Error('Test Exception') 
    };
    const writeStream = new BufferWritableMock();

    CryptoService.encryptionStream.mockImplementation(()=> encryptionStream);
    S3Service.uploadFromStream.mockImplementation(() => writeStream);

    const result = await supertest(appInstance)
        .post(`${storePath}?skip-encryption=false`)
        .attach('file', filePath);

    expect(result.statusCode).toBe(500);

});

Maybe i'm going about this the wrong way...?

Update

In my frantic attempt to get this working I'd update the encryptionStream to DuplexMock this should be PassThrough , the data writeStream wasn't getting called with the code in the original question (I've now update the code above)

Update 2

I'm adding the test below that doensn't work..

it('error in S3 stream', async () => {
    const encryptionStream = new PassThrough();
    const writeStream = new ObjectWritableMock();
    writeStream._write = (chunk, enc, next) => {
        new Error('Test Exception');
    };

    CryptoService.encryptionStream.mockImplementation(()=> encryptionStream);
    S3Service.uploadFromStream.mockImplementation(() => writeStream);
             
    const result = await supertest(appInstance)
        .post(`${storePath}?skip-encryption=false`)
        .attach('file', filePath)
        
    expect(result.statusCode).toBe(500);
        
    }, 10*1000
);
1

There are 1 best solutions below

0
Dog Ears On

After struggling with this for some time, I've identified three issues with the code...

Original Issue (Issue One)

When raising an error in the mock stream I need to call next, when using _read there is no next parameter, but with _write I think it should be:

    writeStream._write = (chunk, enc, next) => {
        next(new Error('Test Exception')); //<-- Don't throw but pass to 'next'
    };

Further issues with SUPERTEST and read ECONNRESET

When trying to create a minimal repo of the issue on my Windows laptop, I ran into more problem. Using supertest my tests were failing with read ECONNRESET, turns out after considerable hair-pulling I realised at least one of these isues was specific to Windows! With that knowledge I discovered this issue which helped identify two further issues:

Error read ECONNRESET (Issues Two)

On windows I noticed each test that returned an error would fail with read ECONNRESET written to the console:

Turns out to fix this on Windows we just need to include the connection: 'keep-alive' header when executing the request with supertest. like so:

const result = await supertest(appInstance)
    .post("/v1/store")
    .query({ "skip-encryption": "true" }) 
    .set('connection', 'keep-alive') //<-- this is important
    .attach('file', filePath)

Error read ECONNRESET (Issue Three)

The final issue was my own fault for not reading the manual. The perviously linked issue also mentions incorrectly formated paths can cause problems, you can see in the code above, I'm including the query prams in the path, include the query string correctly (see below) was the final step to getting my tests working.


const result = await supertest(appInstance)
    .post("/v1/store")
    .query({ "skip-encryption": "true" })  // <-- add query string
    .set('connection', 'keep-alive') 
    .attach('file', filePath)