Skip to content

Commit

Permalink
Basic regression/functional tests for datastore.
Browse files Browse the repository at this point in the history
This commit only tests
- Adding a basic entity with an auto allocated ID, a
  given integer ID or a given name.
- Adding multiple entities via a transaction
- The given kind has no other entities (i.e. no false positives)

This commit adds
- A sample file to set environment variables for a test runner.
- A test runner.
- A utility for getting test relevant environ variables.
  • Loading branch information
dhermes committed Oct 21, 2014
1 parent 7f6e4b1 commit 2c101b4
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,6 @@ docs/_build
# Virtual environment
env/
coverage.xml

# Regression test environment variables.
regression/local_test_setup
Empty file added regression/__init__.py
Empty file.
118 changes: 118 additions & 0 deletions regression/datastore.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import datetime
import pytz
import unittest2

from gcloud import datastore
# This assumes the command is being run via tox hence the
# repository root is the current directory.
from regression import regression_utils


class TestDatastore(unittest2.TestCase):

def setUp(self):
environ = regression_utils.get_environ()
self._dataset_id = environ['dataset_id']
self._client_email = environ['client_email']
self._key_filename = environ['key_filename']
self._datasets = {}

self.entities_to_delete = []

def tearDown(self):
for entity in self.entities_to_delete:
entity.delete()

def _get_dataset(self):
if self._dataset_id not in self._datasets:
self._datasets[self._dataset_id] = datastore.get_dataset(
self._dataset_id, self._client_email, self._key_filename)
return self._datasets[self._dataset_id]

def _get_post(self, name=None, key_id=None, post_content=None):
post_content = post_content or {
'title': 'How to make the perfect pizza in your grill',
'tags': ['pizza', 'grill'],
# NOTE: We don't support datetime.date, but should.
# NOTE: Without a tz, assertEqual fails with
# "can't compare offset-naive and offset-aware datetimes"
'publishedAt': datetime.datetime(2001, 1, 1, tzinfo=pytz.utc),
'author': 'Silvano',
'isDraft': False,
'wordCount': 400,
'rating': 5.0,
}
# Create an entity with the given content in our dataset.
dataset = self._get_dataset()
entity = dataset.entity(kind='Post')
entity.update(post_content)

# Update the entity key.
key = None
if name is not None:
key = entity.key().name(name)
if key_id is not None:
key = entity.key().id(key_id)
if key is not None:
entity.key(key)

return entity

def _generic_test_post(self, name=None, key_id=None):
entity = self._get_post(name=name, key_id=key_id)
entity.save()

# Register entity to be deleted.
self.entities_to_delete.append(entity)

if name is not None:
self.assertEqual(entity.key().name(), name)
if key_id is not None:
self.assertEqual(entity.key().id(), key_id)
retrieved_entity = self._get_dataset().get_entity(entity.key())
# Check the keys are the same.
self.assertEqual(retrieved_entity.key().path(),
entity.key().path())
# Check the data is the same.
retrieved_dict = dict(retrieved_entity.items())
entity_dict = dict(entity.items())
self.assertEqual(retrieved_dict, entity_dict)

def test_post_with_name(self):
self._generic_test_post(name='post1')

def test_post_with_id(self):
self._generic_test_post(key_id=123456789)

def test_post_with_generated_id(self):
self._generic_test_post()

def test_save_multiple(self):
dataset = self._get_dataset()
with dataset.transaction():
entity1 = self._get_post()
entity1.save()
# Register entity to be deleted.
self.entities_to_delete.append(entity1)

second_post_content = {
'title': 'How to make the perfect homemade pasta',
'tags': ['pasta', 'homemade'],
'publishedAt': datetime.datetime(2001, 1, 1),
'author': 'Silvano',
'isDraft': False,
'wordCount': 450,
'rating': 4.5,
}
entity2 = self._get_post(post_content=second_post_content)
entity2.save()
# Register entity to be deleted.
self.entities_to_delete.append(entity2)

keys = [entity1.key(), entity2.key()]
matches = dataset.get_entities(keys)
self.assertEqual(len(matches), 2)

def test_empty_kind(self):
posts = self._get_dataset().query().kind('Post').limit(2).fetch()
self.assertEqual(posts, [])
3 changes: 3 additions & 0 deletions regression/local_test_setup.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export GCLOUD_TESTS_DATASET_ID="my-dataset"
export GCLOUD_TESTS_CLIENT_EMAIL="[email protected]"
export GCLOUD_TESTS_KEY_FILE="path.key"
25 changes: 25 additions & 0 deletions regression/regression_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import os
import sys


# Defaults from shell environ. May be None.
DATASET_ID = os.getenv('GCLOUD_TESTS_DATASET_ID')
CLIENT_EMAIL = os.getenv('GCLOUD_TESTS_CLIENT_EMAIL')
KEY_FILENAME = os.getenv('GCLOUD_TESTS_KEY_FILE')

ENVIRON_ERROR_MSG = """\
To run the regression tests, you need to set some environment variables.
Please check the Contributing guide for instructions.
"""


def get_environ():
if DATASET_ID is None or CLIENT_EMAIL is None or KEY_FILENAME is None:
print >> sys.stderr, ENVIRON_ERROR_MSG
sys.exit(1)

return {
'dataset_id': DATASET_ID,
'client_email': CLIENT_EMAIL,
'key_filename': KEY_FILENAME,
}
28 changes: 28 additions & 0 deletions regression/run_regression.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import argparse
import unittest2


def get_parser():
parser = argparse.ArgumentParser(
description='GCloud test runner against actual project.')
parser.add_argument('--package', dest='package',
choices=('datastore',),
default='datastore', help='Package to be tested.')
return parser


def run_module_tests(module_name):
suite = unittest2.TestSuite()
tests = unittest2.defaultTestLoader.loadTestsFromName(module_name)
suite.addTest(tests)
unittest2.TextTestRunner(verbosity=2).run(suite)


def main():
parser = get_parser()
args = parser.parse_args()
run_module_tests(args.package)


if __name__ == '__main__':
main()
3 changes: 2 additions & 1 deletion run_pylint.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ def is_production_filename(filename):
:rtype: `bool`
:returns: Boolean indicating production status.
"""
return not ('demo' in filename or 'test' in filename)
return not ('demo' in filename or 'test' in filename
or filename.startswith('regression'))


def get_python_files():
Expand Down
8 changes: 8 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,11 @@ deps =
pep8
pylint
unittest2

[testenv:regression]
basepython =
python2.7
commands =
python regression/run_regression.py --package datastore
deps =
unittest2

0 comments on commit 2c101b4

Please sign in to comment.