eb60d7b3a9781fb9a2713f830db185a27d29cd52
[WebKit-https.git] / Websites / perf.webkit.org / server-tests / privileged-api-create-analysis-task-tests.js
1 'use strict';
2
3 let assert = require('assert');
4
5 let MockData = require('./resources/mock-data.js');
6 let TestServer = require('./resources/test-server.js');
7 const addBuilderForReport = require('./resources/common-operations.js').addBuilderForReport;
8 const prepareServerTest = require('./resources/common-operations.js').prepareServerTest;
9
10 const reportWithRevision = [{
11     "buildNumber": "124",
12     "buildTime": "2015-10-27T15:34:51",
13     "revisions": {
14         "WebKit": {
15             "revision": "191622",
16             "timestamp": '2015-10-27T11:36:56.878473Z',
17         },
18     },
19     "builderName": "someBuilder",
20     "builderPassword": "somePassword",
21     "platform": "some platform",
22     "tests": {
23         "Suite": {
24             "metrics": {
25                 "Time": ["Arithmetic"],
26             },
27             "tests": {
28                 "test1": {
29                     "metrics": {"Time": { "current": [11] }},
30                 }
31             }
32         },
33     }}];
34
35 const reportWithRevisionNoTimestamp = [{
36     "buildNumber": "124",
37     "buildTime": "2015-10-27T15:34:51",
38     "revisions": {
39         "WebKit": {
40             "revision": "191622",
41         },
42     },
43     "builderName": "someBuilder",
44     "builderPassword": "somePassword",
45     "platform": "some platform",
46     "tests": {
47         "Suite": {
48             "metrics": {
49                 "Time": ["Arithmetic"],
50             },
51             "tests": {
52                 "test1": {
53                     "metrics": {"Time": { "current": [11] }},
54                 }
55             }
56         },
57     }}];
58
59 const anotherReportWithRevision = [{
60     "buildNumber": "125",
61     "buildTime": "2015-10-27T17:27:41",
62     "revisions": {
63         "WebKit": {
64             "revision": "191623",
65             "timestamp": '2015-10-27T16:38:10.768995Z',
66         },
67     },
68     "builderName": "someBuilder",
69     "builderPassword": "somePassword",
70     "platform": "some platform",
71     "tests": {
72         "Suite": {
73             "metrics": {
74                 "Time": ["Arithmetic"],
75             },
76             "tests": {
77                 "test1": {
78                     "metrics": {"Time": { "current": [12] }},
79                 }
80             }
81         },
82     }}];
83
84 const anotherReportWithRevisionNoTimestamp = [{
85     "buildNumber": "125",
86     "buildTime": "2015-10-27T17:27:41",
87     "revisions": {
88         "WebKit": {
89             "revision": "191623",
90         },
91     },
92     "builderName": "someBuilder",
93     "builderPassword": "somePassword",
94     "platform": "some platform",
95     "tests": {
96         "Suite": {
97             "metrics": {
98                 "Time": ["Arithmetic"],
99             },
100             "tests": {
101                 "test1": {
102                     "metrics": {"Time": { "current": [12] }},
103                 }
104             }
105         },
106     }}];
107
108 describe('/privileged-api/create-analysis-task', function () {
109     prepareServerTest(this);
110
111     it('should return "MissingName" on an empty request', () => {
112         return PrivilegedAPI.sendRequest('create-analysis-task', {}).then((content) => {
113             assert(false, 'should never be reached');
114         }, (error) => {
115             assert.equal(error, 'MissingName');
116         });
117     });
118
119     it('should return "InvalidStartRun" when startRun is missing but endRun is set', () => {
120         return PrivilegedAPI.sendRequest('create-analysis-task', {name: 'hi', endRun: 1}).then((content) => {
121             assert(false, 'should never be reached');
122         }, (error) => {
123             assert.equal(error, 'InvalidStartRun');
124         });
125     });
126
127     it('should return "InvalidEndRun" when endRun is missing but startRun is set', () => {
128         return PrivilegedAPI.sendRequest('create-analysis-task', {name: 'hi', startRun: 1}).then((content) => {
129             assert(false, 'should never be reached');
130         }, (error) => {
131             assert.equal(error, 'InvalidEndRun');
132         });
133     });
134
135     it('should return "InvalidStartRun" when startRun is not a valid integer', () => {
136         return PrivilegedAPI.sendRequest('create-analysis-task', {name: 'hi', startRun: "foo", endRun: 1}).then((content) => {
137             assert(false, 'should never be reached');
138         }, (error) => {
139             assert.equal(error, 'InvalidStartRun');
140         });
141     });
142
143     it('should return "InvalidEndRun" when endRun is not a valid integer', () => {
144         return PrivilegedAPI.sendRequest('create-analysis-task', {name: 'hi', startRun: 1, endRun: "foo"}).then((content) => {
145             assert(false, 'should never be reached');
146         }, (error) => {
147             assert.equal(error, 'InvalidEndRun');
148         });
149     });
150
151     it('should return "InvalidStartRun" when startRun is invalid', () => {
152         return addBuilderForReport(reportWithRevision[0]).then(() => {
153             return TestServer.remoteAPI().postJSON('/api/report/', reportWithRevision);
154         }).then(() => {
155             return PrivilegedAPI.sendRequest('create-analysis-task', {name: 'hi', startRun: 100, endRun: 1}).then((content) => {
156                 assert(false, 'should never be reached');
157             }, (error) => {
158                 assert.equal(error, 'InvalidStartRun');
159             });
160         });
161     });
162
163     it('should return "InvalidEndRun" when endRun is invalid', () => {
164         return addBuilderForReport(reportWithRevision[0]).then(() => {
165             return TestServer.remoteAPI().postJSON('/api/report/', reportWithRevision);
166         }).then(() => {
167             return PrivilegedAPI.sendRequest('create-analysis-task', {name: 'hi', startRun: 1, endRun: 100}).then((content) => {
168                 assert(false, 'should never be reached');
169             }, (error) => {
170                 assert.equal(error, 'InvalidEndRun');
171             });
172         });
173     });
174
175     it('should return "InvalidTimeRange" when startRun and endRun are identical', () => {
176         return addBuilderForReport(reportWithRevision[0]).then(() => {
177             return TestServer.remoteAPI().postJSON('/api/report/', reportWithRevision);
178         }).then(() => {
179             return PrivilegedAPI.sendRequest('create-analysis-task', {name: 'hi', startRun: 1, endRun: 1}).then((content) => {
180                 assert(false, 'should never be reached');
181             }, (error) => {
182                 assert.equal(error, 'InvalidTimeRange');
183             });
184         });
185     });
186
187     it('should return "RunConfigMismatch" when startRun and endRun come from a different test configurations', () => {
188         return addBuilderForReport(reportWithRevision[0]).then(() => {
189             return TestServer.remoteAPI().postJSON('/api/report/', reportWithRevision);
190         }).then(() => {
191             return PrivilegedAPI.sendRequest('create-analysis-task', {name: 'hi', startRun: 1, endRun: 2}).then((content) => {
192                 assert(false, 'should never be reached');
193             }, (error) => {
194                 assert.equal(error, 'RunConfigMismatch');
195             });
196         });
197     });
198
199     it('should create an analysis task when name, startRun, and endRun are set properly', () => {
200         const db = TestServer.database();
201         return addBuilderForReport(reportWithRevision[0]).then(() => {
202             return TestServer.remoteAPI().postJSON('/api/report/', reportWithRevision);
203         }).then(() => {
204             return TestServer.remoteAPI().postJSON('/api/report/', anotherReportWithRevision);
205         }).then(() => {
206             return Manifest.fetch();
207         }).then(() => {
208             const test1 = Test.findByPath(['Suite', 'test1']);
209             const platform = Platform.findByName('some platform');
210             return db.selectFirstRow('test_configurations', {metric: test1.metrics()[0].id(), platform: platform.id()});
211         }).then((configRow) => {
212             return db.selectRows('test_runs', {config: configRow['id']});
213         }).then((testRuns) => {
214             assert.equal(testRuns.length, 2);
215             return PrivilegedAPI.sendRequest('create-analysis-task', {name: 'hi', startRun: testRuns[0]['id'], endRun: testRuns[1]['id']});
216         }).then((content) => {
217             return AnalysisTask.fetchById(content['taskId']);
218         }).then((task) => {
219             assert.equal(task.name(), 'hi');
220             assert(!task.hasResults());
221             assert(!task.hasPendingRequests());
222             assert.deepEqual(task.bugs(), []);
223             assert.deepEqual(task.causes(), []);
224             assert.deepEqual(task.fixes(), []);
225             assert.equal(task.changeType(), null);
226             assert.equal(task.platform().label(), 'some platform');
227             assert.equal(task.metric().test().label(), 'test1');
228         });
229     });
230
231     it('should create an analysis task and use build time as fallback when commit time is not available', () => {
232         const db = TestServer.database();
233         return addBuilderForReport(reportWithRevisionNoTimestamp[0]).then(() => {
234             return TestServer.remoteAPI().postJSON('/api/report/', reportWithRevisionNoTimestamp);
235         }).then(() => {
236             return TestServer.remoteAPI().postJSON('/api/report/', anotherReportWithRevisionNoTimestamp);
237         }).then(() => {
238             return Manifest.fetch();
239         }).then(() => {
240             const test1 = Test.findByPath(['Suite', 'test1']);
241             const platform = Platform.findByName('some platform');
242             return db.selectFirstRow('test_configurations', {metric: test1.metrics()[0].id(), platform: platform.id()});
243         }).then((configRow) => {
244             return db.selectRows('test_runs', {config: configRow['id']});
245         }).then((testRuns) => {
246             assert.equal(testRuns.length, 2);
247             return PrivilegedAPI.sendRequest('create-analysis-task', {name: 'hi', startRun: testRuns[0]['id'], endRun: testRuns[1]['id']});
248         }).then((content) => {
249             return AnalysisTask.fetchById(content['taskId']);
250         }).then((task) => {
251             assert.equal(task.name(), 'hi');
252             assert(!task.hasResults());
253             assert(!task.hasPendingRequests());
254             assert.deepEqual(task.bugs(), []);
255             assert.deepEqual(task.causes(), []);
256             assert.deepEqual(task.fixes(), []);
257             assert.equal(task.changeType(), null);
258             assert.equal(task.platform().label(), 'some platform');
259             assert.equal(task.metric().test().label(), 'test1');
260             assert.equal(task.startTime(), 1445960091000);
261             assert.equal(task.endTime(), 1445966861000);
262         });
263     });
264
265     it('should return "DuplicateAnalysisTask" when there is already an analysis task for the specified range', () => {
266         const db = TestServer.database();
267         let startId;
268         let endId;
269         return addBuilderForReport(reportWithRevision[0]).then(() => {
270             return TestServer.remoteAPI().postJSON('/api/report/', reportWithRevision);
271         }).then(() => {
272             return TestServer.remoteAPI().postJSON('/api/report/', anotherReportWithRevision);
273         }).then(() => {
274             return Manifest.fetch();
275         }).then(() => {
276             const test1 = Test.findByPath(['Suite', 'test1']);
277             const platform = Platform.findByName('some platform');
278             return db.selectFirstRow('test_configurations', {metric: test1.metrics()[0].id(), platform: platform.id()});
279         }).then((configRow) => {
280             return db.selectRows('test_runs', {config: configRow['id']});
281         }).then((testRuns) => {
282             assert.equal(testRuns.length, 2);
283             startId = testRuns[0]['id'];
284             endId = testRuns[1]['id'];
285             return PrivilegedAPI.sendRequest('create-analysis-task', {name: 'hi', startRun: startId, endRun: endId});
286         }).then((content) => {
287             return PrivilegedAPI.sendRequest('create-analysis-task', {name: 'hi', startRun: startId, endRun: endId}).then(() => {
288                 assert(false, 'should never be reached');
289             }, (error) => {
290                 assert.equal(error, 'DuplicateAnalysisTask');
291             });
292         }).then(() => {
293             return db.selectAll('analysis_tasks');
294         }).then((tasks) => {
295             assert.equal(tasks.length, 1);
296         });
297     });
298
299     it('should create an analysis task with analysis strategies when they are specified', () => {
300         const db = TestServer.database();
301         return addBuilderForReport(reportWithRevision[0]).then(() => {
302             return TestServer.remoteAPI().postJSON('/api/report/', reportWithRevision);
303         }).then(() => {
304             return TestServer.remoteAPI().postJSON('/api/report/', anotherReportWithRevision);
305         }).then(() => {
306             return Manifest.fetch();
307         }).then(() => {
308             const test1 = Test.findByPath(['Suite', 'test1']);
309             const platform = Platform.findByName('some platform');
310             return db.selectFirstRow('test_configurations', {metric: test1.metrics()[0].id(), platform: platform.id()});
311         }).then((configRow) => {
312             return db.selectRows('test_runs', {config: configRow['id']});
313         }).then((testRuns) => {
314             assert.equal(testRuns.length, 2);
315             return PrivilegedAPI.sendRequest('create-analysis-task', {
316                 name: 'hi',
317                 startRun: testRuns[0]['id'],
318                 endRun: testRuns[1]['id'],
319                 segmentationStrategy: "time series segmentation",
320                 testRangeStrategy: "student's t-test"});
321         }).then(() => {
322             return Promise.all([db.selectFirstRow('analysis_tasks'), db.selectAll('analysis_strategies')]);
323         }).then((results) => {
324             const [taskRow, strategies] = results;
325             assert(taskRow['segmentation']);
326             assert(taskRow['test_range']);
327
328             const strategyIdMap = {};
329             for (let strategy of strategies)
330                 strategyIdMap[strategy['id']] = strategy;
331
332             assert.equal(strategyIdMap[taskRow['segmentation']]['name'], "time series segmentation");
333             assert.equal(strategyIdMap[taskRow['test_range']]['name'], "student's t-test");
334         });
335     });
336
337 });