Rewrite 'pull-os-versions' script in Javascript to add support for reporting os revis...
[WebKit.git] / Websites / perf.webkit.org / server-tests / tools-os-build-fetcher-tests.js
1 'use strict';
2
3 const assert = require('assert');
4
5 const OSBuildFetcher = require('../tools/js/os-build-fetcher.js').OSBuildFetcher;
6 const MockRemoteAPI = require('../unit-tests/resources/mock-remote-api.js').MockRemoteAPI;
7 const TestServer = require('./resources/test-server.js');
8 const addSlaveForReport = require('./resources/common-operations.js').addSlaveForReport;
9 const prepareServerTest = require('./resources/common-operations.js').prepareServerTest;
10 const MockSubprocess = require('./resources/mock-subprocess.js').MockSubprocess;
11 const MockLogger = require('./resources/mock-logger.js').MockLogger;
12
13
14 describe('OSBuildFetcher', function() {
15     prepareServerTest(this);
16
17     beforeEach(function () {
18         MockRemoteAPI.reset('http://build.webkit.org');
19         MockSubprocess.reset();
20     });
21
22     const emptyReport = {
23         'slaveName': 'someSlave',
24         'slavePassword': 'somePassword',
25     };
26
27     const slaveAuth = {
28         'name': 'someSlave',
29         'password': 'somePassword'
30     };
31
32     const subCommitWithWebKit = {
33         'WebKit': {'revision': '141978'}
34     };
35
36     const anotherSubCommitWithWebKit = {
37         'WebKit': {'revision': '141999',}
38     };
39
40     const anotherSubCommitWithWebKitAndJavaScriptCore = {
41         'WebKit': {'revision': '142000'},
42         'JavaScriptCore': {'revision': '142000'}
43     };
44
45     const osxCommit = {
46         'repository': 'OSX',
47         'revision': 'Sierra16D32',
48         'order': 1603003200
49     };
50
51     const anotherOSXCommit = {
52         'repository': 'OSX',
53         'revision': 'Sierra16E32',
54         'order': 1603003200
55     };
56
57
58     const config = {
59         'name': 'OSX',
60         'customCommands': [
61             {
62                 'command': ['list', 'all osx 16Dxx builds'],
63                 'subCommitCommand': ['list', 'subCommit', 'for', 'revision'],
64                 'linesToIgnore': '^\\.*$',
65                 'minRevision': 'Sierra16D0',
66                 'maxRevision': 'Sierra16D999'
67             },
68             {
69                 'command': ['list', 'all osx 16Exx builds'],
70                 'subCommitCommand': ['list', 'subCommit', 'for', 'revision'],
71                 'linesToIgnore': '^\\.*$',
72                 'minRevision': 'Sierra16E0',
73                 'maxRevision': 'Sierra16E999'
74             }
75         ]
76     };
77
78
79     const configWithoutSubCommitCommand = {
80         'name': 'OSX',
81         'customCommands': [
82             {
83                 'command': ['list', 'all osx 16Dxx builds'],
84                 'linesToIgnore': '^\\.*$',
85                 'minRevision': 'Sierra16D0',
86                 'maxRevision': 'Sierra16D999'
87             },
88             {
89                 'command': ['list', 'all osx 16Exx builds'],
90                 'linesToIgnore': '^\\.*$',
91                 'minRevision': 'Sierra16E0',
92                 'maxRevision': 'Sierra16E999'
93             }
94         ]
95     };
96
97     describe('OSBuilderFetcher._computeOrder', () => {
98         it('should calculate the right order for a given valid revision', () => {
99             const fetcher = new OSBuildFetcher();
100             assert.equal(fetcher._computeOrder('Sierra16D32'), 1603003200);
101             assert.equal(fetcher._computeOrder('16D321'), 1603032100);
102             assert.equal(fetcher._computeOrder('16d321'), 1603032100);
103             assert.equal(fetcher._computeOrder('16D321z'), 1603032126);
104             assert.equal(fetcher._computeOrder('16d321Z'), 1603032126);
105         });
106
107         it('should throw assertion error when given a invalid revision', () => {
108             const fetcher = new OSBuildFetcher();
109             assert.throws(() => fetcher._computeOrder('invalid'), (error) => error.name == 'AssertionError');
110             assert.throws(() => fetcher._computeOrder(''), (error) => error.name == 'AssertionError');
111             assert.throws(() => fetcher._computeOrder('16'), (error) => error.name == 'AssertionError');
112             assert.throws(() => fetcher._computeOrder('16D'), (error) => error.name == 'AssertionError');
113             assert.throws(() => fetcher._computeOrder('123'), (error) => error.name == 'AssertionError');
114             assert.throws(() => fetcher._computeOrder('D123'), (error) => error.name == 'AssertionError');
115             assert.throws(() => fetcher._computeOrder('123z'), (error) => error.name == 'AssertionError');
116             assert.throws(() => fetcher._computeOrder('16[163'), (error) => error.name == 'AssertionError');
117             assert.throws(() => fetcher._computeOrder('16D163['), (error) => error.name == 'AssertionError');
118         })
119     });
120
121     describe('OSBuilderFetcher._commitsForAvailableBuilds', () => {
122         it('should only return commits whose orders are higher than specified order', () => {
123             const logger = new MockLogger;
124             const fetchter = new OSBuildFetcher(null, null, null, MockSubprocess, logger);
125             const waitingForInvocationPromise = MockSubprocess.waitingForInvocation();
126             const fetchCommitsPromise = fetchter._commitsForAvailableBuilds('OSX', ['list', 'build1'], '^\\.*$', 1604000000);
127
128             return waitingForInvocationPromise.then(() => {
129                 assert.equal(MockSubprocess.invocations.length, 1);
130                 assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'build1']);
131                 MockSubprocess.invocations[0].resolve('16D321\n16E321z\n\n16F321');
132                 return fetchCommitsPromise;
133             }).then((results) => {
134                 assert.equal(results.length, 2);
135                 assert.equal(results[0]['repository'], 'OSX');
136                 assert.equal(results[0]['revision'], '16E321z');
137                 assert.equal(results[0]['order'], 1604032126);
138                 assert.equal(results[1]['repository'], 'OSX');
139                 assert.equal(results[1]['revision'], '16F321');
140                 assert.equal(results[1]['order'], 1605032100);
141             });
142         });
143     });
144
145     describe('OSBuildFetcher._addSubCommitsForBuild', () => {
146         it('should add sub-commit info for commits', () => {
147             const logger = new MockLogger;
148             const fetchter = new OSBuildFetcher(null, null, null, MockSubprocess, logger);
149             const waitingForInvocationPromise = MockSubprocess.waitingForInvocation();
150             const addSubCommitPromise = fetchter._addSubCommitsForBuild([osxCommit, anotherOSXCommit], ['subCommit', 'for', 'revision']);
151
152             return waitingForInvocationPromise.then(() => {
153                 assert.equal(MockSubprocess.invocations.length, 1);
154                 assert.deepEqual(MockSubprocess.invocations[0].command, ['subCommit', 'for', 'revision', 'Sierra16D32']);
155                 MockSubprocess.invocations[0].resolve(JSON.stringify(subCommitWithWebKit));
156                 MockSubprocess.reset();
157                 return MockSubprocess.waitingForInvocation();
158             }).then(() => {
159                 assert.equal(MockSubprocess.invocations.length, 1);
160                 assert.deepEqual(MockSubprocess.invocations[0].command, ['subCommit', 'for', 'revision', 'Sierra16E32']);
161                 MockSubprocess.invocations[0].resolve(JSON.stringify(anotherSubCommitWithWebKit));
162                 return addSubCommitPromise;
163             }).then((results) => {
164                 assert.equal(results.length, 2);
165                 assert.equal(results[0]['repository'], osxCommit['repository']);
166                 assert.equal(results[0]['revision'], osxCommit['revision']);
167                 assert.deepEqual(results[0]['subCommits'], subCommitWithWebKit);
168                 assert.equal(results[1]['repository'], anotherOSXCommit['repository']);
169                 assert.equal(results[1]['revision'], anotherOSXCommit['revision']);
170                 assert.deepEqual(results[1]['subCommits'], anotherSubCommitWithWebKit);
171             });
172         });
173
174         it('should fail if the command to get sub-commit info fails', () => {
175             const logger = new MockLogger;
176             const fetchter = new OSBuildFetcher(null, null, null, MockSubprocess, logger);
177             const waitingForInvocationPromise = MockSubprocess.waitingForInvocation();
178             const addSubCommitPromise = fetchter._addSubCommitsForBuild([osxCommit], ['subCommit', 'for', 'revision'])
179
180             return waitingForInvocationPromise.then(() => {
181                 assert.equal(MockSubprocess.invocations.length, 1);
182                 assert.deepEqual(MockSubprocess.invocations[0].command, ['subCommit', 'for', 'revision', 'Sierra16D32']);
183                 MockSubprocess.invocations[0].reject('Failed getting sub-commit');
184
185                 return addSubCommitPromise.then(() => {
186                     assert(false, 'should never be reached');
187                 }, (error_output) => {
188                     assert(error_output);
189                     assert.equal(error_output, 'Failed getting sub-commit');
190                 });
191             });
192         });
193
194
195         it('should fail if entries in sub-commits does not contain revision', () => {
196             const logger = new MockLogger;
197             const fetchter = new OSBuildFetcher(null, null, null, MockSubprocess, logger);
198             const waitingForInvocationPromise = MockSubprocess.waitingForInvocation();
199             const addSubCommitPromise = fetchter._addSubCommitsForBuild([osxCommit], ['subCommit', 'for', 'revision'])
200
201             return waitingForInvocationPromise.then(() => {
202                 assert.equal(MockSubprocess.invocations.length, 1);
203                 assert.deepEqual(MockSubprocess.invocations[0].command, ['subCommit', 'for', 'revision', 'Sierra16D32']);
204                 MockSubprocess.invocations[0].resolve('{"WebKit":{"RandomKey": "RandomValue"}}');
205
206                 return addSubCommitPromise.then(() => {
207                     assert(false, 'should never be reached');
208                 }, (error_output) => {
209                     assert(error_output);
210                     assert.equal(error_output.name, 'AssertionError');
211                 });
212             });
213         })
214     })
215
216     describe('OSBuildFetcher.fetchAndReportNewBuilds', () => {
217         it('should report all build commits with sub-commits', () => {
218             const logger = new MockLogger;
219             const fetchter = new OSBuildFetcher(config, TestServer.remoteAPI(), slaveAuth, MockSubprocess, logger);
220             const db = TestServer.database();
221             let fetchAndReportPromise = null;
222             let fetchAvailableBuildsPromise = null;
223
224             return addSlaveForReport(emptyReport).then(() => {
225                 return Promise.all([
226                     db.insert('repositories', {'id': 10, 'name': 'OSX'}),
227                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16D67', 'order': 1603006700, 'reported': true}),
228                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16D68', 'order': 1603006800, 'reported': true}),
229                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16D69', 'order': 1603006900, 'reported': false}),
230                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16E32', 'order': 1604003200, 'reported': true}),
231                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16E33', 'order': 1604003300, 'reported': true}),
232                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16E33g', 'order': 1604003307, 'reported': true})]);
233             }).then(() => {
234                 return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1603000000&to=1603099900');
235             }).then((result) => {
236                 assert.equal(result['commits'].length, 1);
237                 assert.equal(result['commits'][0]['revision'], 'Sierra16D68');
238                 assert.equal(result['commits'][0]['order'], 1603006800);
239                 return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1604000000&to=1604099900');
240             }).then((result) => {
241                 assert.equal(result['commits'].length, 1);
242                 assert.equal(result['commits'][0]['revision'], 'Sierra16E33g');
243                 assert.equal(result['commits'][0]['order'], 1604003307);
244                 const waitingForInvocationPromise = MockSubprocess.waitingForInvocation();
245                 fetchAvailableBuildsPromise = fetchter._fetchAvailableBuilds();
246                 return waitingForInvocationPromise;
247             }).then(() => {
248                 return MockSubprocess.waitingForInvocation();
249             }).then(() => {
250                 MockSubprocess.invocations.sort((invocation, antoherInvocation) => invocation['command'] > antoherInvocation['command']);
251                 assert.equal(MockSubprocess.invocations.length, 2);
252                 assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'all osx 16Dxx builds']);
253                 assert.deepEqual(MockSubprocess.invocations[1].command, ['list', 'all osx 16Exx builds']);
254                 MockSubprocess.invocations[0].resolve('\n\nSierra16D68\nSierra16D69\n');
255                 MockSubprocess.invocations[1].resolve('\n\nSierra16E32\nSierra16E33\nSierra16E33h\nSierra16E34');
256                 MockSubprocess.reset();
257                 return MockSubprocess.waitingForInvocation();
258             }).then(() => {
259                 MockSubprocess.invocations.sort((invocation, antoherInvocation) => invocation['command'] > antoherInvocation['command']);
260                 assert.equal(MockSubprocess.invocations.length, 2);
261                 assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'subCommit', 'for', 'revision', 'Sierra16D69']);
262                 assert.deepEqual(MockSubprocess.invocations[1].command, ['list', 'subCommit', 'for', 'revision', 'Sierra16E33h']);
263
264                 MockSubprocess.invocations[0].resolve(JSON.stringify(subCommitWithWebKit));
265                 MockSubprocess.invocations[1].resolve(JSON.stringify(anotherSubCommitWithWebKit));
266                 MockSubprocess.reset();
267                 return MockSubprocess.waitingForInvocation();
268             }).then(() => {
269                 assert.equal(MockSubprocess.invocations.length, 1);
270                 assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'subCommit', 'for', 'revision', 'Sierra16E34']);
271                 MockSubprocess.invocations[0].resolve(JSON.stringify(anotherSubCommitWithWebKitAndJavaScriptCore));
272                 return fetchAvailableBuildsPromise;
273             }).then((results) => {
274                 assert.equal(results.length, 3);
275                 MockSubprocess.reset();
276                 fetchAndReportPromise = fetchter.fetchAndReportNewBuilds();
277                 return MockSubprocess.waitingForInvocation();
278             }).then(() => {
279                 return MockSubprocess.waitingForInvocation();
280             }).then(() => {
281                 MockSubprocess.invocations.sort((invocation, antoherInvocation) => invocation['command'] > antoherInvocation['command']);
282                 assert.equal(MockSubprocess.invocations.length, 2);
283                 assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'all osx 16Dxx builds']);
284                 assert.deepEqual(MockSubprocess.invocations[1].command, ['list', 'all osx 16Exx builds']);
285                 MockSubprocess.invocations[0].resolve('\n\nSierra16D68\nSierra16D69\n');
286                 MockSubprocess.invocations[1].resolve('\n\nSierra16E32\nSierra16E33\nSierra16E33h\nSierra16E34');
287                 MockSubprocess.reset();
288                 return MockSubprocess.waitingForInvocation();
289             }).then(() => {
290                 MockSubprocess.invocations.sort((invocation, antoherInvocation) => invocation['command'] > antoherInvocation['command']);
291                 assert.equal(MockSubprocess.invocations.length, 2);
292                 assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'subCommit', 'for', 'revision', 'Sierra16D69']);
293                 assert.deepEqual(MockSubprocess.invocations[1].command, ['list', 'subCommit', 'for', 'revision', 'Sierra16E33h']);
294
295                 MockSubprocess.invocations[0].resolve(JSON.stringify(subCommitWithWebKit));
296                 MockSubprocess.invocations[1].resolve(JSON.stringify(anotherSubCommitWithWebKit));
297
298                 MockSubprocess.reset();
299                 return MockSubprocess.waitingForInvocation();
300             }).then(() => {
301                 assert.equal(MockSubprocess.invocations.length, 1);
302                 MockSubprocess.invocations[0].resolve(JSON.stringify(anotherSubCommitWithWebKitAndJavaScriptCore));
303                 assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'subCommit', 'for', 'revision', 'Sierra16E34']);
304
305                 return fetchAndReportPromise;
306             }).then((result) => {
307                 assert.equal(result['status'], 'OK');
308                 return Promise.all([
309                     db.selectRows('repositories', {'name': 'WebKit'}),
310                     db.selectRows('repositories', {'name': 'JavaScriptCore'}),
311                     db.selectRows('commits', {'revision': 'Sierra16D69'}),
312                     db.selectRows('commits', {'revision': 'Sierra16E33h'}),
313                     db.selectRows('commits', {'revision': 'Sierra16E34'})]);
314             }).then((results) => {
315                 const webkitRepository = results[0];
316                 const jscRepository = results[1];
317                 const osxCommit16D69 = results[2];
318                 const osxCommit16E33h = results[3];
319                 const osxCommit16E34 = results[4];
320
321                 assert.equal(webkitRepository.length, 1);
322                 assert.equal(webkitRepository[0]['owner'], 10);
323                 assert.equal(jscRepository.length, 1)
324                 assert.equal(jscRepository[0]['owner'], 10);
325
326                 assert.equal(osxCommit16D69.length, 1);
327                 assert.equal(osxCommit16D69[0]['repository'], 10);
328                 assert.equal(osxCommit16D69[0]['order'], 1603006900);
329
330                 assert.equal(osxCommit16E33h.length, 1);
331                 assert.equal(osxCommit16E33h[0]['repository'], 10);
332                 assert.equal(osxCommit16E33h[0]['order'], 1604003308);
333
334                 assert.equal(osxCommit16E34.length, 1);
335                 assert.equal(osxCommit16E34[0]['repository'], 10);
336                 assert.equal(osxCommit16E34[0]['order'], 1604003400);
337
338                 return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1603000000&to=1603099900');
339             }).then((result) => {
340                 assert.equal(result['commits'].length, 1);
341                 assert.equal(result['commits'][0]['revision'], 'Sierra16D69');
342                 assert.equal(result['commits'][0]['order'], 1603006900);
343
344                 return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1604000000&to=1604099900');
345             }).then((result) => {
346                 assert.equal(result['commits'].length, 1);
347                 assert.equal(result['commits'][0]['revision'], 'Sierra16E34');
348                 assert.equal(result['commits'][0]['order'], 1604003400);
349             });
350         });
351
352         it('should report commits without sub-commits if "subCommitCommand" is not specified in config', () => {
353             const logger = new MockLogger;
354             const fetchter = new OSBuildFetcher(configWithoutSubCommitCommand, TestServer.remoteAPI(), slaveAuth, MockSubprocess, logger);
355             const db = TestServer.database();
356             let fetchAndReportPromise = null;
357             let fetchAvailableBuildsPromise = null;
358
359             return addSlaveForReport(emptyReport).then(() => {
360                 return Promise.all([
361                     db.insert('repositories', {'id': 10, 'name': 'OSX'}),
362                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16D67', 'order': 1603006700, 'reported': true}),
363                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16D68', 'order': 1603006800, 'reported': true}),
364                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16D69', 'order': 1603006900, 'reported': false}),
365                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16E32', 'order': 1604003200, 'reported': true}),
366                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16E33', 'order': 1604003300, 'reported': true}),
367                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16E33g', 'order': 1604003307, 'reported': true})]);
368             }).then(() => {
369                 return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1603000000&to=1603099900');
370             }).then((result) => {
371                 assert.equal(result['commits'].length, 1);
372                 assert.equal(result['commits'][0]['revision'], 'Sierra16D68');
373                 assert.equal(result['commits'][0]['order'], 1603006800);
374
375                 return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1604000000&to=1604099900');
376             }).then((result) => {
377                 assert.equal(result['commits'].length, 1);
378                 assert.equal(result['commits'][0]['revision'], 'Sierra16E33g');
379                 assert.equal(result['commits'][0]['order'], 1604003307);
380                 const waitingForInvocationPromise = MockSubprocess.waitingForInvocation();
381                 fetchAndReportPromise = fetchter.fetchAndReportNewBuilds();
382                 return waitingForInvocationPromise;
383             }).then(() => {
384                 return MockSubprocess.waitingForInvocation();
385             }).then(() => {
386                 MockSubprocess.invocations.sort((invocation, antoherInvocation) => invocation['command'] > antoherInvocation['command']);
387                 assert.equal(MockSubprocess.invocations.length, 2);
388                 assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'all osx 16Dxx builds']);
389                 assert.deepEqual(MockSubprocess.invocations[1].command, ['list', 'all osx 16Exx builds']);
390                 MockSubprocess.invocations[0].resolve('\n\nSierra16D68\nSierra16D69\n');
391                 MockSubprocess.invocations[1].resolve('\n\nSierra16E32\nSierra16E33\nSierra16E33h\nSierra16E34');
392                 return fetchAndReportPromise;
393             }).then((result) => {
394                 assert.equal(result['status'], 'OK');
395                 return Promise.all([
396                     db.selectRows('repositories', {'name': 'WebKit'}),
397                     db.selectRows('repositories', {'name': 'JavaScriptCore'}),
398                     db.selectRows('commits', {'revision': 'Sierra16D69'}),
399                     db.selectRows('commits', {'revision': 'Sierra16E33h'}),
400                     db.selectRows('commits', {'revision': 'Sierra16E34'})]);
401             }).then((results) => {
402                 const webkitRepository = results[0];
403                 const jscRepository = results[1];
404                 const osxCommit16D69 = results[2];
405                 const osxCommit16E33h = results[3];
406                 const osxCommit16E34 = results[4];
407
408                 assert.equal(webkitRepository.length, 0);
409                 assert.equal(jscRepository.length, 0)
410
411                 assert.equal(osxCommit16D69.length, 1);
412                 assert.equal(osxCommit16D69[0]['repository'], 10);
413                 assert.equal(osxCommit16D69[0]['order'], 1603006900);
414
415                 assert.equal(osxCommit16E33h.length, 1);
416                 assert.equal(osxCommit16E33h[0]['repository'], 10);
417                 assert.equal(osxCommit16E33h[0]['order'], 1604003308);
418
419                 assert.equal(osxCommit16E34.length, 1);
420                 assert.equal(osxCommit16E34[0]['repository'], 10);
421                 assert.equal(osxCommit16E34[0]['order'], 1604003400);
422
423                 return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1603000000&to=1603099900');
424             }).then((result) => {
425                 assert.equal(result['commits'].length, 1);
426                 assert.equal(result['commits'][0]['revision'], 'Sierra16D69');
427                 assert.equal(result['commits'][0]['order'], 1603006900);
428
429                 return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1604000000&to=1604099900');
430             }).then((result) => {
431                 assert.equal(result['commits'].length, 1);
432                 assert.equal(result['commits'][0]['revision'], 'Sierra16E34');
433                 assert.equal(result['commits'][0]['order'], 1604003400);
434             });
435         });
436
437         it('should stop reporting if any custom command fails', () => {
438             const logger = new MockLogger;
439             const fetchter = new OSBuildFetcher(config, TestServer.remoteAPI(), slaveAuth, MockSubprocess, logger);
440             const db = TestServer.database();
441             let fetchAndReportPromise = null;
442
443             return addSlaveForReport(emptyReport).then(() => {
444                 return Promise.all([
445                     db.insert('repositories', {'id': 10, 'name': 'OSX'}),
446                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16D67', 'order': 1603006700, 'reported': true}),
447                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16D68', 'order': 1603006800, 'reported': true}),
448                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16D69', 'order': 1603006900, 'reported': false}),
449                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16E32', 'order': 1604003200, 'reported': true}),
450                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16E33', 'order': 1604003300, 'reported': true}),
451                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16E33g', 'order': 1604003307, 'reported': true})]);
452             }).then(() => {
453                 return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1603000000&to=1603099900');
454             }).then((result) => {
455                 assert.equal(result['commits'].length, 1);
456                 assert.equal(result['commits'][0]['revision'], 'Sierra16D68');
457                 assert.equal(result['commits'][0]['order'], 1603006800);
458
459                 return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1604000000&to=1604099900');
460             }).then((result) => {
461                 assert.equal(result['commits'].length, 1);
462                 assert.equal(result['commits'][0]['revision'], 'Sierra16E33g');
463                 assert.equal(result['commits'][0]['order'], 1604003307);
464
465                 const waitingForInvocationPromise = MockSubprocess.waitingForInvocation();
466                 fetchAndReportPromise = fetchter.fetchAndReportNewBuilds();
467                 return waitingForInvocationPromise;
468             }).then(() => {
469                 return MockSubprocess.waitingForInvocation();
470             }).then(() => {
471                 MockSubprocess.invocations.sort((invocation, antoherInvocation) => invocation['command'] > antoherInvocation['command']);
472                 assert.equal(MockSubprocess.invocations.length, 2);
473                 assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'all osx 16Dxx builds']);
474                 assert.deepEqual(MockSubprocess.invocations[1].command, ['list', 'all osx 16Exx builds']);
475                 MockSubprocess.invocations[0].resolve('\n\nSierra16D68\nSierra16D69\n');
476                 MockSubprocess.invocations[1].resolve('\n\nSierra16E32\nSierra16E33\nSierra16E33h\nSierra16E34');
477                 MockSubprocess.reset();
478                 return MockSubprocess.waitingForInvocation();
479             }).then(() => {
480                 MockSubprocess.invocations.sort((invocation, antoherInvocation) => invocation['command'] > antoherInvocation['command']);
481                 assert.equal(MockSubprocess.invocations.length, 2);
482                 assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'subCommit', 'for', 'revision', 'Sierra16D69']);
483                 assert.deepEqual(MockSubprocess.invocations[1].command, ['list', 'subCommit', 'for', 'revision', 'Sierra16E33h']);
484
485                 MockSubprocess.invocations[0].resolve(JSON.stringify(subCommitWithWebKit));
486                 MockSubprocess.invocations[1].resolve(JSON.stringify(anotherSubCommitWithWebKit));
487                 MockSubprocess.reset();
488                 return MockSubprocess.waitingForInvocation();
489             }).then(() => {
490                 assert.equal(MockSubprocess.invocations.length, 1);
491                 assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'subCommit', 'for', 'revision', 'Sierra16E34']);
492                 MockSubprocess.invocations[0].reject('Command failed');
493
494                 return fetchAndReportPromise.then(() => {
495                     assert(false, 'should never be reached');
496                 }, (error_output) => {
497                     assert(error_output);
498                     assert.equal(error_output, 'Command failed');
499                 });
500             }).then(() => {
501                 return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1603000000&to=1603099900');
502             }).then((result) => {
503                 assert.equal(result['commits'].length, 1);
504                 assert.equal(result['commits'][0]['revision'], 'Sierra16D68');
505                 assert.equal(result['commits'][0]['order'], 1603006800);
506
507                 return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1604000000&to=1604099900');
508             }).then((result) => {
509                 assert.equal(result['commits'].length, 1);
510                 assert.equal(result['commits'][0]['revision'], 'Sierra16E33g');
511                 assert.equal(result['commits'][0]['order'], 1604003307);
512             });
513         })
514     })
515 });