Title: [202001] trunk/Websites/perf.webkit.org
Revision
202001
Author
[email protected]
Date
2016-06-13 12:47:38 -0700 (Mon, 13 Jun 2016)

Log Message

Invalid token error when trying to create an A/B analysis for a range
https://bugs.webkit.org/show_bug.cgi?id=158679

Reviewed by Chris Dumez.

The problem in this particular case was due to another website overriding cookies for our subdomain.
Make PrivilegedAPI robust against its token becoming invalid in general to fix the bug since the cookie
is only available under /privileged-api/ and the v3 UI can't access it for security reasons.

This patch factors out PrivilegedAPI out of remote.js so that it can be tested separately in server tests
as well as unit tests even though RemoteAPI itself is implemented differently in each case.

* init-database.sql: Added a forgotten default value "false" to run_marked_outlier.
* public/v3/index.html:
* public/v3/privileged-api.js: Added. Extracted out of public/v3/remote.js.
(PrivilegedAPI.sendRequest): Fixed the bug. When the initial request fails with "InvalidToken" error,
re-generate the token and re-issue the request.
(PrivilegedAPI.requestCSRFToken):
* public/v3/remote.js:
(RemoteAPI.postJSON): Added to match tools/js/remote.js.
(RemoteAPI.postJSONWithStatus): Ditto.
(PrivilegedAPI): Moved to privileged-api.js.
* server-tests/api-measurement-set-tests.js: Removed the unused require for crypto.
* server-tests/privileged-api-upate-run-status.js: Added tests for /privileged-api/update-run-status.
* server-tests/resources/test-server.js:
(TestServer.prototype.inject): Clear the cookies as well as tokens in PrivilegedAPI.
* tools/js/remote.js:
(RemoteAPI): Added the support for PrivilegedAPI by making cookie set by the server persist.
(RemoteAPI.prototype.clearCookies): Added for tests.
(RemoteAPI.prototype.postJSON): Make sure sendHttpRequest always sends a valid JSON.
(RemoteAPI.prototype.postJSONWithStatus): Added since this API is used PrivilegedAPI.
(RemoteAPI.prototype.sendHttpRequest): Retain the cookie set by the server and send it back in each request.
* tools/js/v3-models.js:
* unit-tests/privileged-api-tests.js: Added unit tests for PrivilegedAPI.
* unit-tests/resources/mock-remote-api.js:
(MockRemoteAPI.postJSON): Added for unit testing.
(MockRemoteAPI.postJSONWithStatus): Ditto.

Modified Paths

Added Paths

Diff

Modified: trunk/Websites/perf.webkit.org/ChangeLog (202000 => 202001)


--- trunk/Websites/perf.webkit.org/ChangeLog	2016-06-13 19:40:01 UTC (rev 202000)
+++ trunk/Websites/perf.webkit.org/ChangeLog	2016-06-13 19:47:38 UTC (rev 202001)
@@ -1,5 +1,45 @@
 2016-06-13  Ryosuke Niwa  <[email protected]>
 
