Update Perf buildbot syncing scripts for Buildbot 0.9
[WebKit.git] / Websites / perf.webkit.org / server-tests / tools-buildbot-triggerable-tests.js
1 'use strict';
2
3 const assert = require('assert');
4
5 const BuildbotTriggerable = require('../tools/js/buildbot-triggerable.js').BuildbotTriggerable;
6 const MockData = require('./resources/mock-data.js');
7 const MockRemoteAPI = require('../unit-tests/resources/mock-remote-api.js').MockRemoteAPI;
8 const TestServer = require('./resources/test-server.js');
9 const prepareServerTest = require('./resources/common-operations.js').prepareServerTest;
10 const MockLogger = require('./resources/mock-logger.js').MockLogger;
11
12 function assertRequestAndResolve(request, method, url, content)
13 {
14     assert.equal(request.method, method);
15     assert.equal(request.url, url);
16     request.resolve(content);
17 }
18
19 describe('BuildbotTriggerable', function () {
20     prepareServerTest(this);
21
22     beforeEach(function () {
23         MockData.resetV3Models();
24         MockRemoteAPI.reset('http://build.webkit.org');
25     });
26
27     describe('syncOnce', () => {
28         it('should schedule the next build request when there are no pending builds', () => {
29             const db = TestServer.database();
30             let syncPromise;
31             return MockData.addMockData(db, ['completed', 'running', 'pending', 'pending']).then(() => {
32                 return Manifest.fetch();
33             }).then(() => {
34                 const config = MockData.mockTestSyncConfigWithSingleBuilder();
35                 const logger = new MockLogger;
36                 const slaveInfo = {name: 'sync-slave', password: 'password'};
37                 const triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
38                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
39                 assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURL(), MockData.mockBuildbotBuilders());
40                 MockRemoteAPI.reset();
41                 return MockRemoteAPI.waitForRequest();
42             }).then(() => {
43                 assert.equal(BuildRequest.all().length, 4);
44                 assert.equal(BuildRequest.findById(700).status(), 'completed');
45                 assert.equal(BuildRequest.findById(701).status(), 'running');
46                 assert.equal(BuildRequest.findById(702).status(), 'pending');
47                 assert.equal(BuildRequest.findById(703).status(), 'pending');
48                 assert.equal(MockRemoteAPI.requests[0].method, 'GET');
49                 assert.equal(MockRemoteAPI.requests[0].url, MockData.pendingBuildsUrl('some-builder-1'));
50                 MockRemoteAPI.requests[0].resolve({});
51                 return MockRemoteAPI.waitForRequest();
52             }).then(() => {
53                 assert.equal(MockRemoteAPI.requests[1].method, 'GET');
54                 assert.equal(MockRemoteAPI.requests[1].url, MockData.recentBuildsUrl('some-builder-1', 2));
55                 MockRemoteAPI.requests[1].resolve({'builds': [MockData.runningBuildData(), MockData.finishedBuildData()]});
56                 return MockRemoteAPI.waitForRequest();
57             }).then(() => {
58                 assert.equal(MockRemoteAPI.requests[2].method, 'POST');
59                 assert.equal(MockRemoteAPI.requests[2].url, '/api/v2/forceschedulers/force-some-builder-1');
60                 assert.deepEqual(MockRemoteAPI.requests[2].data, {'id': 702, 'jsonrpc': '2.0', 'method': 'force', 'params':
61                     {'wk': '191622', 'os': '10.11 15A284', 'build-request-id': 702, 'forcescheduler': 'force-some-builder-1'}});
62                 MockRemoteAPI.requests[2].resolve('OK');
63                 return MockRemoteAPI.waitForRequest();
64             }).then(() => {
65                 assert.equal(MockRemoteAPI.requests[3].method, 'GET');
66                 assert.equal(MockRemoteAPI.requests[3].url, MockData.pendingBuildsUrl('some-builder-1'));
67                 MockRemoteAPI.requests[3].resolve(MockData.pendingBuild())
68                 return MockRemoteAPI.waitForRequest();
69             }).then(() => {
70                 assert.equal(MockRemoteAPI.requests[4].method, 'GET');
71                 assert.equal(MockRemoteAPI.requests[4].url, MockData.recentBuildsUrl('some-builder-1', 2));
72                 MockRemoteAPI.requests[4].resolve({'builds': [MockData.runningBuildData(), MockData.finishedBuildData()]});
73                 return syncPromise;
74             }).then(() => {
75                 return BuildRequest.fetchForTriggerable(MockData.mockTestSyncConfigWithSingleBuilder().triggerableName);
76             }).then(() => {
77                 assert.equal(BuildRequest.all().length, 4);
78                 assert.equal(BuildRequest.findById(700).status(), 'completed');
79                 assert.equal(BuildRequest.findById(701).status(), 'running');
80                 assert.equal(BuildRequest.findById(702).status(), 'scheduled');
81                 assert.equal(BuildRequest.findById(703).status(), 'pending');
82             });
83         });
84
85         it('should not schedule the next build request when there is a pending build', () => {
86             const db = TestServer.database();
87             let syncPromise;
88             return MockData.addMockData(db, ['completed', 'running', 'pending', 'pending']).then(() => {
89                 return Manifest.fetch();
90             }).then(() => {
91                 let config = MockData.mockTestSyncConfigWithSingleBuilder();
92                 let logger = new MockLogger;
93                 let slaveInfo = {name: 'sync-slave', password: 'password'};
94                 let triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
95                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
96                 assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURL(), MockData.mockBuildbotBuilders());
97                 MockRemoteAPI.reset();
98                 return MockRemoteAPI.waitForRequest();
99             }).then(() => {
100                 assert.equal(MockRemoteAPI.requests[0].method, 'GET');
101                 assert.equal(MockRemoteAPI.requests[0].url, MockData.pendingBuildsUrl('some-builder-1'));
102                 MockRemoteAPI.requests[0].resolve(MockData.pendingBuild());
103                 return MockRemoteAPI.waitForRequest();
104             }).then(() => {
105                 assert.equal(MockRemoteAPI.requests[1].method, 'GET');
106                 assert.equal(MockRemoteAPI.requests[1].url, MockData.recentBuildsUrl('some-builder-1', 2));
107                 MockRemoteAPI.requests[1].resolve({'builds' : [MockData.runningBuildData(), MockData.finishedBuildData()]});
108                 return MockRemoteAPI.waitForRequest();
109             }).then(() => {
110                 assert.equal(MockRemoteAPI.requests[2].method, 'GET');
111                 assert.equal(MockRemoteAPI.requests[2].url, MockData.pendingBuildsUrl('some-builder-1'));
112                 MockRemoteAPI.requests[2].resolve(MockData.pendingBuild())
113                 return MockRemoteAPI.waitForRequest();
114             }).then(() => {
115                 assert.equal(MockRemoteAPI.requests[3].method, 'GET');
116                 assert.equal(MockRemoteAPI.requests[3].url, MockData.recentBuildsUrl('some-builder-1', 2));
117                 MockRemoteAPI.requests[3].resolve({'builds' : [MockData.runningBuildData(), MockData.finishedBuildData()]});
118                 return syncPromise;
119             }).then(() => {
120                 assert.equal(BuildRequest.all().length, 4);
121                 assert.equal(BuildRequest.findById(700).status(), 'completed');
122                 assert.equal(BuildRequest.findById(701).status(), 'running');
123                 assert.equal(BuildRequest.findById(702).status(), 'pending');
124                 assert.equal(BuildRequest.findById(703).status(), 'pending');
125                 return BuildRequest.fetchForTriggerable(MockData.mockTestSyncConfigWithSingleBuilder().triggerableName);
126             }).then(() => {
127                 assert.equal(BuildRequest.all().length, 4);
128                 assert.equal(BuildRequest.findById(700).status(), 'completed');
129                 assert.equal(BuildRequest.findById(701).status(), 'running');
130                 assert.equal(BuildRequest.findById(702).status(), 'scheduled');
131                 assert.equal(BuildRequest.findById(703).status(), 'pending');
132             });
133         });
134
135         it('should schedule the build request on a builder without a pending build if it\'s the first request in the group', () => {
136             const db = TestServer.database();
137             let syncPromise;
138             return MockData.addMockData(db, ['pending', 'pending', 'pending', 'pending']).then(() => {
139                 return Manifest.fetch();
140             }).then(() => {
141                 const config = MockData.mockTestSyncConfigWithTwoBuilders();
142                 const logger = new MockLogger;
143                 const slaveInfo = {name: 'sync-slave', password: 'password'};
144                 const triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
145                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
146                 assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURL(), MockData.mockBuildbotBuilders());
147                 MockRemoteAPI.reset();
148                 return MockRemoteAPI.waitForRequest();
149             }).then(() => {
150                 assert.equal(MockRemoteAPI.requests.length, 2);
151                 assert.equal(MockRemoteAPI.requests[0].method, 'GET');
152                 assert.equal(MockRemoteAPI.requests[0].url, MockData.pendingBuildsUrl('some-builder-1'));
153                 MockRemoteAPI.requests[0].resolve(MockData.pendingBuild({buildRequestId: 999}));
154                 assert.equal(MockRemoteAPI.requests[1].method, 'GET');
155                 assert.equal(MockRemoteAPI.requests[1].url, MockData.pendingBuildsUrl('some builder 2'));
156                 MockRemoteAPI.requests[1].resolve({});
157                 return MockRemoteAPI.waitForRequest();
158             }).then(() => {
159                 assert.equal(MockRemoteAPI.requests.length, 4);
160                 assert.equal(MockRemoteAPI.requests[2].method, 'GET');
161                 assert.equal(MockRemoteAPI.requests[2].url, MockData.recentBuildsUrl('some-builder-1', 2));
162                 MockRemoteAPI.requests[2].resolve({});
163                 assert.equal(MockRemoteAPI.requests[3].method, 'GET');
164                 assert.equal(MockRemoteAPI.requests[3].url, MockData.recentBuildsUrl('some builder 2', 2));
165                 MockRemoteAPI.requests[3].resolve({});
166                 return MockRemoteAPI.waitForRequest();
167             }).then(() => {
168                 assert.equal(MockRemoteAPI.requests.length, 5);
169                 assert.equal(MockRemoteAPI.requests[4].method, 'POST');
170                 assert.equal(MockRemoteAPI.requests[4].url, '/api/v2/forceschedulers/force-some-builder-2');
171                 assert.deepEqual(MockRemoteAPI.requests[4].data, {'id': '700', 'jsonrpc': '2.0', 'method': 'force', 'params':
172                     {'wk': '191622', 'os': '10.11 15A284', 'build-request-id': '700', 'forcescheduler': 'force-some-builder-2'}});
173                 MockRemoteAPI.requests[4].resolve('OK');
174                 return MockRemoteAPI.waitForRequest();
175             }).then(() => {
176                 assert.equal(MockRemoteAPI.requests.length, 7);
177                 assert.equal(MockRemoteAPI.requests[5].method, 'GET');
178                 assert.equal(MockRemoteAPI.requests[5].url, MockData.pendingBuildsUrl('some-builder-1'));
179                 MockRemoteAPI.requests[5].resolve(MockData.pendingBuild({buildRequestId: 999}));
180                 assert.equal(MockRemoteAPI.requests[6].method, 'GET');
181                 assert.equal(MockRemoteAPI.requests[6].url, MockData.pendingBuildsUrl('some builder 2'));
182                 MockRemoteAPI.requests[6].resolve(MockData.pendingBuild({builderId: MockData.builderIDForName('some builder 2'), buildRequestId: 700, buildbotBuildRequestId: 17}));
183                 return MockRemoteAPI.waitForRequest();
184             }).then(() => {
185                 assert.equal(MockRemoteAPI.requests.length, 9);
186                 assert.equal(MockRemoteAPI.requests[7].method, 'GET');
187                 assert.equal(MockRemoteAPI.requests[7].url, MockData.recentBuildsUrl('some-builder-1', 2));
188                 MockRemoteAPI.requests[7].resolve({});
189                 assert.equal(MockRemoteAPI.requests[8].method, 'GET');
190                 assert.equal(MockRemoteAPI.requests[8].url, MockData.recentBuildsUrl('some builder 2', 2));
191                 MockRemoteAPI.requests[8].resolve({});
192                 return syncPromise;
193             }).then(() => {
194                 assert.equal(BuildRequest.all().length, 4);
195                 assert.equal(BuildRequest.findById(700).status(), 'pending');
196                 assert.equal(BuildRequest.findById(700).statusUrl(), null);
197                 assert.equal(BuildRequest.findById(701).status(), 'pending');
198                 assert.equal(BuildRequest.findById(701).statusUrl(), null);
199                 assert.equal(BuildRequest.findById(702).status(), 'pending');
200                 assert.equal(BuildRequest.findById(702).statusUrl(), null);
201                 assert.equal(BuildRequest.findById(703).status(), 'pending');
202                 assert.equal(BuildRequest.findById(703).statusUrl(), null);
203                 return BuildRequest.fetchForTriggerable(MockData.mockTestSyncConfigWithTwoBuilders().triggerableName);
204             }).then(() => {
205                 assert.equal(BuildRequest.all().length, 4);
206                 assert.equal(BuildRequest.findById(700).status(), 'scheduled');
207                 assert.equal(BuildRequest.findById(700).statusUrl(), 'http://build.webkit.org/#/buildrequests/17');
208                 assert.equal(BuildRequest.findById(701).status(), 'pending');
209                 assert.equal(BuildRequest.findById(701).statusUrl(), null);
210                 assert.equal(BuildRequest.findById(702).status(), 'pending');
211                 assert.equal(BuildRequest.findById(702).statusUrl(), null);
212                 assert.equal(BuildRequest.findById(703).status(), 'pending');
213                 assert.equal(BuildRequest.findById(703).statusUrl(), null);
214             });
215         });
216
217         it('should not schedule a build request on a different builder than the one the first build request is pending', () => {
218             const db = TestServer.database();
219             let syncPromise;
220             return MockData.addMockData(db, ['pending', 'pending', 'pending', 'pending']).then(() => {
221                 return Manifest.fetch();
222             }).then(() => {
223                 let config = MockData.mockTestSyncConfigWithTwoBuilders();
224                 let logger = new MockLogger;
225                 let slaveInfo = {name: 'sync-slave', password: 'password'};
226                 let triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
227                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
228                 assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURL(), MockData.mockBuildbotBuilders());
229                 MockRemoteAPI.reset();
230                 return MockRemoteAPI.waitForRequest();
231             }).then(() => {
232                 assert.equal(MockRemoteAPI.requests.length, 2);
233                 assert.equal(MockRemoteAPI.requests[0].method, 'GET');
234                 assert.equal(MockRemoteAPI.requests[0].url, MockData.pendingBuildsUrl('some-builder-1'));
235                 MockRemoteAPI.requests[0].resolve(MockData.pendingBuild({buildRequestId: 700, buildbotBuildRequestId: 17}));
236                 assert.equal(MockRemoteAPI.requests[1].method, 'GET');
237                 assert.equal(MockRemoteAPI.requests[1].url, MockData.pendingBuildsUrl('some builder 2'));
238                 MockRemoteAPI.requests[1].resolve({});
239                 return MockRemoteAPI.waitForRequest();
240             }).then(() => {
241                 assert.equal(MockRemoteAPI.requests.length, 4);
242                 assert.equal(MockRemoteAPI.requests[2].method, 'GET');
243                 assert.equal(MockRemoteAPI.requests[2].url, MockData.recentBuildsUrl('some-builder-1', 2));
244                 MockRemoteAPI.requests[2].resolve({});
245                 assert.equal(MockRemoteAPI.requests[3].method, 'GET');
246                 assert.equal(MockRemoteAPI.requests[3].url, MockData.recentBuildsUrl('some builder 2', 2));
247                 MockRemoteAPI.requests[3].resolve({});
248                 return MockRemoteAPI.waitForRequest();
249             }).then(() => {
250                 assert.equal(MockRemoteAPI.requests.length, 6);
251                 assert.equal(MockRemoteAPI.requests[4].method, 'GET');
252                 assert.equal(MockRemoteAPI.requests[4].url, MockData.pendingBuildsUrl('some-builder-1'));
253                 MockRemoteAPI.requests[4].resolve(MockData.pendingBuild({buildRequestId: 700, buildbotBuildRequestId: 17}));
254                 assert.equal(MockRemoteAPI.requests[5].method, 'GET');
255                 assert.equal(MockRemoteAPI.requests[5].url, MockData.pendingBuildsUrl('some builder 2'));
256                 MockRemoteAPI.requests[5].resolve({});
257                 return MockRemoteAPI.waitForRequest();
258             }).then(() => {
259                 assert.equal(MockRemoteAPI.requests.length, 8);
260                 assert.equal(MockRemoteAPI.requests[6].method, 'GET');
261                 assert.equal(MockRemoteAPI.requests[6].url, MockData.recentBuildsUrl('some-builder-1', 2));
262                 MockRemoteAPI.requests[6].resolve({});
263                 assert.equal(MockRemoteAPI.requests[7].method, 'GET');
264                 assert.equal(MockRemoteAPI.requests[7].url, MockData.recentBuildsUrl('some builder 2', 2));
265                 MockRemoteAPI.requests[7].resolve({});
266                 return syncPromise;
267             }).then(() => {
268                 assert.equal(BuildRequest.all().length, 4);
269                 assert.equal(BuildRequest.findById(700).status(), 'pending');
270                 assert.equal(BuildRequest.findById(700).statusUrl(), null);
271                 assert.equal(BuildRequest.findById(701).status(), 'pending');
272                 assert.equal(BuildRequest.findById(701).statusUrl(), null);
273                 assert.equal(BuildRequest.findById(702).status(), 'pending');
274                 assert.equal(BuildRequest.findById(702).statusUrl(), null);
275                 assert.equal(BuildRequest.findById(703).status(), 'pending');
276                 assert.equal(BuildRequest.findById(703).statusUrl(), null);
277                 return BuildRequest.fetchForTriggerable(MockData.mockTestSyncConfigWithTwoBuilders().triggerableName);
278             }).then(() => {
279                 assert.equal(BuildRequest.all().length, 4);
280                 assert.equal(BuildRequest.findById(700).status(), 'scheduled');
281                 assert.equal(BuildRequest.findById(700).statusUrl(), 'http://build.webkit.org/#/buildrequests/17');
282                 assert.equal(BuildRequest.findById(701).status(), 'pending');
283                 assert.equal(BuildRequest.findById(701).statusUrl(), null);
284                 assert.equal(BuildRequest.findById(702).status(), 'pending');
285                 assert.equal(BuildRequest.findById(702).statusUrl(), null);
286                 assert.equal(BuildRequest.findById(703).status(), 'pending');
287                 assert.equal(BuildRequest.findById(703).statusUrl(), null);
288             });
289         });
290
291         it('should update the status of a pending build and schedule a new build if the pending build had started running', () => {
292             const db = TestServer.database();
293             let syncPromise;
294             return MockData.addMockData(db, ['pending', 'pending', 'pending', 'pending']).then(() => {
295                 return Manifest.fetch();
296             }).then(() => {
297                 const config = MockData.mockTestSyncConfigWithTwoBuilders();
298                 const logger = new MockLogger;
299                 const slaveInfo = {name: 'sync-slave', password: 'password'};
300                 const triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
301                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
302                 assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURL(), MockData.mockBuildbotBuilders());
303                 MockRemoteAPI.reset();
304                 return MockRemoteAPI.waitForRequest();
305             }).then(() => {
306                 assert.equal(MockRemoteAPI.requests.length, 2);
307                 assert.equal(MockRemoteAPI.requests[0].method, 'GET');
308                 assert.equal(MockRemoteAPI.requests[0].url, MockData.pendingBuildsUrl('some-builder-1'));
309                 MockRemoteAPI.requests[0].resolve({});
310                 assert.equal(MockRemoteAPI.requests[1].method, 'GET');
311                 assert.equal(MockRemoteAPI.requests[1].url, MockData.pendingBuildsUrl('some builder 2'));
312                 MockRemoteAPI.requests[1].resolve({});
313                 return MockRemoteAPI.waitForRequest();
314             }).then(() => {
315                 assert.equal(MockRemoteAPI.requests.length, 4);
316                 assert.equal(MockRemoteAPI.requests[2].method, 'GET');
317                 assert.equal(MockRemoteAPI.requests[2].url, MockData.recentBuildsUrl('some-builder-1', 2));
318                 MockRemoteAPI.requests[2].resolve({'builds' : [MockData.runningBuildData({buildRequestId: 701}), MockData.finishedBuildData({buildRequestId: 700})]});
319                 assert.equal(MockRemoteAPI.requests[3].method, 'GET');
320                 assert.equal(MockRemoteAPI.requests[3].url, MockData.recentBuildsUrl('some builder 2', 2));
321                 MockRemoteAPI.requests[3].resolve({});
322                 return MockRemoteAPI.waitForRequest();
323             }).then(() => {
324                 assert.equal(MockRemoteAPI.requests.length, 5);
325                 assert.equal(MockRemoteAPI.requests[4].method, 'POST');
326                 assert.equal(MockRemoteAPI.requests[4].url, '/api/v2/forceschedulers/force-some-builder-1');
327                 assert.deepEqual(MockRemoteAPI.requests[4].data, {'id': '702', 'jsonrpc': '2.0', 'method': 'force', 'params':
328                     {'wk': '191622', 'os': '10.11 15A284', 'build-request-id': '702', 'forcescheduler': 'force-some-builder-1'}});
329                 MockRemoteAPI.requests[4].resolve('OK');
330                 return MockRemoteAPI.waitForRequest();
331             }).then(() => {
332                 assert.equal(MockRemoteAPI.requests.length, 7);
333                 assert.equal(MockRemoteAPI.requests[5].method, 'GET');
334                 assert.equal(MockRemoteAPI.requests[5].url, MockData.pendingBuildsUrl('some-builder-1'));
335                 MockRemoteAPI.requests[5].resolve(MockData.pendingBuild({buildRequestId: 702, buildbotBuildRequestId: 17}));
336                 assert.equal(MockRemoteAPI.requests[6].method, 'GET');
337                 assert.equal(MockRemoteAPI.requests[6].url, MockData.pendingBuildsUrl('some builder 2'));
338                 MockRemoteAPI.requests[6].resolve({});
339                 return MockRemoteAPI.waitForRequest();
340             }).then(() => {
341                 assert.equal(MockRemoteAPI.requests.length, 9);
342                 assert.equal(MockRemoteAPI.requests[7].method, 'GET');
343                 assert.equal(MockRemoteAPI.requests[7].url, MockData.recentBuildsUrl('some-builder-1', 2));
344                 MockRemoteAPI.requests[7].resolve({'builds' : [MockData.runningBuildData({buildRequestId: 701}), MockData.finishedBuildData({buildRequestId: 700})]});
345                 assert.equal(MockRemoteAPI.requests[8].method, 'GET');
346                 assert.equal(MockRemoteAPI.requests[8].url, MockData.recentBuildsUrl('some builder 2', 2));
347                 MockRemoteAPI.requests[8].resolve({});
348                 return syncPromise;
349             }).then(() => {
350                 assert.equal(BuildRequest.all().length, 4);
351                 assert.equal(BuildRequest.findById(700).status(), 'pending');
352                 assert.equal(BuildRequest.findById(700).statusUrl(), null);
353                 assert.equal(BuildRequest.findById(701).status(), 'pending');
354                 assert.equal(BuildRequest.findById(701).statusUrl(), null);
355                 assert.equal(BuildRequest.findById(702).status(), 'pending');
356                 assert.equal(BuildRequest.findById(702).statusUrl(), null);
357                 assert.equal(BuildRequest.findById(703).status(), 'pending');
358                 assert.equal(BuildRequest.findById(703).statusUrl(), null);
359                 return BuildRequest.fetchForTriggerable(MockData.mockTestSyncConfigWithTwoBuilders().triggerableName);
360             }).then(() => {
361                 assert.equal(BuildRequest.all().length, 4);
362                 assert.equal(BuildRequest.findById(700).status(), 'failed');
363                 assert.equal(BuildRequest.findById(700).statusUrl(), MockData.statusUrl('some-builder-1', 123));
364                 assert.equal(BuildRequest.findById(701).status(), 'running');
365                 assert.equal(BuildRequest.findById(701).statusUrl(), MockData.statusUrl('some-builder-1', 124));
366                 assert.equal(BuildRequest.findById(702).status(), 'scheduled');
367                 assert.equal(BuildRequest.findById(702).statusUrl(), 'http://build.webkit.org/#/buildrequests/17');
368                 assert.equal(BuildRequest.findById(703).status(), 'pending');
369                 assert.equal(BuildRequest.findById(703).statusUrl(), null);
370             });
371         });
372
373         it('should update the status of a scheduled build if the pending build had started running', () => {
374             const db = TestServer.database();
375             let syncPromise;
376             return MockData.addMockData(db, ['scheduled', 'pending', 'pending', 'pending']).then(() => {
377                 return Manifest.fetch();
378             }).then(() => {
379                 let config = MockData.mockTestSyncConfigWithSingleBuilder();
380                 let logger = new MockLogger;
381                 let slaveInfo = {name: 'sync-slave', password: 'password'};
382                 let triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
383                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
384                 assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURL(), MockData.mockBuildbotBuilders());
385                 MockRemoteAPI.reset();
386                 return MockRemoteAPI.waitForRequest();
387             }).then(() => {
388                 assert.equal(MockRemoteAPI.requests.length, 1);
389                 assert.equal(MockRemoteAPI.requests[0].method, 'GET');
390                 assert.equal(MockRemoteAPI.requests[0].url, MockData.pendingBuildsUrl('some-builder-1'));
391                 MockRemoteAPI.requests[0].resolve(MockData.pendingBuild({buildRequestId: 700, buildbotBuildRequestId: 17}));
392                 return MockRemoteAPI.waitForRequest();
393             }).then(() => {
394                 assert.equal(MockRemoteAPI.requests.length, 2);
395                 assert.equal(MockRemoteAPI.requests[1].method, 'GET');
396                 assert.equal(MockRemoteAPI.requests[1].url, MockData.recentBuildsUrl('some-builder-1', 2));
397                 MockRemoteAPI.requests[1].resolve({});
398                 return MockRemoteAPI.waitForRequest();
399             }).then(() => {
400                 assert.equal(MockRemoteAPI.requests.length, 3);
401                 assert.equal(MockRemoteAPI.requests[2].method, 'GET');
402                 assert.equal(MockRemoteAPI.requests[2].url, MockData.pendingBuildsUrl('some-builder-1'));
403                 MockRemoteAPI.requests[2].resolve({});
404                 return MockRemoteAPI.waitForRequest();
405             }).then(() => {
406                 assert.equal(MockRemoteAPI.requests.length, 4);
407                 assert.equal(MockRemoteAPI.requests[3].method, 'GET');
408                 assert.equal(MockRemoteAPI.requests[3].url, MockData.recentBuildsUrl('some-builder-1', 2));
409                 MockRemoteAPI.requests[3].resolve(MockData.runningBuild({buildRequestId: 700}));
410                 return syncPromise;
411             }).then(() => {
412                 assert.equal(BuildRequest.all().length, 4);
413                 assert.equal(BuildRequest.findById(700).status(), 'scheduled');
414                 assert.equal(BuildRequest.findById(700).statusUrl(), null);
415                 assert.equal(BuildRequest.findById(701).status(), 'pending');
416                 assert.equal(BuildRequest.findById(701).statusUrl(), null);
417                 assert.equal(BuildRequest.findById(702).status(), 'pending');
418                 assert.equal(BuildRequest.findById(702).statusUrl(), null);
419                 assert.equal(BuildRequest.findById(703).status(), 'pending');
420                 assert.equal(BuildRequest.findById(703).statusUrl(), null);
421                 return BuildRequest.fetchForTriggerable(MockData.mockTestSyncConfigWithTwoBuilders().triggerableName);
422             }).then(() => {
423                 assert.equal(BuildRequest.all().length, 4);
424                 assert.equal(BuildRequest.findById(700).status(), 'running');
425                 assert.equal(BuildRequest.findById(700).statusUrl(), MockData.statusUrl('some-builder-1', 124));
426                 assert.equal(BuildRequest.findById(701).status(), 'pending');
427                 assert.equal(BuildRequest.findById(701).statusUrl(), null);
428                 assert.equal(BuildRequest.findById(702).status(), 'pending');
429                 assert.equal(BuildRequest.findById(702).statusUrl(), null);
430                 assert.equal(BuildRequest.findById(703).status(), 'pending');
431                 assert.equal(BuildRequest.findById(703).statusUrl(), null);
432             });
433         });
434
435         it('should schedule a build request on a builder without pending builds if the request belongs to a new test group', () => {
436             const db = TestServer.database();
437             let syncPromise;
438             return Promise.all([
439                 MockData.addMockData(db, ['completed', 'pending', 'pending', 'pending']),
440                 MockData.addAnotherMockTestGroup(db, ['pending', 'pending', 'pending', 'pending'])
441             ]).then(() => {
442                 return Manifest.fetch();
443             }).then(() => {
444                 const config = MockData.mockTestSyncConfigWithTwoBuilders();
445                 const logger = new MockLogger;
446                 const slaveInfo = {name: 'sync-slave', password: 'password'};
447                 const triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
448                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
449                 assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURL(), MockData.mockBuildbotBuilders());
450                 MockRemoteAPI.reset();
451                 return MockRemoteAPI.waitForRequest();
452             }).then(() => {
453                 assert.equal(MockRemoteAPI.requests.length, 2);
454                 assert.equal(MockRemoteAPI.requests[0].method, 'GET');
455                 assert.equal(MockRemoteAPI.requests[0].url, MockData.pendingBuildsUrl('some-builder-1'));
456                 MockRemoteAPI.requests[0].resolve(MockData.pendingBuild({buildRequestId: 702, buildbotBuildRequestId: 17}));
457                 assert.equal(MockRemoteAPI.requests[1].method, 'GET');
458                 assert.equal(MockRemoteAPI.requests[1].url, MockData.pendingBuildsUrl('some builder 2'));
459                 MockRemoteAPI.requests[1].resolve({});
460                 return MockRemoteAPI.waitForRequest();
461             }).then(() => {
462                 assert.equal(MockRemoteAPI.requests.length, 4);
463                 assert.equal(MockRemoteAPI.requests[2].method, 'GET');
464                 assert.equal(MockRemoteAPI.requests[2].url, MockData.recentBuildsUrl('some-builder-1', 2));
465                 MockRemoteAPI.requests[2].resolve({'builds' : [MockData.runningBuildData({buildRequestId: 701}), MockData.finishedBuildData({buildRequestId: 700})]});
466                 assert.equal(MockRemoteAPI.requests[3].method, 'GET');
467                 assert.equal(MockRemoteAPI.requests[3].url, MockData.recentBuildsUrl('some builder 2', 2));
468                 MockRemoteAPI.requests[3].resolve({});
469                 return MockRemoteAPI.waitForRequest();
470             }).then(() => {
471                 assert.equal(MockRemoteAPI.requests.length, 5);
472                 assert.equal(MockRemoteAPI.requests[4].method, 'POST');
473                 assert.equal(MockRemoteAPI.requests[4].url, '/api/v2/forceschedulers/force-some-builder-2');
474                 assert.deepEqual(MockRemoteAPI.requests[4].data, {'id': '710', 'jsonrpc': '2.0', 'method': 'force', 'params':
475                     {'wk': '191622', 'os': '10.11 15A284', 'build-request-id': '710', 'forcescheduler': 'force-some-builder-2'}});
476                 MockRemoteAPI.requests[4].resolve('OK');
477                 return MockRemoteAPI.waitForRequest();
478             }).then(() => {
479                 assert.equal(MockRemoteAPI.requests.length, 7);
480                 assert.equal(MockRemoteAPI.requests[5].method, 'GET');
481                 assert.equal(MockRemoteAPI.requests[5].url, MockData.pendingBuildsUrl('some-builder-1'));
482                 MockRemoteAPI.requests[5].resolve(MockData.pendingBuild({buildRequestId: 702, buildbotBuildRequestId: 17}));
483                 assert.equal(MockRemoteAPI.requests[6].method, 'GET');
484                 assert.equal(MockRemoteAPI.requests[6].url, MockData.pendingBuildsUrl('some builder 2'));
485                 MockRemoteAPI.requests[6].resolve(MockData.pendingBuild({builderId: MockData.builderIDForName('some builder 2'), buildRequestId: 710, buildbotBuildRequestId: 17}));
486                 return MockRemoteAPI.waitForRequest();
487             }).then(() => {
488                 assert.equal(MockRemoteAPI.requests.length, 9);
489                 assert.equal(MockRemoteAPI.requests[7].method, 'GET');
490                 assert.equal(MockRemoteAPI.requests[7].url, MockData.recentBuildsUrl('some-builder-1', 2));
491                 MockRemoteAPI.requests[7].resolve({'builds' : [MockData.runningBuildData({buildRequestId: 701}), MockData.finishedBuildData({buildRequestId: 700})]});
492                 assert.equal(MockRemoteAPI.requests[8].method, 'GET');
493                 assert.equal(MockRemoteAPI.requests[8].url, MockData.recentBuildsUrl('some builder 2', 2));
494                 MockRemoteAPI.requests[8].resolve({});
495                 return syncPromise;
496             }).then(() => {
497                 assert.equal(BuildRequest.all().length, 8);
498                 assert.equal(BuildRequest.findById(700).status(), 'completed');
499                 assert.equal(BuildRequest.findById(700).statusUrl(), null);
500                 assert.equal(BuildRequest.findById(701).status(), 'pending');
501                 assert.equal(BuildRequest.findById(701).statusUrl(), null);
502                 assert.equal(BuildRequest.findById(702).status(), 'pending');
503                 assert.equal(BuildRequest.findById(702).statusUrl(), null);
504                 assert.equal(BuildRequest.findById(703).status(), 'pending');
505                 assert.equal(BuildRequest.findById(703).statusUrl(), null);
506                 assert.equal(BuildRequest.findById(710).status(), 'pending');
507                 assert.equal(BuildRequest.findById(710).statusUrl(), null);
508                 assert.equal(BuildRequest.findById(711).status(), 'pending');
509                 assert.equal(BuildRequest.findById(711).statusUrl(), null);
510                 assert.equal(BuildRequest.findById(712).status(), 'pending');
511                 assert.equal(BuildRequest.findById(712).statusUrl(), null);
512                 assert.equal(BuildRequest.findById(713).status(), 'pending');
513                 assert.equal(BuildRequest.findById(713).statusUrl(), null);
514                 return BuildRequest.fetchForTriggerable(MockData.mockTestSyncConfigWithTwoBuilders().triggerableName);
515             }).then(() => {
516                 assert.equal(BuildRequest.all().length, 8);
517                 assert.equal(BuildRequest.findById(700).status(), 'completed');
518                 assert.equal(BuildRequest.findById(700).statusUrl(), MockData.statusUrl('some-builder-1', 123));
519                 assert.equal(BuildRequest.findById(701).status(), 'running');
520                 assert.equal(BuildRequest.findById(701).statusUrl(), MockData.statusUrl('some-builder-1', 124));
521                 assert.equal(BuildRequest.findById(702).status(), 'scheduled');
522                 assert.equal(BuildRequest.findById(702).statusUrl(), 'http://build.webkit.org/#/buildrequests/17');
523                 assert.equal(BuildRequest.findById(703).status(), 'pending');
524                 assert.equal(BuildRequest.findById(703).statusUrl(), null);
525                 assert.equal(BuildRequest.findById(710).status(), 'scheduled');
526                 assert.equal(BuildRequest.findById(710).statusUrl(), 'http://build.webkit.org/#/buildrequests/17');
527                 assert.equal(BuildRequest.findById(711).status(), 'pending');
528                 assert.equal(BuildRequest.findById(711).statusUrl(), null);
529                 assert.equal(BuildRequest.findById(712).status(), 'pending');
530                 assert.equal(BuildRequest.findById(712).statusUrl(), null);
531                 assert.equal(BuildRequest.findById(713).status(), 'pending');
532                 assert.equal(BuildRequest.findById(713).statusUrl(), null);
533             });
534         });
535
536         it('should schedule a build request on the same scheduler the first request had ran', () => {
537             const db = TestServer.database();
538             let syncPromise;
539             return Promise.all([
540                 MockData.addMockData(db, ['running', 'pending', 'pending', 'pending']),
541                 MockData.addAnotherMockTestGroup(db, ['running', 'pending', 'pending', 'pending'])
542             ]).then(() => {
543                 return Manifest.fetch();
544             }).then(() => {
545                 const config = MockData.mockTestSyncConfigWithTwoBuilders();
546                 const logger = new MockLogger;
547                 const slaveInfo = {name: 'sync-slave', password: 'password'};
548                 const triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
549                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
550                 assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURL(), MockData.mockBuildbotBuilders());
551                 MockRemoteAPI.reset();
552                 return MockRemoteAPI.waitForRequest();
553             }).then(() => {
554                 assert.equal(MockRemoteAPI.requests.length, 2);
555                 assert.equal(MockRemoteAPI.requests[0].method, 'GET');
556                 assert.equal(MockRemoteAPI.requests[0].url, MockData.pendingBuildsUrl('some-builder-1'));
557                 MockRemoteAPI.requests[0].resolve({});
558                 assert.equal(MockRemoteAPI.requests[1].method, 'GET');
559                 assert.equal(MockRemoteAPI.requests[1].url, MockData.pendingBuildsUrl('some builder 2'));
560                 MockRemoteAPI.requests[1].resolve({});
561                 return MockRemoteAPI.waitForRequest();
562             }).then(() => {
563                 assert.equal(MockRemoteAPI.requests.length, 4);
564                 assert.equal(MockRemoteAPI.requests[2].method, 'GET');
565                 assert.equal(MockRemoteAPI.requests[2].url, MockData.recentBuildsUrl('some-builder-1', 2));
566                 MockRemoteAPI.requests[2].resolve(MockData.runningBuild({buildRequestId: 710}));
567                 assert.equal(MockRemoteAPI.requests[3].method, 'GET');
568                 assert.equal(MockRemoteAPI.requests[3].url, MockData.recentBuildsUrl('some builder 2', 2));
569                 MockRemoteAPI.requests[3].resolve(MockData.runningBuild({builderId: MockData.builderIDForName('some builder 2'), buildRequestId: 700}));
570                 return MockRemoteAPI.waitForRequest();
571             }).then(() => {
572                 assert.equal(MockRemoteAPI.requests.length, 6);
573                 assert.equal(MockRemoteAPI.requests[4].method, 'POST');
574                 assert.equal(MockRemoteAPI.requests[4].url, '/api/v2/forceschedulers/force-some-builder-2');
575                 assert.deepEqual(MockRemoteAPI.requests[4].data, {'id': '701', 'jsonrpc': '2.0', 'method': 'force', 'params':
576                     {'wk': '192736', 'os': '10.11 15A284', 'build-request-id': '701', 'forcescheduler': 'force-some-builder-2'}});
577                 MockRemoteAPI.requests[4].resolve('OK');
578                 assert.equal(MockRemoteAPI.requests[5].method, 'POST');
579                 assert.equal(MockRemoteAPI.requests[5].url, '/api/v2/forceschedulers/force-some-builder-1');
580                 assert.deepEqual(MockRemoteAPI.requests[5].data, {'id': '711', 'jsonrpc': '2.0', 'method': 'force', 'params':
581                     {'wk': '192736', 'os': '10.11 15A284', 'build-request-id': '711', 'forcescheduler': 'force-some-builder-1'}});
582                 MockRemoteAPI.requests[5].resolve('OK');
583                 return MockRemoteAPI.waitForRequest();
584             }).then(() => {
585                 assert.equal(MockRemoteAPI.requests.length, 8);
586                 assert.equal(MockRemoteAPI.requests[6].method, 'GET');
587                 assert.equal(MockRemoteAPI.requests[6].url, MockData.pendingBuildsUrl('some-builder-1'));
588                 MockRemoteAPI.requests[6].resolve(MockData.pendingBuild({buildRequestId: 711, buildbotBuildRequestId: 17}));
589                 assert.equal(MockRemoteAPI.requests[7].method, 'GET');
590                 assert.equal(MockRemoteAPI.requests[7].url, MockData.pendingBuildsUrl('some builder 2'));
591                 MockRemoteAPI.requests[7].resolve(MockData.pendingBuild({builderId: MockData.builderIDForName('some builder 2'), buildRequestId: 701, buildbotBuildRequestId: 17}));
592                 return MockRemoteAPI.waitForRequest();
593             }).then(() => {
594                 assert.equal(MockRemoteAPI.requests.length, 10);
595                 assert.equal(MockRemoteAPI.requests[8].method, 'GET');
596                 assert.equal(MockRemoteAPI.requests[8].url, MockData.recentBuildsUrl('some-builder-1', 2));
597                 MockRemoteAPI.requests[8].resolve(MockData.runningBuild({buildRequestId: 710}));
598                 assert.equal(MockRemoteAPI.requests[9].method, 'GET');
599                 assert.equal(MockRemoteAPI.requests[9].url, MockData.recentBuildsUrl('some builder 2', 2));
600                 MockRemoteAPI.requests[9].resolve(MockData.runningBuild({builderId: MockData.builderIDForName('some builder 2'), buildRequestId: 700}));
601                 return syncPromise;
602             }).then(() => {
603                 assert.equal(BuildRequest.all().length, 8);
604                 assert.equal(BuildRequest.findById(700).status(), 'running');
605                 assert.equal(BuildRequest.findById(700).statusUrl(), null);
606                 assert.equal(BuildRequest.findById(701).status(), 'pending');
607                 assert.equal(BuildRequest.findById(701).statusUrl(), null);
608                 assert.equal(BuildRequest.findById(702).status(), 'pending');
609                 assert.equal(BuildRequest.findById(702).statusUrl(), null);
610                 assert.equal(BuildRequest.findById(703).status(), 'pending');
611                 assert.equal(BuildRequest.findById(703).statusUrl(), null);
612                 assert.equal(BuildRequest.findById(710).status(), 'running');
613                 assert.equal(BuildRequest.findById(710).statusUrl(), null);
614                 assert.equal(BuildRequest.findById(711).status(), 'pending');
615                 assert.equal(BuildRequest.findById(711).statusUrl(), null);
616                 assert.equal(BuildRequest.findById(712).status(), 'pending');
617                 assert.equal(BuildRequest.findById(712).statusUrl(), null);
618                 assert.equal(BuildRequest.findById(713).status(), 'pending');
619                 assert.equal(BuildRequest.findById(713).statusUrl(), null);
620                 return BuildRequest.fetchForTriggerable(MockData.mockTestSyncConfigWithTwoBuilders().triggerableName);
621             }).then(() => {
622                 assert.equal(BuildRequest.all().length, 8);
623                 assert.equal(BuildRequest.findById(700).status(), 'running');
624                 assert.equal(BuildRequest.findById(700).statusUrl(), MockData.statusUrl('some builder 2', 124));
625                 assert.equal(BuildRequest.findById(701).status(), 'scheduled');
626                 assert.equal(BuildRequest.findById(701).statusUrl(), 'http://build.webkit.org/#/buildrequests/17');
627                 assert.equal(BuildRequest.findById(702).status(), 'pending');
628                 assert.equal(BuildRequest.findById(702).statusUrl(), null);
629                 assert.equal(BuildRequest.findById(703).status(), 'pending');
630                 assert.equal(BuildRequest.findById(703).statusUrl(), null);
631                 assert.equal(BuildRequest.findById(710).status(), 'running');
632                 assert.equal(BuildRequest.findById(710).statusUrl(), MockData.statusUrl('some-builder-1', 124));
633                 assert.equal(BuildRequest.findById(711).status(), 'scheduled');
634                 assert.equal(BuildRequest.findById(711).statusUrl(), 'http://build.webkit.org/#/buildrequests/17');
635                 assert.equal(BuildRequest.findById(712).status(), 'pending');
636                 assert.equal(BuildRequest.findById(712).statusUrl(), null);
637                 assert.equal(BuildRequest.findById(713).status(), 'pending');
638                 assert.equal(BuildRequest.findById(713).statusUrl(), null);
639             });
640         });
641
642         it('should wait for POST to complete before trying to poll buildbot again', () => {
643             const db = TestServer.database();
644             const requests = MockRemoteAPI.requests;
645             let syncPromise;
646             return Promise.all([
647                 MockData.addMockData(db, ['pending', 'pending', 'pending', 'pending']),
648                 MockData.addAnotherMockTestGroup(db, ['pending', 'pending', 'pending', 'pending'])
649             ]).then(() => Manifest.fetch()).then(() => {
650                 const config = MockData.mockTestSyncConfigWithSingleBuilder();
651                 const logger = new MockLogger;
652                 const slaveInfo = {name: 'sync-slave', password: 'password'};
653                 const triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
654                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
655                 assertRequestAndResolve(requests[0], 'GET', MockData.buildbotBuildersURL(), MockData.mockBuildbotBuilders());
656                 MockRemoteAPI.reset();
657                 return MockRemoteAPI.waitForRequest();
658             }).then(() => {
659                 assert.equal(requests.length, 1);
660                 assert.equal(requests[0].method, 'GET');
661                 assert.equal(requests[0].url, MockData.pendingBuildsUrl('some-builder-1'));
662                 MockRemoteAPI.requests[0].resolve({});
663                 return MockRemoteAPI.waitForRequest();
664             }).then(() => {
665                 assert.equal(requests.length, 2);
666                 assert.equal(requests[1].method, 'GET');
667                 assert.equal(requests[1].url, MockData.recentBuildsUrl('some-builder-1', 2));
668                 requests[1].resolve({});
669                 return MockRemoteAPI.waitForRequest();
670             }).then(() => {
671                 assert.equal(requests.length, 3);
672                 assert.equal(requests[2].method, 'POST');
673                 assert.equal(requests[2].url, '/api/v2/forceschedulers/force-some-builder-1');
674                 assert.deepEqual(requests[2].data, {'id': '700', 'jsonrpc': '2.0', 'method': 'force', 'params':
675                     {'wk': '191622', 'os': '10.11 15A284', 'build-request-id': '700', 'forcescheduler': 'force-some-builder-1'}});
676                 return new Promise((resolve) => setTimeout(resolve, 10));
677             }).then(() => {
678                 assert.equal(requests.length, 3);
679                 requests[2].resolve('OK');
680                 return MockRemoteAPI.waitForRequest();
681             }).then(() => {
682                 assert.equal(requests.length, 4);
683                 assert.equal(requests[3].method, 'GET');
684                 assert.equal(requests[3].url, MockData.pendingBuildsUrl('some-builder-1'));
685                 MockRemoteAPI.requests[3].resolve(MockData.pendingBuild({buildRequestId: 700, buildbotBuildRequestId: 17}));
686                 return MockRemoteAPI.waitForRequest();
687             }).then(() => {
688                 assert.equal(requests.length, 5);
689                 assert.equal(requests[4].method, 'GET');
690                 assert.equal(requests[4].url, MockData.recentBuildsUrl('some-builder-1', 2));
691                 requests[4].resolve({});
692                 return syncPromise;
693             }).then(() => {
694                 return BuildRequest.fetchForTriggerable(MockData.mockTestSyncConfigWithTwoBuilders().triggerableName);
695             }).then(() => {
696                 assert.equal(BuildRequest.all().length, 8);
697                 assert.equal(BuildRequest.findById(700).status(), 'scheduled');
698                 assert.equal(BuildRequest.findById(700).statusUrl(), 'http://build.webkit.org/#/buildrequests/17');
699                 assert.equal(BuildRequest.findById(701).status(), 'pending');
700                 assert.equal(BuildRequest.findById(701).statusUrl(), null);
701                 assert.equal(BuildRequest.findById(702).status(), 'pending');
702                 assert.equal(BuildRequest.findById(702).statusUrl(), null);
703                 assert.equal(BuildRequest.findById(703).status(), 'pending');
704                 assert.equal(BuildRequest.findById(703).statusUrl(), null);
705                 assert.equal(BuildRequest.findById(710).status(), 'pending');
706                 assert.equal(BuildRequest.findById(710).statusUrl(), null);
707                 assert.equal(BuildRequest.findById(711).status(), 'pending');
708                 assert.equal(BuildRequest.findById(711).statusUrl(), null);
709                 assert.equal(BuildRequest.findById(712).status(), 'pending');
710                 assert.equal(BuildRequest.findById(712).statusUrl(), null);
711                 assert.equal(BuildRequest.findById(713).status(), 'pending');
712                 assert.equal(BuildRequest.findById(713).statusUrl(), null);
713             });
714         });
715
716         it('should recover from multiple test groups running simultenously', () => {
717             const db = TestServer.database();
718             const requests = MockRemoteAPI.requests;
719
720             let syncPromise;
721             let triggerable;
722             return Promise.all([
723                 MockData.addMockData(db, ['completed', 'pending', 'pending', 'pending']),
724                 MockData.addAnotherMockTestGroup(db, ['completed', 'pending', 'pending', 'pending'])
725             ]).then(() => {
726                 return Manifest.fetch();
727             }).then(() => {
728                 const config = MockData.mockTestSyncConfigWithSingleBuilder();
729                 const logger = new MockLogger;
730                 const slaveInfo = {name: 'sync-slave', password: 'password'};
731                 triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
732                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
733                 assertRequestAndResolve(requests[0], 'GET', MockData.buildbotBuildersURL(), MockData.mockBuildbotBuilders());
734                 MockRemoteAPI.reset();
735                 return MockRemoteAPI.waitForRequest();
736             }).then(() => {
737                 assert.equal(requests.length, 1);
738                 assertRequestAndResolve(requests[0], 'GET', MockData.pendingBuildsUrl('some-builder-1'), {});
739                 return MockRemoteAPI.waitForRequest();
740             }).then(() => {
741                 assert.equal(requests.length, 2);
742                 assertRequestAndResolve(requests[1], 'GET', MockData.recentBuildsUrl('some-builder-1', 2),
743                     {'builds' : [MockData.runningBuildData({buildRequestId: 700}), MockData.finishedBuildData({buildRequestId: 710})]});
744                 return MockRemoteAPI.waitForRequest();
745             }).then(() => {
746                 assert.equal(requests.length, 3);
747                 assertRequestAndResolve(requests[2], 'POST', '/api/v2/forceschedulers/force-some-builder-1');
748                 assert.deepEqual(MockRemoteAPI.requests[2].data, {'id': '701', 'jsonrpc': '2.0', 'method': 'force', 'params':
749                     {'wk': '192736', 'os': '10.11 15A284', 'build-request-id': '701', 'forcescheduler': 'force-some-builder-1'}});
750                 return MockRemoteAPI.waitForRequest();
751             }).then(() => {
752                 assert.equal(requests.length, 4);
753                 assertRequestAndResolve(requests[3], 'GET', MockData.pendingBuildsUrl('some-builder-1'),
754                     MockData.pendingBuild({buildRequestId: 701, buildbotBuildRequestId: 17}));
755                 return MockRemoteAPI.waitForRequest();
756             }).then(() => {
757                 assert.equal(requests.length, 5);
758                 assertRequestAndResolve(requests[4], 'GET', MockData.recentBuildsUrl('some-builder-1', 2),
759                     {'builds': [MockData.runningBuildData({buildRequestId: 700}), MockData.finishedBuildData({buildRequestId: 710})]});
760                 return syncPromise;
761             }).then(() => {
762                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
763                 assertRequestAndResolve(requests[5], 'GET', MockData.buildbotBuildersURL(), MockData.mockBuildbotBuilders());
764                 return MockRemoteAPI.waitForRequest();
765             }).then(() => {
766                 assert.equal(requests.length, 7);
767                 assertRequestAndResolve(requests[6], 'GET', MockData.pendingBuildsUrl('some-builder-1'), {});
768                 return MockRemoteAPI.waitForRequest();
769             }).then(() => {
770                 assert.equal(requests.length, 8);
771                 assertRequestAndResolve(requests[7], 'GET', MockData.recentBuildsUrl('some-builder-1', 2),
772                     {'builds': [MockData.runningBuildData({buildRequestId: 701}), MockData.runningBuildData({buildRequestId: 700})]});
773                 return MockRemoteAPI.waitForRequest();
774             }).then(() => {
775                 assert.equal(requests.length, 9);
776                 assertRequestAndResolve(requests[8], 'GET', MockData.pendingBuildsUrl('some-builder-1'), {});
777                 return MockRemoteAPI.waitForRequest();
778             }).then(() => {
779                 assert.equal(requests.length, 10);
780                 assertRequestAndResolve(requests[9], 'GET', MockData.recentBuildsUrl('some-builder-1', 2),
781                     {'builds': [MockData.runningBuildData({buildRequestId: 701}), MockData.runningBuildData({buildRequestId: 700})]});
782                 return syncPromise;
783             });
784         });
785
786         it('should recover from missing failed build request', () => {
787             const db = TestServer.database();
788             let syncPromise;
789             return MockData.addMockData(db, ['failed', 'pending', 'pending', 'pending']).then(() => {
790                 return Manifest.fetch();
791             }).then(() => {
792                 const config = MockData.mockTestSyncConfigWithSingleBuilder();
793                 const logger = new MockLogger;
794                 const slaveInfo = {name: 'sync-slave', password: 'password'};
795                 const triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
796                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
797                 assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURL(), MockData.mockBuildbotBuilders());
798                 MockRemoteAPI.reset();
799                 return MockRemoteAPI.waitForRequest();
800             }).then(() => {
801                 assert.equal(MockRemoteAPI.requests.length, 1);
802                 assert.equal(MockRemoteAPI.requests[0].method, 'GET');
803                 assert.equal(MockRemoteAPI.requests[0].url, MockData.pendingBuildsUrl('some-builder-1'));
804                 MockRemoteAPI.requests[0].resolve({});
805                 return MockRemoteAPI.waitForRequest();
806             }).then(() => {
807                 assert.equal(MockRemoteAPI.requests.length, 2);
808                 assert.equal(MockRemoteAPI.requests[1].method, 'GET');
809                 assert.equal(MockRemoteAPI.requests[1].url, MockData.recentBuildsUrl('some-builder-1', 2));
810                 MockRemoteAPI.requests[1].resolve({});
811                 return MockRemoteAPI.waitForRequest();
812             }).then(() => {
813                 assert.equal(MockRemoteAPI.requests.length, 3);
814                 assert.equal(MockRemoteAPI.requests[2].method, 'POST');
815                 assert.equal(MockRemoteAPI.requests[2].url, '/api/v2/forceschedulers/force-some-builder-1');
816                 assert.deepEqual(MockRemoteAPI.requests[2].data, {'id': '701', 'jsonrpc': '2.0', 'method': 'force', 'params':
817                     {'wk': '192736', 'os': '10.11 15A284', 'build-request-id': '701', 'forcescheduler': 'force-some-builder-1'}});
818                 MockRemoteAPI.requests[2].resolve('OK');
819                 return MockRemoteAPI.waitForRequest();
820             }).then(() => {
821                 assert.equal(MockRemoteAPI.requests.length, 4);
822                 assert.equal(MockRemoteAPI.requests[3].method, 'GET');
823                 assert.equal(MockRemoteAPI.requests[3].url, MockData.pendingBuildsUrl('some-builder-1'));
824                 MockRemoteAPI.requests[3].resolve(MockData.pendingBuild({buildRequestId: 701, buildbotBuildRequestId: 17}));
825                 return MockRemoteAPI.waitForRequest();
826             }).then(() => {
827                 assert.equal(MockRemoteAPI.requests.length, 5);
828                 assert.equal(MockRemoteAPI.requests[4].method, 'GET');
829                 assert.equal(MockRemoteAPI.requests[4].url, MockData.recentBuildsUrl('some-builder-1', 2));
830                 MockRemoteAPI.requests[4].resolve({});
831                 return syncPromise;
832             }).then(() => {
833                 assert.equal(BuildRequest.all().length, 4);
834                 assert.equal(BuildRequest.findById(700).status(), 'failed');
835                 assert.equal(BuildRequest.findById(700).statusUrl(), null);
836                 assert.equal(BuildRequest.findById(701).status(), 'pending');
837                 assert.equal(BuildRequest.findById(701).statusUrl(), null);
838                 assert.equal(BuildRequest.findById(702).status(), 'pending');
839                 assert.equal(BuildRequest.findById(702).statusUrl(), null);
840                 assert.equal(BuildRequest.findById(703).status(), 'pending');
841                 assert.equal(BuildRequest.findById(703).statusUrl(), null);
842                 return BuildRequest.fetchForTriggerable(MockData.mockTestSyncConfigWithTwoBuilders().triggerableName);
843             }).then(() => {
844                 assert.equal(BuildRequest.all().length, 4);
845                 assert.equal(BuildRequest.findById(700).status(), 'failed');
846                 assert.equal(BuildRequest.findById(700).statusUrl(), null);
847                 assert.equal(BuildRequest.findById(701).status(), 'scheduled');
848                 assert.equal(BuildRequest.findById(701).statusUrl(), 'http://build.webkit.org/#/buildrequests/17');
849                 assert.equal(BuildRequest.findById(702).status(), 'pending');
850                 assert.equal(BuildRequest.findById(702).statusUrl(), null);
851                 assert.equal(BuildRequest.findById(703).status(), 'pending');
852                 assert.equal(BuildRequest.findById(703).statusUrl(), null);
853             });
854         });
855
856         it('should update the status of a supposedly scheduled build that went missing', () => {
857             const db = TestServer.database();
858             let syncPromise;
859             return MockData.addMockData(db, ['scheduled', 'pending', 'pending', 'pending']).then(() => {
860                 return Manifest.fetch();
861             }).then(() => {
862                 const config = MockData.mockTestSyncConfigWithSingleBuilder();
863                 const logger = new MockLogger;
864                 const slaveInfo = {name: 'sync-slave', password: 'password'};
865                 const triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
866                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
867                 assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURL(), MockData.mockBuildbotBuilders());
868                 MockRemoteAPI.reset();
869                 return MockRemoteAPI.waitForRequest();
870             }).then(() => {
871                 assert.equal(MockRemoteAPI.requests.length, 1);
872                 assert.equal(MockRemoteAPI.requests[0].method, 'GET');
873                 assert.equal(MockRemoteAPI.requests[0].url, MockData.pendingBuildsUrl('some-builder-1'));
874                 MockRemoteAPI.requests[0].resolve({});
875                 return MockRemoteAPI.waitForRequest();
876             }).then(() => {
877                 assert.equal(MockRemoteAPI.requests.length, 2);
878                 assert.equal(MockRemoteAPI.requests[1].method, 'GET');
879                 assert.equal(MockRemoteAPI.requests[1].url, MockData.recentBuildsUrl('some-builder-1', 2));
880                 MockRemoteAPI.requests[1].resolve({});
881                 return MockRemoteAPI.waitForRequest();
882             }).then(() => {
883                 assert.equal(MockRemoteAPI.requests.length, 3);
884                 assert.equal(MockRemoteAPI.requests[2].method, 'GET');
885                 assert.equal(MockRemoteAPI.requests[2].url, MockData.pendingBuildsUrl('some-builder-1'));
886                 MockRemoteAPI.requests[2].resolve({});
887                 return MockRemoteAPI.waitForRequest();
888             }).then(() => {
889                 assert.equal(MockRemoteAPI.requests.length, 4);
890                 assert.equal(MockRemoteAPI.requests[3].method, 'GET');
891                 assert.equal(MockRemoteAPI.requests[3].url, MockData.recentBuildsUrl('some-builder-1', 2));
892                 MockRemoteAPI.requests[3].resolve({});
893                 return syncPromise;
894             }).then(() => {
895                 assert.equal(BuildRequest.all().length, 4);
896                 assert.equal(BuildRequest.findById(700).status(), 'scheduled');
897                 assert.equal(BuildRequest.findById(701).status(), 'pending');
898                 assert.equal(BuildRequest.findById(702).status(), 'pending');
899                 assert.equal(BuildRequest.findById(703).status(), 'pending');
900                 return BuildRequest.fetchForTriggerable(MockData.mockTestSyncConfigWithTwoBuilders().triggerableName);
901             }).then(() => {
902                 assert.equal(BuildRequest.all().length, 4);
903                 assert.equal(BuildRequest.findById(700).status(), 'failed');
904                 assert.equal(BuildRequest.findById(701).status(), 'pending');
905                 assert.equal(BuildRequest.findById(702).status(), 'pending');
906                 assert.equal(BuildRequest.findById(703).status(), 'pending');
907             });
908         });
909
910         it('should schedule a build request of an user created test group before ones created by automatic change detection', () => {
911             const db = TestServer.database();
912             let syncPromise;
913             return Promise.all([
914                 MockData.addMockData(db, ['pending', 'pending', 'pending', 'pending']),
915                 MockData.addAnotherMockTestGroup(db, ['pending', 'pending', 'pending', 'pending'], 'rniwa'),
916             ]).then(() => {
917                 return Manifest.fetch();
918             }).then(() => {
919                 const config = MockData.mockTestSyncConfigWithSingleBuilder();
920                 const logger = new MockLogger;
921                 const slaveInfo = {name: 'sync-slave', password: 'password'};
922                 const triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
923                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
924                 assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURL(), MockData.mockBuildbotBuilders());
925                 MockRemoteAPI.reset();
926                 return MockRemoteAPI.waitForRequest();
927             }).then(() => {
928                 assert.equal(MockRemoteAPI.requests.length, 1);
929                 assert.equal(MockRemoteAPI.requests[0].method, 'GET');
930                 assert.equal(MockRemoteAPI.requests[0].url, MockData.pendingBuildsUrl('some-builder-1'));
931                 MockRemoteAPI.requests[0].resolve({});
932                 return MockRemoteAPI.waitForRequest();
933             }).then(() => {
934                 assert.equal(MockRemoteAPI.requests.length, 2);
935                 assert.equal(MockRemoteAPI.requests[1].method, 'GET');
936                 assert.equal(MockRemoteAPI.requests[1].url, MockData.recentBuildsUrl('some-builder-1', 2));
937                 MockRemoteAPI.requests[1].resolve({});
938                 return MockRemoteAPI.waitForRequest();
939             }).then(() => {
940                 assert.equal(MockRemoteAPI.requests.length, 3);
941                 assert.equal(MockRemoteAPI.requests[2].method, 'POST');
942                 assert.equal(MockRemoteAPI.requests[2].url, '/api/v2/forceschedulers/force-some-builder-1');
943
944                 assert.deepEqual(MockRemoteAPI.requests[2].data, {'id': '710', 'jsonrpc': '2.0', 'method': 'force', 'params':
945                     {'wk': '191622', 'os': '10.11 15A284', 'build-request-id': '710', 'forcescheduler': 'force-some-builder-1'}});
946                 MockRemoteAPI.requests[2].resolve('OK');
947             });
948         });
949     });
950
951     describe('updateTriggerables', () => {
952
953         function refetchManifest()
954         {
955             MockData.resetV3Models();
956             return TestServer.remoteAPI().getJSON('/api/manifest').then((content) => Manifest._didFetchManifest(content));
957         }
958
959         it('should update available triggerables', () => {
960             const db = TestServer.database();
961             let macos;
962             let webkit;
963             return MockData.addMockData(db).then(() => {
964                 return Manifest.fetch();
965             }).then(() => {
966                 macos = Repository.findById(9);
967                 assert.equal(macos.name(), 'macOS');
968                 webkit = Repository.findById(11);
969                 assert.equal(webkit.name(), 'WebKit');
970                 assert.equal(Triggerable.all().length, 1);
971
972                 const triggerable = Triggerable.all()[0];
973                 assert.equal(triggerable.name(), 'build-webkit');
974
975                 const test = Test.findById(MockData.someTestId());
976                 const platform = Platform.findById(MockData.somePlatformId());
977                 assert.equal(Triggerable.findByTestConfiguration(test, platform), null);
978
979                 const groups = TriggerableRepositoryGroup.sortByName(triggerable.repositoryGroups());
980                 assert.equal(groups.length, 1);
981                 assert.equal(groups[0].name(), 'webkit-svn');
982                 assert.deepEqual(groups[0].repositories(), [webkit, macos]);
983
984                 const config = MockData.mockTestSyncConfigWithSingleBuilder();
985                 config.repositoryGroups = {
986                     'system-and-roots': {
987                         description: 'Custom Roots',
988                         repositories: {'macOS': {}},
989                         testProperties: {
990                             'os': {'revision': 'macOS'},
991                             'roots': {'roots': {}}
992                         },
993                         acceptsRoots: true
994                     },
995                     'system-and-webkit': {
996                         repositories: {'WebKit': {'acceptsPatch': true}, 'macOS': {}},
997                         testProperties: {
998                             'os': {'revision': 'macOS'},
999                             'wk': {'revision': 'WebKit'},
1000                             'roots': {'roots': {}},
1001                         },
1002                         buildProperties: {
1003                             'wk': {'revision': 'WebKit'},
1004                             'wk-patch': {'patch': 'WebKit'},
1005                         },
1006                         acceptsRoots: true
1007                     }
1008                 }
1009
1010                 const logger = new MockLogger;
1011                 const slaveInfo = {name: 'sync-slave', password: 'password'};
1012                 const buildbotTriggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
1013                 const triggerablePromise = buildbotTriggerable.initSyncers().then(() => buildbotTriggerable.updateTriggerable());
1014                 assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURL(), MockData.mockBuildbotBuilders());
1015                 return triggerablePromise;
1016             }).then(() => refetchManifest()).then(() => {
1017                 assert.equal(Triggerable.all().length, 1);
1018
1019                 let test = Test.findById(MockData.someTestId());
1020                 let platform = Platform.findById(MockData.somePlatformId());
1021                 let triggerable = Triggerable.findByTestConfiguration(test, platform);
1022                 assert.equal(triggerable.name(), 'build-webkit');
1023
1024                 const groups = TriggerableRepositoryGroup.sortByName(triggerable.repositoryGroups());
1025                 assert.equal(groups.length, 2);
1026                 assert.equal(groups[0].name(), 'system-and-roots');
1027                 assert.equal(groups[0].description(), 'Custom Roots');
1028                 assert.deepEqual(groups[0].repositories(), [macos]);
1029                 assert.equal(groups[0].acceptsCustomRoots(), true);
1030                 assert.equal(groups[1].name(), 'system-and-webkit');
1031                 assert.deepEqual(groups[1].repositories(), [webkit, macos]);
1032                 assert.equal(groups[1].acceptsCustomRoots(), true);
1033
1034                 const config = MockData.mockTestSyncConfigWithSingleBuilder();
1035                 config.repositoryGroups = [ ];
1036
1037                 const logger = new MockLogger;
1038                 const slaveInfo = {name: 'sync-slave', password: 'password'};
1039                 const buildbotTriggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
1040                 const triggerablePromise = buildbotTriggerable.initSyncers().then(() => buildbotTriggerable.updateTriggerable());
1041                 assertRequestAndResolve(MockRemoteAPI.requests[1], 'GET', MockData.buildbotBuildersURL(), MockData.mockBuildbotBuilders());
1042                 return triggerablePromise;
1043             }).then(() => refetchManifest()).then(() => {
1044                 assert.equal(Triggerable.all().length, 1);
1045                 const groups = TriggerableRepositoryGroup.sortByName(Triggerable.all()[0].repositoryGroups());
1046                 assert.equal(groups.length, 2);
1047                 assert.equal(groups[0].name(), 'system-and-roots');
1048                 assert.deepEqual(groups[0].repositories(), [macos]);
1049                 assert.equal(groups[1].name(), 'system-and-webkit');
1050                 assert.deepEqual(groups[1].repositories(), [webkit, macos]);
1051             })
1052         });
1053     });
1054
1055     describe('getBuilderNameToIDMap', () => {
1056
1057         it('should get Builder Name to ID Map', () => {
1058             const config = MockData.mockTestSyncConfigWithSingleBuilder();
1059             const logger = new MockLogger;
1060             const slaveInfo = {name: 'sync-slave', password: 'password'};
1061             const buildbotTriggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
1062             const getBuilderNameToIDMapPromise = buildbotTriggerable.getBuilderNameToIDMap();
1063             assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURL(), MockData.mockBuildbotBuilders());
1064             getBuilderNameToIDMapPromise.then((builderNameToIDMap) => {
1065                 assert.equal(builderNameToIDMap["some builder"], 1)
1066                 assert.equal(builderNameToIDMap["some-builder-1"], 2)
1067                 assert.equal(builderNameToIDMap["some builder 2"], 3)
1068                 assert.equal(builderNameToIDMap["other builder"], 4)
1069                 assert.equal(builderNameToIDMap["some tester"], 5)
1070                 assert.equal(builderNameToIDMap["another tester"], 6)
1071             });
1072         });
1073     });
1074 });