Skip to content

Commit

Permalink
fix: fix downloadFile unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
maliroteh-sf committed May 30, 2024
1 parent 68a0d8f commit 0539201
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 45 deletions.
11 changes: 5 additions & 6 deletions .mocharc.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
{
"require": ["ts-node/register"],
"watch-extensions": "ts",
"recursive": true,
"reporter": "spec",
"timeout": 600000,
"node-option": ["loader=ts-node/esm"]
"require": ["ts-node/register"],
"watch-extensions": "ts",
"recursive": true,
"reporter": "spec",
"node-option": ["loader=ts-node/esm"]
}
1 change: 1 addition & 0 deletions test/unit/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module.exports = {
// Allow describe and it
env: { mocha: true },
rules: {
'class-methods-use-this': 'off',
'no-unused-expressions': 'off', // Allow assert style expressions. i.e. expect(true).to.be.true
'@typescript-eslint/explicit-function-return-type': 'off', // Return types are defined by the source code. Allows for quick overwrites.
'@typescript-eslint/no-empty-function': 'off', // Mocked out the methods that shouldn't do anything in the tests.
Expand Down
149 changes: 110 additions & 39 deletions test/unit/common/CommonUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,72 @@
*/
import os from 'node:os';
import fs from 'node:fs';
import http from 'node:http';
import https from 'node:https';
import path from 'node:path';
import stream from 'node:stream';
import { Messages } from '@salesforce/core';
import { TestContext } from '@salesforce/core/testSetup';
import { stubMethod } from '@salesforce/ts-sinon';
import { expect } from 'chai';
import sinon from 'sinon';
import { CommonUtils } from '../../../src/common/CommonUtils.js';

Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);

// Custom Writable stream that writes to memory, for testing downloadFile
const data: Buffer[] = [];
class MemoryWriteStream extends stream.Writable {
public getData(): string {
return Buffer.concat(data).toString();
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
public _write(chunk: Buffer, encoding: string, callback: (error?: Error | null) => void): void {
data.push(chunk);
callback();
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
public close(callback?: ((err?: NodeJS.ErrnoException | null | undefined) => void) | undefined): void {
// no-op
}
}

describe('CommonUtils', () => {
const downloadDestinationFile = path.join(os.tmpdir(), 'ca.crt');
const messages = Messages.loadMessages('@salesforce/lwc-dev-mobile-core', 'common');
const $$ = new TestContext();

let unlinkStub: sinon.SinonStub<any[], any>;
let httpGetStub: sinon.SinonStub<any[], any>;
let httpsGetStub: sinon.SinonStub<any[], any>;
let mockBadResponse: http.IncomingMessage;
let mockGoodResponse: http.IncomingMessage;
let writeStreamMock: MemoryWriteStream;

beforeEach(() => {
stubMethod($$.SANDBOX, fs, 'createWriteStream').returns(writeStreamMock);
unlinkStub = stubMethod($$.SANDBOX, fs, 'unlink').callsFake((_, callback) => {
callback();
});
httpGetStub = stubMethod($$.SANDBOX, http, 'get');
httpsGetStub = stubMethod($$.SANDBOX, https, 'get');

mockBadResponse = new stream.PassThrough() as unknown as http.IncomingMessage;
mockBadResponse.statusCode = 404;
mockBadResponse.statusMessage = 'Not Found';

mockGoodResponse = new stream.PassThrough() as unknown as http.IncomingMessage;
mockGoodResponse.statusCode = 200;
mockGoodResponse.statusMessage = 'Done';
(mockGoodResponse as unknown as stream.PassThrough).write('This is a test');

writeStreamMock = new MemoryWriteStream();
});

afterEach(() => {
httpGetStub.reset();
$$.restore();
});

Expand Down Expand Up @@ -119,49 +171,46 @@ describe('CommonUtils', () => {
}
});

it('downloadFile function', async () => {
const dest = path.join(os.tmpdir(), 'ca.crt');

// should fail and not create a destination file
try {
await CommonUtils.downloadFile('badurl', dest);
} catch (error) {
expect(error).to.be.an('error');
expect(fs.existsSync(dest)).to.be.false;
}
it('downloadFile fails with bad relative url', async () => {
await verifyDownloadFails('badurl1', mockBadResponse);
});

// should fail and not create a destination file
try {
await CommonUtils.downloadFile('http://badurl', dest);
} catch (error) {
expect(error).to.be.an('error');
expect(fs.existsSync(dest)).to.be.false;
}
it('downloadFile fails with bad http absolute url', async () => {
await verifyDownloadFails('http://badurl2', mockBadResponse);
});

// should fail and not create a destination file
try {
await CommonUtils.downloadFile('https://badurl', dest);
} catch (error) {
expect(error).to.be.an('error');
expect(fs.existsSync(dest)).to.be.false;
}
it('downloadFile fails with bad https absolute url', async () => {
await verifyDownloadFails('https://badurl3', mockBadResponse);
});

// should fail and not create a destination file
try {
await CommonUtils.downloadFile('https://www.google.com/badurl', dest);
} catch (error) {
expect(error).to.be.an('error');
expect(fs.existsSync(dest)).to.be.false;
}
it('downloadFile fails when fetching url goes through but saving to file fails', async () => {
await verifyDownloadFails('https://www.salesforce.com', {
statusCode: 200,
pipe: (writeStream: fs.WriteStream) => {
writeStream.emit('error', new Error('failed to write to file'));
}
});
});

// For now don't run this part on Windows b/c our CI
// environment does not give file write permission.
if (process.platform !== 'win32') {
// should pass and create a destination file
await CommonUtils.downloadFile('https://www.google.com', dest);
expect(fs.existsSync(dest)).to.be.true;
}
}).timeout(20000); // increase timeout for this test
it('downloadFile passes and save the content', async () => {
httpsGetStub.callsFake((_, callback) => {
setTimeout(
() =>
callback({
statusCode: 200,
pipe: (writeStream: fs.WriteStream) => {
writeStream.write('Test Content');
writeStream.emit('finish');
}
}),
100
);
return { on: () => {}, end: () => {} };
});
await CommonUtils.downloadFile('https://www.google.com', downloadDestinationFile);
expect(unlinkStub.called).to.be.false;
expect(writeStreamMock.getData()).to.equal('Test Content');
});

it('read/write text file functions', async () => {
const dest = path.join(os.tmpdir(), 'test_file.txt');
Expand Down Expand Up @@ -300,4 +349,26 @@ describe('CommonUtils', () => {
parentPath: fname
};
}

async function verifyDownloadFails(url: string, mockResponse: any) {
if (url.startsWith('https:')) {
httpsGetStub.callsFake((_, callback) => {
setTimeout(() => callback(mockResponse), 100);
return { on: () => {}, end: () => {} };
});
} else {
httpGetStub.callsFake((_, callback) => {
setTimeout(() => callback(mockResponse), 100);
return { on: () => {}, end: () => {} };
});
}

try {
await CommonUtils.downloadFile(url, downloadDestinationFile);
} catch (error) {
expect(error).to.be.an('error');
expect(unlinkStub.calledOnce).to.be.true;
expect(unlinkStub.getCall(0).args[0]).to.be.equal(downloadDestinationFile);
}
}
});

0 comments on commit 0539201

Please sign in to comment.