+        Invalid token error when trying to create an A/B analysis for a range
+        https://bugs.webkit.org/show_bug.cgi?id=158679
+
+        Reviewed by Chris Dumez.
+
+        The problem in this particular case was due to another website overriding cookies for our subdomain.
+        Make PrivilegedAPI robust against its token becoming invalid in general to fix the bug since the cookie
+        is only available under /privileged-api/ and the v3 UI can't access it for security reasons.
+
+        This patch factors out PrivilegedAPI out of remote.js so that it can be tested separately in server tests
+        as well as unit tests even though RemoteAPI itself is implemented differently in each case.
+
+        * init-database.sql: Added a forgotten default value "false" to run_marked_outlier.
+        * public/v3/index.html:
+        * public/v3/privileged-api.js: Added. Extracted out of public/v3/remote.js.
+        (PrivilegedAPI.sendRequest): Fixed the bug. When the initial request fails with "InvalidToken" error,
+        re-generate the token and re-issue the request.
+        (PrivilegedAPI.requestCSRFToken):
+        * public/v3/remote.js:
+        (RemoteAPI.postJSON): Added to match tools/js/remote.js.
+        (RemoteAPI.postJSONWithStatus): Ditto.
+        (PrivilegedAPI): Moved to privileged-api.js.
+        * server-tests/api-measurement-set-tests.js: Removed the unused require for crypto.
+        * server-tests/privileged-api-upate-run-status.js: Added tests for /privileged-api/update-run-status.
+        * server-tests/resources/test-server.js:
+        (TestServer.prototype.inject): Clear the cookies as well as tokens in PrivilegedAPI.
+        * tools/js/remote.js:
+        (RemoteAPI): Added the support for PrivilegedAPI by making cookie set by the server persist.
+        (RemoteAPI.prototype.clearCookies): Added for tests.
+        (RemoteAPI.prototype.postJSON): Make sure sendHttpRequest always sends a valid JSON.
+        (RemoteAPI.prototype.postJSONWithStatus): Added since this API is used PrivilegedAPI.
+        (RemoteAPI.prototype.sendHttpRequest): Retain the cookie set by the server and send it back in each request.
+        * tools/js/v3-models.js:
+        * unit-tests/privileged-api-tests.js: Added unit tests for PrivilegedAPI.
+        * unit-tests/resources/mock-remote-api.js:
+        (MockRemoteAPI.postJSON): Added for unit testing.
+        (MockRemoteAPI.postJSONWithStatus): Ditto.
+
+2016-06-13  Ryosuke Niwa  <[email protected]>
+
         /admin/tests is very slow
         https://bugs.webkit.org/show_bug.cgi?id=158682
 

Modified: trunk/Websites/perf.webkit.org/init-database.sql (202000 => 202001)


--- trunk/Websites/perf.webkit.org/init-database.sql	2016-06-13 19:40:01 UTC (rev 202000)
+++ trunk/Websites/perf.webkit.org/init-database.sql	2016-06-13 19:47:38 UTC (rev 202001)
@@ -140,7 +140,7 @@
     run_mean_cache double precision,
     run_sum_cache double precision,
     run_square_sum_cache double precision,
-    run_marked_outlier boolean,
+    run_marked_outlier boolean NOT NULL DEFAULT FALSE,
     CONSTRAINT test_config_build_must_be_unique UNIQUE(run_config, run_build));
 CREATE INDEX run_config_index ON test_runs(run_config);
 CREATE INDEX run_build_index ON test_runs(run_build);

Modified: trunk/Websites/perf.webkit.org/public/v3/index.html (202000 => 202001)


--- trunk/Websites/perf.webkit.org/public/v3/index.html	2016-06-13 19:40:01 UTC (rev 202000)
+++ trunk/Websites/perf.webkit.org/public/v3/index.html	2016-06-13 19:47:38 UTC (rev 202001)
@@ -42,6 +42,7 @@
 
         <script src=""
         <script src=""
+        <script src=""
 
         <script src=""
         <script src=""

Added: trunk/Websites/perf.webkit.org/public/v3/privileged-api.js (0 => 202001)


--- trunk/Websites/perf.webkit.org/public/v3/privileged-api.js	                        (rev 0)
+++ trunk/Websites/perf.webkit.org/public/v3/privileged-api.js	2016-06-13 19:47:38 UTC (rev 202001)
@@ -0,0 +1,45 @@
+"use strict";
+
+// FIXME: Use real class syntax once the dependency on data.js has been removed.
+var PrivilegedAPI = class {
+
+    static sendRequest(path, data)
+    {
+        var clonedData = {};
+        for (var key in data)
+            clonedData[key] = data[key];
+
+        return this.requestCSRFToken().then(function (token) {
+            clonedData['token'] = token;
+            return RemoteAPI.postJSONWithStatus('/privileged-api/' + path, clonedData).catch(function (status) {
+                if (status != 'InvalidToken')
+                    return Promise.reject(status);
+                PrivilegedAPI._token = null;
+                return PrivilegedAPI.requestCSRFToken().then(function (token) {
+                    clonedData['token'] = token;
+                    return RemoteAPI.postJSONWithStatus('/privileged-api/' + path, clonedData);
+                });
+            });
+        });
+    }
+
+    static requestCSRFToken()
+    {
+        var maxNetworkLatency = 3 * 60 * 1000; /* 3 minutes */
+        if (this._token && this._expiration > Date.now() + maxNetworkLatency)
+            return Promise.resolve(this._token);
+
+        return RemoteAPI.postJSONWithStatus('/privileged-api/generate-csrf-token').then(function (result) {
+            PrivilegedAPI._token = result['token'];
+            PrivilegedAPI._expiration = new Date(result['expiration']);
+            return PrivilegedAPI._token;
+        });
+    }
+
+}
+
+PrivilegedAPI._token = null;
+PrivilegedAPI._expiration = null;
+
+if (typeof module != 'undefined')
+    module.exports.PrivilegedAPI = PrivilegedAPI;

