Skip to content

Commit

Permalink
Merge pull request #420 from lod/func_testing
Browse files Browse the repository at this point in the history
Fix/reenable zkpylons functional testing
  • Loading branch information
jhesketh committed Aug 30, 2015
2 parents acc7e3f + 237b4e4 commit d71e3c6
Show file tree
Hide file tree
Showing 49 changed files with 2,946 additions and 2,347 deletions.
1 change: 1 addition & 0 deletions requirements.txt
Expand Up @@ -12,6 +12,7 @@ SQLAlchemy>=0.8.0b2
transaction
waitress
zope.sqlalchemy
reportlab

#### LEGACY (zkpylon) requirements:
AuthKit>=0.4.0
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Expand Up @@ -15,4 +15,4 @@ paste.app_factory =
main = zk:main

[pytest]
norecursedirs = .git env TestExample wsgi zk-2.0 zk.egg-info *.egg data alembic docs pbr* zkpylons .tox
norecursedirs = .git env TestExample wsgi zk-2.0 zk.egg-info *.egg data alembic docs pbr* .tox
40 changes: 40 additions & 0 deletions test.ini
@@ -0,0 +1,40 @@
[server:main]
use = egg:waitress#main
host = 127.0.0.1
port = 5000

[app:main]
use = egg:zookeepr
sqlalchemy.url = postgresql://postgres@localhost/zktest
cache_enabled = False
cache_dir = %(here)s/data

authkit.setup.method = forward, cookie
authkit.cookie.secret = CHANGE_ME
authkit.forward.signinpath = /person/signin
authkit.cookie.signoutpath = /person/signout
authkit.cookie.badcookie.page = false

smtp_server = stubbed

[loggers]
keys = root, zk, sqlalchemy

[handlers]
keys = console

[formatters]
keys = generic

[logger_root]
level = DEBUG

[logger_zk]
level = DEBUG

[logger_sqlalchemy]
level = DEBUG

[handler_console]
class = StreamHandler

7 changes: 4 additions & 3 deletions zk/model/attachment.py
Expand Up @@ -13,17 +13,18 @@
class Attachment(Base):
__tablename__ = 'attachment'

# columns
id = sa.Column(sa.types.Integer, primary_key=True)

proposal_id = sa.Column(sa.types.Integer, sa.ForeignKey('proposal.id'), nullable=False)
filename = sa.Column(sa.types.Text, key='_filename', nullable=False, default='attachment')
content_type = sa.Column(sa.types.Text, key='_content_type', nullable=False, default='application/octet-stream')

content = sa.Column(sa.types.Binary, nullable=False)

creation_timestamp = sa.Column(sa.types.DateTime, nullable=False, default=sa.func.current_timestamp())
last_creation_timestamp = sa.Column(sa.types.DateTime, nullable=False, default=sa.func.current_timestamp(), onupdate=sa.func.current_timestamp())

# relations
proposal = sa.orm.relation('Proposal')


def __init__(self, **kwargs):
# remove the args that should never be set via creation
Expand Down
11 changes: 4 additions & 7 deletions zk/model/fulfilment.py
Expand Up @@ -140,12 +140,9 @@ class Fulfilment(Base):
__tablename__ = 'fulfilment'

id = sa.Column(sa.types.Integer, primary_key=True)
person_id = sa.Column(sa.types.Integer, sa.ForeignKey('person.id'),
nullable=False)
type_id = sa.Column(sa.types.Integer, sa.ForeignKey('fulfilment_type.id'),
nullable=False)
status_id = sa.Column(sa.types.Integer, sa.ForeignKey('fulfilment_status.id'),
nullable=False)
person_id = sa.Column(sa.types.Integer, sa.ForeignKey('person.id'), nullable=False)
type_id = sa.Column(sa.types.Integer, sa.ForeignKey('fulfilment_type.id'), nullable=False)
status_id = sa.Column(sa.types.Integer, sa.ForeignKey('fulfilment_status.id'), nullable=False)
code = sa.Column(sa.types.Text, unique=True)
creation_timestamp = sa.Column(sa.types.DateTime, nullable=False,
default=sa.func.current_timestamp())
Expand Down Expand Up @@ -175,7 +172,7 @@ class Fulfilment(Base):
)

# relations
person = sa.orm.relation(Person, backref='fulfilments', lazy='subquery')
person = sa.orm.relation(Person)
type = sa.orm.relation(FulfilmentType)
status = sa.orm.relation(FulfilmentStatus)
groups = sa.orm.relation(FulfilmentGroup, secondary=fulfilment_group_map, backref='fulfilments')
Expand Down
4 changes: 4 additions & 0 deletions zk/model/proposal.py
Expand Up @@ -224,6 +224,10 @@ def find_by_id(cls, id, abort_404 = True):
abort(404, "No such proposal object")
return result

@classmethod
def find_by_title(cls, title):
return Session.query(Proposal).filter_by(title=title).order_by(Proposal.title).all()

