Bug 1048526 - Create a Loop test server for querying OAuth URL parameters to use. r=ckarlof,vladikoff,jaws
authorMatthew Noorenberghe <mozilla@noorenberghe.ca>
Fri, 15 Aug 2014 16:51:55 -0700
changeset 199870 4068cffc03fcc48aa87a75b06d4d681de3b88b30
parent 199869 563f7421eeaa97538294aba91be72eebb07803ce
child 199871 e9ae608b82e896cf4ce444e694a1ff66e009a2a6
push id8239
push usermozilla@noorenberghe.ca
push dateSat, 16 Aug 2014 00:03:10 +0000
treeherderfx-team@70b77e14d448 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersckarlof, vladikoff, jaws
bugs1048526
milestone34.0a1
Bug 1048526 - Create a Loop test server for querying OAuth URL parameters to use. r=ckarlof,vladikoff,jaws
browser/components/loop/test/mochitest/browser.ini
browser/components/loop/test/mochitest/browser_loop_fxa_server.js
browser/components/loop/test/mochitest/head.js
browser/components/loop/test/mochitest/loop_fxa.sjs
--- a/browser/components/loop/test/mochitest/browser.ini
+++ b/browser/components/loop/test/mochitest/browser.ini
@@ -1,8 +1,10 @@
 [DEFAULT]
 support-files =
     head.js
+    loop_fxa.sjs
 
+[browser_loop_fxa_server.js]
 [browser_mozLoop_appVersionInfo.js]
 [browser_mozLoop_prefs.js]
 [browser_mozLoop_doNotDisturb.js]
 skip-if = buildapp == 'mulet'
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/test/mochitest/browser_loop_fxa_server.js
@@ -0,0 +1,72 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test the server mocking FxA integration endpoints on the Loop server.
+ */
+
+"use strict";
+
+const BASE_URL = "http://mochi.test:8888/browser/browser/components/loop/test/mochitest/loop_fxa.sjs?";
+
+registerCleanupFunction(function* () {
+  yield promiseDeletedOAuthParams(BASE_URL);
+});
+
+add_task(function* required_setup_params() {
+  let params = {
+    client_id: "my_client_id",
+    content_uri: "https://example.com/content/",
+    oauth_uri: "https://example.com/oauth/",
+    profile_uri: "https://example.com/profile/",
+    state: "my_state",
+  };
+  let request = yield promiseOAuthParamsSetup(BASE_URL, params);
+  is(request.status, 200, "Check /setup_params status");
+  request = yield promiseParams();
+  is(request.status, 200, "Check /fxa-oauth/params status");
+  for (let param of Object.keys(params)) {
+    is(request.response[param], params[param], "Check /fxa-oauth/params " + param);
+  }
+});
+
+add_task(function* optional_setup_params() {
+  let params = {
+    action: "signin",
+    client_id: "my_client_id",
+    content_uri: "https://example.com/content/",
+    oauth_uri: "https://example.com/oauth/",
+    profile_uri: "https://example.com/profile/",
+    scope: "profile",
+    state: "my_state",
+  };
+  let request = yield promiseOAuthParamsSetup(BASE_URL, params);
+  is(request.status, 200, "Check /setup_params status");
+  request = yield promiseParams();
+  is(request.status, 200, "Check /fxa-oauth/params status");
+  for (let param of Object.keys(params)) {
+    is(request.response[param], params[param], "Check /fxa-oauth/params " + param);
+  }
+});
+
+add_task(function* delete_setup_params() {
+  yield promiseDeletedOAuthParams(BASE_URL);
+  let request = yield promiseParams();
+  is(Object.keys(request.response).length, 0, "Params should have been deleted");
+});
+
+function promiseParams() {
+  let deferred = Promise.defer();
+  let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
+              createInstance(Ci.nsIXMLHttpRequest);
+  xhr.open("POST", BASE_URL + "/fxa-oauth/params", true);
+  xhr.responseType = "json";
+  xhr.addEventListener("load", () => {
+    info("/fxa-oauth/params response:\n" + JSON.stringify(xhr.response, null, 4));
+    deferred.resolve(xhr);
+  });
+  xhr.addEventListener("error", error => deferred.reject(error));
+  xhr.send();
+
+  return deferred.promise;
+}
--- a/browser/components/loop/test/mochitest/head.js
+++ b/browser/components/loop/test/mochitest/head.js
@@ -64,8 +64,33 @@ function loadLoopPanel() {
 
   // Turn off animations to make tests quicker.
   let loopPanel = document.getElementById("loop-notification-panel");
   loopPanel.setAttribute("animate", "false");
 
   // Now get the actual API.
   yield promiseGetMozLoopAPI();
 }