Modified: trunk/Websites/perf.webkit.org/public/v3/remote.js (202000 => 202001)


--- trunk/Websites/perf.webkit.org/public/v3/remote.js	2016-06-13 19:40:01 UTC (rev 202000)
+++ trunk/Websites/perf.webkit.org/public/v3/remote.js	2016-06-13 19:47:38 UTC (rev 202001)
@@ -1,6 +1,18 @@
+"use strict";
 
 var RemoteAPI = {};
 
+RemoteAPI.postJSON = function (path, data)
+{
+    return this.getJSON(path, data || {});
+}
+
+RemoteAPI.postJSONWithStatus = function (path, data)
+{
+    console.log(document.cookie);
+    return this.getJSONWithStatus(path, data || {});
+}
+
 RemoteAPI.getJSON = function(path, data)
 {
     console.assert(!path.startsWith('http:') && !path.startsWith('https:') && !path.startsWith('file:'));
@@ -53,35 +65,3 @@
         return content;
     });
 }
-
-// FIXME: Use real class syntax once the dependency on data.js has been removed.
-PrivilegedAPI = class {
-
-    static sendRequest(path, data)
-    {
-        return this.requestCSRFToken().then(function (token) {
-            var clonedData = {};
-            for (var key in data)
-                clonedData[key] = data[key];
-            clonedData['token'] = token;
-            return RemoteAPI.getJSONWithStatus('../privileged-api/' + path, clonedData);
-        });
-    }
-
-    static requestCSRFToken()
-    {
-        var maxNetworkLatency = 3 * 60 * 1000; /* 3 minutes */
-        if (this._token && this._expiration > Date.now() + maxNetworkLatency)
-            return Promise.resolve(this._token);
-
-        return RemoteAPI.getJSONWithStatus('../privileged-api/generate-csrf-token', {}).then(function (result) {
-            PrivilegedAPI._token = result['token'];
-            PrivilegedAPI._expiration = new Date(result['expiration']);
-            return PrivilegedAPI._token;
-        });
-    }
-
-}
-
-PrivilegedAPI._token = null;
-PrivilegedAPI._expiration = null;

Modified: trunk/Websites/perf.webkit.org/server-tests/api-measurement-set-tests.js (202000 => 202001)


--- trunk/Websites/perf.webkit.org/server-tests/api-measurement-set-tests.js	2016-06-13 19:40:01 UTC (rev 202000)
+++ trunk/Websites/perf.webkit.org/server-tests/api-measurement-set-tests.js	2016-06-13 19:47:38 UTC (rev 202001)
@@ -1,7 +1,6 @@
 'use strict';
 
 const assert = require('assert');
-const crypto = require('crypto');
 
 const TestServer = require('./resources/test-server.js');
 const addBuilderForReport = require('./resources/common-operations.js').addBuilderForReport;

Added: trunk/Websites/perf.webkit.org/server-tests/privileged-api-upate-run-status.js (0 => 202001)


