# -*- coding: utf-8; -*- import datetime import decimal from unittest import TestCase from unittest.mock import patch import colander from pyramid import testing from sqlalchemy import orm from wuttjamaican.conf import WuttaConfig from wuttaweb.forms import schema as mod from wuttaweb.forms import widgets from wuttaweb.testing import DataTestCase, WebTestCase class TestWuttaDateTime(TestCase): def test_deserialize(self): typ = mod.WuttaDateTime() node = colander.SchemaNode(typ) result = typ.deserialize(node, colander.null) self.assertIs(result, colander.null) result = typ.deserialize(node, '2024-12-11T10:33 PM') self.assertIsInstance(result, datetime.datetime) self.assertEqual(result, datetime.datetime(2024, 12, 11, 22, 33)) self.assertIsNone(result.tzinfo) result = typ.deserialize(node, '2024-12-11T22:33:00') self.assertIsInstance(result, datetime.datetime) self.assertEqual(result, datetime.datetime(2024, 12, 11, 22, 33)) self.assertIsNone(result.tzinfo) self.assertRaises(colander.Invalid, typ.deserialize, node, 'bogus') class TestObjectNode(DataTestCase): def setUp(self): self.setup_db() self.request = testing.DummyRequest(wutta_config=self.config) def test_dictify(self): model = self.app.model person = model.Person(full_name="Betty Boop") # unsupported type is converted to string node = mod.ObjectNode(colander.String()) value = node.dictify(person) self.assertEqual(value, "Betty Boop") # but supported type can dictify node = mod.ObjectNode(mod.PersonRef(self.request)) value = node.dictify(person) self.assertIs(value, person) def test_objectify(self): model = self.app.model person = model.Person(full_name="Betty Boop") # unsupported type raises error node = mod.ObjectNode(colander.String()) self.assertRaises(NotImplementedError, node.objectify, person) # but supported type can objectify node = mod.ObjectNode(mod.PersonRef(self.request)) value = node.objectify(person) self.assertIs(value, person) class TestWuttaEnum(WebTestCase): def test_widget_maker(self): enum = self.app.enum typ = mod.WuttaEnum(self.request, enum.UpgradeStatus) widget = typ.widget_maker() self.assertIsInstance(widget, widgets.SelectWidget) class TestWuttaMoney(WebTestCase): def test_widget_maker(self): enum = self.app.enum # default scale typ = mod.WuttaMoney(self.request) widget = typ.widget_maker() self.assertIsInstance(widget, widgets.WuttaMoneyInputWidget) self.assertEqual(widget.scale, 2) # custom scale typ = mod.WuttaMoney(self.request, scale=4) widget = typ.widget_maker() self.assertIsInstance(widget, widgets.WuttaMoneyInputWidget) self.assertEqual(widget.scale, 4) class TestWuttaQuantity(WebTestCase): def test_serialize(self): node = colander.SchemaNode(mod.WuttaQuantity(self.request)) typ = node.typ # null result = typ.serialize(node, colander.null) self.assertIs(result, colander.null) result = typ.serialize(node, None) self.assertIs(result, colander.null) # quantity result = typ.serialize(node, 42) self.assertEqual(result, '42') result = typ.serialize(node, 42.00) self.assertEqual(result, '42') result = typ.serialize(node, decimal.Decimal('42.00')) self.assertEqual(result, '42') result = typ.serialize(node, 42.13) self.assertEqual(result, '42.13') class TestObjectRef(DataTestCase): def setUp(self): self.setup_db() self.request = testing.DummyRequest(wutta_config=self.config) def test_empty_option(self): # null by default typ = mod.ObjectRef(self.request) self.assertIsNone(typ.empty_option) # passing true yields default empty option typ = mod.ObjectRef(self.request, empty_option=True) self.assertEqual(typ.empty_option, ('', "(none)")) # can set explicitly typ = mod.ObjectRef(self.request, empty_option=('foo', 'bar')) self.assertEqual(typ.empty_option, ('foo', 'bar')) # can set just a label typ = mod.ObjectRef(self.request, empty_option="(empty)") self.assertEqual(typ.empty_option, ('', "(empty)")) def test_model_class(self): typ = mod.ObjectRef(self.request) self.assertRaises(NotImplementedError, getattr, typ, 'model_class') def test_serialize(self): model = self.app.model node = colander.SchemaNode(colander.String()) # null typ = mod.ObjectRef(self.request) value = typ.serialize(node, colander.null) self.assertIs(value, colander.null) # model instance person = model.Person(full_name="Betty Boop") self.session.add(person) self.session.commit() self.assertIsNotNone(person.uuid) typ = mod.ObjectRef(self.request) value = typ.serialize(node, person) self.assertEqual(value, person.uuid.hex) # null w/ empty option typ = mod.ObjectRef(self.request, empty_option=('bad', 'BAD')) value = typ.serialize(node, colander.null) self.assertEqual(value, 'bad') def test_deserialize(self): model = self.app.model node = colander.SchemaNode(colander.String()) # null typ = mod.ObjectRef(self.request) value = typ.deserialize(node, colander.null) self.assertIs(value, colander.null) # model instance person = model.Person(full_name="Betty Boop") self.session.add(person) self.session.commit() self.assertIsNotNone(person.uuid) with patch.object(mod.ObjectRef, 'model_class', new=model.Person): with patch.object(mod, 'Session', return_value=self.session): typ = mod.ObjectRef(self.request) value = typ.deserialize(node, person.uuid) self.assertIs(value, person) def test_dictify(self): model = self.app.model node = colander.SchemaNode(colander.String()) # model instance person = model.Person(full_name="Betty Boop") self.session.add(person) self.session.commit() self.assertIsNotNone(person.uuid) typ = mod.ObjectRef(self.request) value = typ.dictify(person) self.assertIs(value, person) def test_objectify(self): model = self.app.model node = colander.SchemaNode(colander.String()) # null typ = mod.ObjectRef(self.request) value = typ.objectify(None) self.assertIsNone(value) with patch.object(mod, 'Session', return_value=self.session): # model instance person = model.Person(full_name="Betty Boop") self.session.add(person) self.session.commit() self.assertIsNotNone(person.uuid) with patch.object(mod.ObjectRef, 'model_class', new=model.Person): # can specify as uuid typ = mod.ObjectRef(self.request) value = typ.objectify(person.uuid) self.assertIs(value, person) # or can specify object proper typ = mod.ObjectRef(self.request) value = typ.objectify(person) self.assertIs(value, person) # error if not found with patch.object(mod.ObjectRef, 'model_class', new=model.Person): typ = mod.ObjectRef(self.request) self.assertRaises(ValueError, typ.objectify, 'WRONG-UUID') def test_get_query(self): model = self.app.model with patch.object(mod.ObjectRef, 'model_class', new=model.Person): with patch.object(mod, 'Session', return_value=self.session): typ = mod.ObjectRef(self.request) query = typ.get_query() self.assertIsInstance(query, orm.Query) def test_sort_query(self): model = self.app.model with patch.object(mod.ObjectRef, 'model_class', new=model.Person): with patch.object(mod, 'Session', return_value=self.session): typ = mod.ObjectRef(self.request) query = typ.get_query() sorted_query = typ.sort_query(query) self.assertIs(sorted_query, query) def test_widget_maker(self): model = self.app.model person = model.Person(full_name="Betty Boop") self.session.add(person) self.session.commit() # basic with patch.object(mod.ObjectRef, 'model_class', new=model.Person): with patch.object(mod, 'Session', return_value=self.session): typ = mod.ObjectRef(self.request) widget = typ.widget_maker() self.assertEqual(len(widget.values), 1) self.assertEqual(widget.values[0][1], "Betty Boop") # empty option with patch.object(mod.ObjectRef, 'model_class', new=model.Person): with patch.object(mod, 'Session', return_value=self.session): typ = mod.ObjectRef(self.request, empty_option=True) widget = typ.widget_maker() self.assertEqual(len(widget.values), 2) self.assertEqual(widget.values[0][1], "(none)") self.assertEqual(widget.values[1][1], "Betty Boop") class TestPersonRef(WebTestCase): def test_sort_query(self): with patch.object(mod, 'Session', return_value=self.session): typ = mod.PersonRef(self.request) query = typ.get_query() self.assertIsInstance(query, orm.Query) sorted_query = typ.sort_query(query) self.assertIsInstance(sorted_query, orm.Query) self.assertIsNot(sorted_query, query) def test_get_object_url(self): self.pyramid_config.add_route('people.view', '/people/{uuid}') model = self.app.model with patch.object(mod, 'Session', return_value=self.session): typ = mod.PersonRef(self.request) person = model.Person(full_name="Barney Rubble") self.session.add(person) self.session.commit() url = typ.get_object_url(person) self.assertIsNotNone(url) self.assertIn(f'/people/{person.uuid}', url) class TestRoleRef(WebTestCase): def test_sort_query(self): with patch.object(mod, 'Session', return_value=self.session): typ = mod.RoleRef(self.request) query = typ.get_query() self.assertIsInstance(query, orm.Query) sorted_query = typ.sort_query(query) self.assertIsInstance(sorted_query, orm.Query) self.assertIsNot(sorted_query, query) def test_get_object_url(self): self.pyramid_config.add_route('roles.view', '/roles/{uuid}') model = self.app.model with patch.object(mod, 'Session', return_value=self.session): typ = mod.RoleRef(self.request) role = model.Role(name='Manager') self.session.add(role) self.session.commit() url = typ.get_object_url(role) self.assertIsNotNone(url) self.assertIn(f'/roles/{role.uuid}', url) class TestUserRef(WebTestCase): def test_sort_query(self): with patch.object(mod, 'Session', return_value=self.session): typ = mod.UserRef(self.request) query = typ.get_query() self.assertIsInstance(query, orm.Query) sorted_query = typ.sort_query(query) self.assertIsInstance(sorted_query, orm.Query) self.assertIsNot(sorted_query, query) def test_get_object_url(self): self.pyramid_config.add_route('users.view', '/users/{uuid}') model = self.app.model with patch.object(mod, 'Session', return_value=self.session): typ = mod.UserRef(self.request) user = model.User(username='barney') self.session.add(user) self.session.commit() url = typ.get_object_url(user) self.assertIsNotNone(url) self.assertIn(f'/users/{user.uuid}', url) class TestUserRefs(DataTestCase): def setUp(self): self.setup_db() self.request = testing.DummyRequest(wutta_config=self.config) def test_widget_maker(self): model = self.app.model with patch.object(mod, 'Session', return_value=self.session): typ = mod.UserRefs(self.request) widget = typ.widget_maker() self.assertIsInstance(widget, widgets.UserRefsWidget) class TestRoleRefs(DataTestCase): def setUp(self): self.setup_db() self.request = testing.DummyRequest(wutta_config=self.config) def test_widget_maker(self): model = self.app.model auth = self.app.get_auth_handler() admin = auth.get_role_administrator(self.session) authed = auth.get_role_authenticated(self.session) anon = auth.get_role_anonymous(self.session) blokes = model.Role(name="Blokes") self.session.add(blokes) self.session.commit() with patch.object(mod, 'Session', return_value=self.session): # with root access, default values include: admin, blokes self.request.is_root = True typ = mod.RoleRefs(self.request) widget = typ.widget_maker() self.assertEqual(len(widget.values), 2) self.assertEqual(widget.values[0][1], "Administrator") self.assertEqual(widget.values[1][1], "Blokes") # without root, default values include: blokes self.request.is_root = False typ = mod.RoleRefs(self.request) widget = typ.widget_maker() self.assertEqual(len(widget.values), 1) self.assertEqual(widget.values[0][1], "Blokes") class TestPermissions(DataTestCase): def setUp(self): self.setup_db() self.request = testing.DummyRequest(wutta_config=self.config) def test_widget_maker(self): # no supported permissions permissions = {} typ = mod.Permissions(self.request, permissions) widget = typ.widget_maker() self.assertEqual(len(widget.values), 0) # supported permissions are morphed to values permissions = { 'widgets': { 'label': "Widgets", 'perms': { 'widgets.polish': { 'label': "Polish the widgets", }, }, }, } typ = mod.Permissions(self.request, permissions) widget = typ.widget_maker() self.assertEqual(len(widget.values), 1) self.assertEqual(widget.values[0], ('widgets.polish', "Polish the widgets")) class TestFileDownload(DataTestCase): def setUp(self): self.setup_db() self.request = testing.DummyRequest(wutta_config=self.config) def test_widget_maker(self): # sanity / coverage check typ = mod.FileDownload(self.request, url='/foo') widget = typ.widget_maker() self.assertIsInstance(widget, widgets.FileDownloadWidget) self.assertEqual(widget.url, '/foo') class TestEmailRecipients(TestCase): def test_serialize(self): typ = mod.EmailRecipients() node = colander.SchemaNode(typ) recips = [ 'alice@example.com', 'bob@example.com', ] recips_str = ', '.join(recips) # values result = typ.serialize(node, recips_str) self.assertEqual(result, '\n'.join(recips)) # null result = typ.serialize(node, colander.null) self.assertIs(result, colander.null) def test_deserialize(self): typ = mod.EmailRecipients() node = colander.SchemaNode(typ) recips = [ 'alice@example.com', 'bob@example.com', ] recips_str = ', '.join(recips) # values result = typ.deserialize(node, recips_str) self.assertEqual(result, recips_str) # null result = typ.deserialize(node, colander.null) self.assertIs(result, colander.null) def test_widget_maker(self): typ = mod.EmailRecipients() widget = typ.widget_maker() self.assertIsInstance(widget, widgets.EmailRecipientsWidget)