blob: 34696ae5589b381d966d35429dbc3666ca37fc5c [file] [log] [blame]
Mitja Nikolausfd452f82018-11-07 11:53:59 +01001"""Tests for the Django database migrations."""
2import logging
3import os
4import tempfile
5from datetime import datetime, date
6
7import pytz
8from django.test import TransactionTestCase, override_settings
9from django.db.migrations.executor import MigrationExecutor
10from django.db import connection
11
12from crashreports.models import Crashreport, HeartBeat, LogFile
13from crashreports.tests.utils import Dummy
14
15
16class 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"))
40class 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 Nikolaus77dd5652018-12-06 11:27:01 +010058 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 Nikolausfd452f82018-11-07 11:53:59 +010062
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 Nikolaus77dd5652018-12-06 11:27:01 +010099 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 Nikolausfd452f82018-11-07 11:53:59 +0100104 crashreport_1
105 )
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100106 _, logfile_2_path = Dummy.create_log_file_with_actual_file(
107 crashreport_2, logfile=Dummy.DEFAULT_LOG_FILE_PATHS[1]
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100108 )
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 Nikolaus77dd5652018-12-06 11:27:01 +0100132 user = Dummy.create_user()
133 device = Dummy.create_device(user)
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100134 heartbeat_timestamp = datetime(2015, 12, 15, 1, 23, 45, tzinfo=pytz.utc)
135
Mitja Nikolaus77dd5652018-12-06 11:27:01 +0100136 heartbeat = Dummy.create_report(
Mitja Nikolausfd452f82018-11-07 11:53:59 +0100137 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())