blob: 8708f3d2d09a30c14393e9b205d26390ea9c6cc8 [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
60 device = Dummy.create_dummy_device(Dummy.create_dummy_user())
61 Dummy.create_dummy_report(HeartBeat, device)
62 Dummy.create_dummy_report(Crashreport, device)
63
64 # Create a second device without any reports
65 Dummy.create_dummy_device(
66 Dummy.create_dummy_user(username=Dummy.USERNAMES[1])
67 )
68
69 # Assert that the status includes the appropriate numbers (a third
70 # device was created by the setUpTestData() method)
71 response = self.fp_staff_client.get(self.status_url)
72 self._assert_status_response_is(
73 response, num_devices=3, num_crashreports=1, num_heartbeats=1
74 )
75
76
77class _VersionTestCase(HiccupStatsAPITestCase):
78 """Abstract class for version-related test cases to inherit from."""
79
80 @staticmethod
81 def _create_dummy_version(**kwargs):
82 return Dummy.create_dummy_version(**kwargs)
83
84 def _get_with_params(self, url, params):
Mitja Nikolause0e83772018-11-05 10:00:53 +010085 return self.fp_staff_client.get("{}?{}".format(url, urlencode(params)))
Mitja Nikolaus03e412b2018-09-18 17:50:15 +020086
87 def _assert_result_length_is(self, response, count):
88 self.assertEqual(response.status_code, status.HTTP_200_OK)
89 self.assertIn("results", response.data)
90 self.assertIn("count", response.data)
91 self.assertEqual(response.data["count"], count)
92 self.assertEqual(len(response.data["results"]), count)
93
94 def _assert_filter_result_matches(
95 self, endpoint_url, unique_entry_name, filter_params, expected_result
96 ):
97 # List entities with filter
98 response = self._get_with_params(endpoint_url, filter_params)
99
100 # Expect only the single matching result to be returned
101 self._assert_result_length_is(response, 1)
102 self.assertEqual(
103 response.data["results"][0][unique_entry_name],
104 getattr(expected_result, unique_entry_name),
105 )
106
107
108class VersionTestCase(_VersionTestCase):
109 """Test the Version and REST endpoint."""
110
111 # pylint: disable=too-many-ancestors
112
113 # The attribute name characterising the unicity of a stats entry (the
114 # named identifier)
115 unique_entry_name = "build_fingerprint"
116 # The collection of unique entries to post
117 unique_entries = Dummy.BUILD_FINGERPRINTS
118 # The URL to retrieve the stats entries from
119 endpoint_url = reverse("hiccup_stats_api_v1_versions")
120
121 def _create_version_entities(self):
122 versions = [
123 self._create_dummy_version(**{self.unique_entry_name: unique_entry})
124 for unique_entry in self.unique_entries
125 ]
126 return versions
127
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200128 def test_endpoint_url_as_fp_staff(self):
129 """Test that Fairphone staff users can access the endpoint URL."""
130 self._assert_get_as_fp_staff_succeeds(self.endpoint_url)
131
132 def test_endpoint_url_as_device_owner(self):
133 """Test that device owner users can not access the endpoint URL."""
134 self._assert_get_as_device_owner_fails(self.endpoint_url)
135
136 def test_endpoint_url_no_auth(self):
137 """Test that non-authenticated users can not access the endpoint URL."""
138 self._assert_get_without_authentication_fails(self.endpoint_url)
139
140 def test_list_versions_empty_database(self):
141 """Test listing of versions on an empty database."""
Mitja Nikolause0e83772018-11-05 10:00:53 +0100142 response = self.fp_staff_client.get(self.endpoint_url)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200143 self._assert_result_length_is(response, 0)
144
145 def test_list_versions(self):
146 """Test listing versions."""
147 versions = self._create_version_entities()
Mitja Nikolause0e83772018-11-05 10:00:53 +0100148 response = self.fp_staff_client.get(self.endpoint_url)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200149 self._assert_result_length_is(response, len(versions))
150
151 def test_filter_versions_by_unique_entry_name(self):
152 """Test filtering versions by their unique entry name."""
153 versions = self._create_version_entities()
Mitja Nikolause0e83772018-11-05 10:00:53 +0100154 response = self.fp_staff_client.get(self.endpoint_url)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200155
156 # Listing all entities should return the correct result length
157 self._assert_result_length_is(response, len(versions))
158
159 # List entities with filter
160 filter_params = {
161 self.unique_entry_name: getattr(versions[0], self.unique_entry_name)
162 }
163 self._assert_filter_result_matches(
164 self.endpoint_url,
165 self.unique_entry_name,
166 filter_params,
167 expected_result=versions[0],
168 )
169
170 def test_filter_versions_by_release_type(self):
171 """Test filtering versions by release type."""
172 # Create versions for all combinations of release types
173 versions = []
174 i = 0
175 for is_official_release in True, False:
176 for is_beta_release in True, False:
177 versions.append(
178 self._create_dummy_version(
179 **{
180 "is_official_release": is_official_release,
181 "is_beta_release": is_beta_release,
182 self.unique_entry_name: self.unique_entries[i],
183 }
184 )
185 )
186 i += 1
187
188 # # Listing all entities should return the correct result length
Mitja Nikolause0e83772018-11-05 10:00:53 +0100189 response = self.fp_staff_client.get(self.endpoint_url)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200190 self._assert_result_length_is(response, len(versions))
191
192 # List each of the entities with the matching filter params
193 for version in versions:
194 filter_params = {
195 "is_official_release": version.is_official_release,
196 "is_beta_release": version.is_beta_release,
197 }
198 self._assert_filter_result_matches(
199 self.endpoint_url,
200 self.unique_entry_name,
201 filter_params,
202 expected_result=version,
203 )
204
205 def test_filter_versions_by_first_seen_date(self):
206 """Test filtering versions by first seen date."""
207 versions = self._create_version_entities()
208
209 # Set the first seen date of an entity
210 versions[0].first_seen_on = Dummy.DATES[2]
211 versions[0].save()
212
213 # Listing all entities should return the correct result length
Mitja Nikolause0e83772018-11-05 10:00:53 +0100214 response = self.fp_staff_client.get(self.endpoint_url)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200215 self._assert_result_length_is(response, len(versions))
216
217 # Expect the single matching result to be returned
218 filter_params = {"first_seen_after": Dummy.DATES[2]}
219 self._assert_filter_result_matches(
220 self.endpoint_url,
221 self.unique_entry_name,
222 filter_params,
223 expected_result=versions[0],
224 )
225
226
227# pylint: disable=too-many-ancestors
228class RadioVersionTestCase(VersionTestCase):
229 """Test the RadioVersion REST endpoint."""
230
231 unique_entry_name = "radio_version"
232 unique_entries = Dummy.RADIO_VERSIONS
233 endpoint_url = reverse("hiccup_stats_api_v1_radio_versions")
234
235 @staticmethod
236 def _create_dummy_version(**kwargs):
Mitja Nikolaus35a02652018-10-01 15:10:18 +0200237 return Dummy.create_dummy_version(RadioVersion, **kwargs)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200238
239
240class VersionDailyTestCase(_VersionTestCase):
241 """Test the VersionDaily REST endpoint."""
242
243 unique_entry_name = "build_fingerprint"
244 unique_entries = Dummy.BUILD_FINGERPRINTS
245 endpoint_url = reverse("hiccup_stats_api_v1_version_daily")
246
247 @staticmethod
248 def _create_dummy_daily_version(version, **kwargs):
249 return Dummy.create_dummy_daily_version(version, **kwargs)
250
251 def _create_version_entities(self):
252 versions = [
253 self._create_dummy_version(**{self.unique_entry_name: unique_entry})
254 for unique_entry in self.unique_entries
255 ]
256 versions_daily = [
257 self._create_dummy_daily_version(version=version)
258 for version in versions
259 ]
260 return versions_daily
261
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200262 def test_endpoint_url_as_fp_staff(self):
263 """Test that Fairphone staff users can access the endpoint URL."""
264 self._assert_get_as_fp_staff_succeeds(self.endpoint_url)
265
266 def test_endpoint_url_as_device_owner(self):
267 """Test that device owner users can not access the endpoint URL."""
268 self._assert_get_as_device_owner_fails(self.endpoint_url)
269
270 def test_endpoint_url_no_auth(self):
271 """Test that non-authenticated users can not access the endpoint URL."""
272 self._assert_get_without_authentication_fails(self.endpoint_url)
273
274 def test_list_daily_versions_empty_database(self):
275 """Test listing of daily versions on an empty database."""
Mitja Nikolause0e83772018-11-05 10:00:53 +0100276 response = self.fp_staff_client.get(self.endpoint_url)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200277 self._assert_result_length_is(response, 0)
278
279 def test_list_daily_versions(self):
280 """Test listing daily versions."""
281 versions_daily = self._create_version_entities()
Mitja Nikolause0e83772018-11-05 10:00:53 +0100282 response = self.fp_staff_client.get(self.endpoint_url)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200283 self._assert_result_length_is(response, len(versions_daily))
284
285 def test_filter_daily_versions_by_version(self):
286 """Test filtering versions by the version they relate to."""
287 # Create VersionDaily entities
288 versions = self._create_version_entities()
289
290 # Listing all entities should return the correct result length
Mitja Nikolause0e83772018-11-05 10:00:53 +0100291 response = self.fp_staff_client.get(self.endpoint_url)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200292 self._assert_result_length_is(response, len(versions))
293
294 # List entities with filter
295 param_name = "version__" + self.unique_entry_name
296 filter_params = {
297 param_name: getattr(versions[0].version, self.unique_entry_name)
298 }
299 self._assert_filter_result_matches(
300 self.endpoint_url,
301 self.unique_entry_name,
302 filter_params,
303 expected_result=versions[0].version,
304 )
305
306 def test_filter_daily_versions_by_date(self):
307 """Test filtering daily versions by date."""
308 # Create Version and VersionDaily entities
309 versions = self._create_version_entities()
310
311 # Update the date
312 versions[0].date = Dummy.DATES[2]
313 versions[0].save()
314
315 # Listing all entities should return the correct result length
Mitja Nikolause0e83772018-11-05 10:00:53 +0100316 response = self.fp_staff_client.get(self.endpoint_url)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200317 self._assert_result_length_is(response, len(versions))
318
319 # Expect the single matching result to be returned
320 filter_params = {"date": versions[0].date}
321 self._assert_filter_result_matches(
322 self.endpoint_url,
323 self.unique_entry_name,
324 filter_params,
325 expected_result=versions[0].version,
326 )
327
328
329class RadioVersionDailyTestCase(VersionDailyTestCase):
330 """Test the RadioVersionDaily REST endpoint."""
331
332 unique_entry_name = "radio_version"
333 unique_entries = Dummy.RADIO_VERSIONS
334 endpoint_url = reverse("hiccup_stats_api_v1_radio_version_daily")
335
336 @staticmethod
337 def _create_dummy_version(**kwargs):
Mitja Nikolaus35a02652018-10-01 15:10:18 +0200338 return Dummy.create_dummy_version(RadioVersion, **kwargs)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200339
340 @staticmethod
341 def _create_dummy_daily_version(version, **kwargs):
342 return Dummy.create_dummy_daily_radio_version(version, **kwargs)
343
344
Mitja Nikolaus7dc86722018-11-27 14:57:39 +0100345@override_settings(MEDIA_ROOT=Dummy.DEFAULT_DUMMY_LOG_FILE_DIRECTORY)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200346class DeviceStatsTestCase(HiccupStatsAPITestCase):
347 """Test the single device stats REST endpoints."""
348
349 device_overview_url = "hiccup_stats_api_v1_device_overview"
350 device_report_history_url = "hiccup_stats_api_v1_device_report_history"
351 device_update_history_url = "hiccup_stats_api_v1_device_update_history"
352 device_logfile_download_url = "hiccup_stats_api_v1_logfile_download"
353
354 def _get_with_params(self, url, params):
355 url = reverse(url, kwargs=params)
356 return self.fp_staff_client.get(url)
357
358 def _assert_device_stats_response_is(
359 self,
360 response,
361 uuid,
362 board_date,
363 num_heartbeats,
364 num_crashreports,
365 num_smpls,
366 crashes_per_day,
367 smpl_per_day,
368 last_active,
369 ):
370 # pylint: disable=too-many-arguments
371 self.assertEqual(response.status_code, status.HTTP_200_OK)
372
373 self.assertIn("uuid", response.data)
374 self.assertIn("board_date", response.data)
375 self.assertIn("heartbeats", response.data)
376 self.assertIn("crashreports", response.data)
377 self.assertIn("smpls", response.data)
378 self.assertIn("crashes_per_day", response.data)
379 self.assertIn("smpl_per_day", response.data)
380 self.assertIn("last_active", response.data)
381
382 self.assertEqual(response.data["uuid"], uuid)
383 self.assertEqual(response.data["board_date"], board_date)
384 self.assertEqual(response.data["heartbeats"], num_heartbeats)
385 self.assertEqual(response.data["crashreports"], num_crashreports)
386 self.assertEqual(response.data["smpls"], num_smpls)
387 self.assertEqual(response.data["crashes_per_day"], crashes_per_day)
388 self.assertEqual(response.data["smpl_per_day"], smpl_per_day)
389 self.assertEqual(response.data["last_active"], last_active)
390
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200391 def test_device_overview_url_as_fp_staff(self):
392 """Test that Fairphone staff users can access the URL."""
393 self._assert_get_as_fp_staff_succeeds(
394 reverse(
395 self.device_overview_url,
396 kwargs={"uuid": self.device_owner_device.uuid},
397 )
398 )
399
400 def test_device_overview_url_as_device_owner(self):
401 """Test that device owner users can not access the URL."""
402 self._assert_get_as_device_owner_fails(
403 reverse(
404 self.device_overview_url,
405 kwargs={"uuid": self.device_owner_device.uuid},
406 )
407 )
408
409 def test_device_overview_url_no_auth(self):
410 """Test that non-authenticated users can not access the URL."""
411 self._assert_get_without_authentication_fails(
412 reverse(
413 self.device_overview_url,
414 kwargs={"uuid": self.device_owner_device.uuid},
415 )
416 )
417
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200418 def test_device_report_history_url_as_fp_staff(self):
419 """Test that FP staff can access device report history URL."""
420 self._assert_get_as_fp_staff_succeeds(
421 reverse(
422 self.device_report_history_url,
423 kwargs={"uuid": self.device_owner_device.uuid},
424 )
425 )
426
427 def test_device_report_history_url_as_device_owner(self):
428 """Test that device owners can't access device report history URL."""
429 self._assert_get_as_device_owner_fails(
430 reverse(
431 self.device_report_history_url,
432 kwargs={"uuid": self.device_owner_device.uuid},
433 )
434 )
435
436 def test_device_report_history_url_no_auth(self):
437 """Test that device report history is not accessible without auth."""
438 self._assert_get_without_authentication_fails(
439 reverse(
440 self.device_report_history_url,
441 kwargs={"uuid": self.device_owner_device.uuid},
442 )
443 )
444
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200445 def test_device_update_history_url_as_fp_staff(self):
446 """Test that FP staff can access device update history URL."""
447 self._assert_get_as_fp_staff_succeeds(
448 reverse(
449 self.device_update_history_url,
450 kwargs={"uuid": self.device_owner_device.uuid},
451 )
452 )
453
454 def test_device_update_history_url_as_device_owner(self):
455 """Test that device owners can't access device update history URL."""
456 self._assert_get_as_device_owner_fails(
457 reverse(
458 self.device_update_history_url,
459 kwargs={"uuid": self.device_owner_device.uuid},
460 )
461 )
462
463 def test_device_update_history_url_no_auth(self):
464 """Test that device update history is not accessible without auth."""
465 self._assert_get_without_authentication_fails(
466 reverse(
467 self.device_update_history_url,
468 kwargs={"uuid": self.device_owner_device.uuid},
469 )
470 )
471
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200472 def tes_logfile_download_url_as_fp_staff(self):
473 """Test that FP staff can access the logfile download URL."""
474 non_existent_logfile_id = 0
475 self.assertFalse(
476 LogFile.objects.filter(id=non_existent_logfile_id).exists()
477 )
478 self._assert_get_as_fp_staff_succeeds(
479 reverse(
480 self.device_logfile_download_url,
481 kwargs={"id_logfile": non_existent_logfile_id},
482 ),
483 expected_status=status.HTTP_404_NOT_FOUND,
484 )
485
486 def test_logfile_download_url_as_device_owner(self):
487 """Test that device owners can't access the logfile download URL."""
488 non_existent_logfile_id = 0
489 self.assertFalse(
490 LogFile.objects.filter(id=non_existent_logfile_id).exists()
491 )
492 self._assert_get_as_device_owner_fails(
493 reverse(
494 self.device_logfile_download_url,
495 kwargs={"id_logfile": non_existent_logfile_id},
496 )
497 )
498
499 def test_logfile_download_url_no_auth(self):
500 """Test that the logfile download URL is not accessible without auth."""
501 non_existent_logfile_id = 0
502 self.assertFalse(
503 LogFile.objects.filter(id=non_existent_logfile_id).exists()
504 )
505 self._assert_get_without_authentication_fails(
506 reverse(
507 self.device_logfile_download_url,
508 kwargs={"id_logfile": non_existent_logfile_id},
509 )
510 )
511
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200512 def test_get_device_stats_no_reports(self):
513 """Test getting device stats for a device without reports."""
514 # Create a device
515 device = Dummy.create_dummy_device(Dummy.create_dummy_user())
516
517 # Get the device statistics
518 response = self._get_with_params(
519 self.device_overview_url, {"uuid": device.uuid}
520 )
521
522 # Assert that the statistics match
523 self._assert_device_stats_response_is(
524 response=response,
525 uuid=str(device.uuid),
526 board_date=device.board_date,
527 num_heartbeats=0,
528 num_crashreports=0,
529 num_smpls=0,
530 crashes_per_day=0.0,
531 smpl_per_day=0.0,
532 last_active=device.board_date,
533 )
534
535 def test_get_device_stats_no_crash_reports(self):
536 """Test getting device stats for a device without crashreports."""
537 # Create a device and a heartbeat
538 device = Dummy.create_dummy_device(Dummy.create_dummy_user())
539 heartbeat = Dummy.create_dummy_report(HeartBeat, device)
540
541 # Get the device statistics
542 response = self._get_with_params(
543 self.device_overview_url, {"uuid": device.uuid}
544 )
545
546 # Assert that the statistics match
547 self._assert_device_stats_response_is(
548 response=response,
549 uuid=str(device.uuid),
550 board_date=device.board_date,
551 num_heartbeats=1,
552 num_crashreports=0,
553 num_smpls=0,
554 crashes_per_day=0.0,
555 smpl_per_day=0.0,
556 last_active=heartbeat.date,
557 )
558
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200559 def test_get_device_stats_no_heartbeats(self):
560 """Test getting device stats for a device without heartbeats."""
561 # Create a device and crashreport
562 device = Dummy.create_dummy_device(Dummy.create_dummy_user())
563 Dummy.create_dummy_report(Crashreport, device)
564
565 # Get the device statistics
566 response = self._get_with_params(
567 self.device_overview_url, {"uuid": device.uuid}
568 )
569
570 # Assert that the statistics match
571 self._assert_device_stats_response_is(
572 response=response,
573 uuid=str(device.uuid),
574 board_date=device.board_date,
575 num_heartbeats=0,
576 num_crashreports=1,
577 num_smpls=0,
578 crashes_per_day=0.0,
579 smpl_per_day=0.0,
580 last_active=device.board_date,
581 )
582
583 def test_get_device_stats(self):
584 """Test getting device stats for a device."""
585 # Create a device with a heartbeat and one report of each type
586 device = Dummy.create_dummy_device(Dummy.create_dummy_user())
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100587 crashreport_date = Dummy.DEFAULT_DUMMY_CRASHREPORT_VALUES["date"]
588 heartbeat = Dummy.create_dummy_report(
589 HeartBeat, device, date=crashreport_date.date()
590 )
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200591 for boot_reason in (
592 Crashreport.SMPL_BOOT_REASONS
593 + Crashreport.CRASH_BOOT_REASONS
594 + ["other boot reason"]
595 ):
596 Dummy.create_dummy_report(
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100597 Crashreport,
598 device,
599 boot_reason=boot_reason,
600 date=crashreport_date,
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200601 )
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100602 crashreport_date += timedelta(milliseconds=1)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200603
604 # Get the device statistics
605 response = self._get_with_params(
606 self.device_overview_url, {"uuid": device.uuid}
607 )
608
609 # Assert that the statistics match
610 self._assert_device_stats_response_is(
611 response=response,
612 uuid=str(device.uuid),
613 board_date=device.board_date,
614 num_heartbeats=1,
615 num_crashreports=len(Crashreport.CRASH_BOOT_REASONS),
616 num_smpls=len(Crashreport.SMPL_BOOT_REASONS),
617 crashes_per_day=len(Crashreport.CRASH_BOOT_REASONS),
618 smpl_per_day=len(Crashreport.SMPL_BOOT_REASONS),
619 last_active=heartbeat.date,
620 )
621
622 def test_get_device_stats_multiple_days(self):
623 """Test getting device stats for a device that sent more reports."""
624 # Create a device with some heartbeats and reports over time
625 device = Dummy.create_dummy_device(Dummy.create_dummy_user())
626 num_days = 100
627 for i in range(num_days):
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100628 report_date = datetime.now(tz=pytz.utc) + timedelta(days=i)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200629 heartbeat = Dummy.create_dummy_report(
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100630 HeartBeat, device, date=report_date.date()
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200631 )
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100632 Dummy.create_dummy_report(Crashreport, device, date=report_date)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200633 Dummy.create_dummy_report(
634 Crashreport,
635 device,
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100636 date=report_date + timedelta(minutes=1),
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200637 boot_reason=Crashreport.SMPL_BOOT_REASONS[0],
638 )
639
640 # Get the device statistics
641 response = self._get_with_params(
642 self.device_overview_url, {"uuid": device.uuid}
643 )
644
645 # Assert that the statistics match
646 self._assert_device_stats_response_is(
647 response=response,
648 uuid=str(device.uuid),
649 board_date=device.board_date,
650 num_heartbeats=num_days,
651 num_crashreports=num_days,
652 num_smpls=num_days,
653 crashes_per_day=1,
654 smpl_per_day=1,
655 last_active=heartbeat.date,
656 )
657
658 def test_get_device_stats_multiple_days_missing_heartbeat(self):
659 """Test getting device stats for a device with missing heartbeat."""
660 # Create a device with some heartbeats and reports over time
661 device = Dummy.create_dummy_device(Dummy.create_dummy_user())
662 num_days = 100
663 skip_day = round(num_days / 2)
664 for i in range(num_days):
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100665 report_date = datetime.now(tz=pytz.utc) + timedelta(days=i)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200666 # Skip creation of heartbeat at one day
667 if i != skip_day:
668 heartbeat = Dummy.create_dummy_report(
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100669 HeartBeat, device, date=report_date.date()
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200670 )
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100671 Dummy.create_dummy_report(Crashreport, device, date=report_date)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200672
673 # Get the device statistics
674 response = self._get_with_params(
675 self.device_overview_url, {"uuid": device.uuid}
676 )
677
678 # Assert that the statistics match
679 self._assert_device_stats_response_is(
680 response=response,
681 uuid=str(device.uuid),
682 board_date=device.board_date,
683 num_heartbeats=num_days - 1,
684 num_crashreports=num_days,
685 num_smpls=0,
686 crashes_per_day=num_days / (num_days - 1),
687 smpl_per_day=0,
688 last_active=heartbeat.date,
689 )
690
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200691 def test_get_device_report_history_no_reports(self):
692 """Test getting report history stats for a device without reports."""
693 # Create a device
694 device = Dummy.create_dummy_device(Dummy.create_dummy_user())
695
696 # Get the device report history statistics
697 response = self._get_with_params(
698 self.device_report_history_url, {"uuid": device.uuid}
699 )
700
701 # Assert that the report history is empty
702 self.assertEqual([], response.data)
703
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200704 def test_get_device_report_history(self):
705 """Test getting report history stats for a device."""
706 # Create a device with a heartbeat and one report of each type
707 device = Dummy.create_dummy_device(Dummy.create_dummy_user())
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100708 crashreport_date = Dummy.DEFAULT_DUMMY_CRASHREPORT_VALUES["date"]
709 heartbeat = Dummy.create_dummy_report(
710 HeartBeat, device, date=crashreport_date.date()
711 )
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200712 for boot_reason in (
713 Crashreport.SMPL_BOOT_REASONS
714 + Crashreport.CRASH_BOOT_REASONS
715 + ["other boot reason"]
716 ):
717 Dummy.create_dummy_report(
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100718 Crashreport,
719 device,
720 boot_reason=boot_reason,
721 date=crashreport_date,
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200722 )
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100723 crashreport_date += timedelta(milliseconds=1)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200724
725 # Get the device report history statistics
726 response = self._get_with_params(
727 self.device_report_history_url, {"uuid": device.uuid}
728 )
729
730 # Assert that the statistics match
731 report_history = [
732 {
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100733 "date": heartbeat.date,
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200734 "heartbeats": 1,
735 "smpl": len(Crashreport.SMPL_BOOT_REASONS),
736 "prob_crashes": len(Crashreport.CRASH_BOOT_REASONS),
737 "other": 1,
738 }
739 ]
740 self.assertEqual(report_history, response.data)
741
Mitja Nikolaus21f3b1b2018-11-02 17:10:02 +0100742 def test_get_device_report_history_multiple_days(self):
743 """Test getting report history stats for a device for multiple days."""
744 device = Dummy.create_dummy_device(Dummy.create_dummy_user())
745 expected_report_history = []
746
747 # Create a device with a heartbeat and one report of each type for 10
748 # days
749 report_date = Dummy.DEFAULT_DUMMY_CRASHREPORT_VALUES["date"]
750 for _ in range(10):
751 report_date = report_date + timedelta(days=1)
752
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100753 Dummy.create_dummy_report(
754 HeartBeat, device, date=report_date.date()
755 )
756 for i, boot_reason in enumerate(
Mitja Nikolaus21f3b1b2018-11-02 17:10:02 +0100757 Crashreport.SMPL_BOOT_REASONS
758 + Crashreport.CRASH_BOOT_REASONS
759 + ["other boot reason"]
760 ):
761 Dummy.create_dummy_report(
762 Crashreport,
763 device,
764 boot_reason=boot_reason,
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100765 date=report_date + timedelta(milliseconds=i),
Mitja Nikolaus21f3b1b2018-11-02 17:10:02 +0100766 )
767
768 # Create the expected report history object
769 expected_report_history.append(
770 {
771 "date": report_date.date(),
772 "heartbeats": 1,
773 "smpl": len(Crashreport.SMPL_BOOT_REASONS),
774 "prob_crashes": len(Crashreport.CRASH_BOOT_REASONS),
775 "other": 1,
776 }
777 )
778
779 # Sort the expected values by date
780 expected_report_history.sort(key=operator.itemgetter("date"))
781
782 # Get the device report history statistics
783 response = self._get_with_params(
784 self.device_report_history_url, {"uuid": device.uuid}
785 )
786
787 # Assert that the statistics match
788 self.assertEqual(expected_report_history, response.data)
789
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200790 def test_get_device_update_history_no_reports(self):
791 """Test getting update history stats for a device without reports."""
792 # Create a device
793 device = Dummy.create_dummy_device(Dummy.create_dummy_user())
794
795 # Get the device report history statistics
796 response = self._get_with_params(
797 self.device_update_history_url, {"uuid": device.uuid}
798 )
799
800 # Assert that the update history is empty
801 self.assertEqual([], response.data)
802
803 def test_get_device_update_history(self):
804 """Test getting update history stats for a device."""
805 # Create a device with a heartbeat and one report of each type
806 device = Dummy.create_dummy_device(Dummy.create_dummy_user())
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100807 crashreport_date = Dummy.DEFAULT_DUMMY_CRASHREPORT_VALUES["date"]
808 heartbeat = Dummy.create_dummy_report(
809 HeartBeat, device, date=crashreport_date.date()
810 )
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200811 for boot_reason in (
812 Crashreport.SMPL_BOOT_REASONS
813 + Crashreport.CRASH_BOOT_REASONS
814 + ["other boot reason"]
815 ):
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100816 params = {"boot_reason": boot_reason, "date": crashreport_date}
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200817 Dummy.create_dummy_report(Crashreport, device, **params)
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100818 crashreport_date += timedelta(milliseconds=1)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200819
820 # Get the device update history statistics
821 response = self._get_with_params(
822 self.device_update_history_url, {"uuid": device.uuid}
823 )
824
825 # Assert that the statistics match
826 update_history = [
827 {
828 "build_fingerprint": heartbeat.build_fingerprint,
829 "heartbeats": 1,
830 "max": device.id,
831 "other": 1,
832 "prob_crashes": len(Crashreport.CRASH_BOOT_REASONS),
833 "smpl": len(Crashreport.SMPL_BOOT_REASONS),
834 "update_date": heartbeat.date,
835 }
836 ]
837 self.assertEqual(update_history, response.data)
838
839 def test_get_device_update_history_multiple_updates(self):
840 """Test getting update history stats with multiple updates."""
841 # Create a device with a heartbeats and crashreport for each build
842 # fingerprint in the dummy values
843 device = Dummy.create_dummy_device(Dummy.create_dummy_user())
844 expected_update_history = []
845 for i, build_fingerprint in enumerate(Dummy.BUILD_FINGERPRINTS):
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100846 report_date = datetime.now(tz=pytz.utc) + timedelta(days=i)
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200847 Dummy.create_dummy_report(
848 HeartBeat,
849 device,
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100850 date=report_date,
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200851 build_fingerprint=build_fingerprint,
852 )
853 Dummy.create_dummy_report(
854 Crashreport,
855 device,
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100856 date=report_date,
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200857 build_fingerprint=build_fingerprint,
858 )
859
860 # Create the expected update history object
861 expected_update_history.append(
862 {
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100863 "update_date": report_date.date(),
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200864 "build_fingerprint": build_fingerprint,
865 "max": device.id,
866 "prob_crashes": 1,
867 "smpl": 0,
868 "other": 0,
869 "heartbeats": 1,
870 }
871 )
Mitja Nikolaus21f3b1b2018-11-02 17:10:02 +0100872 # Sort the expected values by update date
873 expected_update_history.sort(key=operator.itemgetter("update_date"))
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200874
Mitja Nikolaus21f3b1b2018-11-02 17:10:02 +0100875 # Get the device update history statistics
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200876 response = self._get_with_params(
877 self.device_update_history_url, {"uuid": device.uuid}
878 )
Mitja Nikolaus03e412b2018-09-18 17:50:15 +0200879
880 # Assert that the statistics match
881 self.assertEqual(expected_update_history, response.data)
882
883 def test_download_non_existing_logfile(self):
884 """Test download of a non existing log file."""
885 # Try to get a log file
886 response = self._get_with_params(
887 self.device_logfile_download_url, {"id_logfile": 0}
888 )
889
890 # Assert that the log file was not found
891 self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
892
893 def test_download_logfile(self):
894 """Test download of log files."""
895 # Create a device with a crash report along with log file
896 device = Dummy.create_dummy_device(Dummy.create_dummy_user())
897 crashreport = Dummy.create_dummy_report(Crashreport, device)
898 logfile = Dummy.create_dummy_log_file(crashreport)
899
900 # Get the log file
901 response = self._get_with_params(
902 self.device_logfile_download_url, {"id_logfile": logfile.id}
903 )
904
905 # Assert that the log file contents are in the response data
906 self.assertEqual(response.status_code, status.HTTP_200_OK)
907 self.assertIn(Dummy.DEFAULT_DUMMY_LOG_FILE_NAME, response.data)
908 expected_logfile_content = Dummy.read_logfile_contents(
909 logfile.logfile.path, Dummy.DEFAULT_DUMMY_LOG_FILE_NAME
910 )
911 self.assertEqual(
912 response.data[Dummy.DEFAULT_DUMMY_LOG_FILE_NAME],
913 expected_logfile_content,
914 )