Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/* global createHttpServer, readFile */
const { sinon } = ChromeUtils.importESModule(
);
const { ObliviousHTTP } = ChromeUtils.importESModule(
"resource://gre/modules/ObliviousHTTP.sys.mjs"
);
function BinaryHttpResponse(status, headerNames, headerValues, content) {
this.status = status;
this.headerNames = headerNames;
this.headerValues = headerValues;
this.content = content;
}
BinaryHttpResponse.prototype = {
QueryInterface: ChromeUtils.generateQI(["nsIBinaryHttpResponse"]),
};
const {
ANALYSIS_RESPONSE_SCHEMA,
ANALYSIS_REQUEST_SCHEMA,
RECOMMENDATIONS_RESPONSE_SCHEMA,
RECOMMENDATIONS_REQUEST_SCHEMA,
ATTRIBUTION_RESPONSE_SCHEMA,
ATTRIBUTION_REQUEST_SCHEMA,
ANALYZE_RESPONSE_SCHEMA,
ANALYZE_REQUEST_SCHEMA,
ANALYSIS_STATUS_RESPONSE_SCHEMA,
ANALYSIS_STATUS_REQUEST_SCHEMA,
} = ChromeUtils.importESModule(
"chrome://global/content/shopping/ProductConfig.mjs"
);
const { ShoppingProduct } = ChromeUtils.importESModule(
"chrome://global/content/shopping/ShoppingProduct.mjs"
);
const ANALYSIS_API_MOCK = "http://example.com/api/analysis_response.json";
const RECOMMENDATIONS_API_MOCK =
const ATTRIBUTION_API_MOCK = "http://example.com/api/attribution_response.json";
const ANALYSIS_API_MOCK_INVALID =
const API_SERVICE_UNAVAILABLE =
const API_ERROR_ONCE = "http://example.com/errors/error_once.json";
const API_ERROR_BAD_REQUEST = "http://example.com/errors/bad_request.json";
const API_ERROR_UNPROCESSABLE =
const API_ERROR_TOO_MANY_REQUESTS =
const API_ANALYSIS_IN_PROGRESS =
const REPORTING_API_MOCK = "http://example.com/api/report_response.json";
const ANALYZE_API_MOCK = "http://example.com/api/analyze_pending.json";
const TEST_AID =
"1ALhiNLkZ2yR4al5lcP1Npbtlpl5toDfKRgJOATjeieAL6i5Dul99l9+ZTiIWyybUzGysChAdrOA6BWrMqr0EvjoymiH3veZ++XuOvJnC0y1NB/IQQtUzlYEO028XqVUJWJeJte47nPhnK2pSm2QhbdeKbxEnauKAty1cFQeEaBUP7LkvUgxh1GDzflwcVfuKcgMr7hOM3NzjYR2RN3vhmT385Ps4wUj--cv2ucc+1nozldFrl--i9GYyjuHYFFi+EgXXZ3ZsA==";
const server = createHttpServer({ hosts: ["example.com"] });
server.registerDirectory("/api/", do_get_file("/data"));
// Path to test API call that will always fail.
server.registerPathHandler(
new URL(API_SERVICE_UNAVAILABLE).pathname,
(request, response) => {
response.setStatusLine(request.httpVersion, 503, "Service Unavailable");
response.setHeader(
"Content-Type",
"application/json; charset=utf-8",
false
);
response.write(readFile("data/service_unavailable.json", false));
}
);
// Path to test API call that will fail once and then succeeded.
let apiErrors = 0;
server.registerPathHandler(
new URL(API_ERROR_ONCE).pathname,
(request, response) => {
response.setHeader(
"Content-Type",
"application/json; charset=utf-8",
false
);
if (apiErrors == 0) {
response.setStatusLine(request.httpVersion, 503, "Service Unavailable");
response.write(readFile("/data/service_unavailable.json"));
apiErrors++;
} else {
response.setStatusLine(request.httpVersion, 200, "OK");
response.write(readFile("/data/analysis_response.json"));
apiErrors = 0;
}
}
);
// Request is missing required parameters.
server.registerPathHandler(
new URL(API_ERROR_BAD_REQUEST).pathname,
(request, response) => {
response.setStatusLine(request.httpVersion, 400, "Bad Request");
response.setHeader(
"Content-Type",
"application/json; charset=utf-8",
false
);
response.write(readFile("data/bad_request.json", false));
}
);
// Request contains a nonsense product identifier or non supported website.
server.registerPathHandler(
new URL(API_ERROR_UNPROCESSABLE).pathname,
(request, response) => {
response.setStatusLine(request.httpVersion, 422, "Unprocessable entity");
response.setHeader(
"Content-Type",
"application/json; charset=utf-8",
false
);
response.write(readFile("data/unprocessable_entity.json", false));
}
);
// Too many requests to the API.
server.registerPathHandler(
new URL(API_ERROR_TOO_MANY_REQUESTS).pathname,
(request, response) => {
response.setStatusLine(request.httpVersion, 429, "Too many requests");
response.setHeader(
"Content-Type",
"application/json; charset=utf-8",
false
);
response.write(readFile("data/too_many_requests.json", false));
}
);
// Path to test API call that will still be processing twice and then succeeded.
let pollingTries = 0;
server.registerPathHandler(new URL(API_POLL).pathname, (request, response) => {
response.setHeader("Content-Type", "application/json; charset=utf-8", false);
if (pollingTries == 0) {
response.setStatusLine(request.httpVersion, 200, "OK");
response.write(readFile("/data/analysis_status_pending_response.json"));
pollingTries++;
} else if (pollingTries == 1) {
response.setStatusLine(request.httpVersion, 200, "OK");
response.write(readFile("/data/analysis_status_in_progress_response.json"));
pollingTries++;
} else {
response.setStatusLine(request.httpVersion, 200, "OK");
response.write(readFile("/data/analysis_status_completed_response.json"));
pollingTries = 0;
}
});
// Path to test API call that will always need analysis.
server.registerPathHandler(
new URL(API_ANALYSIS_IN_PROGRESS).pathname,
(request, response) => {
response.setHeader(
"Content-Type",
"application/json; charset=utf-8",
false
);
response.setStatusLine(request.httpVersion, 200, "OK");
response.write(readFile("/data/analysis_status_in_progress_response.json"));
}
);
let ohttp = Cc["@mozilla.org/network/oblivious-http;1"].getService(
Ci.nsIObliviousHttp
);
let ohttpServer = ohttp.server();
server.registerPathHandler(
new URL(API_OHTTP_CONFIG).pathname,
(request, response) => {
let bstream = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(
Ci.nsIBinaryOutputStream
);
bstream.setOutputStream(response.bodyOutputStream);
bstream.writeByteArray(ohttpServer.encodedConfig);
}
);
let gExpectedOHTTPMethod = "POST";
let gExpectedProductDetails;
server.registerPathHandler(
new URL(API_OHTTP_RELAY).pathname,
async (request, response) => {
let inputStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(
Ci.nsIBinaryInputStream
);
inputStream.setInputStream(request.bodyInputStream);
let requestBody = inputStream.readByteArray(inputStream.available());
let ohttpRequest = ohttpServer.decapsulate(requestBody);
let bhttp = Cc["@mozilla.org/network/binary-http;1"].getService(
Ci.nsIBinaryHttp
);
let decodedRequest = bhttp.decodeRequest(ohttpRequest.request);
Assert.equal(
decodedRequest.method,
gExpectedOHTTPMethod,
"Should get expected HTTP method"
);
Assert.deepEqual(decodedRequest.headerNames.sort(), [
"Accept",
"Content-Type",
]);
Assert.deepEqual(decodedRequest.headerValues, [
"application/json",
"application/json",
]);
if (gExpectedOHTTPMethod == "POST") {
Assert.equal(
new TextDecoder().decode(new Uint8Array(decodedRequest.content)),
gExpectedProductDetails,
"Expected body content."
);
}
response.processAsync();
let innerResponse = await fetch("http://example.com" + decodedRequest.path);
let bytes = new Uint8Array(await innerResponse.arrayBuffer());
let binaryResponse = new BinaryHttpResponse(
innerResponse.status,
["Content-Type"],
["application/json"],
bytes
);
let encResponse = ohttpRequest.encapsulate(
bhttp.encodeResponse(binaryResponse)
);
response.setStatusLine(request.httpVersion, 200, "OK");
response.setHeader("Content-Type", "message/ohttp-res", false);
let bstream = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(
Ci.nsIBinaryOutputStream
);
bstream.setOutputStream(response.bodyOutputStream);
bstream.writeByteArray(encResponse);
response.finish();
}
);
add_task(async function test_product_requestAnalysis() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: false });
Assert.ok(product.isProduct(), "Should recognize a valid product.");
let analysis = await product.requestAnalysis(undefined, {
url: ANALYSIS_API_MOCK,
requestSchema: ANALYSIS_REQUEST_SCHEMA,
responseSchema: ANALYSIS_RESPONSE_SCHEMA,
});
Assert.ok(
typeof analysis == "object",
"Analysis object is loaded from JSON and validated"
);
});
add_task(async function test_product_requestAnalysis_OHTTP() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: false });
Assert.ok(product.isProduct(), "Should recognize a valid product.");
gExpectedProductDetails = JSON.stringify({
product_id: "926485654",
website: "walmart.com",
});
enableOHTTP();
let analysis = await product.requestAnalysis(undefined, {
url: ANALYSIS_API_MOCK,
requestSchema: ANALYSIS_REQUEST_SCHEMA,
responseSchema: ANALYSIS_RESPONSE_SCHEMA,
});
Assert.deepEqual(
analysis,
await fetch(ANALYSIS_API_MOCK).then(r => r.json()),
"Analysis object is loaded from JSON and validated"
);
disableOHTTP();
});
add_task(async function test_product_requestAnalysis_invalid() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: false });
Assert.ok(product.isProduct(), "Should recognize a valid product.");
let analysis = await product.requestAnalysis(undefined, {
url: ANALYSIS_API_MOCK_INVALID,
requestSchema: ANALYSIS_REQUEST_SCHEMA,
responseSchema: ANALYSIS_RESPONSE_SCHEMA,
});
Assert.equal(analysis, undefined, "Analysis object is invalidated");
});
add_task(async function test_product_requestAnalysis_invalid_allowed() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: true });
Assert.ok(product.isProduct(), "Should recognize a valid product.");
let analysis = await product.requestAnalysis(undefined, {
url: ANALYSIS_API_MOCK_INVALID,
requestSchema: ANALYSIS_REQUEST_SCHEMA,
responseSchema: ANALYSIS_RESPONSE_SCHEMA,
});
Assert.equal(analysis.grade, 0.85, "Analysis is invalid but allowed");
});
add_task(async function test_product_requestAnalysis_broken_config() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: false });
Assert.ok(product.isProduct(), "Should recognize a valid product.");
gExpectedProductDetails = JSON.stringify({
product_id: "926485654",
website: "walmart.com",
});
let analysis = await product.requestAnalysis(undefined, {
url: ANALYSIS_API_MOCK,
requestSchema: ANALYSIS_REQUEST_SCHEMA,
responseSchema: ANALYSIS_RESPONSE_SCHEMA,
});
// Because the config is missing, the OHTTP request can't be constructed,
// so we should fail.
Assert.equal(analysis, undefined, "Analysis object is invalidated");
disableOHTTP();
});
add_task(async function test_product_requestAnalysis_invalid_ohttp() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: false });
Assert.ok(product.isProduct(), "Should recognize a valid product.");
gExpectedProductDetails = JSON.stringify({
product_id: "926485654",
website: "walmart.com",
});
enableOHTTP();
let analysis = await product.requestAnalysis(undefined, {
url: ANALYSIS_API_MOCK_INVALID,
requestSchema: ANALYSIS_REQUEST_SCHEMA,
responseSchema: ANALYSIS_RESPONSE_SCHEMA,
});
Assert.equal(analysis, undefined, "Analysis object is invalidated");
disableOHTTP();
});
add_task(async function test_product_requestAnalysis_invalid_allowed_ohttp() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: true });
Assert.ok(product.isProduct(), "Should recognize a valid product.");
gExpectedProductDetails = JSON.stringify({
product_id: "926485654",
website: "walmart.com",
});
enableOHTTP();
let analysis = await product.requestAnalysis(undefined, {
url: ANALYSIS_API_MOCK_INVALID,
requestSchema: ANALYSIS_REQUEST_SCHEMA,
responseSchema: ANALYSIS_RESPONSE_SCHEMA,
});
Assert.equal(analysis.grade, 0.85, "Analysis is invalid but allowed");
disableOHTTP();
});
add_task(async function test_product_requestRecommendations() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: false });
if (product.isProduct()) {
let recommendations = await product.requestRecommendations(undefined, {
url: RECOMMENDATIONS_API_MOCK,
requestSchema: RECOMMENDATIONS_REQUEST_SCHEMA,
responseSchema: RECOMMENDATIONS_RESPONSE_SCHEMA,
});
Assert.ok(
Array.isArray(recommendations),
"Recommendations array is loaded from JSON and validated"
);
}
});
add_task(async function test_product_requestAnalysis_retry_failure() {
const TEST_TIMEOUT = 100;
const RETRIES = 3;
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: false });
let sandbox = sinon.createSandbox();
let spy = sandbox.spy(ShoppingProduct, "request");
let startTime = Cu.now();
let totalTime = TEST_TIMEOUT * Math.pow(2, RETRIES - 1);
if (product.isProduct()) {
let analysis = await product.requestAnalysis(undefined, {
url: API_SERVICE_UNAVAILABLE,
requestSchema: ANALYSIS_REQUEST_SCHEMA,
responseSchema: ANALYSIS_RESPONSE_SCHEMA,
});
Assert.equal(analysis, null, "Analysis object is null");
Assert.equal(
spy.callCount,
RETRIES + 1,
`Request was retried ${RETRIES} times after a failure`
);
Assert.ok(
Cu.now() - startTime >= totalTime,
`Waited for at least ${totalTime}ms`
);
}
sandbox.restore();
});
add_task(async function test_product_requestAnalysis_retry_success() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: false });
let sandbox = sinon.createSandbox();
let spy = sandbox.spy(ShoppingProduct, "request");
// Make sure API error count is reset
apiErrors = 0;
if (product.isProduct()) {
let analysis = await product.requestAnalysis(undefined, {
url: API_ERROR_ONCE,
requestSchema: ANALYSIS_REQUEST_SCHEMA,
responseSchema: ANALYSIS_RESPONSE_SCHEMA,
});
Assert.equal(spy.callCount, 2, `Request succeeded after a failure`);
Assert.ok(
typeof analysis == "object",
"Analysis object is loaded from JSON and validated"
);
}
sandbox.restore();
});
add_task(async function test_product_bad_request() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: false });
if (product.isProduct()) {
let errorResult = await product.requestAnalysis(undefined, {
url: API_ERROR_BAD_REQUEST,
requestSchema: ANALYSIS_REQUEST_SCHEMA,
responseSchema: ANALYSIS_RESPONSE_SCHEMA,
});
Assert.ok(
typeof errorResult == "object",
"Error object is loaded from JSON"
);
Assert.equal(errorResult.status, 400, "Error status is passed");
Assert.equal(errorResult.error, "Bad Request", "Error message is passed");
}
});
add_task(async function test_product_unprocessable_entity() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: false });
if (product.isProduct()) {
let errorResult = await product.requestAnalysis(undefined, {
url: API_ERROR_UNPROCESSABLE,
requestSchema: ANALYSIS_REQUEST_SCHEMA,
responseSchema: ANALYSIS_RESPONSE_SCHEMA,
});
Assert.ok(
typeof errorResult == "object",
"Error object is loaded from JSON"
);
Assert.equal(errorResult.status, 422, "Error status is passed");
Assert.equal(
errorResult.error,
"Unprocessable entity",
"Error message is passed"
);
}
});
add_task(async function test_ohttp_headers() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: false });
Assert.ok(product.isProduct(), "Should recognize a valid product.");
gExpectedProductDetails = JSON.stringify({
product_id: "926485654",
website: "walmart.com",
});
enableOHTTP();
let configURL = Services.prefs.getCharPref("toolkit.shopping.ohttpConfigURL");
let config = await ObliviousHTTP.getOHTTPConfig(configURL);
Assert.ok(config, "Should have gotten a config.");
let ohttpDetails = await ObliviousHTTP.ohttpRequest(
API_OHTTP_RELAY,
config,
ANALYSIS_API_MOCK,
{
method: "POST",
body: gExpectedProductDetails,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
signal: new AbortController().signal,
}
);
Assert.equal(ohttpDetails.status, 200, "Request should return 200 OK.");
Assert.ok(ohttpDetails.ok, "Request should succeed.");
let responseHeaders = Object.fromEntries(ohttpDetails.headers.entries());
Assert.deepEqual(
responseHeaders,
{ "content-type": "application/json" },
"Should have expected response headers."
);
disableOHTTP();
});
add_task(async function test_ohttp_too_many_requests() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: false });
Assert.ok(product.isProduct(), "Should recognize a valid product.");
gExpectedProductDetails = JSON.stringify({
product_id: "926485654",
website: "walmart.com",
});
enableOHTTP();
let configURL = Services.prefs.getCharPref("toolkit.shopping.ohttpConfigURL");
let config = await ObliviousHTTP.getOHTTPConfig(configURL);
Assert.ok(config, "Should have gotten a config.");
let ohttpDetails = await ObliviousHTTP.ohttpRequest(
API_OHTTP_RELAY,
config,
API_ERROR_TOO_MANY_REQUESTS,
{
method: "POST",
body: gExpectedProductDetails,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
signal: new AbortController().signal,
}
);
Assert.equal(ohttpDetails.status, 429, "Request should return 429.");
Assert.equal(ohttpDetails.ok, false, "Request should not be ok.");
disableOHTTP();
});
add_task(async function test_product_uninit() {
let product = new ShoppingProduct();
Assert.equal(
product._abortController.signal.aborted,
false,
"Abort signal is false"
);
product.uninit();
Assert.equal(
product._abortController.signal.aborted,
true,
"Abort signal is given after uninit"
);
});
add_task(async function test_product_sendAttributionEvent_impression() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: false });
if (product.isProduct()) {
let event = await ShoppingProduct.sendAttributionEvent(
"impression",
TEST_AID,
"firefox_toolkit_tests",
{
url: ATTRIBUTION_API_MOCK,
requestSchema: ATTRIBUTION_REQUEST_SCHEMA,
responseSchema: ATTRIBUTION_RESPONSE_SCHEMA,
}
);
Assert.deepEqual(
event,
await fetch(ATTRIBUTION_API_MOCK).then(r => r.json()),
"Events object is loaded from JSON and validated"
);
}
});
add_task(async function test_product_sendAttributionEvent_click() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: false });
if (product.isProduct()) {
let event = await ShoppingProduct.sendAttributionEvent(
"click",
TEST_AID,
"firefox_toolkit_tests",
{
url: ATTRIBUTION_API_MOCK,
requestSchema: ATTRIBUTION_REQUEST_SCHEMA,
responseSchema: ATTRIBUTION_RESPONSE_SCHEMA,
}
);
Assert.deepEqual(
event,
await fetch(ATTRIBUTION_API_MOCK).then(r => r.json()),
"Events object is loaded from JSON and validated"
);
}
});
add_task(async function test_product_sendAttributionEvent_impression_OHTTP() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: false });
Assert.ok(product.isProduct(), "Should recognize a valid product.");
gExpectedProductDetails = JSON.stringify({
event_source: "firefox_toolkit_tests",
event_name: "trusted_deals_impression",
aidvs: [TEST_AID],
});
enableOHTTP();
let event = await ShoppingProduct.sendAttributionEvent(
"impression",
TEST_AID,
"firefox_toolkit_tests",
{
url: ATTRIBUTION_API_MOCK,
requestSchema: ATTRIBUTION_REQUEST_SCHEMA,
responseSchema: ATTRIBUTION_RESPONSE_SCHEMA,
}
);
Assert.deepEqual(
event,
await fetch(ATTRIBUTION_API_MOCK).then(r => r.json()),
"Events object is loaded from JSON and validated"
);
disableOHTTP();
});
add_task(async function test_product_sendAttributionEvent_click_OHTTP() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: false });
Assert.ok(product.isProduct(), "Should recognize a valid product.");
gExpectedProductDetails = JSON.stringify({
event_source: "firefox_toolkit_tests",
event_name: "trusted_deals_link_clicked",
aid: TEST_AID,
});
enableOHTTP();
let event = await ShoppingProduct.sendAttributionEvent(
"click",
TEST_AID,
"firefox_toolkit_tests",
{
url: ATTRIBUTION_API_MOCK,
requestSchema: ATTRIBUTION_REQUEST_SCHEMA,
responseSchema: ATTRIBUTION_RESPONSE_SCHEMA,
}
);
Assert.deepEqual(
event,
await fetch(ATTRIBUTION_API_MOCK).then(r => r.json()),
"Events object is loaded from JSON and validated"
);
disableOHTTP();
});
add_task(async function test_product_sendAttributionEvent_placement_OHTTP() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: false });
Assert.ok(product.isProduct(), "Should recognize a valid product.");
gExpectedProductDetails = JSON.stringify({
event_source: "firefox_toolkit_tests",
event_name: "trusted_deals_placement",
aidvs: [TEST_AID],
});
enableOHTTP();
let event = await ShoppingProduct.sendAttributionEvent(
"placement",
TEST_AID,
"firefox_toolkit_tests",
{
url: ATTRIBUTION_API_MOCK,
requestSchema: ATTRIBUTION_REQUEST_SCHEMA,
responseSchema: ATTRIBUTION_RESPONSE_SCHEMA,
}
);
Assert.deepEqual(
event,
await fetch(ATTRIBUTION_API_MOCK).then(r => r.json()),
"Events object is loaded from JSON and validated"
);
disableOHTTP();
});
add_task(async function test_product_requestAnalysis_poll() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: false });
let sandbox = sinon.createSandbox();
let spy = sandbox.spy(ShoppingProduct, "request");
let startTime = Cu.now();
const INITIAL_TIMEOUT = 100;
const TIMEOUT = 50;
const TRIES = 10;
let totalTime = INITIAL_TIMEOUT + TIMEOUT;
pollingTries = 0;
if (!product.isProduct()) {
return;
}
let analysis = await product.pollForAnalysisCompleted({
url: API_POLL,
requestSchema: ANALYSIS_STATUS_REQUEST_SCHEMA,
responseSchema: ANALYSIS_STATUS_RESPONSE_SCHEMA,
pollInitialWait: INITIAL_TIMEOUT,
pollTimeout: TIMEOUT,
pollAttempts: TRIES,
});
Assert.equal(spy.callCount, 3, "Request is done processing");
Assert.ok(
typeof analysis == "object",
"Analysis object is loaded from JSON and validated"
);
Assert.equal(analysis.status, "completed", "Analysis is completed");
Assert.equal(analysis.progress, 100.0, "Progress is 100%");
Assert.ok(
Cu.now() - startTime >= totalTime,
`Waited for at least ${totalTime}ms`
);
sandbox.restore();
});
add_task(async function test_product_requestAnalysis_poll_max() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: false });
let sandbox = sinon.createSandbox();
let spy = sandbox.spy(ShoppingProduct, "request");
let startTime = Cu.now();
const INITIAL_TIMEOUT = 100;
const TIMEOUT = 50;
const TRIES = 4;
let totalTime = INITIAL_TIMEOUT + TIMEOUT * 3;
pollingTries = 0;
if (!product.isProduct()) {
return;
}
let analysis = await product.pollForAnalysisCompleted({
url: API_ANALYSIS_IN_PROGRESS,
requestSchema: ANALYSIS_STATUS_REQUEST_SCHEMA,
responseSchema: ANALYSIS_STATUS_RESPONSE_SCHEMA,
pollInitialWait: INITIAL_TIMEOUT,
pollTimeout: TIMEOUT,
pollAttempts: TRIES,
});
Assert.equal(spy.callCount, TRIES, "Request is done processing");
Assert.ok(
typeof analysis == "object",
"Analysis object is loaded from JSON and validated"
);
Assert.equal(analysis.status, "in_progress", "Analysis not done");
Assert.ok(
Cu.now() - startTime >= totalTime,
`Waited for at least ${totalTime}ms`
);
sandbox.restore();
});
add_task(async function test_product_requestAnalysisCreationStatus() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: false });
if (!product.isProduct()) {
return;
}
let analysis = await product.requestAnalysisCreationStatus(undefined, {
url: API_ANALYSIS_IN_PROGRESS,
requestSchema: ANALYSIS_STATUS_REQUEST_SCHEMA,
responseSchema: ANALYSIS_STATUS_RESPONSE_SCHEMA,
});
Assert.ok(
typeof analysis == "object",
"Analysis object is loaded from JSON and validated"
);
Assert.equal(analysis.status, "in_progress", "Analysis is in progress");
Assert.equal(analysis.progress, 50.0, "Progress is 50%");
});
add_task(async function test_product_requestCreateAnalysis() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: false });
if (!product.isProduct()) {
return;
}
let analysis = await product.requestCreateAnalysis(undefined, {
url: ANALYZE_API_MOCK,
requestSchema: ANALYZE_REQUEST_SCHEMA,
responseSchema: ANALYZE_RESPONSE_SCHEMA,
});
Assert.ok(
typeof analysis == "object",
"Analyze object is loaded from JSON and validated"
);
Assert.equal(analysis.status, "pending", "Analysis is pending");
});
add_task(async function test_product_sendReport() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: false });
Assert.ok(product.isProduct(), "Should recognize a valid product.");
let report = await product.sendReport(undefined, {
url: REPORTING_API_MOCK,
});
Assert.ok(
typeof report == "object",
"Report object is loaded from JSON and validated"
);
Assert.equal(report.message, "report created", "Report is created.");
});
add_task(async function test_product_sendReport_OHTTP() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: false });
Assert.ok(product.isProduct(), "Should recognize a valid product.");
gExpectedProductDetails = JSON.stringify({
product_id: "926485654",
website: "walmart.com",
});
enableOHTTP();
let report = await product.sendReport(undefined, {
url: REPORTING_API_MOCK,
});
Assert.ok(
typeof report == "object",
"Report object is loaded from JSON and validated"
);
Assert.equal(report.message, "report created", "Report is created.");
disableOHTTP();
});
add_task(async function test_product_analysisProgress_event() {
let uri = new URL("https://www.walmart.com/ip/926485654");
let product = new ShoppingProduct(uri, { allowValidationFailure: false });
const INITIAL_TIMEOUT = 0;
const TIMEOUT = 0;
const TRIES = 1;
if (!product.isProduct()) {
return;
}
let analysisProgressEventData;
product.on("analysis-progress", (eventName, progress) => {
analysisProgressEventData = progress;
});
await product.pollForAnalysisCompleted({
url: API_ANALYSIS_IN_PROGRESS,
requestSchema: ANALYSIS_STATUS_REQUEST_SCHEMA,
responseSchema: ANALYSIS_STATUS_RESPONSE_SCHEMA,
pollInitialWait: INITIAL_TIMEOUT,
pollTimeout: TIMEOUT,
pollAttempts: TRIES,
});
Assert.equal(
analysisProgressEventData,
50,
"Analysis progress event data is emitted"
);
});