@classmethod
def find_all(cls):
return Session.query(Proposal).order_by(Proposal.id).all()
Expand Down
15 changes: 13 additions & 2 deletions zk/tests/model/conftest.py
Expand Up @@ -9,6 +9,12 @@
import zk.model.meta as meta
import zkpylons.model.meta as pymeta

from ConfigParser import ConfigParser

# Get settings from config file, only need it once
ini = ConfigParser()
ini.read("test.ini")

@pytest.yield_fixture
def app_config():
config = testing.setUp()
Expand All @@ -20,10 +26,15 @@ def app_config():
@pytest.yield_fixture
def db_session(app_config):
# Set up SQLAlchemy to provide DB access
# TODO: engine config should be from config file
meta.engine = create_engine('postgresql://postgres@localhost/zktest')
meta.engine = create_engine(ini.get("app:main", "sqlalchemy.url"))
meta.Session.configure(bind=meta.engine)
meta.Session.configure(autoflush=True)

# Drop all data to establish known state
meta.engine.execute("drop schema if exists public cascade")
meta.engine.execute("create schema public")

# Recreate tables
meta.Base.metadata.create_all(meta.engine)

# Also need to set zkpylons version of meta, hours of fun if you don't
Expand Down
154 changes: 134 additions & 20 deletions zk/tests/model/fixtures.py
Expand Up @@ -6,11 +6,20 @@
from zk.model.invoice import Invoice
from zk.model.invoice_item import InvoiceItem
from zk.model.registration import Registration
from zk.model.registration_product import RegistrationProduct
from zk.model.attachment import Attachment
from zk.model.stream import Stream
from zk.model.review import Review
from zk.model.role import Role
from zk.model.config import Config
from zk.model.password_reset_confirmation import PasswordResetConfirmation
from zk.model.url_hash import URLHash
from zk.model.product_category import ProductCategory
from zk.model.product import Product
from zk.model.ceiling import Ceiling
from zk.model.fulfilment import Fulfilment, FulfilmentItem, FulfilmentType, FulfilmentStatus

from datetime import datetime

import factory
from factory.alchemy import SQLAlchemyModelFactory
Expand Down Expand Up @@ -66,6 +75,114 @@ class Meta: model = Config
value = factory.LazyAttribute(lambda x: faker.word())
description = factory.LazyAttribute(lambda x: faker.sentence(nb_words=15))

class CeilingFactory(_ModelFactory):
class Meta: model = Ceiling
name = factory.Sequence(lambda n: "name%03d" % n)

class URLHashFactory(_ModelFactory):
class Meta: model = URLHash
id = factory.Sequence(lambda n: n)
url = '/'
url_hash = factory.Sequence(lambda n: "D00D%03d" % n)
timestamp = datetime.now()

class PersonFactory(_ModelFactory):
class Meta: model = Person

id = factory.Sequence(lambda n: n)
email_address = factory.Sequence(lambda n: "email%03d@personfactory.com" % n)

creation_timestamp = '2000-01-01'
last_modification_timestamp = '2000-01-01'
url_hash = "A"*64
activated = True
i_agree = True

# Set default passwords
# password is set to an encrypted version of password
# raw_password is set to an unencrypted version of password, but not stored in DB
post__raw_password = 'a_swell_password'
password = 'a_swell_password'

# Some fields are overriden by the Person constructor, so we can't set them
# So we stash them, generate the object, then override the value
# This technique takes advantage of the factory_boy post_generation hook
@classmethod
def _generate(cls, create, attrs):
override = ["creation_timestamp", "activated", "badge_printed", "url_hash"]
for key in override:
if key in attrs:
attrs["post__"+key] = attrs.pop(key)

# Person __init__ function uses Config.get('password_salt')
try:
Config.get('password_salt')
except:
ConfigFactory.create(key="password_salt", value=23)
return super(PersonFactory, cls)._generate(create, attrs)

# post_generation extracts named (post__) elements, presents them after creation
@factory.post_generation
def post(obj, create, extracted, **kwargs):
for key in kwargs:
obj.__dict__[key] = kwargs[key]

class CompletePersonFactory(PersonFactory):
# Set lots of additional detail to avoid the incomplete profile flag
firstname = factory.Sequence(lambda n: "jim%03d" % n)
lastname = factory.Sequence(lambda n: "kibbles%03d" % n)
address1 = 'Somewhere',
city = 'Over the rainbow',
postcode = 'Way up high',

@classmethod
def reset_sequence(cls):
PersonFactory.reset_sequence()

class FulfilmentStatusFactory(_ModelFactory):
class Meta: model = FulfilmentStatus
id = factory.Sequence(lambda n: n)
name = factory.Sequence(lambda n: "status %03d" % n)

class FulfilmentTypeFactory(_ModelFactory):
class Meta: model = FulfilmentType
id = factory.Sequence(lambda n: n)
initial_status = factory.SubFactory(FulfilmentStatusFactory)
name = factory.Sequence(lambda n: "type %03d" % n)