--- trunk/Websites/perf.webkit.org/server-tests/privileged-api-upate-run-status.js	                        (rev 0)
+++ trunk/Websites/perf.webkit.org/server-tests/privileged-api-upate-run-status.js	2016-06-13 19:47:38 UTC (rev 202001)
@@ -0,0 +1,134 @@
+'use strict';
+
+require('../tools/js/v3-models.js');
+
+const assert = require('assert');
+
+const TestServer = require('./resources/test-server.js');
+const addBuilderForReport = require('./resources/common-operations.js').addBuilderForReport;
+const connectToDatabaseInEveryTest = require('./resources/common-operations.js').connectToDatabaseInEveryTest;
+
+describe("/privileged-api/update-run-status", function () {
+    this.timeout(1000);
+    TestServer.inject();
+    connectToDatabaseInEveryTest();
+
+    const reportWithRevision = [{
+        "buildNumber": "124",
+        "buildTime": "2013-02-28T15:34:51",
+        "revisions": {
+            "WebKit": {
+                "revision": "191622",
+                "timestamp": (new Date(1445945816878)).toISOString(),
+            },
+        },
+        "builderName": "someBuilder",
+        "builderPassword": "somePassword",
+        "platform": "some platform",
+        "tests": {
+            "Suite": {
+                "tests": {
+                    "test1": {
+                        "metrics": {"Time": { "current": [11] }}
+                    }
+                }
+            },
+        }}];
+
+    it("should be able to mark a run as an outlier", function (done) {
+        const db = TestServer.database();
+        let id;
+        addBuilderForReport(reportWithRevision[0]).then(function () {
+            return TestServer.remoteAPI().postJSON('/api/report/', reportWithRevision);
+        }).then(function (response) {
+            assert.equal(response['status'], 'OK');
+            return db.selectAll('test_runs');
+        }).then(function (runRows) {
+            assert.equal(runRows.length, 1);
+            assert.equal(runRows[0]['mean_cache'], 11);
+            assert.equal(runRows[0]['iteration_count_cache'], 1);
+            assert.equal(runRows[0]['marked_outlier'], false);
+            id = runRows[0]['id'];
+            return PrivilegedAPI.requestCSRFToken();
+        }).then(function () {
+            return PrivilegedAPI.sendRequest('update-run-status', {'run': id, 'markedOutlier': true, 'token': PrivilegedAPI._token});
+        }).then(function () {
+            return db.selectAll('test_runs');
+        }).then(function (runRows) {
+            assert.equal(runRows.length, 1);
+            assert.equal(runRows[0]['mean_cache'], 11);
+            assert.equal(runRows[0]['iteration_count_cache'], 1);
+            assert.equal(runRows[0]['marked_outlier'], true);
+            done();
+        }).catch(done);
+    });
+
+    it("should reject when the token is not set in cookie", function (done) {
+        const db = TestServer.database();
+        addBuilderForReport(reportWithRevision[0]).then(function () {
+            return TestServer.remoteAPI().postJSON('/api/report/', reportWithRevision);
+        }).then(function (response) {
+            assert.equal(response['status'], 'OK');
+            return db.selectAll('test_runs');
+        }).then(function (runRows) {
+            assert.equal(runRows.length, 1);
+            assert.equal(runRows[0]['marked_outlier'], false);
+            return PrivilegedAPI.requestCSRFToken();
+        }).then(function () {
+            RemoteAPI.clearCookies();
+            return RemoteAPI.postJSONWithStatus('/privileged-api/update-run-status', {token: PrivilegedAPI._token});
+        }).then(function () {
+            assert(false, 'PrivilegedAPI.sendRequest should reject');
+        }, function (response) {
+            assert.equal(response['status'], 'InvalidToken');
+            done();
+        }).catch(done);
+    });
+
+    it("should reject when the token in the request content is bad", function (done) {
+        const db = TestServer.database();
+        addBuilderForReport(reportWithRevision[0]).then(function () {
+            return TestServer.remoteAPI().postJSON('/api/report/', reportWithRevision);
+        }).then(function (response) {
+            assert.equal(response['status'], 'OK');
+            return db.selectAll('test_runs');
+        }).then(function (runRows) {
+            assert.equal(runRows.length, 1);
+            assert.equal(runRows[0]['marked_outlier'], false);
+            return PrivilegedAPI.requestCSRFToken();
+        }).then(function () {
+            return RemoteAPI.postJSONWithStatus('/privileged-api/update-run-status', {token: 'bad'});
+        }).then(function () {
+            assert(false, 'PrivilegedAPI.sendRequest should reject');
+        }, function (response) {
+            assert.equal(response['status'], 'InvalidToken');
+            done();
+        }).catch(done);
+    });
+
+    it("should be able to unmark a run as an outlier", function (done) {
+        const db = TestServer.database();
+        addBuilderForReport(reportWithRevision[0]).then(function () {
+            return TestServer.remoteAPI().postJSON('/api/report/', reportWithRevision);
+        }).then(function (response) {
+            assert.equal(response['status'], 'OK');
+            return db.selectAll('test_runs');
+        }).then(function (runRows) {
+            assert.equal(runRows.length, 1);
+            assert.equal(runRows[0]['marked_outlier'], false);
+            return PrivilegedAPI.sendRequest('update-run-status', {'run': runRows[0]['id'], 'markedOutlier': true});
+        }).then(function () {
+            return db.selectAll('test_runs');
+        }).then(function (runRows) {
+            assert.equal(runRows.length, 1);
+            assert.equal(runRows[0]['marked_outlier'], true);
+            return PrivilegedAPI.sendRequest('update-run-status', {'run': runRows[0]['id'], 'markedOutlier': false});
+        }).then(function () {
+            return db.selectAll('test_runs');
+        }).then(function (runRows) {
+            assert.equal(runRows.length, 1);
+            assert.equal(runRows[0]['marked_outlier'], false);
+            done();
+        }).catch(done);
+    });
+});

