Mitja Nikolaus | fd452f8 | 2018-11-07 11:53:59 +0100 | [diff] [blame] | 1 | """Tests for the Django database migrations.""" |
| 2 | import logging |
| 3 | import os |
| 4 | import tempfile |
| 5 | from datetime import datetime, date |
| 6 | |
| 7 | import pytz |
| 8 | from django.test import TransactionTestCase, override_settings |
| 9 | from django.db.migrations.executor import MigrationExecutor |
| 10 | from django.db import connection |
| 11 | |
| 12 | from crashreports.models import Crashreport, HeartBeat, LogFile |
| 13 | from crashreports.tests.utils import Dummy |
| 14 | |
| 15 | |
| 16 | class MigrationTestCase(TransactionTestCase): |
| 17 | """Test for Django database migrations.""" |
| 18 | |
| 19 | # Make data from migrations available in the test cases |
| 20 | serialized_rollback = True |
| 21 | |
| 22 | # These must be defined by subclasses. |
| 23 | migrate_from = None |
| 24 | migrate_to = None |
| 25 | |
| 26 | def setUp(self): |
| 27 | """Set up the database up to the state of the first migration.""" |
| 28 | super(MigrationTestCase, self).setUp() |
| 29 | |
| 30 | self.executor = MigrationExecutor(connection) |
| 31 | self.executor.migrate(self.migrate_from) |
| 32 | |
| 33 | def migrate_to_dest(self): |
| 34 | """Migrate the database to the desired destination migration.""" |
| 35 | self.executor.loader.build_graph() |
| 36 | self.executor.migrate(self.migrate_to) |
| 37 | |
| 38 | |
| 39 | @override_settings(MEDIA_ROOT=tempfile.mkdtemp(".hiccup-tests")) |
| 40 | class DropDuplicatesMigrationTestCase(MigrationTestCase): |
| 41 | """Test the migration for dropping duplicate heartbeats and crashreports.""" |
| 42 | |
| 43 | migrate_from = [("crashreports", "0005_add_fp_staff_group")] |
| 44 | migrate_to = [ |
| 45 | ("crashreports", "0006_add_unique_constraints_and_drop_duplicates") |
| 46 | ] |
| 47 | |
| 48 | def test_duplicate_heartbeats_are_deleted(self): |
| 49 | """Test that duplicate heartbeats are deleted after migrating.""" |
| 50 | self._assert_duplicates_are_deleted(HeartBeat) |
| 51 | |
| 52 | def test_duplicate_crashreports_are_deleted(self): |
| 53 | """Test that duplicate crashreports are deleted after migrating.""" |
| 54 | self._assert_duplicates_are_deleted(Crashreport) |
| 55 | |
| 56 | def _assert_duplicates_are_deleted(self, object_type): |
| 57 | # Create a user, device and two duplicate reports |
Mitja Nikolaus | 77dd565 | 2018-12-06 11:27:01 +0100 | [diff] [blame^] | 58 | user = Dummy.create_user() |
| 59 | device = Dummy.create_device(user) |
| 60 | report_1 = Dummy.create_report(object_type, device) |
| 61 | Dummy.create_report(object_type, device) |
Mitja Nikolaus | fd452f8 | 2018-11-07 11:53:59 +0100 | [diff] [blame] | 62 | |
| 63 | # Assert that 2 instances have been created |
| 64 | self.assertEqual(object_type.objects.count(), 2) |
| 65 | |
| 66 | # Run the migration |
| 67 | logger = logging.getLogger("crashreports") |
| 68 | with self.assertLogs(logger, "DEBUG") as logging_watcher: |
| 69 | self.migrate_to_dest() |
| 70 | |
| 71 | # Assert the correct message is logged |
| 72 | self.assertTrue( |
| 73 | { |
| 74 | "INFO:crashreports.migrations." |
| 75 | "0006_add_unique_constraints_and_drop_duplicates:" |
| 76 | "Found 1 {} instances that have duplicates. " |
| 77 | "These will be removed.".format(object_type.__name__), |
| 78 | "DEBUG:crashreports.migrations" |
| 79 | ".0006_add_unique_constraints_and_drop_duplicates:Removing " |
| 80 | "duplicates: {}".format( |
| 81 | str( |
| 82 | { |
| 83 | "device": device.id, |
| 84 | "date": report_1.date, |
| 85 | "min_id": report_1.id, |
| 86 | "num_duplicates": 2, |
| 87 | } |
| 88 | ) |
| 89 | ), |
| 90 | }.issubset(set(logging_watcher.output)) |
| 91 | ) |
| 92 | |
| 93 | # Assert that only one instance is left in the database |
| 94 | self.assertEqual(object_type.objects.count(), 1) |
| 95 | |
| 96 | def test_delete_duplicate_crashreport_with_logfile(self): |
| 97 | """Test deletion of a duplicate crashreport with logfile.""" |
| 98 | # Create a user, device and two duplicate reports with logfiles |
Mitja Nikolaus | 77dd565 | 2018-12-06 11:27:01 +0100 | [diff] [blame^] | 99 | user = Dummy.create_user() |
| 100 | device = Dummy.create_device(user) |
| 101 | crashreport_1 = Dummy.create_report(Crashreport, device) |
| 102 | crashreport_2 = Dummy.create_report(Crashreport, device) |
| 103 | _, logfile_1_path = Dummy.create_log_file_with_actual_file( |
Mitja Nikolaus | fd452f8 | 2018-11-07 11:53:59 +0100 | [diff] [blame] | 104 | crashreport_1 |
| 105 | ) |
Mitja Nikolaus | 77dd565 | 2018-12-06 11:27:01 +0100 | [diff] [blame^] | 106 | _, logfile_2_path = Dummy.create_log_file_with_actual_file( |
| 107 | crashreport_2, logfile=Dummy.DEFAULT_LOG_FILE_PATHS[1] |
Mitja Nikolaus | fd452f8 | 2018-11-07 11:53:59 +0100 | [diff] [blame] | 108 | ) |
| 109 | |
| 110 | # Assert that 2 crashreports and logfiles have been created |
| 111 | self.assertEqual(Crashreport.objects.count(), 2) |
| 112 | self.assertEqual(LogFile.objects.count(), 2) |
| 113 | self.assertTrue(os.path.isfile(logfile_1_path)) |
| 114 | self.assertTrue(os.path.isfile(logfile_2_path)) |
| 115 | |
| 116 | # Run the migration |
| 117 | self.migrate_to_dest() |
| 118 | |
| 119 | # Assert that only one crashreport and one logfile is left in the |
| 120 | # database |
| 121 | self.assertEqual(Crashreport.objects.count(), 1) |
| 122 | self.assertEqual(Crashreport.objects.first().logfiles.count(), 1) |
| 123 | self.assertEqual(LogFile.objects.count(), 1) |
| 124 | |
| 125 | # Assert that the correct log file has been deleted |
| 126 | self.assertTrue(os.path.isfile(logfile_1_path)) |
| 127 | self.assertFalse(os.path.isfile(logfile_2_path)) |
| 128 | |
| 129 | def test_change_of_date_field_type(self): |
| 130 | """Test that the 'date' field of heartbeats is changed to a date.""" |
| 131 | # Create a user, device and a heartbeat |
Mitja Nikolaus | 77dd565 | 2018-12-06 11:27:01 +0100 | [diff] [blame^] | 132 | user = Dummy.create_user() |
| 133 | device = Dummy.create_device(user) |
Mitja Nikolaus | fd452f8 | 2018-11-07 11:53:59 +0100 | [diff] [blame] | 134 | heartbeat_timestamp = datetime(2015, 12, 15, 1, 23, 45, tzinfo=pytz.utc) |
| 135 | |
Mitja Nikolaus | 77dd565 | 2018-12-06 11:27:01 +0100 | [diff] [blame^] | 136 | heartbeat = Dummy.create_report( |
Mitja Nikolaus | fd452f8 | 2018-11-07 11:53:59 +0100 | [diff] [blame] | 137 | HeartBeat, device, date=heartbeat_timestamp |
| 138 | ) |
| 139 | |
| 140 | # Assert that the date is of type datetime |
| 141 | self.assertIsInstance(heartbeat.date, datetime) |
| 142 | |
| 143 | # Run the migration |
| 144 | self.migrate_to_dest() |
| 145 | |
| 146 | # Assert that the date is now of type date and has the correct value |
| 147 | heartbeat = HeartBeat.objects.first() |
| 148 | self.assertIsInstance(heartbeat.date, date) |
| 149 | self.assertEqual(heartbeat.date, heartbeat_timestamp.date()) |