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