Modified: trunk/Websites/perf.webkit.org/server-tests/resources/test-server.js (202000 => 202001)


--- trunk/Websites/perf.webkit.org/server-tests/resources/test-server.js	2016-06-13 19:40:01 UTC (rev 202000)
+++ trunk/Websites/perf.webkit.org/server-tests/resources/test-server.js	2016-06-13 19:47:38 UTC (rev 202001)
@@ -233,6 +233,12 @@
             self.cleanDataDirectory();
             originalRemote = global.RemoteAPI;
             global.RemoteAPI = self._remote;
+            self._remote.clearCookies();
+
+            if (global.PrivilegedAPI) {
+                global.PrivilegedAPI._token = null;
+                global.PrivilegedAPI._expiration = null;
+            }
         });
 
         after(function () {

Modified: trunk/Websites/perf.webkit.org/tools/js/remote.js (202000 => 202001)


--- trunk/Websites/perf.webkit.org/tools/js/remote.js	2016-06-13 19:40:01 UTC (rev 202000)
+++ trunk/Websites/perf.webkit.org/tools/js/remote.js	2016-06-13 19:47:38 UTC (rev 202001)
@@ -9,6 +9,7 @@
     constructor(server)
     {
         this._server = null;
+        this._cookies = new Map;
         if (server)
             this.configure(server);
     }
@@ -50,6 +51,8 @@
         };
     }
 
