Skip to content

Commit

Permalink
fixed an issue with 577 error codes (#182)
Browse files Browse the repository at this point in the history
* fixed an issue with 577 error codes

* increased test coverage

* Get tests back to full covrage

---------

Co-authored-by: Christopher Dwyer-Perkins <[email protected]>
  • Loading branch information
sethmaclean and chrisdp authored Jan 22, 2025
1 parent 608729e commit 912a655
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 4 deletions.
119 changes: 116 additions & 3 deletions src/RokuDeploy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1103,7 +1103,7 @@ describe('index', () => {
});
});

it('rejects when response contains invalid password status code', async () => {
it('rejects when response contains update device messaging', async () => {
options.failOnCompileError = true;
mockDoPostRequest(`'Failed to check for software update'`, 200);

Expand All @@ -1117,6 +1117,54 @@ describe('index', () => {
}
});

it('rejects when response contains update device messaging and bad status code on first call', async () => {
options.failOnCompileError = true;
let spy = sinon.stub(rokuDeploy as any, 'doPostRequest').callsFake((params: any) => {
let results: any;
if (params?.formData['mysubmit'] === 'Replace') {
results = { response: { statusCode: 500 }, body: `'Failed to check for software update'` };
} else {
results = { response: { statusCode: 200 }, body: `` };
}
rokuDeploy['checkRequest'](results);
return Promise.resolve(results);
});

try {
await rokuDeploy.publish(options);
assert.fail('Should not have succeeded due to roku server compilation failure');
} catch (err) {
expect(spy.callCount).to.eql(1);
expect((err as any).message).to.eql(
errors.UpdateCheckRequiredError.MESSAGE
);
}
});

it('rejects when response contains update device messaging and bad status code on second call', async () => {
options.failOnCompileError = true;
let spy = sinon.stub(rokuDeploy as any, 'doPostRequest').callsFake((params: any) => {
let results: any;
if (params?.formData['mysubmit'] === 'Replace') {
results = { response: { statusCode: 500 }, body: `` };
} else {
results = { response: { statusCode: 500 }, body: `'Failed to check for software update'` };
}
rokuDeploy['checkRequest'](results);
return Promise.resolve(results);
});

try {
await rokuDeploy.publish(options);
assert.fail('Should not have succeeded due to roku server compilation failure');
} catch (err) {
expect(spy.callCount).to.eql(2);
expect((err as any).message).to.eql(
errors.UpdateCheckRequiredError.MESSAGE
);
}
});

it('handles successful deploy', () => {
options.failOnCompileError = true;
mockDoPostRequest();
Expand Down Expand Up @@ -1208,13 +1256,38 @@ describe('index', () => {
assert.fail('Should not have succeeded');
});

it('Should throw an excpetion', async () => {
it('Should throw an exception and call doPost once', async () => {
options.failOnCompileError = true;
mockDoPostRequest('', 577);
let spy = mockDoPostRequest('', 577);

try {
await rokuDeploy.publish(options);
} catch (e) {
expect(spy.callCount).to.eql(1);
assert.ok('Exception was thrown as expected');
expect(e).to.be.instanceof(errors.UpdateCheckRequiredError);
return;
}
assert.fail('Should not have succeeded');
});

it('Should throw an exception and should call doPost twice', async () => {
options.failOnCompileError = true;
let spy = sinon.stub(rokuDeploy as any, 'doPostRequest').callsFake((params: any) => {
let results: any;
if (params?.formData['mysubmit'] === 'Replace') {
results = { response: { statusCode: 500 }, body: `'not an update error'` };
} else {
results = { response: { statusCode: 577 }, body: `` };
}
rokuDeploy['checkRequest'](results);
return Promise.resolve(results);
});

try {
await rokuDeploy.publish(options);
} catch (e) {
expect(spy.callCount).to.eql(2);
assert.ok('Exception was thrown as expected');
expect(e).to.be.instanceof(errors.UpdateCheckRequiredError);
return;
Expand Down Expand Up @@ -3698,6 +3771,46 @@ describe('index', () => {
});
});

describe('isUpdateRequiredError', () => {
it('returns true if the status code is 577', () => {
expect(
rokuDeploy['isUpdateRequiredError']({ results: { response: { statusCode: 577 } } })
).to.be.true;
});

it('returns true if the body is an update response from device', () => {
const response = `<html>\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"HandheldFriendly\" content=\"True\">\n <title> Roku Development Kit </title>\n\n <link rel=\"stylesheet\" type=\"text/css\" media=\"screen\" href=\"css/global.css\" />\n</head>\n<body>\n <div id=\"root\" style=\"background: #fff\">\n\n </div>\n\n <script type=\"text/javascript\" src=\"css/global.js\"></script>\n <script type=\"text/javascript\">\n \n // Include core components and resounce bundle (needed)\n Shell.resource.set(null, {\n endpoints: {} \n });\n Shell.create('Roku.Event.Key');\n Shell.create('Roku.Events.Resize');\n Shell.create('Roku.Events.Scroll'); \n // Create global navigation and render it\n var nav = Shell.create('Roku.Nav')\n .trigger('Enable standalone and utility mode - hide user menu, shopping cart, and etc.')\n .trigger('Use compact footer')\n .trigger('Hide footer')\n .trigger('Render', document.getElementById('root'))\n .trigger('Remove all feature links from header')\n\n // Retrieve main content body node\n var node = nav.invoke('Get main body section mounting node');\n \n // Create page container and page header\n var container = Shell.create('Roku.Nav.Page.Standard').trigger('Render', node);\n node = container.invoke('Get main body node');\n container.invoke('Get headline node').innerHTML = 'Failed to check for software update';\n\t // Cannot reach Software Update Server\n node.innerHTML = '<p>Please make sure that your Roku device is connected to internet and running most recent software.</p> <p> After connecting to internet, go to system settings and check for software update.</p> ';\n\n var hrDiv = document.createElement('div');\n hrDiv.innerHTML = '<hr />';\n node.appendChild(hrDiv);\n\n var d = document.createElement('div');\n d.innerHTML = '<br />';\n node.appendChild(d);\n\n </script>\n\n\n <div style=\"display:none\">\n\n <font color=\"red\">Please make sure that your Roku device is connected to internet, and running most recent software version (d=953108)</font>\n\n </div>\n\n</body>\n</html>\n`;
expect(
rokuDeploy['isUpdateRequiredError']({ results: { response: { statusCode: 500 }, body: response } })
).to.be.true;
});

it('returns false on missing results', () => {
expect(
rokuDeploy['isUpdateRequiredError']({ })
).to.be.false;
});

it('returns false on missing response', () => {
expect(
rokuDeploy['isUpdateRequiredError']({ results: {} })
).to.be.false;
});

it('returns false on missing status code', () => {
expect(
rokuDeploy['isUpdateRequiredError']({ results: { response: {} } })
).to.be.false;
});

it('returns false on non-string missing body', () => {
expect(
rokuDeploy['isUpdateRequiredError']({ results: { response: { statusCode: 500 }, body: false } })
).to.be.false;
});

});

function mockDoGetRequest(body = '', statusCode = 200) {
return sinon.stub(rokuDeploy as any, 'doGetRequest').callsFake((params) => {
let results = { response: { statusCode: statusCode }, body: body };
Expand Down
11 changes: 10 additions & 1 deletion src/RokuDeploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -468,14 +468,16 @@ export class RokuDeploy {
//fail if this is a compile error
if (this.isCompileError(replaceError.message) && options.failOnCompileError) {
throw new errors.CompileError('Compile error', replaceError, replaceError.results);
} else if (this.isUpdateRequiredError(replaceError)) {
throw replaceError;
} else {
requestOptions.formData.mysubmit = 'Install';
response = await this.doPostRequest(requestOptions);
}
}
} catch (e: any) {
//if this is a 577 error, we have high confidence that the device needs to do an update check
if (e.results?.response?.statusCode === 577) {
if (this.isUpdateRequiredError(e)) {
throw new errors.UpdateCheckRequiredError(response, requestOptions, e);

//a reset connection could be cause by several things, but most likely it's due to the device needing to check for updates
Expand Down Expand Up @@ -529,6 +531,13 @@ export class RokuDeploy {
return !!/["']\s*Failed\s*to\s*check\s*for\s*software\s*update\s*["']/i.exec(responseHtml);
}

/**
* Checks to see if the exception is due to the device needing to check for updates
*/
private isUpdateRequiredError(e: any): boolean {
return e.results?.response?.statusCode === 577 || (typeof e.results?.body === 'string' && this.isUpdateCheckRequiredResponse(e.results.body));
}

/**
* Converts existing loaded package to squashfs for faster loading packages
* @param options
Expand Down

0 comments on commit 912a655

Please sign in to comment.