diff --git a/rattail/pyramid/tests/__init__.py b/rattail/pyramid/tests/__init__.py new file mode 100644 index 00000000..7acf2387 --- /dev/null +++ b/rattail/pyramid/tests/__init__.py @@ -0,0 +1,53 @@ + +import unittest + +from pyramid import testing + + +class TestCase(unittest.TestCase): + """ + Base class for all test suites. + """ + + def setUp(self): + self.config = testing.setUp() + # self.config = testing.setUp(settings={ + # 'mako.directories': [ + # 'rattail.pyramid:templates', + # 'edbob.pyramid:templates', + # ], + # }) + + def tearDown(self): + testing.tearDown() + + +# class DataTestCase(TestCase): +# """ +# Base class for all test suites which require fixture data. +# """ + +# def setUp(self): +# from sqlalchemy import create_engine +# from edbob import db +# from rattail.pyramid import Session +# from edbob.db.util import install_core_schema +# from edbob.db.extensions import activate_extension +# from rattail.pyramid.tests.fixtures import load_fixtures + +# engine = create_engine('postgresql://rattail:1pKglVgdHOP1MYGVdUZr@localhost/rattail.test') + +# db.engines = {'default': engine} +# db.engine = engine +# db.Session.configure(bind=engine) +# Session.configure(bind=engine) + +# install_core_schema(engine) +# activate_extension('rattail', engine) +# load_fixtures(engine) +# super(DataTestCase, self).setUp() + +# # def tearDown(self): +# # from rattail.pyramid import Session +# # super(DataTestCase, self).tearDown() +# # Session.configure(bind=None) diff --git a/rattail/pyramid/tests/fixtures.py b/rattail/pyramid/tests/fixtures.py new file mode 100644 index 00000000..a07825fd --- /dev/null +++ b/rattail/pyramid/tests/fixtures.py @@ -0,0 +1,28 @@ + +import fixture + +from rattail.db import model + + +class DepartmentData(fixture.DataSet): + + class grocery: + number = 1 + name = 'Grocery' + + class supplements: + number = 2 + name = 'Supplements' + + +def load_fixtures(engine): + + dbfixture = fixture.SQLAlchemyFixture( + env={ + 'DepartmentData': model.Department, + }, + engine=engine) + + data = dbfixture.data(DepartmentData) + + data.setup() diff --git a/rattail/pyramid/tests/test_departments.py b/rattail/pyramid/tests/test_departments.py new file mode 100644 index 00000000..dfbc909e --- /dev/null +++ b/rattail/pyramid/tests/test_departments.py @@ -0,0 +1,46 @@ + +from pyramid import testing + +from rattail.pyramid.tests import TestCase +from rattail.pyramid.views import departments + +from rattail.db.model import Department + + +class DepartmentTests(TestCase): + """ + Test Department views. + """ + + def test_grid(self): + request = testing.DummyRequest(has_perm=lambda p: True) + view = departments.DepartmentsGrid(request) + view._data = None + view._sort_config = None + self.assertIsInstance(view, departments.DepartmentsGrid) + self.assertIsInstance(view.filter_map(), dict) + self.assertIsInstance(view.filter_config(), dict) + self.assertIsInstance(view.sort_map(), dict) + self.assertIsInstance(view.grid(), object) + + def test_crud(self): + request = testing.DummyRequest() + view = departments.DepartmentCrud(request) + self.assertIsInstance(view, departments.DepartmentCrud) + self.assertIsInstance(view.fieldset(Department), object) + + def test_autocomplete(self): + request = testing.DummyRequest() + view = departments.DepartmentsAutocomplete(request) + self.assertIsInstance(view, departments.DepartmentsAutocomplete) + + def test_vendor_grid(self): + request = testing.DummyRequest(params={'uuid': 'bogus'}) + view = departments.DepartmentsByVendorGrid(request) + view._data = None + self.assertIsInstance(view, departments.DepartmentsByVendorGrid) + self.assertIsInstance(view.query(), object) + self.assertIsInstance(view.grid(), object) + + def test_includeme(self): + self.config.include(departments) diff --git a/rattail/pyramid/tests/test_root.py b/rattail/pyramid/tests/test_root.py new file mode 100644 index 00000000..22f6b34e --- /dev/null +++ b/rattail/pyramid/tests/test_root.py @@ -0,0 +1,11 @@ + +from rattail.pyramid.tests import TestCase + + +class RootTests(TestCase): + """ + Test root module. + """ + + def test_includeme(self): + self.config.include('rattail.pyramid') diff --git a/rattail/pyramid/tests/test_views.py b/rattail/pyramid/tests/test_views.py new file mode 100644 index 00000000..e891b903 --- /dev/null +++ b/rattail/pyramid/tests/test_views.py @@ -0,0 +1,11 @@ + +from rattail.pyramid.tests import TestCase + + +class ViewTests(TestCase): + """ + Test root views module. + """ + + def test_includeme(self): + self.config.include('rattail.pyramid.views') diff --git a/rattail/pyramid/views/batches/core.py b/rattail/pyramid/views/batches/core.py index 82f289df..b1eefb5e 100644 --- a/rattail/pyramid/views/batches/core.py +++ b/rattail/pyramid/views/batches/core.py @@ -40,12 +40,13 @@ from edbob.pyramid.views import SearchableAlchemyGridView, CrudView, View import rattail from rattail import batches +from rattail.db.model import Batch from rattail.threads import Thread class BatchesGrid(SearchableAlchemyGridView): - mapped_class = rattail.Batch + mapped_class = Batch config_prefix = 'batches' sort = 'id' @@ -53,15 +54,15 @@ class BatchesGrid(SearchableAlchemyGridView): def executed_is(q, v): if v == 'True': - return q.filter(rattail.Batch.executed != None) + return q.filter(Batch.executed != None) else: - return q.filter(rattail.Batch.executed == None) + return q.filter(Batch.executed == None) def executed_isnot(q, v): if v == 'True': - return q.filter(rattail.Batch.executed == None) + return q.filter(Batch.executed == None) else: - return q.filter(rattail.Batch.executed != None) + return q.filter(Batch.executed != None) return self.make_filter_map( exact=['id'], @@ -113,7 +114,7 @@ class BatchesGrid(SearchableAlchemyGridView): class BatchCrud(CrudView): - mapped_class = rattail.Batch + mapped_class = Batch home_route = 'batches' def fieldset(self, model): @@ -159,7 +160,7 @@ class ExecuteBatch(View): def __call__(self): uuid = self.request.matchdict['uuid'] - batch = Session.query(rattail.Batch).get(uuid) if uuid else None + batch = Session.query(Batch).get(uuid) if uuid else None if not batch: return HTTPFound(location=self.request.route_url('batches')) diff --git a/rattail/pyramid/views/brands.py b/rattail/pyramid/views/brands.py index 5cc9009b..a3d9aec2 100644 --- a/rattail/pyramid/views/brands.py +++ b/rattail/pyramid/views/brands.py @@ -29,12 +29,12 @@ from edbob.pyramid.views import ( SearchableAlchemyGridView, CrudView, AutocompleteView) -import rattail +from rattail.db.model import Brand class BrandsGrid(SearchableAlchemyGridView): - mapped_class = rattail.Brand + mapped_class = Brand config_prefix = 'brands' sort = 'name' @@ -70,7 +70,7 @@ class BrandsGrid(SearchableAlchemyGridView): class BrandCrud(CrudView): - mapped_class = rattail.Brand + mapped_class = Brand home_route = 'brands' def fieldset(self, model): @@ -84,7 +84,7 @@ class BrandCrud(CrudView): class BrandsAutocomplete(AutocompleteView): - mapped_class = rattail.Brand + mapped_class = Brand fieldname = 'name' diff --git a/rattail/pyramid/views/categories.py b/rattail/pyramid/views/categories.py index 166adc8b..d2927f4a 100644 --- a/rattail/pyramid/views/categories.py +++ b/rattail/pyramid/views/categories.py @@ -28,12 +28,12 @@ from edbob.pyramid.views import SearchableAlchemyGridView, CrudView -import rattail +from rattail.db.model import Category class CategoriesGrid(SearchableAlchemyGridView): - mapped_class = rattail.Category + mapped_class = Category config_prefix = 'categories' sort = 'number' @@ -70,7 +70,7 @@ class CategoriesGrid(SearchableAlchemyGridView): class CategoryCrud(CrudView): - mapped_class = rattail.Category + mapped_class = Category home_route = 'categories' def fieldset(self, model): diff --git a/rattail/pyramid/views/customergroups.py b/rattail/pyramid/views/customergroups.py index d3cdcd75..af3a3bf6 100644 --- a/rattail/pyramid/views/customergroups.py +++ b/rattail/pyramid/views/customergroups.py @@ -28,13 +28,13 @@ from edbob.pyramid.views import SearchableAlchemyGridView -import rattail from rattail.pyramid.views import CrudView +from rattail.db.model import CustomerGroup class CustomerGroupsGrid(SearchableAlchemyGridView): - mapped_class = rattail.CustomerGroup + mapped_class = CustomerGroup config_prefix = 'customer_groups' sort = 'name' @@ -71,7 +71,7 @@ class CustomerGroupsGrid(SearchableAlchemyGridView): class CustomerGroupCrud(CrudView): - mapped_class = rattail.CustomerGroup + mapped_class = CustomerGroup home_route = 'customer_groups' pretty_name = "Customer Group" diff --git a/rattail/pyramid/views/departments.py b/rattail/pyramid/views/departments.py index 9ee7b3d0..ecc19c84 100644 --- a/rattail/pyramid/views/departments.py +++ b/rattail/pyramid/views/departments.py @@ -30,12 +30,12 @@ from edbob.pyramid.views import ( SearchableAlchemyGridView, CrudView, AlchemyGridView, AutocompleteView) -import rattail +from rattail.db.model import Department, Product, ProductCost, Vendor class DepartmentsGrid(SearchableAlchemyGridView): - mapped_class = rattail.Department + mapped_class = Department config_prefix = 'departments' sort = 'name' @@ -72,7 +72,7 @@ class DepartmentsGrid(SearchableAlchemyGridView): class DepartmentCrud(CrudView): - mapped_class = rattail.Department + mapped_class = Department home_route = 'departments' def fieldset(self, model): @@ -87,19 +87,19 @@ class DepartmentCrud(CrudView): class DepartmentsByVendorGrid(AlchemyGridView): - mapped_class = rattail.Department + mapped_class = Department config_prefix = 'departments.by_vendor' checkboxes = True partial_only = True def query(self): q = self.make_query() - q = q.outerjoin(rattail.Product) - q = q.join(rattail.ProductCost) - q = q.join(rattail.Vendor) - q = q.filter(rattail.Vendor.uuid == self.request.params['uuid']) + q = q.outerjoin(Product) + q = q.join(ProductCost) + q = q.join(Vendor) + q = q.filter(Vendor.uuid == self.request.params['uuid']) q = q.distinct() - q = q.order_by(rattail.Department.name) + q = q.order_by(Department.name) return q def grid(self): @@ -114,7 +114,7 @@ class DepartmentsByVendorGrid(AlchemyGridView): class DepartmentsAutocomplete(AutocompleteView): - mapped_class = rattail.Department + mapped_class = Department fieldname = 'name' diff --git a/rattail/pyramid/views/labels.py b/rattail/pyramid/views/labels.py index be2be88a..aa26b0ea 100644 --- a/rattail/pyramid/views/labels.py +++ b/rattail/pyramid/views/labels.py @@ -37,12 +37,12 @@ from edbob.pyramid.views import SearchableAlchemyGridView, CrudView from edbob.pyramid.grids.search import BooleanSearchFilter from edbob.pyramid.forms import StrippingFieldRenderer -import rattail +from rattail.db.model import LabelProfile class ProfilesGrid(SearchableAlchemyGridView): - mapped_class = rattail.LabelProfile + mapped_class = LabelProfile config_prefix = 'label_profiles' sort = 'ordinal' @@ -82,7 +82,7 @@ class ProfilesGrid(SearchableAlchemyGridView): class ProfileCrud(CrudView): - mapped_class = rattail.LabelProfile + mapped_class = LabelProfile home_route = 'label_profiles' pretty_name = "Label Profile" update_cancel_route = 'label_profile.read' @@ -134,7 +134,7 @@ class ProfileCrud(CrudView): def printer_settings(request): uuid = request.matchdict['uuid'] - profile = Session.query(rattail.LabelProfile).get(uuid) if uuid else None + profile = Session.query(LabelProfile).get(uuid) if uuid else None if not profile: return HTTPFound(location=request.route_url('label_profiles')) diff --git a/rattail/pyramid/views/products.py b/rattail/pyramid/views/products.py index 5073cb6f..34af8a8c 100644 --- a/rattail/pyramid/views/products.py +++ b/rattail/pyramid/views/products.py @@ -38,13 +38,14 @@ import edbob from edbob.pyramid.progress import SessionProgress from edbob.pyramid.views import SearchableAlchemyGridView -import rattail import rattail.labels from rattail import sil from rattail import batches from rattail.threads import Thread from rattail.exceptions import LabelPrintingError -from rattail.db.model import ProductPrice, ProductCode +from rattail.db.model import ( + Product, ProductPrice, ProductCost, ProductCode, + Brand, Vendor, Department, Subdepartment, LabelProfile) from rattail.pyramid import Session from rattail.pyramid.forms import (AutocompleteFieldRenderer, @@ -54,7 +55,7 @@ from rattail.pyramid.views import CrudView class ProductsGrid(SearchableAlchemyGridView): - mapped_class = rattail.Product + mapped_class = Product config_prefix = 'products' sort = 'description' @@ -62,29 +63,29 @@ class ProductsGrid(SearchableAlchemyGridView): def join_vendor(q): q = q.outerjoin( - rattail.ProductCost, + ProductCost, and_( - rattail.ProductCost.product_uuid == rattail.Product.uuid, - rattail.ProductCost.preference == 1, + ProductCost.product_uuid == Product.uuid, + ProductCost.preference == 1, )) - q = q.outerjoin(rattail.Vendor) + q = q.outerjoin(Vendor) return q return { 'brand': - lambda q: q.outerjoin(rattail.Brand), + lambda q: q.outerjoin(Brand), 'department': - lambda q: q.outerjoin(rattail.Department, - rattail.Department.uuid == rattail.Product.department_uuid), + lambda q: q.outerjoin(Department, + Department.uuid == Product.department_uuid), 'subdepartment': - lambda q: q.outerjoin(rattail.Subdepartment, - rattail.Subdepartment.uuid == rattail.Product.subdepartment_uuid), + lambda q: q.outerjoin(Subdepartment, + Subdepartment.uuid == Product.subdepartment_uuid), 'regular_price': - lambda q: q.outerjoin(rattail.ProductPrice, - rattail.ProductPrice.uuid == rattail.Product.regular_price_uuid), + lambda q: q.outerjoin(ProductPrice, + ProductPrice.uuid == Product.regular_price_uuid), 'current_price': - lambda q: q.outerjoin(rattail.ProductPrice, - rattail.ProductPrice.uuid == rattail.Product.current_price_uuid), + lambda q: q.outerjoin(ProductPrice, + ProductPrice.uuid == Product.current_price_uuid), 'vendor': join_vendor, 'code': @@ -101,7 +102,7 @@ class ProductsGrid(SearchableAlchemyGridView): except ValueError: return q else: - return q.filter(rattail.Product.upc == v) if v else q + return q.filter(Product.upc == v) if v else q def filter_not(q, v): try: @@ -109,17 +110,17 @@ class ProductsGrid(SearchableAlchemyGridView): except ValueError: return q else: - return q.filter(rattail.Product.upc != v) if v else q + return q.filter(Product.upc != v) if v else q return {'is': filter_is, 'nt': filter_not} return self.make_filter_map( ilike=['description', 'size'], upc=filter_upc(), - brand=self.filter_ilike(rattail.Brand.name), - department=self.filter_ilike(rattail.Department.name), - subdepartment=self.filter_ilike(rattail.Subdepartment.name), - vendor=self.filter_ilike(rattail.Vendor.name), + brand=self.filter_ilike(Brand.name), + department=self.filter_ilike(Department.name), + subdepartment=self.filter_ilike(Subdepartment.name), + vendor=self.filter_ilike(Vendor.name), code=self.filter_ilike(ProductCode.code)) def filter_config(self): @@ -139,21 +140,21 @@ class ProductsGrid(SearchableAlchemyGridView): def sort_map(self): return self.make_sort_map( 'upc', 'description', 'size', - brand=self.sorter(rattail.Brand.name), - department=self.sorter(rattail.Department.name), - subdepartment=self.sorter(rattail.Subdepartment.name), - regular_price=self.sorter(rattail.ProductPrice.price), - current_price=self.sorter(rattail.ProductPrice.price), - vendor=self.sorter(rattail.Vendor.name)) + brand=self.sorter(Brand.name), + department=self.sorter(Department.name), + subdepartment=self.sorter(Subdepartment.name), + regular_price=self.sorter(ProductPrice.price), + current_price=self.sorter(ProductPrice.price), + vendor=self.sorter(Vendor.name)) def query(self): q = self.make_query() - q = q.options(joinedload(rattail.Product.brand)) - q = q.options(joinedload(rattail.Product.department)) - q = q.options(joinedload(rattail.Product.subdepartment)) - q = q.options(joinedload(rattail.Product.regular_price)) - q = q.options(joinedload(rattail.Product.current_price)) - q = q.options(joinedload(rattail.Product.vendor)) + q = q.options(joinedload(Product.brand)) + q = q.options(joinedload(Product.department)) + q = q.options(joinedload(Product.subdepartment)) + q = q.options(joinedload(Product.regular_price)) + q = q.options(joinedload(Product.current_price)) + q = q.options(joinedload(Product.vendor)) return q def grid(self): @@ -184,7 +185,7 @@ class ProductsGrid(SearchableAlchemyGridView): g.deletable = True g.delete_route_name = 'product.delete' - q = Session.query(rattail.LabelProfile) + q = Session.query(LabelProfile) if q.count(): def labels(row): return link_to("Print", '#', class_='print-label') @@ -193,15 +194,15 @@ class ProductsGrid(SearchableAlchemyGridView): return g def render_kwargs(self): - q = Session.query(rattail.LabelProfile) - q = q.filter(rattail.LabelProfile.visible == True) - q = q.order_by(rattail.LabelProfile.ordinal) + q = Session.query(LabelProfile) + q = q.filter(LabelProfile.visible == True) + q = q.order_by(LabelProfile.ordinal) return {'label_profiles': q.all()} class ProductCrud(CrudView): - mapped_class = rattail.Product + mapped_class = Product home_route = 'products' def get_model(self, key): @@ -239,12 +240,12 @@ class ProductCrud(CrudView): def print_labels(request): profile = request.params.get('profile') - profile = Session.query(rattail.LabelProfile).get(profile) if profile else None + profile = Session.query(LabelProfile).get(profile) if profile else None if not profile: return {'error': "Label profile not found"} product = request.params.get('product') - product = Session.query(rattail.Product).get(product) if product else None + product = Session.query(Product).get(product) if product else None if not product: return {'error': "Product not found"} diff --git a/rattail/pyramid/views/stores.py b/rattail/pyramid/views/stores.py index 9e16dfe2..6f050f72 100644 --- a/rattail/pyramid/views/stores.py +++ b/rattail/pyramid/views/stores.py @@ -30,34 +30,34 @@ from sqlalchemy import and_ from edbob.pyramid.views import SearchableAlchemyGridView -import rattail from rattail.pyramid.views import CrudView +from rattail.db.model import Store, StoreEmailAddress, StorePhoneNumber class StoresGrid(SearchableAlchemyGridView): - mapped_class = rattail.Store + mapped_class = Store config_prefix = 'stores' sort = 'id' def join_map(self): return { 'email': - lambda q: q.outerjoin(rattail.StoreEmailAddress, and_( - rattail.StoreEmailAddress.parent_uuid == rattail.Store.uuid, - rattail.StoreEmailAddress.preference == 1)), + lambda q: q.outerjoin(StoreEmailAddress, and_( + StoreEmailAddress.parent_uuid == Store.uuid, + StoreEmailAddress.preference == 1)), 'phone': - lambda q: q.outerjoin(rattail.StorePhoneNumber, and_( - rattail.StorePhoneNumber.parent_uuid == rattail.Store.uuid, - rattail.StorePhoneNumber.preference == 1)), + lambda q: q.outerjoin(StorePhoneNumber, and_( + StorePhoneNumber.parent_uuid == Store.uuid, + StorePhoneNumber.preference == 1)), } def filter_map(self): return self.make_filter_map( exact=['id'], ilike=['name'], - email=self.filter_ilike(rattail.StoreEmailAddress.address), - phone=self.filter_ilike(rattail.StorePhoneNumber.number)) + email=self.filter_ilike(StoreEmailAddress.address), + phone=self.filter_ilike(StorePhoneNumber.number)) def filter_config(self): return self.make_filter_config( @@ -68,8 +68,8 @@ class StoresGrid(SearchableAlchemyGridView): def sort_map(self): return self.make_sort_map( 'id', 'name', - email=self.sorter(rattail.StoreEmailAddress.address), - phone=self.sorter(rattail.StorePhoneNumber.number)) + email=self.sorter(StoreEmailAddress.address), + phone=self.sorter(StorePhoneNumber.number)) def grid(self): g = self.make_grid() @@ -94,7 +94,7 @@ class StoresGrid(SearchableAlchemyGridView): class StoreCrud(CrudView): - mapped_class = rattail.Store + mapped_class = Store home_route = 'stores' def fieldset(self, model): diff --git a/rattail/pyramid/views/subdepartments.py b/rattail/pyramid/views/subdepartments.py index 65291ce4..cf36453b 100644 --- a/rattail/pyramid/views/subdepartments.py +++ b/rattail/pyramid/views/subdepartments.py @@ -28,12 +28,12 @@ from edbob.pyramid.views import SearchableAlchemyGridView, CrudView -import rattail +from rattail.db.model import Subdepartment class SubdepartmentsGrid(SearchableAlchemyGridView): - mapped_class = rattail.Subdepartment + mapped_class = Subdepartment config_prefix = 'subdepartments' sort = 'name' @@ -71,7 +71,7 @@ class SubdepartmentsGrid(SearchableAlchemyGridView): class SubdepartmentCrud(CrudView): - mapped_class = rattail.Subdepartment + mapped_class = Subdepartment home_route = 'subdepartments' def fieldset(self, model): diff --git a/rattail/pyramid/views/users.py b/rattail/pyramid/views/users.py index 169de42f..b7d4c5de 100644 --- a/rattail/pyramid/views/users.py +++ b/rattail/pyramid/views/users.py @@ -28,16 +28,16 @@ import formalchemy -import edbob from edbob.pyramid.views import users from rattail.pyramid.views import CrudView from rattail.pyramid.forms import PersonFieldRenderer +from rattail.db.model import User class UserCrud(CrudView): - mapped_class = edbob.User + mapped_class = User home_route = 'users' def fieldset(self, user): diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..f1fb856f --- /dev/null +++ b/setup.cfg @@ -0,0 +1,7 @@ +[nosetests] +nocapture = 1 +cover-package = rattail.pyramid +cover-erase = 1 +cover-inclusive = 1 +cover-html = 1 +cover-html-dir = htmlcov diff --git a/setup.py b/setup.py index f43f8060..b905fbf6 100644 --- a/setup.py +++ b/setup.py @@ -94,6 +94,8 @@ setup( ], install_requires = requires, + tests_require = requires + ['nose', 'coverage'], + test_suite = 'nose.collector', namespace_packages = ['rattail'], packages = find_packages(),