Skip to content

Commit

Permalink
Merge branch '1117-factory_boy' into 1117-start-new-test-suite
Browse files Browse the repository at this point in the history
  • Loading branch information
Sean Hammond committed Jul 31, 2013
2 parents d8dfa43 + 8481935 commit 1f95401
Show file tree
Hide file tree
Showing 6 changed files with 330 additions and 69 deletions.
32 changes: 0 additions & 32 deletions ckan/new_tests/data.py

This file was deleted.

96 changes: 96 additions & 0 deletions ckan/new_tests/factories.py
@@ -0,0 +1,96 @@
'''A collection of factory classes for building CKAN users, datasets, etc.
These are meant to be used by tests to create any objects or "test fixtures"
that are needed for the tests. They're written using factory_boy:
http://factoryboy.readthedocs.org/en/latest/
These are not meant to be used for the actual testing, e.g. if you're writing a
test for the user_create action function then call user_create, don't test it
via the User factory below.
Usage:
# Create a user with the factory's default attributes, and get back a
# user dict:
user_dict = factories.User()
# You can create a second user the same way. For attributes that can't be
# the same (e.g. you can't have two users with the same name) a new value
# will be generated each time you use the factory:
another_user_dict = factories.User()
# Create a user and specify your own user name and email (this works
# with any params that CKAN's user_create() accepts):
custom_user_dict = factories.User(name='bob', email='bob@bob.com')
# Get a user dict containing the attributes (name, email, password, etc.)
# that the factory would use to create a user, but without actually
# creating the user in CKAN:
user_attributes_dict = factories.User.attributes()
# If you later want to create a user using these attributes, just pass them
# to the factory:
user = factories.User(**user_attributes_dict)
'''
import factory

import ckan.model
import ckan.logic
import ckan.new_tests.helpers as helpers


class User(factory.Factory):
'''A factory class for creating CKAN users.'''

# This is the class that UserFactory will create and return instances
# of.
FACTORY_FOR = ckan.model.User

# These are the default params that will be used to create new users.
fullname = 'Mr. Test User'
password = 'pass'
about = 'Just another test user.'

# Generate a different user name param for each user that gets created.
name = factory.Sequence(
lambda n: 'test_user_{n}'.format(n=n))

# Compute the email param for each user based on the values of the other
# params above.
email = factory.LazyAttribute(
lambda a: '{0}@ckan.org'.format(a.name).lower())

# I'm not sure how to support factory_boy's .build() feature in CKAN,
# so I've disabled it here.
@classmethod
def _build(cls, target_class, *args, **kwargs):
raise NotImplementedError(".build() isn't supported in CKAN")

# To make factory_boy work with CKAN we override _create() and make it call
# a CKAN action function.
# We might also be able to do this by using factory_boy's direct SQLAlchemy
# support: http://factoryboy.readthedocs.org/en/latest/orms.html#sqlalchemy
@classmethod
def _create(cls, target_class, *args, **kwargs):
if args:
assert False, "Positional args aren't supported, use keyword args."
user_dict = helpers.call_action('user_create', **kwargs)
return user_dict


def validator_data_dict():
'''Return a data dict with some arbitrary data in it, suitable to be passed
to validator functions for testing.
'''
return {('other key',): 'other value'}


def validator_errors_dict():
'''Return an errors dict with some arbitrary errors in it, suitable to be
passed to validator functions for testing.
'''
return {('other key',): ['other error']}
10 changes: 5 additions & 5 deletions ckan/new_tests/lib/navl/test_validators.py
Expand Up @@ -6,7 +6,7 @@

import nose.tools

import ckan.new_tests.data as test_data
import ckan.new_tests.factories as factories


class TestValidators(object):
Expand All @@ -28,12 +28,12 @@ def test_ignore_missing_with_value_missing(self):
key = ('key to be validated',)

# The data to pass to the validator function for validation.
data = test_data.validator_data_dict()
data = factories.validator_data_dict()
if value != 'skip':
data[key] = value

# The errors dict to pass to the validator function.
errors = test_data.validator_errors_dict()
errors = factories.validator_errors_dict()
errors[key] = []

# Make copies of the data and errors dicts for asserting later.
Expand Down Expand Up @@ -66,9 +66,9 @@ def test_ignore_missing_with_a_value(self):
import ckan.lib.navl.validators as validators

key = ('key to be validated',)
data = test_data.validator_data_dict()
data = factories.validator_data_dict()
data[key] = 'value to be validated'
errors = test_data.validator_errors_dict()
errors = factories.validator_errors_dict()
errors[key] = []

# Make copies of the data and errors dicts for asserting later.
Expand Down
38 changes: 19 additions & 19 deletions ckan/new_tests/logic/action/test_update.py
Expand Up @@ -6,7 +6,7 @@

import ckan.logic as logic
import ckan.new_tests.helpers as helpers
import ckan.new_tests.data as data
import ckan.new_tests.factories as factories


def datetime_from_string(s):
Expand Down Expand Up @@ -49,15 +49,15 @@ def test_user_update_name(self):
# 4. Do absolutely nothing else!

# 1. Setup.
user = helpers.call_action('user_create', **data.typical_user())
user = factories.User()

# 2. Call the function that is being tested, once only.
# FIXME we have to pass the email address and password to user_update
# even though we're not updating those fields, otherwise validation
# fails.
helpers.call_action('user_update', id=user['name'],
email=user['email'],
password=data.typical_user()['password'],
password=factories.User.attributes()['password'],
name='updated',
)

Expand All @@ -70,21 +70,22 @@ def test_user_update_name(self):
# 4. Do absolutely nothing else!

def test_user_update_with_id_that_does_not_exist(self):
user_dict = data.typical_user()
user_dict = factories.User.attributes()
user_dict['id'] = "there's no user with this id"

