Fixes to the data model for Usage to make sure that resources are constructed correctly; only one resource ID may exist at once, and must be associatd with a Tenant. Test coverage for creating and manipulating Usage objects.

This commit is contained in:
Aurynn Shaw 2013-08-09 13:24:12 +12:00
parent a8963fe5cf
commit f0eff01084
4 changed files with 169 additions and 23 deletions

View File

@ -9,5 +9,5 @@ class Resource(Base):
id = Column(String, primary_key=True)
type_ = Column(String)
tenant_id = Column(String, ForeignKey("tenants.id"), unique=True)
tenant_id = Column(String, ForeignKey("tenants.id"), primary_key=True)
tenant = relationship("Tenant", backref=backref("tenants"))

View File

@ -2,7 +2,7 @@ from . import Base
from .resources import Resource
from sqlalchemy import Column, types, ForeignKey, CheckConstraint, String, Integer, type_coerce, func, Sequence
from sqlalchemy.orm import relationship, backref
# from sqlalchemy.schema import CheckConstraint
from sqlalchemy.schema import ForeignKeyConstraint
import datetime
from sqlalchemy.dialects.postgresql import ExcludeConstraint, TSRANGE, ARRAY
@ -24,33 +24,50 @@ class Usage(Base):
__tablename__ = 'usage'
id_ = Column(Integer, Sequence('usage_id_seq'), primary_key=True)
resource_id = Column(String, ForeignKey("resources.id"))
resource_id = Column(String)
# tenant = Column(String, nullable=False)
tenant_id = Column(String, ForeignKey("tenants.id"))
tenant_id = Column(String)
volume = Column(String, nullable=False)
time = Column(TSRange, nullable=False)
created = Column(types.DateTime, nullable=False)
resource = relationship("Resource", backref=backref("resources", order_by=created))
tenant = relationship("Tenant", backref=backref("usage", order_by=created))
__table_args__ = (
ExcludeConstraint(
('tenant_id', '='),
('resource_id', '='),
('time', '&&')
),
ForeignKeyConstraint(
["resource_id", "tenant_id"],
["resources.id", "resources.tenant_id"],
name="fk_resource", use_alter=True
),
)
resource = relationship(Resource, primaryjoin=
resource_id == Resource.id)
tenant = relationship(Resource, primaryjoin=
tenant_id== Resource.tenant_id)
# resource = relationship("Resource", backref=backref("resources", order_by=created))
# tenant = relationship("Tenant", backref=backref("usage", order_by=created))
def __init__(self, resource, tenant, value, start, end):
assert start < end
assert isinstance(start, datetime.datetime)
assert isinstance(end, datetime.datetime)
assert resource.tenant is not None
assert tenant is not None
assert resource.tenant.id == tenant.id
self.resource = resource
self.tenant = tenant
self.tenant = resource # Same resource
self.time = "[%s,%s]" % (start, end)
self.created = datetime.datetime.now()
self.volume = value

View File

@ -52,7 +52,10 @@ class TestInstancing(unittest.TestCase):
start = datetime.datetime.now() - datetime.timedelta(days=30)
end = datetime.datetime.now()
u = usage.Usage(resources.Resource(), tenants.Tenant() , 1, start, end )
r = resources.Resource()
t = tenants.Tenant()
r.tenant = t
u = usage.Usage(r, t , 1, start, end )
self.assertTrue ( isinstance( u, usage.Usage ) )
def test_instance_tenant(self):

View File

