From ed18262b8a0fa3718302b22e3a5dffdb79328416 Mon Sep 17 00:00:00 2001 From: sarah256 Date: Jun 24 2019 13:35:04 +0000 Subject: [PATCH 1/2] Include Freshmaker event start and completion times in the REST API --- diff --git a/freshmaker/migrations/versions/2358b6f55f24_.py b/freshmaker/migrations/versions/2358b6f55f24_.py new file mode 100644 index 0000000..3f8e09d --- /dev/null +++ b/freshmaker/migrations/versions/2358b6f55f24_.py @@ -0,0 +1,21 @@ +"""Add time done for Freshmaker events + +Revision ID: 2358b6f55f24 +Revises: fbc2eac9bfa5 +Create Date: 2019-06-20 10:00:31.190304 + +""" + +revision = '2358b6f55f24' +down_revision = 'fbc2eac9bfa5' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.add_column('events', sa.Column('time_done', sa.DateTime(), nullable=True)) + + +def downgrade(): + op.drop_column('events', 'time_done') diff --git a/freshmaker/models.py b/freshmaker/models.py index 8bb3c47..7bbdc54 100644 --- a/freshmaker/models.py +++ b/freshmaker/models.py @@ -146,6 +146,7 @@ class Event(FreshmakerBase): state = db.Column(db.Integer, nullable=False) state_reason = db.Column(db.String, nullable=True) time_created = db.Column(db.DateTime, nullable=True) + time_done = db.Column(db.DateTime, nullable=True) # AppenderQuery for getting builds associated with this Event. builds = relationship("ArtifactBuild", back_populates="event", lazy="dynamic", cascade="all, delete-orphan", @@ -311,13 +312,17 @@ class Event(FreshmakerBase): def transition(self, state, state_reason=None): """ - Sets the state and state_reason of this Event. + Sets the time_done, state, and state_reason of this Event. :param state: EventState value :param state_reason: Reason why this state has been set. :return: True/False, whether state was changed """ + # Log the time done + if state == EventState.FAILED.value or state == EventState.COMPLETE.value: + self.time_done = datetime.utcnow() + # Log the state and state_reason if state == EventState.FAILED.value: log_fnc = log.error @@ -384,6 +389,8 @@ class Event(FreshmakerBase): "state": self.state, "state_name": EventState(self.state).name, "state_reason": self.state_reason, + "time_created": _utc_datetime_to_iso(self.time_created), + "time_done": _utc_datetime_to_iso(self.time_done), "url": event_url, "dry_run": self.dry_run, "requester": self.requester, diff --git a/tests/test_models.py b/tests/test_models.py index de4a9d2..c87b244 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -20,6 +20,9 @@ # # Written by Jan Kaluza +import datetime +from mock import patch + from freshmaker import db, events from freshmaker.models import ArtifactBuild, ArtifactType from freshmaker.models import Event, EventState, EVENT_TYPES, EventDependency @@ -170,7 +173,10 @@ class TestModels(helpers.ModelsTestCase): str(event), "") def test_event_json_min(self): - event = Event.create(db.session, "test_msg_id5", "RHSA-2017-289", events.TestingEvent) + with patch('freshmaker.models.datetime') as datetime_patch: + datetime_patch.utcnow.return_value = datetime.datetime(2017, 8, 21, 13, 42, 20) + event = Event.create(db.session, "test_msg_id5", "RHSA-2017-289", events.TestingEvent) + build = ArtifactBuild.create(db.session, event, "ed", "module", 1234) build.state = ArtifactBuildState.FAILED ArtifactBuild.create(db.session, event, "mksh", "module", 1235, build) @@ -186,6 +192,8 @@ class TestModels(helpers.ModelsTestCase): 'state': 0, 'state_name': 'INITIALIZED', 'state_reason': None, + 'time_created': '2017-08-21T13:42:20Z', + 'time_done': None, 'url': 'http://localhost:5001/api/1/events/1', 'requested_rebuilds': [], 'requester_metadata': {}, diff --git a/tests/test_views.py b/tests/test_views.py index c54fa6a..6166771 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -21,6 +21,7 @@ import unittest import json +import datetime import six import contextlib import flask @@ -321,6 +322,15 @@ class TestViews(helpers.ModelsTestCase): evs = json.loads(resp.get_data(as_text=True))['items'] self.assertEqual(len(evs), 2) + def test_query_event_complete(self): + event = db.session.query(models.Event).get(1) + with patch('freshmaker.models.datetime') as datetime_patch: + datetime_patch.utcnow.return_value = datetime.datetime(2099, 8, 21, 13, 42, 20) + event.transition(models.EventState.COMPLETE.value) + resp = self.client.get('/api/1/events/1') + data = json.loads(resp.get_data(as_text=True)) + self.assertEqual(data['time_done'], '2099-08-21T13:42:20Z') + def test_query_event_by_message_id(self): resp = self.client.get('/api/1/events/?message_id=2017-00000000-0000-0000-0000-000000000001') evs = json.loads(resp.get_data(as_text=True))['items'] @@ -531,10 +541,11 @@ class TestManualTriggerRebuild(helpers.ModelsTestCase): time.return_value = 123 from_advisory_id.return_value = ErrataAdvisory( 123, 'name', 'REL_PREP', ['rpm']) - - resp = self.client.post('/api/1/builds/', - data=json.dumps({'errata_id': 1}), - content_type='application/json') + with patch('freshmaker.models.datetime') as datetime_patch: + datetime_patch.utcnow.return_value = datetime.datetime(2017, 8, 21, 13, 42, 20) + resp = self.client.post('/api/1/builds/', + data=json.dumps({'errata_id': 1}), + content_type='application/json') data = json.loads(resp.get_data(as_text=True)) # Other fields are predictible. @@ -547,6 +558,8 @@ class TestManualTriggerRebuild(helpers.ModelsTestCase): u'state': 0, u'state_name': u'INITIALIZED', u'state_reason': None, + u'time_created': u'2017-08-21T13:42:20Z', + u'time_done': None, u'url': u'/api/1/events/1', u'dry_run': False, u'requester': 'tester1', From ab9ff7508096b29507659eaa64f99c8bf6008fdd Mon Sep 17 00:00:00 2001 From: sarah256 Date: Jun 24 2019 13:35:04 +0000 Subject: [PATCH 2/2] Mock the version returned in `test_about_api` Since the unit test was relying on Freshmaker to not be installed for it to succeed, mocking the version makes it consistent in both scenarios. --- diff --git a/tests/test_views.py b/tests/test_views.py index 6166771..6bfd6f7 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -461,10 +461,11 @@ class TestViews(helpers.ModelsTestCase): self.assertEqual(data['message'], 'No such build state found.') def test_about_api(self): - resp = self.client.get('/api/1/about/') + # Since the version is always changing, let's just mock it to be consistent + with patch('freshmaker.views.version', '1.0.0'): + resp = self.client.get('/api/1/about/') data = json.loads(resp.get_data(as_text=True)) - # version is 'unknown' in case of skip_install=True in tox.ini - self.assertEqual(data['version'], 'unknown') + self.assertEqual(data['version'], '1.0.0') @patch("freshmaker.views.ImageVerifier") def test_verify_image(self, verifier):