+
+function promiseOAuthParamsSetup(baseURL, params) {
+  let deferred = Promise.defer();
+  let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
+              createInstance(Ci.nsIXMLHttpRequest);
+  xhr.open("POST", baseURL + "/setup_params", true);
+  xhr.setRequestHeader("X-Params", JSON.stringify(params));
+  xhr.addEventListener("load", () => deferred.resolve(xhr));
+  xhr.addEventListener("error", error => deferred.reject(error));
+  xhr.send();
+
+  return deferred.promise;
+}
+
+function promiseDeletedOAuthParams(baseURL) {
+  let deferred = Promise.defer();
+  let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
+              createInstance(Ci.nsIXMLHttpRequest);
+  xhr.open("DELETE", baseURL + "/setup_params", true);
+  xhr.addEventListener("load", () => deferred.resolve(xhr));
+  xhr.addEventListener("error", error => deferred.reject(error));
+  xhr.send();
+
+  return deferred.promise;
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/test/mochitest/loop_fxa.sjs
@@ -0,0 +1,85 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * This is a mock server that implements the FxA endpoints on the Loop server.
+ */
+
+"use strict";
+
+const REQUIRED_PARAMS = ["client_id", "content_uri", "oauth_uri", "profile_uri", "state"];
+
+/**
+ * Entry point for HTTP requests.
+ */
+function handleRequest(request, response) {
+  switch (request.queryString) {
+    case "/setup_params":
+      setup_params(request, response);
+      return;
+    case "/fxa-oauth/params":
+      params(request, response);
+      return;
+  }
+  response.setStatusLine(request.httpVersion, 404, "Not Found");
+}
+
+/**
+ * POST /setup_params
+ * DELETE /setup_params
+ *
+ * Test-only endpoint to setup the /fxa-oauth/params response.
+ *
+ * For a POST the X-Params header should contain a JSON object with keys to set for /fxa-oauth/params.
+ * A DELETE request will delete the stored parameters and should be run in a cleanup function to
+ * avoid interfering with subsequen tests.
+ */
+function setup_params(request, response) {
+  response.setHeader("Content-Type", "text/plain", false);
+  if (request.method == "DELETE") {
+    setSharedState("/fxa-oauth/params", "");
+    response.write("Params deleted");
+    return;
+  }
+  let params = JSON.parse(request.getHeader("X-Params"));
+  if (!params) {
+    response.setStatusLine(request.httpVersion, 400, "Bad Request");
+    return;
+  }
+  setSharedState("/fxa-oauth/params", JSON.stringify(params));
+  response.write("Params updated");
+}
+
+/**
+ * POST /fxa-oauth/params endpoint
+ *
+ * Fetch OAuth parameters used to start the OAuth flow in the browser.
+ * Parameters: None
+ * Response: JSON containing an object of oauth parameters.
+ */
+function params(request, response) {
+  if (request.method != "POST") {
+    response.setStatusLine(request.httpVersion, 405, "Method Not Allowed");
+    response.setHeader("Allow", "POST", false);
+
+    // Add a button to make a POST request to make this endpoint easier to debug in the browser.
+    response.write("<form method=POST><button type=submit>POST</button></form>");
+    return;
+  }
+
+  let origin = request.scheme + "://" + request.host + ":" + request.port;
+
+  let params = JSON.parse(getSharedState("/fxa-oauth/params") || "{}");
+
+  // Warn if required parameters are missing.
+  for (let paramName of REQUIRED_PARAMS) {
+    if (!(paramName in params)) {
+      dump("Warning: " + paramName + " is a required parameter\n");
+    }
+  }
+
+  // Save the result so we have the effective `state` value.
+  setSharedState("/fxa-oauth/params", JSON.stringify(params));
+  response.setHeader("Content-Type", "application/json; charset=utf-8", false);
+  response.write(JSON.stringify(params, null, 2));
+}