Fix: 'stats reset' command fails with AttributeError
delete_stats() returns count_per_model from
self.stats_model.objects.all().delete(). Keys of that dict are set to
model._meta.label in Collector.delete() (called by QuerySet.delete()).
Further, add a matching test case.
Issue: HIC-151
Change-Id: I96838820cb7f8fd7f32825acc1cb52845484bb6a
diff --git a/crashreport_stats/management/commands/stats.py b/crashreport_stats/management/commands/stats.py
index 2852d46..5ac2dea 100644
--- a/crashreport_stats/management/commands/stats.py
+++ b/crashreport_stats/management/commands/stats.py
@@ -422,11 +422,12 @@
# Default the count of deleted models to 0 if missing
if not counts_per_model:
counts_per_model = {
- engine.stats_model: 0,
- engine.daily_stats_model: 0}
+ engine.stats_model._meta.label: 0,
+ engine.daily_stats_model._meta.label: 0}
for model, count in counts_per_model.items():
+ name = model.split('.')[-1]
self._success(
- '{} {} deleted'.format(count, model.__name__))
+ '{} {} deleted'.format(count, name))
# Reset the metadata
count, _ = StatsMetadata.objects.all().delete()
diff --git a/crashreport_stats/tests.py b/crashreport_stats/tests.py
index d206402..e852700 100644
--- a/crashreport_stats/tests.py
+++ b/crashreport_stats/tests.py
@@ -59,6 +59,10 @@
'date': DATES[1]
}
+ DEFAULT_DUMMY_STATSMETADATA_VALUES = {
+ 'updated_at': datetime(2018, 6, 15, 2, 12, 24, tzinfo=pytz.utc),
+ }
+
DEFAULT_DUMMY_DEVICE_VALUES = {
'board_date': datetime(2015, 12, 15, 1, 23, 45, tzinfo=pytz.utc),
'chipset': 'Qualcomm MSM8974PRO-AA',
@@ -224,6 +228,23 @@
entity.save()
return entity
+ @staticmethod
+ def create_dummy_stats_metadata(**kwargs):
+ """Create a dummy stats metadata instance.
+
+ The dummy instance is created and saved to the database.
+ Args:
+ **kwargs:
+ Optional arguments to extend/overwrite the default values.
+
+ Returns: The created stats metadata instance.
+
+ """
+ entity = StatsMetadata(**Dummy.update_copy(
+ Dummy.DEFAULT_DUMMY_STATSMETADATA_VALUES, kwargs))
+ entity.save()
+ return entity
+
class _VersionTestCase(APITestCase):
"""Abstract class for version-related test cases to inherit from."""
@@ -744,7 +765,7 @@
_COUNTER_NAMES = ['heartbeats', 'crashes', 'smpl', 'other']
_COUNTER_ACTIONS = ['created', 'updated']
- def _assert_command_output_matches(self, command, facts, models):
+ def _assert_command_output_matches(self, command, number, facts, models):
"""Validate the debug output of a command.
The debug output is matched against the facts and models given in
@@ -754,11 +775,13 @@
call_command('stats', command, *self._CMD_ARGS, stdout=buffer)
output = buffer.getvalue().splitlines()
- expected_output = '0 {model} {fact}'
+ expected_output = '{number} {model} {fact}'
for model in models:
for fact in facts:
self.assertIn(
- expected_output.format(model=model.__name__, fact=fact),
+ expected_output.format(number=number,
+ model=model.__name__,
+ fact=fact),
output)
def test_reset_command_on_empty_db(self):
@@ -766,7 +789,7 @@
The reset command should yield nothing on an empty database.
"""
- self._assert_command_output_matches('reset', ['deleted'],
+ self._assert_command_output_matches('reset', 0, ['deleted'],
self._ALL_MODELS)
def test_update_command_on_empty_db(self):
@@ -779,5 +802,23 @@
pattern.format(action=counter_action, counter=counter_name)
for counter_action in self._COUNTER_ACTIONS
for counter_name in self._COUNTER_NAMES]
- self._assert_command_output_matches('update', facts,
+ self._assert_command_output_matches('update', 0, facts,
self._STATS_MODELS)
+
+ def test_reset_command_deletion_of_instances(self):
+ """Test the deletion of stats model instances with the reset command.
+
+ This test validates that model instances get deleted when the
+ reset command is called on a database that only contains a single
+ model instance for each class.
+ """
+ # Create dummy version instances
+ version = Dummy.create_dummy_version()
+ radio_version = Dummy.create_dummy_radio_version()
+ Dummy.create_dummy_daily_version(version)
+ Dummy.create_dummy_daily_radio_version(radio_version)
+ Dummy.create_dummy_stats_metadata()
+
+ # We expect that the model instances get deleted
+ self._assert_command_output_matches('reset', 1, ['deleted'],
+ self._ALL_MODELS)