From 10526db851504268af62bc37799d4ad35973fe83 Mon Sep 17 00:00:00 2001 From: Christian Holbrook Date: Thu, 8 Feb 2024 12:01:31 -0700 Subject: [PATCH 1/5] Retry the convertToSquahsfs request given the HPE_INVALID_CONSTANT error --- src/RokuDeploy.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/RokuDeploy.ts b/src/RokuDeploy.ts index 3a04b2e..ce50edb 100644 --- a/src/RokuDeploy.ts +++ b/src/RokuDeploy.ts @@ -507,8 +507,23 @@ export class RokuDeploy { archive: '', mysubmit: 'Convert to squashfs' }); - - let results = await this.doPostRequest(requestOptions); + let results; + try { + results = await this.doPostRequest(requestOptions); + } catch (error) { + if ((error as any)?.code === 'HPE_INVALID_CONSTANT') { + try { + results = await this.doPostRequest(requestOptions, false); + if (/"fileType"\s*:\s*"squashfs"/.test(results.body)) { + return results; + } + } catch (e) { + throw error; + } + } else { + throw error; + } + } if (results.body.indexOf('Conversion succeeded') === -1) { throw new errors.ConvertError('Squashfs conversion failed'); } From 13e6a7071105e79d7be4bf055f979a87adde46c7 Mon Sep 17 00:00:00 2001 From: Christian Holbrook Date: Thu, 8 Feb 2024 12:58:55 -0700 Subject: [PATCH 2/5] Add a clarifying comment and a new test case --- src/RokuDeploy.spec.ts | 17 +++++++++++++++++ src/RokuDeploy.ts | 4 ++++ 2 files changed, 21 insertions(+) diff --git a/src/RokuDeploy.spec.ts b/src/RokuDeploy.spec.ts index d93702f..d1efb95 100644 --- a/src/RokuDeploy.spec.ts +++ b/src/RokuDeploy.spec.ts @@ -1195,8 +1195,25 @@ describe('index', () => { } assert.fail('Should not have succeeded'); }); + + it('should fail with HTTP_INVALID_CONSTANT and then succeed on retry', async () => { + let doPostStub = sinon.stub(rokuDeploy as any, 'doPostRequest'); + doPostStub.onFirstCall().throws((params) => { + throw new HPE_INVALID_CONSTANT_ERROR(); + }); + doPostStub.onSecondCall().returns({ body: '..."fileType":"squashfs"...' }); + try { + await rokuDeploy.convertToSquashfs(options); + } catch (e) { + assert.fail('Should not have throw'); + } + }); }); + class HPE_INVALID_CONSTANT_ERROR extends Error { + code = 'HPE_INVALID_CONSTANT'; + } + describe('rekeyDevice', () => { beforeEach(() => { const body = ` diff --git a/src/RokuDeploy.ts b/src/RokuDeploy.ts index ce50edb..6ae62d8 100644 --- a/src/RokuDeploy.ts +++ b/src/RokuDeploy.ts @@ -511,6 +511,10 @@ export class RokuDeploy { try { results = await this.doPostRequest(requestOptions); } catch (error) { + //Occasionally this error is seen if the zip size and file name length at the + //wrong combination. The device fails to respond to our request with a valid response. + //The device successfully converted the zip, so ping the device and and check the response + //for "fileType": "squashfs" then return a happy response, otherwise throw the original error if ((error as any)?.code === 'HPE_INVALID_CONSTANT') { try { results = await this.doPostRequest(requestOptions, false); From 52cb24f097b01d54499f2ee1a40692e632ec763a Mon Sep 17 00:00:00 2001 From: Christian Holbrook Date: Thu, 8 Feb 2024 13:22:45 -0700 Subject: [PATCH 3/5] Add two more test cases to get to 100% code coverage --- src/RokuDeploy.spec.ts | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/RokuDeploy.spec.ts b/src/RokuDeploy.spec.ts index d1efb95..b3b1d34 100644 --- a/src/RokuDeploy.spec.ts +++ b/src/RokuDeploy.spec.ts @@ -1208,6 +1208,38 @@ describe('index', () => { assert.fail('Should not have throw'); } }); + + it('should fail with HTTP_INVALID_CONSTANT and then fail on retry', async () => { + let doPostStub = sinon.stub(rokuDeploy as any, 'doPostRequest'); + doPostStub.onFirstCall().throws((params) => { + throw new HPE_INVALID_CONSTANT_ERROR(); + }); + doPostStub.onSecondCall().returns({ body: '..."fileType":"zip"...' }); + try { + await rokuDeploy.convertToSquashfs(options); + } catch (e) { + expect(e).to.be.instanceof(errors.ConvertError); + return; + } + assert.fail('Should not have throw'); + }); + + it('should fail with HTTP_INVALID_CONSTANT and then throw on retry', async () => { + let doPostStub = sinon.stub(rokuDeploy as any, 'doPostRequest'); + doPostStub.onFirstCall().throws((params) => { + throw new HPE_INVALID_CONSTANT_ERROR(); + }); + doPostStub.onSecondCall().throws((params) => { + throw new Error('Never seen'); + }); + try { + await rokuDeploy.convertToSquashfs(options); + } catch (e) { + expect(e).to.be.instanceof(HPE_INVALID_CONSTANT_ERROR); + return; + } + assert.fail('Should not have throw'); + }); }); class HPE_INVALID_CONSTANT_ERROR extends Error { From e2d255ad77ab94e36d7c3d331ac42283e7f01d25 Mon Sep 17 00:00:00 2001 From: Christian Holbrook Date: Thu, 8 Feb 2024 13:30:51 -0700 Subject: [PATCH 4/5] Another test in pursuit of 100% --- src/RokuDeploy.spec.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/RokuDeploy.spec.ts b/src/RokuDeploy.spec.ts index b3b1d34..5eb944e 100644 --- a/src/RokuDeploy.spec.ts +++ b/src/RokuDeploy.spec.ts @@ -1196,6 +1196,20 @@ describe('index', () => { assert.fail('Should not have succeeded'); }); + it('should fail with thrown error', async () => { + let doPostStub = sinon.stub(rokuDeploy as any, 'doPostRequest'); + doPostStub.onFirstCall().throws((params) => { + throw new Error('Some error'); + }); + try { + await rokuDeploy.convertToSquashfs(options); + } catch (e) { + expect(e).to.be.instanceof(Error); + return; + } + assert.fail('Should not have throw'); + }); + it('should fail with HTTP_INVALID_CONSTANT and then succeed on retry', async () => { let doPostStub = sinon.stub(rokuDeploy as any, 'doPostRequest'); doPostStub.onFirstCall().throws((params) => { From 8d23ac93bc5699c05470324314252d49a60578e7 Mon Sep 17 00:00:00 2001 From: Christian Holbrook Date: Thu, 8 Feb 2024 15:32:35 -0700 Subject: [PATCH 5/5] Remove optional chaining on catch. The error is always going to be set --- src/RokuDeploy.spec.ts | 38 ++++++++++++++++++++++---------------- src/RokuDeploy.ts | 2 +- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/RokuDeploy.spec.ts b/src/RokuDeploy.spec.ts index 5eb944e..0e0daea 100644 --- a/src/RokuDeploy.spec.ts +++ b/src/RokuDeploy.spec.ts @@ -1196,37 +1196,38 @@ describe('index', () => { assert.fail('Should not have succeeded'); }); - it('should fail with thrown error', async () => { + it('should throw with HPE_INVALID_CONSTANT and then succeed on retry', async () => { let doPostStub = sinon.stub(rokuDeploy as any, 'doPostRequest'); doPostStub.onFirstCall().throws((params) => { - throw new Error('Some error'); + throw new ErrorWithCode(); }); + doPostStub.onSecondCall().returns({ body: '..."fileType":"squashfs"...' }); try { await rokuDeploy.convertToSquashfs(options); } catch (e) { - expect(e).to.be.instanceof(Error); - return; + assert.fail('Should not have throw'); } - assert.fail('Should not have throw'); }); - it('should fail with HTTP_INVALID_CONSTANT and then succeed on retry', async () => { + it('should throw and not retry', async () => { let doPostStub = sinon.stub(rokuDeploy as any, 'doPostRequest'); doPostStub.onFirstCall().throws((params) => { - throw new HPE_INVALID_CONSTANT_ERROR(); + throw new ErrorWithCode('Something else'); }); - doPostStub.onSecondCall().returns({ body: '..."fileType":"squashfs"...' }); try { await rokuDeploy.convertToSquashfs(options); } catch (e) { - assert.fail('Should not have throw'); + expect(e).to.be.instanceof(ErrorWithCode); + expect(e['code']).to.be.eql('Something else'); + return; } + assert.fail('Should not have throw'); }); - it('should fail with HTTP_INVALID_CONSTANT and then fail on retry', async () => { + it('should throw with HPE_INVALID_CONSTANT and then fail on retry', async () => { let doPostStub = sinon.stub(rokuDeploy as any, 'doPostRequest'); doPostStub.onFirstCall().throws((params) => { - throw new HPE_INVALID_CONSTANT_ERROR(); + throw new ErrorWithCode(); }); doPostStub.onSecondCall().returns({ body: '..."fileType":"zip"...' }); try { @@ -1238,10 +1239,10 @@ describe('index', () => { assert.fail('Should not have throw'); }); - it('should fail with HTTP_INVALID_CONSTANT and then throw on retry', async () => { + it('should fail with HPE_INVALID_CONSTANT and then throw on retry', async () => { let doPostStub = sinon.stub(rokuDeploy as any, 'doPostRequest'); doPostStub.onFirstCall().throws((params) => { - throw new HPE_INVALID_CONSTANT_ERROR(); + throw new ErrorWithCode(); }); doPostStub.onSecondCall().throws((params) => { throw new Error('Never seen'); @@ -1249,15 +1250,20 @@ describe('index', () => { try { await rokuDeploy.convertToSquashfs(options); } catch (e) { - expect(e).to.be.instanceof(HPE_INVALID_CONSTANT_ERROR); + expect(e).to.be.instanceof(ErrorWithCode); return; } assert.fail('Should not have throw'); }); }); - class HPE_INVALID_CONSTANT_ERROR extends Error { - code = 'HPE_INVALID_CONSTANT'; + class ErrorWithCode extends Error { + code; + + constructor(code = 'HPE_INVALID_CONSTANT') { + super(); + this.code = code; + } } describe('rekeyDevice', () => { diff --git a/src/RokuDeploy.ts b/src/RokuDeploy.ts index 6ae62d8..5468799 100644 --- a/src/RokuDeploy.ts +++ b/src/RokuDeploy.ts @@ -515,7 +515,7 @@ export class RokuDeploy { //wrong combination. The device fails to respond to our request with a valid response. //The device successfully converted the zip, so ping the device and and check the response //for "fileType": "squashfs" then return a happy response, otherwise throw the original error - if ((error as any)?.code === 'HPE_INVALID_CONSTANT') { + if ((error as any).code === 'HPE_INVALID_CONSTANT') { try { results = await this.doPostRequest(requestOptions, false); if (/"fileType"\s*:\s*"squashfs"/.test(results.body)) {