with nose.tools.assert_raises(logic.NotFound) as context:
helpers.call_action('user_update', **user_dict)
# TODO: Could assert the actual error message, not just the exception?
# (Could also do this with many of the tests below.)

def test_user_update_with_no_id(self):
user_dict = data.typical_user()
user_dict = factories.User.attributes()
assert 'id' not in user_dict
with nose.tools.assert_raises(logic.ValidationError) as context:
helpers.call_action('user_update', **user_dict)

def test_user_update_with_invalid_name(self):
user = helpers.call_action('user_create', **data.typical_user())
user = factories.User()

invalid_names = ('', 'a', False, 0, -1, 23, 'new', 'edit', 'search',
'a'*200, 'Hi!', )
Expand All @@ -94,9 +95,8 @@ def test_user_update_with_invalid_name(self):
helpers.call_action('user_update', **user)

def test_user_update_to_name_that_already_exists(self):
fred = helpers.call_action('user_create', **data.typical_user())
bob = helpers.call_action('user_create', name='bob',
email='bob@bob.com', password='pass')
fred = factories.User(name='fred')
bob = factories.User(name='bob')

# Try to update fred and change his user name to bob, which is already
# bob's user name
Expand All @@ -107,7 +107,7 @@ def test_user_update_to_name_that_already_exists(self):
def test_user_update_password(self):
'''Test that updating a user's password works successfully.'''

user = helpers.call_action('user_create', **data.typical_user())
user = factories.User()

# FIXME we have to pass the email address to user_update even though
# we're not updating it, otherwise validation fails.
Expand All @@ -123,7 +123,7 @@ def test_user_update_password(self):
assert updated_user.validate_password('new password')

def test_user_update_with_short_password(self):
user = helpers.call_action('user_create', **data.typical_user())
user = factories.User()

user['password'] = 'xxx' # This password is too short.
with nose.tools.assert_raises(logic.ValidationError) as context:
Expand All @@ -137,9 +137,9 @@ def test_user_update_with_empty_password(self):
changed either.
'''
user_dict = data.typical_user()
user_dict = factories.User.attributes()
original_password = user_dict['password']
user_dict = helpers.call_action('user_create', **user_dict)
user_dict = factories.User(**user_dict)

user_dict['password'] = ''
helpers.call_action('user_update', **user_dict)
Expand All @@ -149,14 +149,14 @@ def test_user_update_with_empty_password(self):
assert updated_user.validate_password(original_password)

def test_user_update_with_null_password(self):
user = helpers.call_action('user_create', **data.typical_user())
user = factories.User()

user['password'] = None
with nose.tools.assert_raises(logic.ValidationError) as context:
helpers.call_action('user_update', **user)

def test_user_update_with_invalid_password(self):
user = helpers.call_action('user_create', **data.typical_user())
user = factories.User()

for password in (False, -1, 23, 30.7):
user['password'] = password
Expand All @@ -168,15 +168,15 @@ def test_user_update_with_invalid_password(self):
def test_user_update_activity_stream(self):
'''Test that the right activity is emitted when updating a user.'''

user = helpers.call_action('user_create', **data.typical_user())
user = factories.User()
before = datetime.datetime.now()

# FIXME we have to pass the email address and password to user_update
# even though we're not updating those fields, otherwise validation
# fails.
helpers.call_action('user_update', id=user['name'],
email=user['email'],
password=data.typical_user()['password'],
password=factories.User.attributes()['password'],
name='updated',
)

Expand All @@ -203,7 +203,7 @@ def test_user_update_with_custom_schema(self):
'''
import ckan.logic.schema

user = helpers.call_action('user_create', **data.typical_user())
user = factories.User()

# A mock validator method, it doesn't do anything but it records what
# params it gets called with and how many times.
Expand All @@ -219,7 +219,7 @@ def test_user_update_with_custom_schema(self):
# trying to update them, or validation fails.
helpers.call_action('user_update', context={'schema': schema},
id=user['name'], email=user['email'],
password=data.typical_user()['password'],
password=factories.User.attributes()['password'],
name='updated',
)

Expand Down
11 changes: 5 additions & 6 deletions ckan/new_tests/logic/auth/test_update.py
Expand Up @@ -2,7 +2,7 @@
'''
import ckan.new_tests.helpers as helpers
import ckan.new_tests.data as data
import ckan.new_tests.factories as factories


class TestUpdate(object):
Expand Down Expand Up @@ -35,7 +35,7 @@ def _call_auth(self, auth_name, context=None, **kwargs):
def test_user_update_visitor_cannot_update_user(self):
'''Visitors should not be able to update users' accounts.'''

user = helpers.call_action('user_create', **data.typical_user())
user = factories.User()
user['name'] = 'updated'

# Try to update the user, but without passing any API key.
Expand All @@ -49,9 +49,8 @@ def test_user_update_visitor_cannot_update_user(self):
def test_user_update_user_cannot_update_another_user(self):
'''Users should not be able to update other users' accounts.'''

fred = helpers.call_action('user_create', **data.typical_user())
bob = helpers.call_action('user_create', name='bob',
email='bob@bob.com', password='pass')
fred = factories.User(name='fred')
bob = factories.User(name='bob')
fred['name'] = 'updated'

# Make Bob try to update Fred's user account.
Expand All @@ -62,7 +61,7 @@ def test_user_update_user_cannot_update_another_user(self):
def test_user_update_user_can_update_herself(self):
'''Users should be authorized to update their own accounts.'''

user = helpers.call_action('user_create', **data.typical_user())
user = factories.User()

context = {'user': user['name']}
user['name'] = 'updated'
Expand Down

0 comments on commit 1f95401

Please sign in to comment.