1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/services/common/tests/unit/test_tokenserverclient.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,464 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + * http://creativecommons.org/publicdomain/zero/1.0/ */ 1.6 + 1.7 +Cu.import("resource://services-common/async.js"); 1.8 +Cu.import("resource://services-common/tokenserverclient.js"); 1.9 + 1.10 +function run_test() { 1.11 + initTestLogging("Trace"); 1.12 + 1.13 + run_next_test(); 1.14 +} 1.15 + 1.16 +add_test(function test_working_bid_exchange() { 1.17 + _("Ensure that working BrowserID token exchange works as expected."); 1.18 + 1.19 + let service = "http://example.com/foo"; 1.20 + let duration = 300; 1.21 + 1.22 + let server = httpd_setup({ 1.23 + "/1.0/foo/1.0": function(request, response) { 1.24 + do_check_true(request.hasHeader("accept")); 1.25 + do_check_false(request.hasHeader("x-conditions-accepted")); 1.26 + do_check_eq("application/json", request.getHeader("accept")); 1.27 + 1.28 + response.setStatusLine(request.httpVersion, 200, "OK"); 1.29 + response.setHeader("Content-Type", "application/json"); 1.30 + 1.31 + let body = JSON.stringify({ 1.32 + id: "id", 1.33 + key: "key", 1.34 + api_endpoint: service, 1.35 + uid: "uid", 1.36 + duration: duration, 1.37 + }); 1.38 + response.bodyOutputStream.write(body, body.length); 1.39 + } 1.40 + }); 1.41 + 1.42 + let client = new TokenServerClient(); 1.43 + let cb = Async.makeSpinningCallback(); 1.44 + let url = server.baseURI + "/1.0/foo/1.0"; 1.45 + client.getTokenFromBrowserIDAssertion(url, "assertion", cb); 1.46 + let result = cb.wait(); 1.47 + do_check_eq("object", typeof(result)); 1.48 + do_check_attribute_count(result, 5); 1.49 + do_check_eq(service, result.endpoint); 1.50 + do_check_eq("id", result.id); 1.51 + do_check_eq("key", result.key); 1.52 + do_check_eq("uid", result.uid); 1.53 + do_check_eq(duration, result.duration); 1.54 + server.stop(run_next_test); 1.55 +}); 1.56 + 1.57 +add_test(function test_invalid_arguments() { 1.58 + _("Ensure invalid arguments to APIs are rejected."); 1.59 + 1.60 + let args = [ 1.61 + [null, "assertion", function() {}], 1.62 + ["http://example.com/", null, function() {}], 1.63 + ["http://example.com/", "assertion", null] 1.64 + ]; 1.65 + 1.66 + for each (let arg in args) { 1.67 + try { 1.68 + let client = new TokenServerClient(); 1.69 + client.getTokenFromBrowserIDAssertion(arg[0], arg[1], arg[2]); 1.70 + do_throw("Should never get here."); 1.71 + } catch (ex) { 1.72 + do_check_true(ex instanceof TokenServerClientError); 1.73 + } 1.74 + } 1.75 + 1.76 + run_next_test(); 1.77 +}); 1.78 + 1.79 +add_test(function test_conditions_required_response_handling() { 1.80 + _("Ensure that a conditions required response is handled properly."); 1.81 + 1.82 + let description = "Need to accept conditions"; 1.83 + let tosURL = "http://example.com/tos"; 1.84 + 1.85 + let server = httpd_setup({ 1.86 + "/1.0/foo/1.0": function(request, response) { 1.87 + do_check_false(request.hasHeader("x-conditions-accepted")); 1.88 + 1.89 + response.setStatusLine(request.httpVersion, 403, "Forbidden"); 1.90 + response.setHeader("Content-Type", "application/json"); 1.91 + 1.92 + let body = JSON.stringify({ 1.93 + errors: [{description: description, location: "body", name: ""}], 1.94 + urls: {tos: tosURL} 1.95 + }); 1.96 + response.bodyOutputStream.write(body, body.length); 1.97 + } 1.98 + }); 1.99 + 1.100 + let client = new TokenServerClient(); 1.101 + let url = server.baseURI + "/1.0/foo/1.0"; 1.102 + 1.103 + function onResponse(error, token) { 1.104 + do_check_true(error instanceof TokenServerClientServerError); 1.105 + do_check_eq(error.cause, "conditions-required"); 1.106 + do_check_null(token); 1.107 + 1.108 + do_check_eq(error.urls.tos, tosURL); 1.109 + 1.110 + server.stop(run_next_test); 1.111 + } 1.112 + 1.113 + client.getTokenFromBrowserIDAssertion(url, "assertion", onResponse); 1.114 +}); 1.115 + 1.116 +add_test(function test_invalid_403_no_content_type() { 1.117 + _("Ensure that a 403 without content-type is handled properly."); 1.118 + 1.119 + let server = httpd_setup({ 1.120 + "/1.0/foo/1.0": function(request, response) { 1.121 + response.setStatusLine(request.httpVersion, 403, "Forbidden"); 1.122 + // No Content-Type header by design. 1.123 + 1.124 + let body = JSON.stringify({ 1.125 + errors: [{description: "irrelevant", location: "body", name: ""}], 1.126 + urls: {foo: "http://bar"} 1.127 + }); 1.128 + response.bodyOutputStream.write(body, body.length); 1.129 + } 1.130 + }); 1.131 + 1.132 + let client = new TokenServerClient(); 1.133 + let url = server.baseURI + "/1.0/foo/1.0"; 1.134 + 1.135 + function onResponse(error, token) { 1.136 + do_check_true(error instanceof TokenServerClientServerError); 1.137 + do_check_eq(error.cause, "malformed-response"); 1.138 + do_check_null(token); 1.139 + 1.140 + do_check_null(error.urls); 1.141 + 1.142 + server.stop(run_next_test); 1.143 + } 1.144 + 1.145 + client.getTokenFromBrowserIDAssertion(url, "assertion", onResponse); 1.146 +}); 1.147 + 1.148 +add_test(function test_invalid_403_bad_json() { 1.149 + _("Ensure that a 403 with JSON that isn't proper is handled properly."); 1.150 + 1.151 + let server = httpd_setup({ 1.152 + "/1.0/foo/1.0": function(request, response) { 1.153 + response.setStatusLine(request.httpVersion, 403, "Forbidden"); 1.154 + response.setHeader("Content-Type", "application/json; charset=utf-8"); 1.155 + 1.156 + let body = JSON.stringify({ 1.157 + foo: "bar" 1.158 + }); 1.159 + response.bodyOutputStream.write(body, body.length); 1.160 + } 1.161 + }); 1.162 + 1.163 + let client = new TokenServerClient(); 1.164 + let url = server.baseURI + "/1.0/foo/1.0"; 1.165 + 1.166 + function onResponse(error, token) { 1.167 + do_check_true(error instanceof TokenServerClientServerError); 1.168 + do_check_eq(error.cause, "malformed-response"); 1.169 + do_check_null(token); 1.170 + do_check_null(error.urls); 1.171 + 1.172 + server.stop(run_next_test); 1.173 + } 1.174 + 1.175 + client.getTokenFromBrowserIDAssertion(url, "assertion", onResponse); 1.176 +}); 1.177 + 1.178 +add_test(function test_403_no_urls() { 1.179 + _("Ensure that a 403 without a urls field is handled properly."); 1.180 + 1.181 + let server = httpd_setup({ 1.182 + "/1.0/foo/1.0": function(request, response) { 1.183 + response.setStatusLine(request.httpVersion, 403, "Forbidden"); 1.184 + response.setHeader("Content-Type", "application/json; charset=utf-8"); 1.185 + 1.186 + let body = "{}"; 1.187 + response.bodyOutputStream.write(body, body.length); 1.188 + } 1.189 + }); 1.190 + 1.191 + let client = new TokenServerClient(); 1.192 + let url = server.baseURI + "/1.0/foo/1.0"; 1.193 + 1.194 + client.getTokenFromBrowserIDAssertion(url, "assertion", 1.195 + function onResponse(error, result) { 1.196 + do_check_true(error instanceof TokenServerClientServerError); 1.197 + do_check_eq(error.cause, "malformed-response"); 1.198 + do_check_null(result); 1.199 + 1.200 + server.stop(run_next_test); 1.201 + 1.202 + }); 1.203 +}); 1.204 + 1.205 +add_test(function test_send_extra_headers() { 1.206 + _("Ensures that the condition acceptance header is sent when asked."); 1.207 + 1.208 + let duration = 300; 1.209 + let server = httpd_setup({ 1.210 + "/1.0/foo/1.0": function(request, response) { 1.211 + do_check_true(request.hasHeader("x-foo")); 1.212 + do_check_eq(request.getHeader("x-foo"), "42"); 1.213 + 1.214 + do_check_true(request.hasHeader("x-bar")); 1.215 + do_check_eq(request.getHeader("x-bar"), "17"); 1.216 + 1.217 + response.setStatusLine(request.httpVersion, 200, "OK"); 1.218 + response.setHeader("Content-Type", "application/json"); 1.219 + 1.220 + let body = JSON.stringify({ 1.221 + id: "id", 1.222 + key: "key", 1.223 + api_endpoint: "http://example.com/", 1.224 + uid: "uid", 1.225 + duration: duration, 1.226 + }); 1.227 + response.bodyOutputStream.write(body, body.length); 1.228 + } 1.229 + }); 1.230 + 1.231 + let client = new TokenServerClient(); 1.232 + let url = server.baseURI + "/1.0/foo/1.0"; 1.233 + 1.234 + function onResponse(error, token) { 1.235 + do_check_null(error); 1.236 + 1.237 + // Other tests validate other things. 1.238 + 1.239 + server.stop(run_next_test); 1.240 + } 1.241 + 1.242 + let extra = { 1.243 + "X-Foo": 42, 1.244 + "X-Bar": 17 1.245 + }; 1.246 + client.getTokenFromBrowserIDAssertion(url, "assertion", onResponse, extra); 1.247 +}); 1.248 + 1.249 +add_test(function test_error_404_empty() { 1.250 + _("Ensure that 404 responses without proper response are handled properly."); 1.251 + 1.252 + let server = httpd_setup(); 1.253 + 1.254 + let client = new TokenServerClient(); 1.255 + let url = server.baseURI + "/foo"; 1.256 + client.getTokenFromBrowserIDAssertion(url, "assertion", function(error, r) { 1.257 + do_check_true(error instanceof TokenServerClientServerError); 1.258 + do_check_eq(error.cause, "malformed-response"); 1.259 + 1.260 + do_check_neq(null, error.response); 1.261 + do_check_null(r); 1.262 + 1.263 + server.stop(run_next_test); 1.264 + }); 1.265 +}); 1.266 + 1.267 +add_test(function test_error_404_proper_response() { 1.268 + _("Ensure that a Cornice error report for 404 is handled properly."); 1.269 + 1.270 + let server = httpd_setup({ 1.271 + "/1.0/foo/1.0": function(request, response) { 1.272 + response.setStatusLine(request.httpVersion, 404, "Not Found"); 1.273 + response.setHeader("Content-Type", "application/json; charset=utf-8"); 1.274 + 1.275 + let body = JSON.stringify({ 1.276 + status: 404, 1.277 + errors: [{description: "No service", location: "body", name: ""}], 1.278 + }); 1.279 + 1.280 + response.bodyOutputStream.write(body, body.length); 1.281 + } 1.282 + }); 1.283 + 1.284 + function onResponse(error, token) { 1.285 + do_check_true(error instanceof TokenServerClientServerError); 1.286 + do_check_eq(error.cause, "unknown-service"); 1.287 + do_check_null(token); 1.288 + 1.289 + server.stop(run_next_test); 1.290 + } 1.291 + 1.292 + let client = new TokenServerClient(); 1.293 + let url = server.baseURI + "/1.0/foo/1.0"; 1.294 + client.getTokenFromBrowserIDAssertion(url, "assertion", onResponse); 1.295 +}); 1.296 + 1.297 +add_test(function test_bad_json() { 1.298 + _("Ensure that malformed JSON is handled properly."); 1.299 + 1.300 + let server = httpd_setup({ 1.301 + "/1.0/foo/1.0": function(request, response) { 1.302 + response.setStatusLine(request.httpVersion, 200, "OK"); 1.303 + response.setHeader("Content-Type", "application/json"); 1.304 + 1.305 + let body = '{"id": "id", baz}' 1.306 + response.bodyOutputStream.write(body, body.length); 1.307 + } 1.308 + }); 1.309 + 1.310 + let client = new TokenServerClient(); 1.311 + let url = server.baseURI + "/1.0/foo/1.0"; 1.312 + client.getTokenFromBrowserIDAssertion(url, "assertion", function(error, r) { 1.313 + do_check_neq(null, error); 1.314 + do_check_eq("TokenServerClientServerError", error.name); 1.315 + do_check_eq(error.cause, "malformed-response"); 1.316 + do_check_neq(null, error.response); 1.317 + do_check_eq(null, r); 1.318 + 1.319 + server.stop(run_next_test); 1.320 + }); 1.321 +}); 1.322 + 1.323 +add_test(function test_400_response() { 1.324 + _("Ensure HTTP 400 is converted to malformed-request."); 1.325 + 1.326 + let server = httpd_setup({ 1.327 + "/1.0/foo/1.0": function(request, response) { 1.328 + response.setStatusLine(request.httpVersion, 400, "Bad Request"); 1.329 + response.setHeader("Content-Type", "application/json; charset=utf-8"); 1.330 + 1.331 + let body = "{}"; // Actual content may not be used. 1.332 + response.bodyOutputStream.write(body, body.length); 1.333 + } 1.334 + }); 1.335 + 1.336 + let client = new TokenServerClient(); 1.337 + let url = server.baseURI + "/1.0/foo/1.0"; 1.338 + client.getTokenFromBrowserIDAssertion(url, "assertion", function(error, r) { 1.339 + do_check_neq(null, error); 1.340 + do_check_eq("TokenServerClientServerError", error.name); 1.341 + do_check_neq(null, error.response); 1.342 + do_check_eq(error.cause, "malformed-request"); 1.343 + 1.344 + server.stop(run_next_test); 1.345 + }); 1.346 +}); 1.347 + 1.348 +add_test(function test_401_with_error_cause() { 1.349 + _("Ensure 401 cause is specified in body.status"); 1.350 + 1.351 + let server = httpd_setup({ 1.352 + "/1.0/foo/1.0": function(request, response) { 1.353 + response.setStatusLine(request.httpVersion, 401, "Unauthorized"); 1.354 + response.setHeader("Content-Type", "application/json; charset=utf-8"); 1.355 + 1.356 + let body = JSON.stringify({status: "no-soup-for-you"}); 1.357 + response.bodyOutputStream.write(body, body.length); 1.358 + } 1.359 + }); 1.360 + 1.361 + let client = new TokenServerClient(); 1.362 + let url = server.baseURI + "/1.0/foo/1.0"; 1.363 + client.getTokenFromBrowserIDAssertion(url, "assertion", function(error, r) { 1.364 + do_check_neq(null, error); 1.365 + do_check_eq("TokenServerClientServerError", error.name); 1.366 + do_check_neq(null, error.response); 1.367 + do_check_eq(error.cause, "no-soup-for-you"); 1.368 + 1.369 + server.stop(run_next_test); 1.370 + }); 1.371 +}); 1.372 + 1.373 +add_test(function test_unhandled_media_type() { 1.374 + _("Ensure that unhandled media types throw an error."); 1.375 + 1.376 + let server = httpd_setup({ 1.377 + "/1.0/foo/1.0": function(request, response) { 1.378 + response.setStatusLine(request.httpVersion, 200, "OK"); 1.379 + response.setHeader("Content-Type", "text/plain"); 1.380 + 1.381 + let body = "hello, world"; 1.382 + response.bodyOutputStream.write(body, body.length); 1.383 + } 1.384 + }); 1.385 + 1.386 + let url = server.baseURI + "/1.0/foo/1.0"; 1.387 + let client = new TokenServerClient(); 1.388 + client.getTokenFromBrowserIDAssertion(url, "assertion", function(error, r) { 1.389 + do_check_neq(null, error); 1.390 + do_check_eq("TokenServerClientServerError", error.name); 1.391 + do_check_neq(null, error.response); 1.392 + do_check_eq(null, r); 1.393 + 1.394 + server.stop(run_next_test); 1.395 + }); 1.396 +}); 1.397 + 1.398 +add_test(function test_rich_media_types() { 1.399 + _("Ensure that extra tokens in the media type aren't rejected."); 1.400 + 1.401 + let duration = 300; 1.402 + let server = httpd_setup({ 1.403 + "/foo": function(request, response) { 1.404 + response.setStatusLine(request.httpVersion, 200, "OK"); 1.405 + response.setHeader("Content-Type", "application/json; foo=bar; bar=foo"); 1.406 + 1.407 + let body = JSON.stringify({ 1.408 + id: "id", 1.409 + key: "key", 1.410 + api_endpoint: "foo", 1.411 + uid: "uid", 1.412 + duration: duration, 1.413 + }); 1.414 + response.bodyOutputStream.write(body, body.length); 1.415 + } 1.416 + }); 1.417 + 1.418 + let url = server.baseURI + "/foo"; 1.419 + let client = new TokenServerClient(); 1.420 + client.getTokenFromBrowserIDAssertion(url, "assertion", function(error, r) { 1.421 + do_check_eq(null, error); 1.422 + 1.423 + server.stop(run_next_test); 1.424 + }); 1.425 +}); 1.426 + 1.427 +add_test(function test_exception_during_callback() { 1.428 + _("Ensure that exceptions thrown during callback handling are handled."); 1.429 + 1.430 + let duration = 300; 1.431 + let server = httpd_setup({ 1.432 + "/foo": function(request, response) { 1.433 + response.setStatusLine(request.httpVersion, 200, "OK"); 1.434 + response.setHeader("Content-Type", "application/json"); 1.435 + 1.436 + let body = JSON.stringify({ 1.437 + id: "id", 1.438 + key: "key", 1.439 + api_endpoint: "foo", 1.440 + uid: "uid", 1.441 + duration: duration, 1.442 + }); 1.443 + response.bodyOutputStream.write(body, body.length); 1.444 + } 1.445 + }); 1.446 + 1.447 + let url = server.baseURI + "/foo"; 1.448 + let client = new TokenServerClient(); 1.449 + let cb = Async.makeSpinningCallback(); 1.450 + let callbackCount = 0; 1.451 + 1.452 + client.getTokenFromBrowserIDAssertion(url, "assertion", function(error, r) { 1.453 + do_check_eq(null, error); 1.454 + 1.455 + cb(); 1.456 + 1.457 + callbackCount += 1; 1.458 + throw new Error("I am a bad function!"); 1.459 + }); 1.460 + 1.461 + cb.wait(); 1.462 + // This relies on some heavy event loop magic. The error in the main 1.463 + // callback should already have been raised at this point. 1.464 + do_check_eq(callbackCount, 1); 1.465 + 1.466 + server.stop(run_next_test); 1.467 +});