Skip to content

Commit

Permalink
Added tests for achievements
Browse files Browse the repository at this point in the history
  • Loading branch information
Digant C Kasundra committed May 14, 2015
1 parent 223423e commit e8aa8ab
Show file tree
Hide file tree
Showing 3 changed files with 195 additions and 13 deletions.
105 changes: 92 additions & 13 deletions hermes/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import functools
import logging

from sqlalchemy import create_engine, or_, union_all, desc
from sqlalchemy import create_engine, or_, union_all, desc, and_
from sqlalchemy.event import listen
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.declarative import declarative_base, declared_attr
Expand Down Expand Up @@ -83,20 +83,11 @@ def model_name(self):
return obj_name

@classmethod
def before_create(cls, session, user_id):
""" Hook for before object creation."""

def after_create(self, user_id):
""" Hook for after object creation."""

@classmethod
def create(cls, session, _user_id, **kwargs):
def create(cls, session, **kwargs):
commit = kwargs.pop("commit", True)
try:
cls.before_create(session, _user_id)
obj = cls(**kwargs).add(session)
session.flush()
obj.after_create(_user_id)
if commit:
session.commit()
except Exception:
Expand All @@ -105,7 +96,7 @@ def create(cls, session, _user_id, **kwargs):

return obj

def update(self, user_id, **kwargs):
def update(self, **kwargs):
session = self.session
try:
for key, value in kwargs.iteritems():
Expand Down Expand Up @@ -336,6 +327,44 @@ def create(

return obj

@classmethod
def question_the_fates(cls, session, event):
"""Look through the Fates and see if we need to create or close Achievements
Args:
session: active database session
event: the Event for which we need to question the Fates
"""
host = event.host
event_type = event.event_type

fates = session.query(Fate).all()

# Examine all the Fates.
for fate in fates:
# If this type of Event is a creation type for a Fate,
# create an Achievement
if fate.creation_event_type == event_type:
Achievement.create(session, host, event)

# If this type of Event is a completion type for a Fate,
# find all open Achievements for the related creation type and
# mark them as complete.
if fate.completion_event_type == event_type:
# FIXME -- can't this be done with one query?
open_achievements = (
session.query(Achievement).filter(
Achievement.completion_event == None
)
)
for open_achievement in open_achievements:
if (
open_achievement.creation_event.event_type
== fate.creation_event_type
and open_achievement.host == event.host
):
open_achievement.achieve(event)


class Event(Model):
__tablename__ = "events"
Expand Down Expand Up @@ -395,6 +424,8 @@ def create(
session.rollback()
raise

Fate.question_the_fates(session, obj)

return obj


Expand All @@ -408,7 +439,7 @@ class Achievement(Model):
host = relationship(Host, lazy="joined", backref="achievements")
creation_time = Column(DateTime, default=datetime.utcnow, nullable=False)
ack_time = Column(DateTime, default=datetime.utcnow, nullable=True)
completion_time = Column(DateTime, default=datetime.utcnow, nullable=False)
completion_time = Column(DateTime, nullable=True)
creation_event_id = Column(
Integer, ForeignKey("events.id"), nullable=False, index=True
)
Expand All @@ -424,6 +455,54 @@ class Achievement(Model):
foreign_keys=[completion_event_id]
)

@classmethod
def create(
cls, session,
host, creation_event
):
"""Create an Achievement
Args:
host: the host to which this event pertains
creation_event: the Event that lead to the creation of this achievement
Returns:
a newly created Achievement
"""
if host is None:
raise exc.ValidationError(
"Host cannot be null for an achievement"
)
if creation_event is None:
raise exc.ValidationError(
"Creation Event cannot be null for an achievement"
)

try:
obj = cls(
host=host, creation_event=creation_event
)
obj.add(session)
session.flush()

except Exception:
session.rollback()
raise

return obj

def achieve(self, event):
"""Mark an achievement as completed.
Args:
event: the event that closed this achievement
"""
self.update(
completion_event=event, completion_time=datetime.now()
)




class Quest(Model):
__tablename__ = "quests"
Expand Down
102 changes: 102 additions & 0 deletions tests/model_tests/test_achievements.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import pytest

from sqlalchemy import desc
from sqlalchemy.exc import IntegrityError

from hermes import exc
from hermes import models

from .fixtures import db_engine, session, sample_data1


def test_lifecycle(sample_data1):
"""Test the automatic creation and closing of achievements based on Events and Fates"""
achievements = sample_data1.query(models.Achievement).all()
assert len(achievements) == 0

fate = sample_data1.query(models.Fate).first()
host = sample_data1.query(models.Host).first()

models.Event.create(sample_data1, host, "system", fate.creation_event_type)

event = (
sample_data1.query(models.Event)
.order_by(desc(models.Event.id)).first()
)

assert event.host == host
assert event.event_type == fate.creation_event_type

achievements = sample_data1.query(models.Achievement).all()
assert len(achievements) == 1
assert achievements[0].completion_time is None
assert achievements[0].completion_event is None
assert achievements[0].creation_event == event

models.Event.create(
sample_data1, host, "system", fate.completion_event_type
)

event = (
sample_data1.query(models.Event)
.order_by(desc(models.Event.id)).first()
)

assert event.host == host
assert event.event_type == fate.completion_event_type

achievements = sample_data1.query(models.Achievement).all()
assert len(achievements) == 1
assert achievements[0].completion_time is not None
assert achievements[0].completion_event == event

def test_lifecycle_complex(sample_data1):
"""Test the automatic creation and closing of achievements based on Events and Fates.
This version is a bit more complex in that we make sure unaffiliated achievements are left untouched."""
achievements = sample_data1.query(models.Achievement).all()
assert len(achievements) == 0

fates = sample_data1.query(models.Fate).all()
fate1 = fates[0]
fate2 = fates[1]

hosts = sample_data1.query(models.Host).all()
host1 = hosts[0]
host2 = hosts[1]

models.Event.create(sample_data1, host1, "system", fate1.creation_event_type)
models.Event.create(sample_data1, host2, "system", fate2.creation_event_type)

event = (
sample_data1.query(models.Event)
.order_by(desc(models.Event.id)).first()
)

assert event.host == host2
assert event.event_type == fate2.creation_event_type

achievements = sample_data1.query(models.Achievement).all()
assert len(achievements) == 2
assert achievements[0].completion_time is None
assert achievements[0].completion_event is None
assert achievements[1].completion_time is None
assert achievements[1].completion_event is None

models.Event.create(
sample_data1, host1, "system", fate1.completion_event_type
)

event = (
sample_data1.query(models.Event)
.order_by(desc(models.Event.id)).first()
)

assert event.host == host1
assert event.event_type == fate1.completion_event_type

achievements = sample_data1.query(models.Achievement).all()
assert len(achievements) == 2
assert achievements[0].completion_time is not None
assert achievements[0].completion_event is not None
assert achievements[1].completion_time is None
assert achievements[1].completion_event is None
1 change: 1 addition & 0 deletions tests/model_tests/test_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def test_creation(sample_data1):


def test_duplicate(sample_data1):
"""Test to ensure duplicate events are fine b/c there can be multiple identical events"""
event_types = sample_data1.query(models.EventType).all()
assert len(event_types) == 6
event_type1 = event_types[0]
Expand Down

0 comments on commit e8aa8ab

Please sign in to comment.