From f8d197a8bac454151d0c457efa72fc98ae959118 Mon Sep 17 00:00:00 2001
From: Chenxiong Qi <cqi@redhat.com>
Date: Jan 04 2018 02:48:10 +0000
Subject: Avoid adding duplicate dependent event


Signed-off-by: Chenxiong Qi <cqi@redhat.com>

---

diff --git a/freshmaker/migrations/versions/f3223db11e48_add_unique_index_to_eventdependency.py b/freshmaker/migrations/versions/f3223db11e48_add_unique_index_to_eventdependency.py
new file mode 100644
index 0000000..36a40d5
--- /dev/null
+++ b/freshmaker/migrations/versions/f3223db11e48_add_unique_index_to_eventdependency.py
@@ -0,0 +1,26 @@
+"""Add unique index to EventDependency
+
+Revision ID: f3223db11e48
+Revises: e06434b3ef5e
+Create Date: 2018-01-04 09:45:25.153391
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = 'f3223db11e48'
+down_revision = 'e06434b3ef5e'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.create_index('idx_event_dependency_rel', 'event_dependencies', ['event_id', 'event_dependency_id'], unique=True)
+    # ### end Alembic commands ###
+
+
+def downgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.drop_index('idx_event_dependency_rel', table_name='event_dependencies')
+    # ### end Alembic commands ###
diff --git a/freshmaker/models.py b/freshmaker/models.py
index 1bf7ac9..27c8232 100644
--- a/freshmaker/models.py
+++ b/freshmaker/models.py
@@ -235,9 +235,24 @@ class Event(FreshmakerBase):
         return INVERSE_EVENT_TYPES[self.event_type_id]
 
     def add_event_dependency(self, session, event):
-        dep = EventDependency(event_id=self.id,
-                              event_dependency_id=event.id)
-        session.add(dep)
+        """Add a dependent event
+
+        :param session: the `db.session`.
+        :param event: the dependent event to be added.
+        :type event: :py:class:`Event`
+        :return: instance of :py:class:`EventDependency`. Caller is responsible
+            for committing changes to database. If `event` has been added
+            already, nothing changed and `None` will be returned.
+        """
+        dep = session.query(EventDependency.id).filter_by(
+            event_id=self.id, event_dependency_id=event.id).first()
+        if dep is None:
+            dep = EventDependency(event_id=self.id,
+                                  event_dependency_id=event.id)
+            session.add(dep)
+            return dep
+        else:
+            return None
 
     @property
     def event_dependencies(self):
@@ -356,6 +371,13 @@ class EventDependency(FreshmakerBase):
     event_dependency_id = db.Column(db.Integer, db.ForeignKey('events.id'), nullable=False)
 
 
+Index(
+    'idx_event_dependency_rel',
+    EventDependency.event_id,
+    EventDependency.event_dependency_id,
+    unique=True)
+
+
 class ArtifactBuild(FreshmakerBase):
     __tablename__ = "artifact_builds"
     id = db.Column(db.Integer, primary_key=True)
diff --git a/tests/test_models.py b/tests/test_models.py
index 241cc9d..580b2f6 100644
--- a/tests/test_models.py
+++ b/tests/test_models.py
@@ -80,19 +80,6 @@ class TestModels(unittest.TestCase):
         self.assertEqual(build3.get_root_dep_on(), build1)
         self.assertEqual(build4.get_root_dep_on(), build1)
 
-    def test_event_dependencies(self):
-        event = Event.create(db.session, "test_msg_id", "test", events.TestingEvent)
-        db.session.commit()
-        self.assertEqual(event.event_dependencies, [])
-
-        event1 = Event.create(db.session, "test_msg_id2", "test2", events.TestingEvent)
-        db.session.commit()
-        event.add_event_dependency(db.session, event1)
-        db.session.commit()
-        self.assertEqual(event.event_dependencies, [event1])
-        self.assertEqual(event.event_dependencies[0].search_key, "test2")
-        self.assertEqual(event1.event_dependencies, [])
-
     def test_depending_artifact_builds(self):
         event = Event.create(db.session, "test_msg_id", "test", events.TestingEvent)
         parent = ArtifactBuild.create(db.session, event, "parent", "module", 1234)
@@ -358,3 +345,58 @@ class TestArtifactBuildComposesRel(unittest.TestCase):
             self.assertEqual(
                 builds,
                 sorted([rel.build.id for rel in compose.builds]))
+
+
+class TestEventDependency(unittest.TestCase):
+    """Test Event.add_event_dependency"""
+
+    def setUp(self):
+        db.session.remove()
+        db.drop_all()
+        db.create_all()
+        db.session.commit()
+
+    def tearDown(self):
+        db.session.remove()
+        db.drop_all()
+        db.session.commit()
+
+    def test_event_dependencies(self):
+        event = Event.create(db.session, "test_msg_id", "test", events.TestingEvent)
+        db.session.commit()
+        self.assertEqual(event.event_dependencies, [])
+
+    def test_add_a_dependent_event(self):
+        event = Event.create(db.session, "test_msg_id", "test", events.TestingEvent)
+        event1 = Event.create(db.session, "test_msg_id2", "test2", events.TestingEvent)
+        db.session.commit()
+
+        event.add_event_dependency(db.session, event1)
+        db.session.commit()
+
+        self.assertEqual(event.event_dependencies, [event1])
+        self.assertEqual(event.event_dependencies[0].search_key, "test2")
+        self.assertEqual(event1.event_dependencies, [])
+
+    def test_add_existing_dependent_event(self):
+        event = Event.create(db.session, "test_msg_id", "test", events.TestingEvent)
+        event1 = Event.create(db.session, "test_msg_id2", "test2", events.TestingEvent)
+        db.session.commit()
+        event.add_event_dependency(db.session, event1)
+        db.session.commit()
+
+        rel = event.add_event_dependency(db.session, event1)
+
+        self.assertIsNone(rel)
+        self.assertEqual(event.event_dependencies, [event1])
+
+    def test_return_added_dependency_relationship(self):
+        event = Event.create(db.session, "test_msg_id", "test", events.TestingEvent)
+        event1 = Event.create(db.session, "test_msg_id2", "test2", events.TestingEvent)
+        db.session.commit()
+
+        dep_rel = event.add_event_dependency(db.session, event1)
+        db.session.commit()
+
+        self.assertEqual(event.id, dep_rel.event_id)
+        self.assertEqual(event1.id, dep_rel.event_dependency_id)