@ -5,8 +5,17 @@ from sqlalchemy.exc import IntegrityError, InvalidRequestError
from sqlalchemy.orm.exc import FlushError
import os
from artifice.models.usage import Usage
from artifice.models.tenants import Tenant
from artifice.models.resources import Resource
from datetime import datetime, timedelta
TENANT_ID = "test tenant"
RESOURCE_ID = "test resource"
RESOURCE_ID_TWO = "A DIFFERENT RESOURCE"
USAGE_ID = 12345
class SessionBase(unittest.TestCase):
@ -114,6 +123,8 @@ class TestResource(SessionBase):
r.id = RESOURCE_ID
self.session.add(r)
self.objects.append(r)
try:
self.session.commit()
except IntegrityError:
@ -122,29 +133,144 @@ class TestResource(SessionBase):
self.fail(e)
class TestUsage(unittest.TestCase):
class TestUsage(SessionBase):
"""Tests various states of the Usage objects."""
def setUp(self):
pass
# def setUp(self):
# super(TestUsage, self).setUp()
def tearDown(self):
pass
# self.resource
def test_save_to_database(self):
pass
# def tearDown(self):
# pass
def test_save_usage_to_database(self):
r = Resource()
r.id = RESOURCE_ID
t = Tenant()
t.id = TENANT_ID
r.tenant = t
self.objects.extend((r, t))
start = datetime.now() - timedelta(days=30)
end = datetime.now()
u = Usage(r, t, 1, start, end )
u.id = USAGE_ID
self.objects.append(u)
self.session.add(u)
self.session.add(r)
self.session.add(t)
self.session.commit()
u2 = self.session.query(Usage)[0]
self.assertTrue( u2.resource.id == r.id )
self.assertTrue( u2.tenant.tenant.id == t.id )
self.assertTrue( u2.created == u.created )
print u2.time
def test_overlap_throws_exception(self):
self.test_save_usage_to_database()
r = self.session.query(Resource).filter(Resource.id == RESOURCE_ID)[0]
t = self.session.query(Tenant).filter(Tenant.id == TENANT_ID)[0]
start = datetime.now() - timedelta(days=15)
end = datetime.now()
u2 = Usage(r, t, 2, start, end)
self.session.add(u2)
try:
self.session.commit()
except IntegrityError:
self.assertTrue(True)
except Exception as e:
self.fail(e)
def test_overlap_with_different_resource_succeeds(self):
self.test_save_usage_to_database()
t = self.session.query(Tenant).filter(Tenant.id == TENANT_ID)[0]
r = Resource()
r.id = RESOURCE_ID_TWO
r.tenant = t
start = datetime.now() - timedelta(days=30)
end = datetime.now()
u = Usage(r, t, 2, start, end)
self.objects.extend((r, u))
self.session.add(u)
self.session.add(r)
try:
self.session.commit()
except IntegrityError as e:
self.fail("Integrity violation: %s" % e)
except Exception as e:
self.fail("Major exception: %s" % e)
def test_non_overlap_succeeds(self):
self.test_save_usage_to_database()
r = self.session.query(Resource).filter(Resource.id == RESOURCE_ID)[0]
t = self.session.query(Tenant).filter(Tenant.id == TENANT_ID)[0]
start = datetime.now()
end = datetime.now() + timedelta(days=30)
u = Usage(r, t, 1, start, end)
self.session.add(u)
try:
self.session.commit()
self.objects.append(u)
except IntegrityError as e:
self.fail("Integrity violation: %s" % e)
except Exception as e:
self.fail("Fail: %s" % e)
def test_tenant_does_not_exist_fails(self):
pass
def test_non_overlap_does_not_throw_exception(self):
def test_resource_does_not_exist_fails(self):
pass
def test_tenant_does_not_exist(self):
pass
def test_resource_belongs_to_different_tenant_fails(self):
self.test_save_usage_to_database()
t = Tenant()
t.id = "TENANT TWO"
r = self.session.query(Resource).filter(Resource.id == RESOURCE_ID)[0]
start = datetime.now()
end = datetime.now() + timedelta(days=30)
self.session.add(t)
self.objects.append(t)
try:
u = Usage(r, t, 1, start, end)
self.session.commit()
self.objects.append(u)
self.fail("Should not have saved!")
except (IntegrityError, AssertionError) as e :
self.assertTrue(True) # Pass
except Exception as e:
self.fail(e.__class__)
def test_resource_does_not_exist(self):
pass