af9a9646cd74ec3da7c58df46f25625a90ab6cd3
[WebKit-https.git] / Websites / perf.webkit.org / server-tests / api-update-triggerable.js
1 'use strict';
2
3 const assert = require('assert');
4
5 require('../tools/js/v3-models.js');
6
7 const TestServer = require('./resources/test-server.js');
8 const MockData = require('./resources/mock-data.js');
9 const addSlaveForReport = require('./resources/common-operations.js').addSlaveForReport;
10 const prepareServerTest = require('./resources/common-operations.js').prepareServerTest;
11
12 describe('/api/update-triggerable/', function () {
13     prepareServerTest(this);
14
15     const emptyUpdate = {
16         'slaveName': 'someSlave',
17         'slavePassword': 'somePassword',
18         'triggerable': 'build-webkit',
19         'configurations': [],
20     };
21
22     const smallUpdate = {
23         'slaveName': 'someSlave',
24         'slavePassword': 'somePassword',
25         'triggerable': 'build-webkit',
26         'configurations': [
27             {test: MockData.someTestId(), platform: MockData.somePlatformId()}
28         ],
29     };
30
31     it('should reject when slave name is missing', () => {
32         return TestServer.remoteAPI().postJSON('/api/update-triggerable/', {}).then((response) => {
33             assert.equal(response['status'], 'MissingSlaveName');
34         });
35     });
36
37     it('should reject when there are no slaves', () => {
38         const update = {slaveName: emptyUpdate.slaveName, slavePassword: emptyUpdate.slavePassword};
39         return TestServer.remoteAPI().postJSON('/api/update-triggerable/', update).then((response) => {
40             assert.equal(response['status'], 'SlaveNotFound');
41         });
42     });
43
44     it('should reject when the slave password doesn\'t match', () => {
45         return MockData.addMockData(TestServer.database()).then(() => {
46             return addSlaveForReport(emptyUpdate);
47         }).then(() => {
48             const report = {slaveName: emptyUpdate.slaveName, slavePassword: 'badPassword'};
49             return TestServer.remoteAPI().postJSON('/api/update-triggerable/', emptyUpdate);
50         }).then((response) => {
51             assert.equal(response['status'], 'OK');
52         });
53     });
54
55     it('should accept an empty report', () => {
56         return MockData.addMockData(TestServer.database()).then(() => {
57             return addSlaveForReport(emptyUpdate);
58         }).then(() => {
59             return TestServer.remoteAPI().postJSON('/api/update-triggerable/', emptyUpdate);
60         }).then((response) => {
61             assert.equal(response['status'], 'OK');
62         });
63     });
64
65     it('delete existing configurations when accepting an empty report', () => {
66         const db = TestServer.database();
67         return MockData.addMockData(db).then(() => {
68             return Promise.all([
69                 addSlaveForReport(emptyUpdate),
70                 db.insert('triggerable_configurations',
71                     {'triggerable': 1000 /* build-webkit */, 'test': MockData.someTestId(), 'platform': MockData.somePlatformId()})
72             ]);
73         }).then(() => {
74             return TestServer.remoteAPI().postJSON('/api/update-triggerable/', emptyUpdate);
75         }).then((response) => {
76             assert.equal(response['status'], 'OK');
77             return db.selectAll('triggerable_configurations', 'test');
78         }).then((rows) => {
79             assert.equal(rows.length, 0);
80         });
81     });
82
83     it('should add configurations in the update', () => {
84         const db = TestServer.database();
85         return MockData.addMockData(db).then(() => {
86             return addSlaveForReport(smallUpdate);
87         }).then(() => {
88             return TestServer.remoteAPI().postJSON('/api/update-triggerable/', smallUpdate);
89         }).then((response) => {
90             assert.equal(response['status'], 'OK');
91             return db.selectAll('triggerable_configurations', 'test');
92         }).then((rows) => {
93             assert.equal(rows.length, 1);
94             assert.equal(rows[0]['test'], smallUpdate.configurations[0]['test']);
95             assert.equal(rows[0]['platform'], smallUpdate.configurations[0]['platform']);
96         });
97     });
98
99     it('should reject when a configuration is malformed', () => {
100         return MockData.addMockData(TestServer.database()).then(() => {
101             return addSlaveForReport(smallUpdate);
102         }).then(() => {
103             const update = {
104                 'slaveName': 'someSlave',
105                 'slavePassword': 'somePassword',
106                 'triggerable': 'build-webkit',
107                 'configurations': [{}],
108             };
109             return TestServer.remoteAPI().postJSON('/api/update-triggerable/', update);
110         }).then((response) => {
111             assert.equal(response['status'], 'InvalidConfigurationEntry');
112         });
113     });
114
115     function updateWithOSXRepositoryGroup()
116     {
117         return {
118             'slaveName': 'someSlave',
119             'slavePassword': 'somePassword',
120             'triggerable': 'empty-triggerable',
121             'configurations': [
122                 {test: MockData.someTestId(), platform: MockData.somePlatformId()}
123             ],
124             'repositoryGroups': [
125                 {name: 'system-only', repositories: [MockData.macosRepositoryId()]},
126             ]
127         };
128     }
129
130     it('should reject when repositoryGroups is not an array', () => {
131         const update = updateWithOSXRepositoryGroup();
132         update.repositoryGroups = 1;
133         return MockData.addEmptyTriggerable(TestServer.database()).then(() => {
134             return addSlaveForReport(update);
135         }).then(() => {
136             return TestServer.remoteAPI().postJSON('/api/update-triggerable/', update);
137         }).then((response) => {
138             assert.equal(response['status'], 'InvalidRepositoryGroups');
139         });
140     });
141
142     it('should reject when the name of a repository group is not specified', () => {
143         const update = updateWithOSXRepositoryGroup();
144         delete update.repositoryGroups[0].name;
145         return MockData.addEmptyTriggerable(TestServer.database()).then(() => {
146             return addSlaveForReport(update);
147         }).then(() => {
148             return TestServer.remoteAPI().postJSON('/api/update-triggerable/', update);
149         }).then((response) => {
150             assert.equal(response['status'], 'InvalidRepositoryGroup');
151         });
152     });
153
154     it('should reject when the repository list is not specified for a repository group', () => {
155         const update = updateWithOSXRepositoryGroup();
156         delete update.repositoryGroups[0].repositories;
157         return MockData.addEmptyTriggerable(TestServer.database()).then(() => {
158             return addSlaveForReport(update);
159         }).then(() => {
160             return TestServer.remoteAPI().postJSON('/api/update-triggerable/', update);
161         }).then((response) => {
162             assert.equal(response['status'], 'InvalidRepositoryGroup');
163         });
164     });
165
166     it('should reject when the repository list of a repository group is not an array', () => {
167         const update = updateWithOSXRepositoryGroup();
168         update.repositoryGroups[0].repositories = 'hi';
169         return MockData.addEmptyTriggerable(TestServer.database()).then(() => {
170             return addSlaveForReport(update);
171         }).then(() => {
172             return TestServer.remoteAPI().postJSON('/api/update-triggerable/', update);
173         }).then((response) => {
174             assert.equal(response['status'], 'InvalidRepositoryGroup');
175         });
176     });
177
178     it('should reject when a repository group contains an invalid repository id', () => {
179         const update = updateWithOSXRepositoryGroup();
180         update.repositoryGroups[0].repositories[0] = 999;
181         return MockData.addEmptyTriggerable(TestServer.database()).then(() => {
182             return addSlaveForReport(update);
183         }).then(() => {
184             return TestServer.remoteAPI().postJSON('/api/update-triggerable/', update);
185         }).then((response) => {
186             assert.equal(response['status'], 'InvalidRepository');
187         });
188     });
189
190     it('should reject when a repository group contains a duplicate repository id', () => {
191         const update = updateWithOSXRepositoryGroup();
192         const group = update.repositoryGroups[0];
193         group.repositories.push(group.repositories[0]);
194         return MockData.addEmptyTriggerable(TestServer.database()).then(() => {
195             return addSlaveForReport(update);
196         }).then(() => {
197             return TestServer.remoteAPI().postJSON('/api/update-triggerable/', update);
198         }).then((response) => {
199             assert.equal(response['status'], 'InvalidRepository');
200         });
201     });
202
203     it('should add a new repository group when there are none', () => {
204         const db = TestServer.database();
205         return MockData.addEmptyTriggerable(db).then(() => {
206             return addSlaveForReport(updateWithOSXRepositoryGroup());
207         }).then(() => {
208             return TestServer.remoteAPI().postJSON('/api/update-triggerable/', updateWithOSXRepositoryGroup());
209         }).then((response) => {
210             assert.equal(response['status'], 'OK');
211             return Promise.all([db.selectAll('triggerable_configurations', 'test'), db.selectAll('triggerable_repository_groups')]);
212         }).then((result) => {
213             const [configurations, repositoryGroups] = result;
214
215             assert.equal(configurations.length, 1);
216             assert.equal(configurations[0]['test'], MockData.someTestId());
217             assert.equal(configurations[0]['platform'], MockData.somePlatformId());
218
219             assert.equal(repositoryGroups.length, 1);
220             assert.equal(repositoryGroups[0]['name'], 'system-only');
221             assert.equal(repositoryGroups[0]['triggerable'], MockData.emptyTriggeragbleId());
222         });
223     });
224
225     it('should not add a duplicate repository group when there is a group of the same name', () => {
226         const db = TestServer.database();
227         let initialResult;
228         return MockData.addEmptyTriggerable(db).then(() => {
229             return addSlaveForReport(updateWithOSXRepositoryGroup());
230         }).then(() => {
231             return TestServer.remoteAPI().postJSONWithStatus('/api/update-triggerable/', updateWithOSXRepositoryGroup());
232         }).then((response) => {
233             return Promise.all([db.selectAll('triggerable_configurations', 'test'), db.selectAll('triggerable_repository_groups')]);
234         }).then((result) => {
235             initialResult = result;
236             return TestServer.remoteAPI().postJSONWithStatus('/api/update-triggerable/', updateWithOSXRepositoryGroup());
237         }).then(() => {
238             return Promise.all([db.selectAll('triggerable_configurations', 'test'), db.selectAll('triggerable_repository_groups')]);
239         }).then((result) => {
240             const [initialConfigurations, initialRepositoryGroups] = initialResult;
241             const [configurations, repositoryGroups] = result;
242             assert.deepEqual(configurations, initialConfigurations);
243             assert.deepEqual(repositoryGroups, initialRepositoryGroups);
244         })
245     });
246
247     it('should not add a duplicate repository group when there is a group of the same name', () => {
248         const db = TestServer.database();
249         let initialResult;
250         return MockData.addEmptyTriggerable(db).then(() => {
251             return addSlaveForReport(updateWithOSXRepositoryGroup());
252         }).then(() => {
253             return TestServer.remoteAPI().postJSONWithStatus('/api/update-triggerable/', updateWithOSXRepositoryGroup());
254         }).then((response) => {
255             return Promise.all([db.selectAll('triggerable_configurations', 'test'), db.selectAll('triggerable_repository_groups')]);
256         }).then((result) => {
257             initialResult = result;
258             return TestServer.remoteAPI().postJSONWithStatus('/api/update-triggerable/', updateWithOSXRepositoryGroup());
259         }).then(() => {
260             return Promise.all([db.selectAll('triggerable_configurations', 'test'), db.selectAll('triggerable_repository_groups')]);
261         }).then((result) => {
262             const [initialConfigurations, initialRepositoryGroups] = initialResult;
263             const [configurations, repositoryGroups] = result;
264             assert.deepEqual(configurations, initialConfigurations);
265             assert.deepEqual(repositoryGroups, initialRepositoryGroups);
266         })
267     });
268
269     it('should update the description of a repository group when the name matches', () => {
270         const db = TestServer.database();
271         const initialUpdate = updateWithOSXRepositoryGroup();
272         const secondUpdate = updateWithOSXRepositoryGroup();
273         secondUpdate.repositoryGroups[0].description = 'this group is awesome';
274         return MockData.addEmptyTriggerable(db).then(() => {
275             return addSlaveForReport(initialUpdate);
276         }).then(() => {
277             return TestServer.remoteAPI().postJSONWithStatus('/api/update-triggerable/', initialUpdate);
278         }).then((response) => db.selectAll('triggerable_repository_groups')).then((repositoryGroups) => {
279             assert.equal(repositoryGroups.length, 1);
280             assert.equal(repositoryGroups[0]['name'], 'system-only');
281             assert.equal(repositoryGroups[0]['description'], null);
282             return TestServer.remoteAPI().postJSONWithStatus('/api/update-triggerable/', secondUpdate);
283         }).then(() => db.selectAll('triggerable_repository_groups')).then((repositoryGroups) => {
284             assert.equal(repositoryGroups.length, 1);
285             assert.equal(repositoryGroups[0]['name'], 'system-only');
286             assert.equal(repositoryGroups[0]['description'], 'this group is awesome');
287         });
288     });
289
290     function updateWithMacWebKitRepositoryGroups()
291     {
292         return {
293             'slaveName': 'someSlave',
294             'slavePassword': 'somePassword',
295             'triggerable': 'empty-triggerable',
296             'configurations': [
297                 {test: MockData.someTestId(), platform: MockData.somePlatformId()}
298             ],
299             'repositoryGroups': [
300                 {name: 'system-only', repositories: [MockData.macosRepositoryId()]},
301                 {name: 'system-and-webkit', repositories: [MockData.webkitRepositoryId(), MockData.macosRepositoryId()]},
302             ]
303         };
304     }
305
306     function mapRepositoriesByGroup(repositories)
307     {
308         const map = {};
309         for (const row of repositories) {
310             const groupId = row['group'];
311             if (!(groupId in map))
312                 map[groupId] = [];
313             map[groupId].push(row['repository']);
314         }
315         return map;
316     }
317
318     it('should replace a repository when the repository group name matches', () => {
319         const db = TestServer.database();
320         const initialUpdate = updateWithMacWebKitRepositoryGroups();
321         const secondUpdate = updateWithMacWebKitRepositoryGroups();
322         let initialGroups;
323         secondUpdate.repositoryGroups[1].repositories[0] = MockData.gitWebkitRepositoryId();
324         return MockData.addEmptyTriggerable(db).then(() => {
325             return addSlaveForReport(initialUpdate);
326         }).then(() => {
327             return TestServer.remoteAPI().postJSONWithStatus('/api/update-triggerable/', initialUpdate);
328         }).then((response) => {
329             return Promise.all([db.selectAll('triggerable_repository_groups', 'name'), db.selectAll('triggerable_repositories', 'repository')]);
330         }).then((result) => {
331             const [repositoryGroups, repositories] = result;
332             assert.equal(repositoryGroups.length, 2);
333             assert.equal(repositoryGroups[0]['name'], 'system-and-webkit');
334             assert.equal(repositoryGroups[0]['triggerable'], MockData.emptyTriggeragbleId());
335             assert.equal(repositoryGroups[1]['name'], 'system-only');
336             assert.equal(repositoryGroups[1]['triggerable'], MockData.emptyTriggeragbleId());
337             initialGroups = repositoryGroups;
338
339             const repositoriesByGroup = mapRepositoriesByGroup(repositories);
340             assert.equal(Object.keys(repositoriesByGroup).length, 2);
341             assert.deepEqual(repositoriesByGroup[repositoryGroups[0]['id']], [MockData.macosRepositoryId(), MockData.webkitRepositoryId()]);
342             assert.deepEqual(repositoriesByGroup[repositoryGroups[1]['id']], [MockData.macosRepositoryId()]);
343
344             return TestServer.remoteAPI().postJSONWithStatus('/api/update-triggerable/', secondUpdate);
345         }).then(() => {
346             return Promise.all([db.selectAll('triggerable_repository_groups', 'name'), db.selectAll('triggerable_repositories', 'repository')]);
347         }).then((result) => {
348             const [repositoryGroups, repositories] = result;
349             assert.deepEqual(repositoryGroups, initialGroups);
350
351             const repositoriesByGroup = mapRepositoriesByGroup(repositories);
352             assert.equal(Object.keys(repositoriesByGroup).length, 2);
353             assert.deepEqual(repositoriesByGroup[initialGroups[0]['id']], [MockData.macosRepositoryId(), MockData.gitWebkitRepositoryId()]);
354             assert.deepEqual(repositoriesByGroup[initialGroups[1]['id']], [MockData.macosRepositoryId()]);
355         });
356     });
357
358     it('should replace a repository when the list of repositories matches', () => {
359         const db = TestServer.database();
360         const initialUpdate = updateWithMacWebKitRepositoryGroups();
361         const secondUpdate = updateWithMacWebKitRepositoryGroups();
362         let initialGroups;
363         let initialRepositories;
364         secondUpdate.repositoryGroups[0].name = 'mac-only';
365         return MockData.addEmptyTriggerable(db).then(() => {
366             return addSlaveForReport(initialUpdate);
367         }).then(() => {
368             return TestServer.remoteAPI().postJSONWithStatus('/api/update-triggerable/', initialUpdate);
369         }).then((response) => {
370             return Promise.all([db.selectAll('triggerable_repository_groups', 'name'), db.selectAll('triggerable_repositories', 'repository')]);
371         }).then((result) => {
372             const [repositoryGroups, repositories] = result;
373             assert.equal(repositoryGroups.length, 2);
374             assert.equal(repositoryGroups[0]['name'], 'system-and-webkit');
375             assert.equal(repositoryGroups[0]['triggerable'], MockData.emptyTriggeragbleId());
376             assert.equal(repositoryGroups[1]['name'], 'system-only');
377             assert.equal(repositoryGroups[1]['triggerable'], MockData.emptyTriggeragbleId());
378             initialGroups = repositoryGroups;
379
380             const repositoriesByGroup = mapRepositoriesByGroup(repositories);
381             assert.equal(Object.keys(repositoriesByGroup).length, 2);
382             assert.deepEqual(repositoriesByGroup[repositoryGroups[0]['id']], [MockData.macosRepositoryId(), MockData.webkitRepositoryId()]);
383             assert.deepEqual(repositoriesByGroup[repositoryGroups[1]['id']], [MockData.macosRepositoryId()]);
384             initialRepositories = repositories;
385
386             return TestServer.remoteAPI().postJSONWithStatus('/api/update-triggerable/', secondUpdate);
387         }).then(() => {
388             return Promise.all([db.selectAll('triggerable_repository_groups', 'name'), db.selectAll('triggerable_repositories', 'repository')]);
389         }).then((result) => {
390             const [repositoryGroups, repositories] = result;
391
392             assert.equal(repositoryGroups.length, 2);
393             assert.equal(repositoryGroups[0]['name'], 'mac-only');
394             assert.equal(repositoryGroups[0]['triggerable'], initialGroups[1]['triggerable']);
395             assert.equal(repositoryGroups[1]['name'], 'system-and-webkit');
396             assert.equal(repositoryGroups[1]['triggerable'], initialGroups[0]['triggerable']);
397
398             assert.deepEqual(repositories, initialRepositories);
399         });
400     });
401
402 });