class FulfilmentFactory(_ModelFactory):
class Meta: model = Fulfilment
id = factory.Sequence(lambda n: n)
person = factory.SubFactory(PersonFactory)
type = factory.SubFactory(FulfilmentTypeFactory)
status = factory.SubFactory(FulfilmentStatusFactory)

class ProductCategoryFactory(_ModelFactory):
class Meta: model = ProductCategory
id = factory.Sequence(lambda n: n)
name = factory.Sequence(lambda n: "product category %03d" % n)
description = factory.Sequence(lambda n: "factory generated category %03d" % n)
display = 'radio'
display_mode = 'grid'
display_order = 10
invoice_free_products = True

class ProductFactory(_ModelFactory):
class Meta: model = Product
id = factory.Sequence(lambda n: n)
fulfilment_type = factory.SubFactory(FulfilmentTypeFactory)
category = factory.SubFactory(ProductCategoryFactory)
active = True
description = factory.Sequence(lambda n: "factory generated product %03d" % n)
cost = factory.Sequence(lambda n: n*100)

class FulfilmentItemFactory(_ModelFactory):
class Meta: model = FulfilmentItem
id = factory.Sequence(lambda n: n)
fulfilment = factory.SubFactory(FulfilmentFactory)
product = factory.SubFactory(ProductFactory)
qty = factory.Sequence(lambda n: (n+1)*3)

class ProposalFactory(_ModelFactory):
class Meta: model = Proposal
id = factory.Sequence(lambda n: n)
Expand All @@ -89,34 +206,23 @@ class Meta: model = Proposal
# last_modification_timestamp = default current_timestamp()


class PersonFactory(_ModelFactory):
class Meta: model = Person

class PasswordResetConfirmationFactory(_ModelFactory):
class Meta: model = PasswordResetConfirmation
id = factory.Sequence(lambda n: n)
email_address = factory.Sequence(lambda n: "email%03d@example.com" % n)
email_address = factory.Sequence(lambda n: "email%03d@passwordresetconfirmationfactory.com" % n)
url_hash = "R"*64
timestamp = factory.LazyAttribute(lambda o: datetime.now())

creation_timestamp = '2000-01-01'
last_modification_timestamp = '2000-01-01'
url_hash = "A"*64
activated = True
i_agree = True

# Some fields are overriden by the Person constructor, so we can't set them
# Some fields are overriden by the constructor, so we can't set them
# So we stash them, generate the object, then override the value
# This technique takes advantage of the factory_boy post_generation hook
@classmethod
def _generate(cls, create, attrs):
override = ["creation_timestamp", "activated", "badge_printed", "url_hash"]
override = ["timestamp"]
for key in override:
if key in attrs:
attrs["post__"+key] = attrs.pop(key)

# Person __init__ function uses Config.get('password_salt')
try:
Config.get('password_salt')
except:
ConfigFactory.create(key="password_salt", value=23)
return super(PersonFactory, cls)._generate(create, attrs)
return super(PasswordResetConfirmationFactory, cls)._generate(create, attrs)

# post_generation extracts named (post__) elements, presents them after creation
@factory.post_generation
Expand Down Expand Up @@ -148,13 +254,14 @@ class Meta: model = Review

class InvoiceFactory(_ModelFactory):
class Meta: model = Invoice
id = factory.Sequence(lambda n: n)
person = factory.SubFactory(PersonFactory)


class InvoiceItemFactory(_ModelFactory):
class Meta: model = InvoiceItem
id = factory.Sequence(lambda n: n)
# TODO: invoice is required
invoice = factory.SubFactory(InvoiceFactory)
description = factory.Sequence(lambda n: "factory generated item %03d" % n)
qty = factory.Sequence(lambda n: n+1)
cost = factory.Sequence(lambda n: (n+1)*10)
Expand All @@ -163,3 +270,10 @@ class Meta: model = InvoiceItem
class RegistrationFactory(_ModelFactory):
class Meta: model = Registration
id = factory.Sequence(lambda n: n)


class RegistrationProductFactory(_ModelFactory):
class Meta: model = RegistrationProduct
registration_id = factory.SubFactory(RegistrationFactory)
product_id = factory.SubFactory(ProductFactory)
qty = 1
4 changes: 2 additions & 2 deletions zk/tests/model/test_invoice.py
Expand Up @@ -6,9 +6,9 @@

class TestInvoice(object):
def test_item_add(self, db_session):
invoice = InvoiceFactory()
invoice_item = InvoiceItemFactory()
invoice.items.append(invoice_item) # invoice_item must be attached to invoice before flush
invoice = InvoiceFactory(items=[invoice_item])

db_session.flush()

assert invoice_item in db_session.query(InvoiceItem).all()
Expand Down
32 changes: 0 additions & 32 deletions zk/tests/tests.py

This file was deleted.

0 comments on commit d71e3c6

Please sign in to comment.