blob: 4502fbb8a6bd9e4bd5d75c79ae8083ae8cffd939 [file] [log] [blame]
Mitja Nikolaus03e412b2018-09-18 17:50:15 +02001"""Tests for the rest_endpoints module."""
Mitja Nikolaus03e412b2018-09-18 17:50:15 +02002import operator
Mitja Nikolaus21f3b1b2018-11-02 17:10:02 +01003from datetime import datetime, timedelta
Mitja Nikolaus03e412b2018-09-18 17:50:15 +02004
5import pytz
Franz-Xaver Geiger38a66bc2018-10-09 14:52:26 +02006from django.test import override_settings
Mitja Nikolaus03e412b2018-09-18 17:50:15 +02007
8from django.urls import reverse
9from django.utils.http import urlencode
10
11from rest_framework import status
12
13from crashreport_stats.models import RadioVersion
14from crashreport_stats.tests.utils import Dummy, HiccupStatsAPITestCase
15
16from crashreports.models import Crashreport, HeartBeat, LogFile
17
18# pylint: disable=too-many-public-methods
19
20
21class StatusTestCase(HiccupStatsAPITestCase):
22 """Test the status endpoint."""
23
24 status_url = reverse("hiccup_stats_api_v1_status")
25
26 def _assert_status_response_is(
27 self, response, num_devices, num_crashreports, num_heartbeats
28 ):
29 self.assertEqual(response.status_code, status.HTTP_200_OK)
30 self.assertIn("devices", response.data)
31 self.assertIn("crashreports", response.data)
32 self.assertIn("heartbeats", response.data)
33 self.assertEqual(response.data["devices"], num_devices)
34 self.assertEqual(response.data["crashreports"], num_crashreports)
35 self.assertEqual(response.data["heartbeats"], num_heartbeats)
36
Mitja Nikolaus03e412b2018-09-18 17:50:15 +020037 def test_status_url_as_fp_staff(self):
38 """Test that Fairphone staff users can access the status URL."""
39 self._assert_get_as_fp_staff_succeeds(self.status_url)
40
41 def test_status_url_as_device_owner(self):
42 """Test that device owner users can not access the status URL."""
43 self._assert_get_as_device_owner_fails(self.status_url)
44
45 def test_status_url_no_auth(self):
46 """Test that non-authenticated users can not access the status URL."""
47 self._assert_get_without_authentication_fails(self.status_url)
48
49 def test_get_status_empty_database(self):
50 """Get the status when the database is empty."""
51 response = self.fp_staff_client.get(self.status_url)
52
53 # Assert that only the device that was created by the setUpTestData()
54 # method is found.
55 self._assert_status_response_is(response, 1, 0, 0)
56
57 def test_get_status(self):
58 """Get the status after some reports have been created."""
59 # Create a device with a heartbeat and a crash report
Mitja Nikolaus77dd5652018-12-06 11:27:01 +010060 device = Dummy.create_device(Dummy.create_user())
61 Dummy.create_report(HeartBeat, device)
62 Dummy.create_report(Crashreport, device)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +020063
64 # Create a second device without any reports
Mitja Nikolaus77dd5652018-12-06 11:27:01 +010065 Dummy.create_device(Dummy.create_user(username=Dummy.USERNAMES[1]))
Mitja Nikolaus03e412b2018-09-18 17:50:15 +020066
67 # Assert that the status includes the appropriate numbers (a third
68 # device was created by the setUpTestData() method)
69 response = self.fp_staff_client.get(self.status_url)
70 self._assert_status_response_is(
71 response, num_devices=3, num_crashreports=1, num_heartbeats=1
72 )
73
74
75class _VersionTestCase(HiccupStatsAPITestCase):
76 """Abstract class for version-related test cases to inherit from."""
77
78 @staticmethod
79 def _create_dummy_version(**kwargs):
Mitja Nikolaus77dd5652018-12-06 11:27:01 +010080 return Dummy.create_version(**kwargs)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +020081
82 def _get_with_params(self, url, params):
Mitja Nikolause0e83772018-11-05 10:00:53 +010083 return self.fp_staff_client.get("{}?{}".format(url, urlencode(params)))
Mitja Nikolaus03e412b2018-09-18 17:50:15 +020084
85 def _assert_result_length_is(self, response, count):
86 self.assertEqual(response.status_code, status.HTTP_200_OK)
87 self.assertIn("results", response.data)
88 self.assertIn("count", response.data)
89 self.assertEqual(response.data["count"], count)
90 self.assertEqual(len(response.data["results"]), count)
91
92 def _assert_filter_result_matches(
93 self, endpoint_url, unique_entry_name, filter_params, expected_result
94 ):
95 # List entities with filter
96 response = self._get_with_params(endpoint_url, filter_params)
97
98 # Expect only the single matching result to be returned
99 self._assert_result_length_is(response, 1)
100 self.assertEqual(
101 response.data["results"][0][unique_entry_name],
102 getattr(expected_result, unique_entry_name),
103 )
104
105
106class VersionTestCase(_VersionTestCase):
107 """Test the Version and REST endpoint."""
108
109 # pylint: disable=too-many-ancestors
110
111 # The attribute name characterising the unicity of a stats entry (the
112 # named identifier)
113 unique_entry_name = "build_fingerprint"
114 # The collection of unique entries to post
115 unique_entries = Dummy.BUILD_FINGERPRINTS
116 # The URL to retrieve the stats entries from
117 endpoint_url = reverse("hiccup_stats_api_v1_versions")
118
119 def _create_version_entities(self):
120 versions = [
121 self._create_dummy_version(**{self.unique_entry_name: unique_entry})
122 for unique_entry in self.unique_entries
123 ]
124 return versions
125
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200126 def test_endpoint_url_as_fp_staff(self):
127 """Test that Fairphone staff users can access the endpoint URL."""
128 self._assert_get_as_fp_staff_succeeds(self.endpoint_url)
129
130 def test_endpoint_url_as_device_owner(self):
131 """Test that device owner users can not access the endpoint URL."""
132 self._assert_get_as_device_owner_fails(self.endpoint_url)
133
134 def test_endpoint_url_no_auth(self):
135 """Test that non-authenticated users can not access the endpoint URL."""
136 self._assert_get_without_authentication_fails(self.endpoint_url)
137
138 def test_list_versions_empty_database(self):
139 """Test listing of versions on an empty database."""
Mitja Nikolause0e83772018-11-05 10:00:53 +0100140 response = self.fp_staff_client.get(self.endpoint_url)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200141 self._assert_result_length_is(response, 0)
142
143 def test_list_versions(self):
144 """Test listing versions."""
145 versions = self._create_version_entities()
Mitja Nikolause0e83772018-11-05 10:00:53 +0100146 response = self.fp_staff_client.get(self.endpoint_url)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200147 self._assert_result_length_is(response, len(versions))
148
149 def test_filter_versions_by_unique_entry_name(self):
150 """Test filtering versions by their unique entry name."""
151 versions = self._create_version_entities()
Mitja Nikolause0e83772018-11-05 10:00:53 +0100152 response = self.fp_staff_client.get(self.endpoint_url)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200153
154 # Listing all entities should return the correct result length
155 self._assert_result_length_is(response, len(versions))
156
157 # List entities with filter
158 filter_params = {
159 self.unique_entry_name: getattr(versions[0], self.unique_entry_name)
160 }
161 self._assert_filter_result_matches(
162 self.endpoint_url,
163 self.unique_entry_name,
164 filter_params,
165 expected_result=versions[0],
166 )
167
168 def test_filter_versions_by_release_type(self):
169 """Test filtering versions by release type."""
170 # Create versions for all combinations of release types
171 versions = []
172 i = 0
173 for is_official_release in True, False:
174 for is_beta_release in True, False:
175 versions.append(
176 self._create_dummy_version(
177 **{
178 "is_official_release": is_official_release,
179 "is_beta_release": is_beta_release,
180 self.unique_entry_name: self.unique_entries[i],
181 }
182 )
183 )
184 i += 1
185
186 # # Listing all entities should return the correct result length
Mitja Nikolause0e83772018-11-05 10:00:53 +0100187 response = self.fp_staff_client.get(self.endpoint_url)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200188 self._assert_result_length_is(response, len(versions))
189
190 # List each of the entities with the matching filter params
191 for version in versions:
192 filter_params = {
193 "is_official_release": version.is_official_release,
194 "is_beta_release": version.is_beta_release,
195 }
196 self._assert_filter_result_matches(
197 self.endpoint_url,
198 self.unique_entry_name,
199 filter_params,
200 expected_result=version,
201 )
202
203 def test_filter_versions_by_first_seen_date(self):
204 """Test filtering versions by first seen date."""
205 versions = self._create_version_entities()
206
207 # Set the first seen date of an entity
208 versions[0].first_seen_on = Dummy.DATES[2]
209 versions[0].save()
210
211 # Listing all entities should return the correct result length
Mitja Nikolause0e83772018-11-05 10:00:53 +0100212 response = self.fp_staff_client.get(self.endpoint_url)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200213 self._assert_result_length_is(response, len(versions))
214
215 # Expect the single matching result to be returned
216 filter_params = {"first_seen_after": Dummy.DATES[2]}
217 self._assert_filter_result_matches(
218 self.endpoint_url,
219 self.unique_entry_name,
220 filter_params,
221 expected_result=versions[0],
222 )
223
224
225# pylint: disable=too-many-ancestors
226class RadioVersionTestCase(VersionTestCase):
227 """Test the RadioVersion REST endpoint."""
228
229 unique_entry_name = "radio_version"
230 unique_entries = Dummy.RADIO_VERSIONS
231 endpoint_url = reverse("hiccup_stats_api_v1_radio_versions")
232
233 @staticmethod
234 def _create_dummy_version(**kwargs):
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100235 return Dummy.create_version(RadioVersion, **kwargs)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200236
237
238class VersionDailyTestCase(_VersionTestCase):
239 """Test the VersionDaily REST endpoint."""
240
241 unique_entry_name = "build_fingerprint"
242 unique_entries = Dummy.BUILD_FINGERPRINTS
243 endpoint_url = reverse("hiccup_stats_api_v1_version_daily")
244
245 @staticmethod
246 def _create_dummy_daily_version(version, **kwargs):
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100247 return Dummy.create_daily_version(version, **kwargs)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200248
249 def _create_version_entities(self):
250 versions = [
251 self._create_dummy_version(**{self.unique_entry_name: unique_entry})
252 for unique_entry in self.unique_entries
253 ]
254 versions_daily = [
255 self._create_dummy_daily_version(version=version)
256 for version in versions
257 ]
258 return versions_daily
259
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200260 def test_endpoint_url_as_fp_staff(self):
261 """Test that Fairphone staff users can access the endpoint URL."""
262 self._assert_get_as_fp_staff_succeeds(self.endpoint_url)
263
264 def test_endpoint_url_as_device_owner(self):
265 """Test that device owner users can not access the endpoint URL."""
266 self._assert_get_as_device_owner_fails(self.endpoint_url)
267
268 def test_endpoint_url_no_auth(self):
269 """Test that non-authenticated users can not access the endpoint URL."""
270 self._assert_get_without_authentication_fails(self.endpoint_url)
271
272 def test_list_daily_versions_empty_database(self):
273 """Test listing of daily versions on an empty database."""
Mitja Nikolause0e83772018-11-05 10:00:53 +0100274 response = self.fp_staff_client.get(self.endpoint_url)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200275 self._assert_result_length_is(response, 0)
276
277 def test_list_daily_versions(self):
278 """Test listing daily versions."""
279 versions_daily = self._create_version_entities()
Mitja Nikolause0e83772018-11-05 10:00:53 +0100280 response = self.fp_staff_client.get(self.endpoint_url)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200281 self._assert_result_length_is(response, len(versions_daily))
282
283 def test_filter_daily_versions_by_version(self):
284 """Test filtering versions by the version they relate to."""
285 # Create VersionDaily entities
286 versions = self._create_version_entities()
287
288 # Listing all entities should return the correct result length
Mitja Nikolause0e83772018-11-05 10:00:53 +0100289 response = self.fp_staff_client.get(self.endpoint_url)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200290 self._assert_result_length_is(response, len(versions))
291
292 # List entities with filter
293 param_name = "version__" + self.unique_entry_name
294 filter_params = {
295 param_name: getattr(versions[0].version, self.unique_entry_name)
296 }
297 self._assert_filter_result_matches(
298 self.endpoint_url,
299 self.unique_entry_name,
300 filter_params,
301 expected_result=versions[0].version,
302 )
303
304 def test_filter_daily_versions_by_date(self):
305 """Test filtering daily versions by date."""
306 # Create Version and VersionDaily entities
307 versions = self._create_version_entities()
308
309 # Update the date
310 versions[0].date = Dummy.DATES[2]
311 versions[0].save()
312
313 # Listing all entities should return the correct result length
Mitja Nikolause0e83772018-11-05 10:00:53 +0100314 response = self.fp_staff_client.get(self.endpoint_url)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200315 self._assert_result_length_is(response, len(versions))
316
317 # Expect the single matching result to be returned
318 filter_params = {"date": versions[0].date}
319 self._assert_filter_result_matches(
320 self.endpoint_url,
321 self.unique_entry_name,
322 filter_params,
323 expected_result=versions[0].version,
324 )
325
326
327class RadioVersionDailyTestCase(VersionDailyTestCase):
328 """Test the RadioVersionDaily REST endpoint."""
329
330 unique_entry_name = "radio_version"
331 unique_entries = Dummy.RADIO_VERSIONS
332 endpoint_url = reverse("hiccup_stats_api_v1_radio_version_daily")
333
334 @staticmethod
335 def _create_dummy_version(**kwargs):
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100336 return Dummy.create_version(RadioVersion, **kwargs)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200337
338 @staticmethod
339 def _create_dummy_daily_version(version, **kwargs):
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100340 return Dummy.create_daily_radio_version(version, **kwargs)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200341
342
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100343@override_settings(MEDIA_ROOT=Dummy.DEFAULT_LOG_FILE_DIRECTORY)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200344class DeviceStatsTestCase(HiccupStatsAPITestCase):
345 """Test the single device stats REST endpoints."""
346
347 device_overview_url = "hiccup_stats_api_v1_device_overview"
348 device_report_history_url = "hiccup_stats_api_v1_device_report_history"
349 device_update_history_url = "hiccup_stats_api_v1_device_update_history"
350 device_logfile_download_url = "hiccup_stats_api_v1_logfile_download"
351
352 def _get_with_params(self, url, params):
353 url = reverse(url, kwargs=params)
354 return self.fp_staff_client.get(url)
355
356 def _assert_device_stats_response_is(
357 self,
358 response,
359 uuid,
360 board_date,
361 num_heartbeats,
362 num_crashreports,
363 num_smpls,
364 crashes_per_day,
365 smpl_per_day,
366 last_active,
367 ):
368 # pylint: disable=too-many-arguments
369 self.assertEqual(response.status_code, status.HTTP_200_OK)
370
371 self.assertIn("uuid", response.data)
372 self.assertIn("board_date", response.data)
373 self.assertIn("heartbeats", response.data)
374 self.assertIn("crashreports", response.data)
375 self.assertIn("smpls", response.data)
376 self.assertIn("crashes_per_day", response.data)
377 self.assertIn("smpl_per_day", response.data)
378 self.assertIn("last_active", response.data)
379
380 self.assertEqual(response.data["uuid"], uuid)
381 self.assertEqual(response.data["board_date"], board_date)
382 self.assertEqual(response.data["heartbeats"], num_heartbeats)
383 self.assertEqual(response.data["crashreports"], num_crashreports)
384 self.assertEqual(response.data["smpls"], num_smpls)
385 self.assertEqual(response.data["crashes_per_day"], crashes_per_day)
386 self.assertEqual(response.data["smpl_per_day"], smpl_per_day)
387 self.assertEqual(response.data["last_active"], last_active)
388
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200389 def test_device_overview_url_as_fp_staff(self):
390 """Test that Fairphone staff users can access the URL."""
391 self._assert_get_as_fp_staff_succeeds(
392 reverse(
393 self.device_overview_url,
394 kwargs={"uuid": self.device_owner_device.uuid},
395 )
396 )
397
398 def test_device_overview_url_as_device_owner(self):
399 """Test that device owner users can not access the URL."""
400 self._assert_get_as_device_owner_fails(
401 reverse(
402 self.device_overview_url,
403 kwargs={"uuid": self.device_owner_device.uuid},
404 )
405 )
406
407 def test_device_overview_url_no_auth(self):
408 """Test that non-authenticated users can not access the URL."""
409 self._assert_get_without_authentication_fails(
410 reverse(
411 self.device_overview_url,
412 kwargs={"uuid": self.device_owner_device.uuid},
413 )
414 )
415
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200416 def test_device_report_history_url_as_fp_staff(self):
417 """Test that FP staff can access device report history URL."""
418 self._assert_get_as_fp_staff_succeeds(
419 reverse(
420 self.device_report_history_url,
421 kwargs={"uuid": self.device_owner_device.uuid},
422 )
423 )
424
425 def test_device_report_history_url_as_device_owner(self):
426 """Test that device owners can't access device report history URL."""
427 self._assert_get_as_device_owner_fails(
428 reverse(
429 self.device_report_history_url,
430 kwargs={"uuid": self.device_owner_device.uuid},
431 )
432 )
433
434 def test_device_report_history_url_no_auth(self):
435 """Test that device report history is not accessible without auth."""
436 self._assert_get_without_authentication_fails(
437 reverse(
438 self.device_report_history_url,
439 kwargs={"uuid": self.device_owner_device.uuid},
440 )
441 )
442
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200443 def test_device_update_history_url_as_fp_staff(self):
444 """Test that FP staff can access device update history URL."""
445 self._assert_get_as_fp_staff_succeeds(
446 reverse(
447 self.device_update_history_url,
448 kwargs={"uuid": self.device_owner_device.uuid},
449 )
450 )
451
452 def test_device_update_history_url_as_device_owner(self):
453 """Test that device owners can't access device update history URL."""
454 self._assert_get_as_device_owner_fails(
455 reverse(
456 self.device_update_history_url,
457 kwargs={"uuid": self.device_owner_device.uuid},
458 )
459 )
460
461 def test_device_update_history_url_no_auth(self):
462 """Test that device update history is not accessible without auth."""
463 self._assert_get_without_authentication_fails(
464 reverse(
465 self.device_update_history_url,
466 kwargs={"uuid": self.device_owner_device.uuid},
467 )
468 )
469
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200470 def tes_logfile_download_url_as_fp_staff(self):
471 """Test that FP staff can access the logfile download URL."""
472 non_existent_logfile_id = 0
473 self.assertFalse(
474 LogFile.objects.filter(id=non_existent_logfile_id).exists()
475 )
476 self._assert_get_as_fp_staff_succeeds(
477 reverse(
478 self.device_logfile_download_url,
479 kwargs={"id_logfile": non_existent_logfile_id},
480 ),
481 expected_status=status.HTTP_404_NOT_FOUND,
482 )
483
484 def test_logfile_download_url_as_device_owner(self):
485 """Test that device owners can't access the logfile download URL."""
486 non_existent_logfile_id = 0
487 self.assertFalse(
488 LogFile.objects.filter(id=non_existent_logfile_id).exists()
489 )
490 self._assert_get_as_device_owner_fails(
491 reverse(
492 self.device_logfile_download_url,
493 kwargs={"id_logfile": non_existent_logfile_id},
494 )
495 )
496
497 def test_logfile_download_url_no_auth(self):
498 """Test that the logfile download URL is not accessible without auth."""
499 non_existent_logfile_id = 0
500 self.assertFalse(
501 LogFile.objects.filter(id=non_existent_logfile_id).exists()
502 )
503 self._assert_get_without_authentication_fails(
504 reverse(
505 self.device_logfile_download_url,
506 kwargs={"id_logfile": non_existent_logfile_id},
507 )
508 )
509
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200510 def test_get_device_stats_no_reports(self):
511 """Test getting device stats for a device without reports."""
512 # Create a device
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100513 device = Dummy.create_device(Dummy.create_user())
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200514
515 # Get the device statistics
516 response = self._get_with_params(
517 self.device_overview_url, {"uuid": device.uuid}
518 )
519
520 # Assert that the statistics match
521 self._assert_device_stats_response_is(
522 response=response,
523 uuid=str(device.uuid),
524 board_date=device.board_date,
525 num_heartbeats=0,
526 num_crashreports=0,
527 num_smpls=0,
528 crashes_per_day=0.0,
529 smpl_per_day=0.0,
530 last_active=device.board_date,
531 )
532
533 def test_get_device_stats_no_crash_reports(self):
534 """Test getting device stats for a device without crashreports."""
535 # Create a device and a heartbeat
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100536 device = Dummy.create_device(Dummy.create_user())
537 heartbeat = Dummy.create_report(HeartBeat, device)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200538
539 # Get the device statistics
540 response = self._get_with_params(
541 self.device_overview_url, {"uuid": device.uuid}
542 )
543
544 # Assert that the statistics match
545 self._assert_device_stats_response_is(
546 response=response,
547 uuid=str(device.uuid),
548 board_date=device.board_date,
549 num_heartbeats=1,
550 num_crashreports=0,
551 num_smpls=0,
552 crashes_per_day=0.0,
553 smpl_per_day=0.0,
554 last_active=heartbeat.date,
555 )
556
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200557 def test_get_device_stats_no_heartbeats(self):
558 """Test getting device stats for a device without heartbeats."""
559 # Create a device and crashreport
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100560 device = Dummy.create_device(Dummy.create_user())
561 Dummy.create_report(Crashreport, device)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200562
563 # Get the device statistics
564 response = self._get_with_params(
565 self.device_overview_url, {"uuid": device.uuid}
566 )
567
568 # Assert that the statistics match
569 self._assert_device_stats_response_is(
570 response=response,
571 uuid=str(device.uuid),
572 board_date=device.board_date,
573 num_heartbeats=0,
574 num_crashreports=1,
575 num_smpls=0,
576 crashes_per_day=0.0,
577 smpl_per_day=0.0,
578 last_active=device.board_date,
579 )
580
581 def test_get_device_stats(self):
582 """Test getting device stats for a device."""
583 # Create a device with a heartbeat and one report of each type
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100584 device = Dummy.create_device(Dummy.create_user())
585 crashreport_date = Dummy.DEFAULT_CRASHREPORT_VALUES["date"]
586 heartbeat = Dummy.create_report(
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100587 HeartBeat, device, date=crashreport_date.date()
588 )
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200589 for boot_reason in (
590 Crashreport.SMPL_BOOT_REASONS
591 + Crashreport.CRASH_BOOT_REASONS
592 + ["other boot reason"]
593 ):
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100594 Dummy.create_report(
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100595 Crashreport,
596 device,
597 boot_reason=boot_reason,
598 date=crashreport_date,
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200599 )
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100600 crashreport_date += timedelta(milliseconds=1)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200601
602 # Get the device statistics
603 response = self._get_with_params(
604 self.device_overview_url, {"uuid": device.uuid}
605 )
606
607 # Assert that the statistics match
608 self._assert_device_stats_response_is(
609 response=response,
610 uuid=str(device.uuid),
611 board_date=device.board_date,
612 num_heartbeats=1,
613 num_crashreports=len(Crashreport.CRASH_BOOT_REASONS),
614 num_smpls=len(Crashreport.SMPL_BOOT_REASONS),
615 crashes_per_day=len(Crashreport.CRASH_BOOT_REASONS),
616 smpl_per_day=len(Crashreport.SMPL_BOOT_REASONS),
617 last_active=heartbeat.date,
618 )
619
620 def test_get_device_stats_multiple_days(self):
621 """Test getting device stats for a device that sent more reports."""
622 # Create a device with some heartbeats and reports over time
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100623 device = Dummy.create_device(Dummy.create_user())
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200624 num_days = 100
625 for i in range(num_days):
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100626 report_date = datetime.now(tz=pytz.utc) + timedelta(days=i)
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100627 heartbeat = Dummy.create_report(
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100628 HeartBeat, device, date=report_date.date()
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200629 )
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100630 Dummy.create_report(Crashreport, device, date=report_date)
631 Dummy.create_report(
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200632 Crashreport,
633 device,
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100634 date=report_date + timedelta(minutes=1),
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200635 boot_reason=Crashreport.SMPL_BOOT_REASONS[0],
636 )
637
638 # Get the device statistics
639 response = self._get_with_params(
640 self.device_overview_url, {"uuid": device.uuid}
641 )
642
643 # Assert that the statistics match
644 self._assert_device_stats_response_is(
645 response=response,
646 uuid=str(device.uuid),
647 board_date=device.board_date,
648 num_heartbeats=num_days,
649 num_crashreports=num_days,
650 num_smpls=num_days,
651 crashes_per_day=1,
652 smpl_per_day=1,
653 last_active=heartbeat.date,
654 )
655
656 def test_get_device_stats_multiple_days_missing_heartbeat(self):
657 """Test getting device stats for a device with missing heartbeat."""
658 # Create a device with some heartbeats and reports over time
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100659 device = Dummy.create_device(Dummy.create_user())
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200660 num_days = 100
661 skip_day = round(num_days / 2)
662 for i in range(num_days):
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100663 report_date = datetime.now(tz=pytz.utc) + timedelta(days=i)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200664 # Skip creation of heartbeat at one day
665 if i != skip_day:
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100666 heartbeat = Dummy.create_report(
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100667 HeartBeat, device, date=report_date.date()
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200668 )
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100669 Dummy.create_report(Crashreport, device, date=report_date)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200670
671 # Get the device statistics
672 response = self._get_with_params(
673 self.device_overview_url, {"uuid": device.uuid}
674 )
675
676 # Assert that the statistics match
677 self._assert_device_stats_response_is(
678 response=response,
679 uuid=str(device.uuid),
680 board_date=device.board_date,
681 num_heartbeats=num_days - 1,
682 num_crashreports=num_days,
683 num_smpls=0,
684 crashes_per_day=num_days / (num_days - 1),
685 smpl_per_day=0,
686 last_active=heartbeat.date,
687 )
688
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200689 def test_get_device_report_history_no_reports(self):
690 """Test getting report history stats for a device without reports."""
691 # Create a device
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100692 device = Dummy.create_device(Dummy.create_user())
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200693
694 # Get the device report history statistics
695 response = self._get_with_params(
696 self.device_report_history_url, {"uuid": device.uuid}
697 )
698
699 # Assert that the report history is empty
700 self.assertEqual([], response.data)
701
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200702 def test_get_device_report_history(self):
703 """Test getting report history stats for a device."""
704 # Create a device with a heartbeat and one report of each type
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100705 device = Dummy.create_device(Dummy.create_user())
706 crashreport_date = Dummy.DEFAULT_CRASHREPORT_VALUES["date"]
707 heartbeat = Dummy.create_report(
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100708 HeartBeat, device, date=crashreport_date.date()
709 )
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200710 for boot_reason in (
711 Crashreport.SMPL_BOOT_REASONS
712 + Crashreport.CRASH_BOOT_REASONS
713 + ["other boot reason"]
714 ):
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100715 Dummy.create_report(
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100716 Crashreport,
717 device,
718 boot_reason=boot_reason,
719 date=crashreport_date,
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200720 )
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100721 crashreport_date += timedelta(milliseconds=1)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200722
723 # Get the device report history statistics
724 response = self._get_with_params(
725 self.device_report_history_url, {"uuid": device.uuid}
726 )
727
728 # Assert that the statistics match
729 report_history = [
730 {
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100731 "date": heartbeat.date,
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200732 "heartbeats": 1,
733 "smpl": len(Crashreport.SMPL_BOOT_REASONS),
734 "prob_crashes": len(Crashreport.CRASH_BOOT_REASONS),
735 "other": 1,
736 }
737 ]
738 self.assertEqual(report_history, response.data)
739
Mitja Nikolaus21f3b1b2018-11-02 17:10:02 +0100740 def test_get_device_report_history_multiple_days(self):
741 """Test getting report history stats for a device for multiple days."""
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100742 device = Dummy.create_device(Dummy.create_user())
Mitja Nikolaus21f3b1b2018-11-02 17:10:02 +0100743 expected_report_history = []
744
745 # Create a device with a heartbeat and one report of each type for 10
746 # days
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100747 report_date = Dummy.DEFAULT_CRASHREPORT_VALUES["date"]
Mitja Nikolaus21f3b1b2018-11-02 17:10:02 +0100748 for _ in range(10):
749 report_date = report_date + timedelta(days=1)
750
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100751 Dummy.create_report(HeartBeat, device, date=report_date.date())
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100752 for i, boot_reason in enumerate(
Mitja Nikolaus21f3b1b2018-11-02 17:10:02 +0100753 Crashreport.SMPL_BOOT_REASONS
754 + Crashreport.CRASH_BOOT_REASONS
755 + ["other boot reason"]
756 ):
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100757 Dummy.create_report(
Mitja Nikolaus21f3b1b2018-11-02 17:10:02 +0100758 Crashreport,
759 device,
760 boot_reason=boot_reason,
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100761 date=report_date + timedelta(milliseconds=i),
Mitja Nikolaus21f3b1b2018-11-02 17:10:02 +0100762 )
763
764 # Create the expected report history object
765 expected_report_history.append(
766 {
767 "date": report_date.date(),
768 "heartbeats": 1,
769 "smpl": len(Crashreport.SMPL_BOOT_REASONS),
770 "prob_crashes": len(Crashreport.CRASH_BOOT_REASONS),
771 "other": 1,
772 }
773 )
774
775 # Sort the expected values by date
776 expected_report_history.sort(key=operator.itemgetter("date"))
777
778 # Get the device report history statistics
779 response = self._get_with_params(
780 self.device_report_history_url, {"uuid": device.uuid}
781 )
782
783 # Assert that the statistics match
784 self.assertEqual(expected_report_history, response.data)
785
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200786 def test_get_device_update_history_no_reports(self):
787 """Test getting update history stats for a device without reports."""
788 # Create a device
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100789 device = Dummy.create_device(Dummy.create_user())
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200790
791 # Get the device report history statistics
792 response = self._get_with_params(
793 self.device_update_history_url, {"uuid": device.uuid}
794 )
795
796 # Assert that the update history is empty
797 self.assertEqual([], response.data)
798
799 def test_get_device_update_history(self):
800 """Test getting update history stats for a device."""
801 # Create a device with a heartbeat and one report of each type
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100802 device = Dummy.create_device(Dummy.create_user())
803 crashreport_date = Dummy.DEFAULT_CRASHREPORT_VALUES["date"]
804 heartbeat = Dummy.create_report(
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100805 HeartBeat, device, date=crashreport_date.date()
806 )
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200807 for boot_reason in (
808 Crashreport.SMPL_BOOT_REASONS
809 + Crashreport.CRASH_BOOT_REASONS
810 + ["other boot reason"]
811 ):
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100812 params = {"boot_reason": boot_reason, "date": crashreport_date}
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100813 Dummy.create_report(Crashreport, device, **params)
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100814 crashreport_date += timedelta(milliseconds=1)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200815
816 # Get the device update history statistics
817 response = self._get_with_params(
818 self.device_update_history_url, {"uuid": device.uuid}
819 )
820
821 # Assert that the statistics match
822 update_history = [
823 {
824 "build_fingerprint": heartbeat.build_fingerprint,
825 "heartbeats": 1,
826 "max": device.id,
827 "other": 1,
828 "prob_crashes": len(Crashreport.CRASH_BOOT_REASONS),
829 "smpl": len(Crashreport.SMPL_BOOT_REASONS),
830 "update_date": heartbeat.date,
831 }
832 ]
833 self.assertEqual(update_history, response.data)
834
835 def test_get_device_update_history_multiple_updates(self):
836 """Test getting update history stats with multiple updates."""
837 # Create a device with a heartbeats and crashreport for each build
838 # fingerprint in the dummy values
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100839 device = Dummy.create_device(Dummy.create_user())
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200840 expected_update_history = []
841 for i, build_fingerprint in enumerate(Dummy.BUILD_FINGERPRINTS):
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100842 report_date = datetime.now(tz=pytz.utc) + timedelta(days=i)
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100843 Dummy.create_report(
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200844 HeartBeat,
845 device,
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100846 date=report_date,
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200847 build_fingerprint=build_fingerprint,
848 )
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100849 Dummy.create_report(
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200850 Crashreport,
851 device,
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100852 date=report_date,
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200853 build_fingerprint=build_fingerprint,
854 )
855
856 # Create the expected update history object
857 expected_update_history.append(
858 {
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100859 "update_date": report_date.date(),
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200860 "build_fingerprint": build_fingerprint,
861 "max": device.id,
862 "prob_crashes": 1,
863 "smpl": 0,
864 "other": 0,
865 "heartbeats": 1,
866 }
867 )
Mitja Nikolaus21f3b1b2018-11-02 17:10:02 +0100868 # Sort the expected values by update date
869 expected_update_history.sort(key=operator.itemgetter("update_date"))
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200870
Mitja Nikolaus21f3b1b2018-11-02 17:10:02 +0100871 # Get the device update history statistics
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200872 response = self._get_with_params(
873 self.device_update_history_url, {"uuid": device.uuid}
874 )
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200875
876 # Assert that the statistics match
877 self.assertEqual(expected_update_history, response.data)
878
879 def test_download_non_existing_logfile(self):
880 """Test download of a non existing log file."""
881 # Try to get a log file
882 response = self._get_with_params(
883 self.device_logfile_download_url, {"id_logfile": 0}
884 )
885
886 # Assert that the log file was not found
887 self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
888
889 def test_download_logfile(self):
890 """Test download of log files."""
891 # Create a device with a crash report along with log file
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100892 device = Dummy.create_device(Dummy.create_user())
893 crashreport = Dummy.create_report(Crashreport, device)
894 logfile = Dummy.create_log_file(crashreport)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200895
896 # Get the log file
897 response = self._get_with_params(
898 self.device_logfile_download_url, {"id_logfile": logfile.id}
899 )
900
901 # Assert that the log file contents are in the response data
902 self.assertEqual(response.status_code, status.HTTP_200_OK)
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100903 self.assertIn(Dummy.DEFAULT_LOG_FILE_NAME, response.data)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200904 expected_logfile_content = Dummy.read_logfile_contents(
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100905 logfile.logfile.path, Dummy.DEFAULT_LOG_FILE_NAME
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200906 )
907 self.assertEqual(
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100908 response.data[Dummy.DEFAULT_LOG_FILE_NAME], expected_logfile_content
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200909 )