+    clearCookies() { this._cookies = new Map; }
+
     getJSON(path)
     {
         return this.sendHttpRequest(path, 'GET', null, null).then(function (result) {
@@ -69,7 +72,7 @@
     postJSON(path, data)
     {
         const contentType = 'application/json';
-        const payload = JSON.stringify(data);
+        const payload = JSON.stringify(data || {});
         return this.sendHttpRequest(path, 'POST', 'application/json', payload).then(function (result) {
             try {
                 return JSON.parse(result.responseText);
@@ -80,6 +83,15 @@
         });
     }
 
+    postJSONWithStatus(path, data)
+    {
+        return this.postJSON(path, data).then(function (result) {
+            if (result['status'] != 'OK')
+                return Promise.reject(result);
+            return result;
+        });
+    }
+
     postFormUrlencodedData(path, data)
     {
         const contentType = 'application/x-www-form-urlencoded';
@@ -92,6 +104,7 @@
     sendHttpRequest(path, method, contentType, content)
     {
         let server = this._server;
+        const self = this;
         return new Promise(function (resolve, reject) {
             let options = {
                 hostname: server.host,
@@ -105,7 +118,15 @@
                 let responseText = '';
                 response.setEncoding('utf8');
                 response.on('data', function (chunk) { responseText += chunk; });
-                response.on('end', function () { resolve({statusCode: response.statusCode, responseText: responseText}); });
+                response.on('end', function () {
+                    if ('set-cookie' in response.headers) {
+                        for (const cookie of response.headers['set-cookie']) {
+                            var nameValue = cookie.split('=')
+                            self._cookies.set(nameValue[0], nameValue[1]);
+                        }
+                    }
+                    resolve({statusCode: response.statusCode, responseText: responseText});
+                });
             });
 
             request.on('error', reject);
@@ -113,6 +134,12 @@
             if (contentType)
                 request.setHeader('Content-Type', contentType);
 
+            if (self._cookies.size) {
+                request.setHeader('Cookie', Array.from(self._cookies.keys()).map(function (key) {
+                    return `${key}=${self._cookies.get(key)}`;
+                }).join('; '));
+            }
+
             if (content)
                 request.write(content);
 

Modified: trunk/Websites/perf.webkit.org/tools/js/v3-models.js (202000 => 202001)


--- trunk/Websites/perf.webkit.org/tools/js/v3-models.js	2016-06-13 19:40:01 UTC (rev 202000)
+++ trunk/Websites/perf.webkit.org/tools/js/v3-models.js	2016-06-13 19:47:38 UTC (rev 202001)
@@ -27,6 +27,7 @@
 importFromV3('models/test.js', 'Test');
 importFromV3('models/test-group.js', 'TestGroup');
 
+importFromV3('privileged-api.js', 'PrivilegedAPI');
 importFromV3('instrumentation.js', 'Instrumentation');
 
 global.Statistics = require('../../public/shared/statistics.js');

Added: trunk/Websites/perf.webkit.org/unit-tests/privileged-api-tests.js (0 => 202001)


--- trunk/Websites/perf.webkit.org/unit-tests/privileged-api-tests.js	                        (rev 0)
+++ trunk/Websites/perf.webkit.org/unit-tests/privileged-api-tests.js	2016-06-13 19:47:38 UTC (rev 202001)
@@ -0,0 +1,182 @@
+'use strict';
+
+const assert = require('assert');
+
+let MockRemoteAPI = require('./resources/mock-remote-api.js').MockRemoteAPI;
+require('../tools/js/v3-models.js');
+
+describe('PrivilegedAPI', function () {
+    let requests = MockRemoteAPI.inject();
+
+    beforeEach(function () {
+        PrivilegedAPI._token = null;
+    })
+
+    describe('requestCSRFToken', function () {
+        it('should generate a new token', function () {
+            PrivilegedAPI.requestCSRFToken();
+            assert.equal(requests.length, 1);
+            assert.equal(requests[0].url, '/privileged-api/generate-csrf-token');
+        });
+
+        it('should not generate a new token if the existing token had not expired', function (done) {
+            const tokenRequest = PrivilegedAPI.requestCSRFToken();
+            assert.equal(requests.length, 1);
+            assert.equal(requests[0].url, '/privileged-api/generate-csrf-token');
+            requests[0].resolve({
+                token: 'abc',
+                expiration: Date.now() + 3600 * 1000,
+            });
+            tokenRequest.then(function (token) {
+                assert.equal(token, 'abc');
+                PrivilegedAPI.requestCSRFToken();
+                assert.equal(requests.length, 1);
+                done();
+            }).catch(done);
+        });
+
+        it('should generate a new token if the existing token had already expired', function (done) {
+            const tokenRequest = PrivilegedAPI.requestCSRFToken();
+            assert.equal(requests.length, 1);
+            assert.equal(requests[0].url, '/privileged-api/generate-csrf-token');
+            requests[0].resolve({
+                token: 'abc',
+                expiration: Date.now() - 1,
+            });
+            tokenRequest.then(function (token) {
+                assert.equal(token, 'abc');
+                PrivilegedAPI.requestCSRFToken();
+                assert.equal(requests.length, 2);
+                assert.equal(requests[1].url, '/privileged-api/generate-csrf-token');
+                done();
+            }).catch(done);
+        });
+    });
+    
+    describe('sendRequest', function () {
+
+        it('should generate a new token if no token had been fetched', function (done) {
+            PrivilegedAPI.sendRequest('test', {});
+            assert.equal(requests.length, 1);
+            assert.equal(requests[0].url, '/privileged-api/generate-csrf-token');
+            requests[0].resolve({
+                token: 'abc',
+                expiration: Date.now() + 100 * 1000,
+            });
+            Promise.resolve().then(function () {
+                return Promise.resolve();
+            }).then(function () {
+                assert.equal(requests.length, 2);
+                assert.equal(requests[1].url, '/privileged-api/test');
+                done();
+            }).catch(done);
+        });
+
+        it('should not generate a new token if the existing token had not been expired', function (done) {
+            PrivilegedAPI.sendRequest('test', {});
+            assert.equal(requests.length, 1);
+            assert.equal(requests[0].url, '/privileged-api/generate-csrf-token');
+            requests[0].resolve({
+                token: 'abc',
+                expiration: Date.now() + 3600 * 1000,
+            });
+            Promise.resolve().then(function () {
+                return Promise.resolve();
+            }).then(function () {
+                assert.equal(requests.length, 2);
+                assert.equal(requests[1].url, '/privileged-api/test');
+                PrivilegedAPI.sendRequest('test2', {});
+                return Promise.resolve();
+            }).then(function () {
+                assert.equal(requests.length, 3);
+                assert.equal(requests[2].url, '/privileged-api/test2');
+                done();
+            }).catch(done);
+        });
+
+        it('should reject immediately when a token generation had failed', function (done) {
+            const request = PrivilegedAPI.sendRequest('test', {});
+            let caught = false;
+            request.catch(function () { caught = true; });
+            assert.equal(requests.length, 1);
+            assert.equal(requests[0].url, '/privileged-api/generate-csrf-token');
+            requests[0].reject({status: 'FailedToGenerateToken'});
+            Promise.resolve().then(function () {
+                return Promise.resolve();
+            }).then(function () {
+                assert.equal(requests.length, 1);
+                assert(caught);
+                done();
+            }).catch(done);
+        });
+
+        it('should re-generate token when it had become invalid', function (done) {
+            PrivilegedAPI.sendRequest('test', {});
+            assert.equal(requests.length, 1);
+            assert.equal(requests[0].url, '/privileged-api/generate-csrf-token');
+            requests[0].resolve({
+                token: 'abc',
+                expiration: Date.now() + 3600 * 1000,
+            });
+            Promise.resolve().then(function () {
+                return Promise.resolve();
+            }).then(function () {
+                assert.equal(requests.length, 2);
+                assert.equal(requests[1].data.token, 'abc');
+                assert.equal(requests[1].url, '/privileged-api/test');
+                requests[1].reject('InvalidToken');
+                return Promise.resolve();
+            }).then(function () {
+                assert.equal(requests.length, 3);
+                assert.equal(requests[2].url, '/privileged-api/generate-csrf-token');
+                requests[2].resolve({
+                    token: 'def',
+                    expiration: Date.now() + 3600 * 1000,
+                });
+                return Promise.resolve();
+            }).then(function () {
+                assert.equal(requests.length, 4);
+                assert.equal(requests[3].data.token, 'def');
+                assert.equal(requests[3].url, '/privileged-api/test');
+                done();
+            }).catch(done);
+        });
+
+        it('should not re-generate token when the re-fetched token was invalid', function (done) {
+            PrivilegedAPI.sendRequest('test', {});
+            assert.equal(requests.length, 1);
+            assert.equal(requests[0].url, '/privileged-api/generate-csrf-token');
+            requests[0].resolve({
+                token: 'abc',
+                expiration: Date.now() + 3600 * 1000,
+            });
+            Promise.resolve().then(function () {
+                return Promise.resolve();
+            }).then(function () {
+                assert.equal(requests.length, 2);
+                assert.equal(requests[1].data.token, 'abc');
+                assert.equal(requests[1].url, '/privileged-api/test');
+                requests[1].reject('InvalidToken');
+                return Promise.resolve();
+            }).then(function () {
+                assert.equal(requests.length, 3);
+                assert.equal(requests[2].url, '/privileged-api/generate-csrf-token');
+                requests[2].resolve({
+                    token: 'def',
+                    expiration: Date.now() + 3600 * 1000,
+                });
+                return Promise.resolve();
+            }).then(function () {
+                assert.equal(requests.length, 4);
+                assert.equal(requests[3].data.token, 'def');
+                assert.equal(requests[3].url, '/privileged-api/test');
+                requests[3].reject('InvalidToken');
+            }).then(function () {
+                assert.equal(requests.length, 4);
+                done();
+            }).catch(done);
+        });
+
+    });
+
+});

Modified: trunk/Websites/perf.webkit.org/unit-tests/resources/mock-remote-api.js (202000 => 202001)


--- trunk/Websites/perf.webkit.org/unit-tests/resources/mock-remote-api.js	2016-06-13 19:40:01 UTC (rev 202000)
+++ trunk/Websites/perf.webkit.org/unit-tests/resources/mock-remote-api.js	2016-06-13 19:47:38 UTC (rev 202001)
@@ -16,6 +16,14 @@
     {
         return this._addRequest(url, 'GET', null);
     },
+    postJSON: function (url, data)
+    {
+        return this._addRequest(url, 'POST', data);
+    },
+    postJSONWithStatus: function (url, data)
+    {
+        return this._addRequest(url, 'POST', data);
+    },
     postFormUrlencodedData: function (url, data)
     {
         return this._addRequest(url, 'POST', data);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to