3
0
Fork 0

feat: add Users view; improve CRUD master for SQLAlchemy models

This commit is contained in:
Lance Edgar 2024-08-12 21:17:08 -05:00
parent 33589f1cd8
commit eac3b81918
33 changed files with 1510 additions and 253 deletions

View file

@ -8,46 +8,15 @@ import deform
from pyramid import testing
from wuttjamaican.conf import WuttaConfig
from wuttaweb.forms import base
from wuttaweb.forms import base, widgets
from wuttaweb import helpers
class TestFieldList(TestCase):
def test_insert_before(self):
fields = base.FieldList(['f1', 'f2'])
self.assertEqual(fields, ['f1', 'f2'])
# typical
fields.insert_before('f1', 'XXX')
self.assertEqual(fields, ['XXX', 'f1', 'f2'])
fields.insert_before('f2', 'YYY')
self.assertEqual(fields, ['XXX', 'f1', 'YYY', 'f2'])
# appends new field if reference field is invalid
fields.insert_before('f3', 'ZZZ')
self.assertEqual(fields, ['XXX', 'f1', 'YYY', 'f2', 'ZZZ'])
def test_insert_after(self):
fields = base.FieldList(['f1', 'f2'])
self.assertEqual(fields, ['f1', 'f2'])
# typical
fields.insert_after('f1', 'XXX')
self.assertEqual(fields, ['f1', 'XXX', 'f2'])
fields.insert_after('XXX', 'YYY')
self.assertEqual(fields, ['f1', 'XXX', 'YYY', 'f2'])
# appends new field if reference field is invalid
fields.insert_after('f3', 'ZZZ')
self.assertEqual(fields, ['f1', 'XXX', 'YYY', 'f2', 'ZZZ'])
class TestForm(TestCase):
def setUp(self):
self.config = WuttaConfig(defaults={
'wutta.web.menus.handler_spec': 'tests.utils:NullMenuHandler',
'wutta.web.menus.handler_spec': 'tests.util:NullMenuHandler',
})
self.app = self.config.get_app()
self.request = testing.DummyRequest(wutta_config=self.config, use_oruga=False)
@ -74,7 +43,7 @@ class TestForm(TestCase):
def test_init_with_none(self):
form = self.make_form()
self.assertIsNone(form.fields)
self.assertEqual(form.fields, [])
def test_init_with_fields(self):
form = self.make_form(fields=['foo', 'bar'])
@ -115,6 +84,79 @@ class TestForm(TestCase):
form.set_fields(['baz'])
self.assertEqual(form.fields, ['baz'])
def test_remove(self):
form = self.make_form(fields=['one', 'two', 'three', 'four'])
self.assertEqual(form.fields, ['one', 'two', 'three', 'four'])
form.remove('two', 'three')
self.assertEqual(form.fields, ['one', 'four'])
def test_set_node(self):
form = self.make_form(fields=['foo', 'bar'])
self.assertEqual(form.nodes, {})
# complete node
node = colander.SchemaNode(colander.Bool(), name='foo')
form.set_node('foo', node)
self.assertIs(form.nodes['foo'], node)
# type only
typ = colander.Bool()
form.set_node('foo', typ)
node = form.nodes['foo']
self.assertIsInstance(node, colander.SchemaNode)
self.assertIsInstance(node.typ, colander.Bool)
self.assertEqual(node.name, 'foo')
# schema is updated if already present
schema = form.get_schema()
self.assertIsNotNone(schema)
typ = colander.Date()
form.set_node('foo', typ)
node = form.nodes['foo']
self.assertIsInstance(node, colander.SchemaNode)
self.assertIsInstance(node.typ, colander.Date)
self.assertEqual(node.name, 'foo')
def test_set_widget(self):
form = self.make_form(fields=['foo', 'bar'])
self.assertEqual(form.widgets, {})
# basic
widget = widgets.SelectWidget()
form.set_widget('foo', widget)
self.assertIs(form.widgets['foo'], widget)
# schema is updated if already present
schema = form.get_schema()
self.assertIsNotNone(schema)
self.assertIs(schema['foo'].widget, widget)
new_widget = widgets.TextInputWidget()
form.set_widget('foo', new_widget)
self.assertIs(form.widgets['foo'], new_widget)
self.assertIs(schema['foo'].widget, new_widget)
def test_set_validator(self):
form = self.make_form(fields=['foo', 'bar'])
self.assertEqual(form.validators, {})
def validate1(node, value):
pass
# basic
form.set_validator('foo', validate1)
self.assertIs(form.validators['foo'], validate1)
def validate2(node, value):
pass
# schema is updated if already present
schema = form.get_schema()
self.assertIsNotNone(schema)
self.assertIs(schema['foo'].validator, validate1)
form.set_validator('foo', validate2)
self.assertIs(form.validators['foo'], validate2)
self.assertIs(schema['foo'].validator, validate2)
def test_get_schema(self):
model = self.app.model
form = self.make_form()
@ -144,6 +186,13 @@ class TestForm(TestCase):
self.assertIn('name', schema)
self.assertIn('value', schema)
# but node overrides are honored when auto-generating
form = self.make_form(model_class=model.Setting)
value_node = colander.SchemaNode(colander.Bool(), name='value')
form.set_node('value', value_node)
schema = form.get_schema()
self.assertIs(schema['value'], value_node)
# schema is auto-generated if model_instance provided
form = self.make_form(model_instance=model.Setting(name='uhoh'))
self.assertEqual(form.fields, ['name', 'value'])
@ -166,7 +215,23 @@ class TestForm(TestCase):
form.set_required('bar', False)
schema = form.get_schema()
self.assertIs(schema['foo'].missing, colander.required)
self.assertIsNone(schema['bar'].missing)
self.assertIs(schema['bar'].missing, colander.null)
# validator overrides are honored
def validate(node, value): pass
form = self.make_form(model_class=model.Setting)
form.set_validator('name', validate)
schema = form.get_schema()
self.assertIs(schema['name'].validator, validate)
# validator can be set for whole form
form = self.make_form(model_class=model.Setting)
schema = form.get_schema()
self.assertIsNone(schema.validator)
form = self.make_form(model_class=model.Setting)
form.set_validator(None, validate)
schema = form.get_schema()
self.assertIs(schema.validator, validate)
def test_get_deform(self):
model = self.app.model
@ -372,34 +437,6 @@ class TestForm(TestCase):
self.assertEqual(len(errors), 1)
self.assertEqual(errors[0], "something is wrong")
def test_get_vue_field_value(self):
schema = self.make_schema()
form = self.make_form(schema=schema)
# null field value
value = form.get_vue_field_value('foo')
self.assertEqual(value, 'null')
# non-default / explicit value
# TODO: surely need a different approach to set value
dform = form.get_deform()
dform['foo'].cstruct = 'blarg'
value = form.get_vue_field_value('foo')
self.assertEqual(value, '"blarg"')
def test_jsonify_value(self):
form = self.make_form()
# null field value
value = form.jsonify_value(colander.null)
self.assertEqual(value, 'null')
value = form.jsonify_value(None)
self.assertEqual(value, 'null')
# string value
value = form.jsonify_value('blarg')
self.assertEqual(value, '"blarg"')
def test_validate(self):
schema = self.make_schema()
form = self.make_form(schema=schema)

199
tests/forms/test_schema.py Normal file
View file

@ -0,0 +1,199 @@
# -*- coding: utf-8; -*-
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 tests.util import DataTestCase
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 raises error
node = mod.ObjectNode(colander.String())
self.assertRaises(NotImplementedError, node.dictify, person)
# 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 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)
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):
typ = mod.ObjectRef(self.request, session=self.session)
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)
# 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):
typ = mod.ObjectRef(self.request, session=self.session)
value = typ.objectify(person.uuid)
self.assertIs(value, person)
# error if not found
with patch.object(mod.ObjectRef, 'model_class', new=model.Person):
typ = mod.ObjectRef(self.request, session=self.session)
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):
typ = mod.ObjectRef(self.request, session=self.session)
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):
typ = mod.ObjectRef(self.request, session=self.session)
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):
typ = mod.ObjectRef(self.request, session=self.session)
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):
typ = mod.ObjectRef(self.request, session=self.session, 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(DataTestCase):
def setUp(self):
self.setup_db()
self.request = testing.DummyRequest(wutta_config=self.config)
def test_sort_query(self):
typ = mod.PersonRef(self.request, session=self.session)
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)

View file

@ -0,0 +1,32 @@
# -*- coding: utf-8; -*-
import colander
import deform
from pyramid import testing
from wuttaweb.forms import widgets
from wuttaweb.forms.schema import PersonRef
from tests.util import WebTestCase
class TestObjectRefWidget(WebTestCase):
def test_serialize(self):
model = self.app.model
person = model.Person(full_name="Betty Boop")
self.session.add(person)
self.session.commit()
# standard (editable)
node = colander.SchemaNode(PersonRef(self.request, session=self.session))
widget = widgets.ObjectRefWidget(self.request)
field = deform.Field(node)
html = widget.serialize(field, person.uuid)
self.assertIn('<select ', html)
# readonly
node = colander.SchemaNode(PersonRef(self.request, session=self.session))
node.model_instance = person
widget = widgets.ObjectRefWidget(self.request)
field = deform.Field(node)
html = widget.serialize(field, person.uuid, readonly=True)
self.assertEqual(html, '<span>Betty Boop</span>')

View file

@ -14,7 +14,7 @@ class TestGrid(TestCase):
def setUp(self):
self.config = WuttaConfig(defaults={
'wutta.web.menus.handler_spec': 'tests.utils:NullMenuHandler',
'wutta.web.menus.handler_spec': 'tests.util:NullMenuHandler',
})
self.app = self.config.get_app()
@ -35,7 +35,7 @@ class TestGrid(TestCase):
# empty
grid = self.make_grid()
self.assertIsNone(grid.key)
self.assertIsNone(grid.columns)
self.assertEqual(grid.columns, [])
self.assertIsNone(grid.data)
# now with columns
@ -56,8 +56,8 @@ class TestGrid(TestCase):
# empty
grid = self.make_grid()
self.assertIsNone(grid.columns)
self.assertIsNone(grid.get_columns())
self.assertEqual(grid.columns, [])
self.assertEqual(grid.get_columns(), [])
# explicit
grid = self.make_grid(columns=['foo', 'bar'])
@ -69,6 +69,12 @@ class TestGrid(TestCase):
self.assertEqual(grid.columns, ['name', 'value'])
self.assertEqual(grid.get_columns(), ['name', 'value'])
def test_remove(self):
grid = self.make_grid(columns=['one', 'two', 'three', 'four'])
self.assertEqual(grid.columns, ['one', 'two', 'three', 'four'])
grid.remove('two', 'three')
self.assertEqual(grid.columns, ['one', 'four'])
def test_linked_columns(self):
grid = self.make_grid(columns=['foo', 'bar'])
self.assertEqual(grid.linked_columns, [])
@ -118,18 +124,17 @@ class TestGrid(TestCase):
def test_get_vue_data(self):
# null by default
# empty if no columns defined
grid = self.make_grid()
data = grid.get_vue_data()
self.assertIsNone(data)
self.assertEqual(data, [])
# is usually a list
# typical data is a list
mydata = [
{'foo': 'bar'},
]
grid = self.make_grid(data=mydata)
grid = self.make_grid(columns=['foo'], data=mydata)
data = grid.get_vue_data()
self.assertIs(data, mydata)
self.assertEqual(data, [{'foo': 'bar'}])
# if grid has actions, that list may be supplemented

View file

@ -215,7 +215,7 @@ class TestBeforeRender(TestCase):
def setUp(self):
self.config = WuttaConfig(defaults={
'wutta.web.menus.handler_spec': 'tests.utils:NullMenuHandler',
'wutta.web.menus.handler_spec': 'tests.util:NullMenuHandler',
})
def make_request(self):

View file

@ -1,8 +1,10 @@
# -*- coding: utf-8; -*-
import json
from unittest import TestCase
from unittest.mock import patch, MagicMock
import colander
from fanstatic import Library, Resource
from pyramid import testing
@ -10,6 +12,37 @@ from wuttjamaican.conf import WuttaConfig
from wuttaweb import util
class TestFieldList(TestCase):
def test_insert_before(self):
fields = util.FieldList(['f1', 'f2'])
self.assertEqual(fields, ['f1', 'f2'])
# typical
fields.insert_before('f1', 'XXX')
self.assertEqual(fields, ['XXX', 'f1', 'f2'])
fields.insert_before('f2', 'YYY')
self.assertEqual(fields, ['XXX', 'f1', 'YYY', 'f2'])
# appends new field if reference field is invalid
fields.insert_before('f3', 'ZZZ')
self.assertEqual(fields, ['XXX', 'f1', 'YYY', 'f2', 'ZZZ'])
def test_insert_after(self):
fields = util.FieldList(['f1', 'f2'])
self.assertEqual(fields, ['f1', 'f2'])
# typical
fields.insert_after('f1', 'XXX')
self.assertEqual(fields, ['f1', 'XXX', 'f2'])
fields.insert_after('XXX', 'YYY')
self.assertEqual(fields, ['f1', 'XXX', 'YYY', 'f2'])
# appends new field if reference field is invalid
fields.insert_after('f3', 'ZZZ')
self.assertEqual(fields, ['f1', 'XXX', 'YYY', 'f2', 'ZZZ'])
class TestGetLibVer(TestCase):
def setUp(self):
@ -455,3 +488,40 @@ class TestRenderCsrfToken(TestCase):
self.assertIn('name="_csrf"', html)
token = util.get_csrf_token(self.request)
self.assertIn(f'value="{token}"', html)
class TestMakeJsonSafe(TestCase):
def setUp(self):
self.config = WuttaConfig()
self.app = self.config.get_app()
def test_null(self):
value = util.make_json_safe(colander.null)
self.assertIsNone(value)
value = util.make_json_safe(None)
self.assertIsNone(value)
def test_invalid(self):
model = self.app.model
person = model.Person(full_name="Betty Boop")
self.assertRaises(TypeError, json.dumps, person)
value = util.make_json_safe(person, key='person')
self.assertEqual(value, "Betty Boop")
def test_dict(self):
model = self.app.model
person = model.Person(full_name="Betty Boop")
data = {
'foo': 'bar',
'person': person,
}
self.assertRaises(TypeError, json.dumps, data)
value = util.make_json_safe(data)
self.assertEqual(value, {
'foo': 'bar',
'person': "Betty Boop",
})

View file

@ -7,9 +7,36 @@ from pyramid import testing
from wuttjamaican.conf import WuttaConfig
from wuttaweb import subscribers
from wuttaweb.menus import MenuHandler
class WebTestCase(TestCase):
class DataTestCase(TestCase):
"""
Base class for test suites requiring a full (typical) database.
"""
def setUp(self):
self.setup_db()
def setup_db(self):
self.config = WuttaConfig(defaults={
'wutta.db.default.url': 'sqlite://',
})
self.app = self.config.get_app()
# init db
model = self.app.model
model.Base.metadata.create_all(bind=self.config.appdb_engine)
self.session = self.app.make_session()
def tearDown(self):
self.teardown_db()
def teardown_db(self):
pass
class WebTestCase(DataTestCase):
"""
Base class for test suites requiring a full (typical) web app.
"""
@ -18,24 +45,15 @@ class WebTestCase(TestCase):
self.setup_web()
def setup_web(self):
self.config = WuttaConfig(defaults={
'wutta.db.default.url': 'sqlite://',
'wutta.web.menus.handler_spec': 'tests.utils:NullMenuHandler',
})
self.setup_db()
self.request = testing.DummyRequest()
self.pyramid_config = testing.setUp(request=self.request, settings={
'wutta_config': self.config,
'mako.directories': ['wuttaweb:templates'],
# TODO: have not need this yet, but will?
# 'pyramid_deform.template_search_path': 'wuttaweb:templates/deform',
})
# init db
self.app = self.config.get_app()
model = self.app.model
model.Base.metadata.create_all(bind=self.config.appdb_engine)
self.session = self.app.make_session()
# init web
self.pyramid_config.include('pyramid_mako')
self.pyramid_config.include('wuttaweb.static')
@ -55,3 +73,12 @@ class WebTestCase(TestCase):
def teardown_web(self):
testing.tearDown()
self.teardown_db()
class NullMenuHandler(MenuHandler):
"""
Dummy menu handler for testing.
"""
def make_menus(self, request, **kwargs):
return []

View file

@ -1,11 +0,0 @@
# -*- coding: utf-8; -*-
from wuttaweb.menus import MenuHandler
class NullMenuHandler(MenuHandler):
"""
Dummy menu handler for testing.
"""
def make_menus(self, request, **kwargs):
return []

View file

@ -11,8 +11,7 @@ from pyramid.httpexceptions import HTTPFound, HTTPNotFound
from wuttjamaican.conf import WuttaConfig
from wuttaweb.views import master
from wuttaweb.subscribers import new_request_set_user
from tests.views.utils import WebTestCase
from tests.util import WebTestCase
class TestMasterView(WebTestCase):
@ -387,6 +386,19 @@ class TestMasterView(WebTestCase):
with patch.object(view, 'get_query', new=get_query):
self.assertRaises(ValueError, view.get_grid_data, session=self.session)
def test_configure_grid(self):
model = self.app.model
# uuid field is pruned
with patch.multiple(master.MasterView, create=True,
model_class=model.Setting):
view = master.MasterView(self.request)
grid = view.make_grid(model_class=model.Setting,
columns=['uuid', 'name', 'value'])
self.assertIn('uuid', grid.columns)
view.configure_grid(grid)
self.assertNotIn('uuid', grid.columns)
def test_get_instance(self):
model = self.app.model
self.app.save_setting(self.session, 'foo', 'bar')
@ -454,7 +466,7 @@ class TestMasterView(WebTestCase):
model_name='Widget',
model_key='uuid'):
view = master.MasterView(self.request)
form = view.make_model_form()
form = view.make_model_form(fields=['name', 'description'])
form.validated = {'name': 'first'}
obj = view.objectify(form)
self.assertIs(obj, form.validated)
@ -666,7 +678,6 @@ class TestMasterView(WebTestCase):
self.assertIn('frazzle', response.text)
def delete_instance(setting):
print(setting) # TODO
self.app.delete_setting(self.session, setting['name'])
# post request to save settings

View file

@ -7,7 +7,7 @@ from sqlalchemy import orm
from pyramid.httpexceptions import HTTPNotFound
from wuttaweb.views import people
from tests.views.utils import WebTestCase
from tests.util import WebTestCase
class TestPersonView(WebTestCase):

View file

@ -2,11 +2,10 @@
from unittest.mock import patch
from tests.views.utils import WebTestCase
from pyramid.httpexceptions import HTTPNotFound
from wuttaweb.views import settings
from tests.util import WebTestCase
class TestAppInfoView(WebTestCase):

58
tests/views/test_users.py Normal file
View file

@ -0,0 +1,58 @@
# -*- coding: utf-8; -*-
from unittest.mock import patch
from sqlalchemy import orm
import colander
from pyramid.httpexceptions import HTTPNotFound
from wuttaweb.views import users
from tests.util import WebTestCase
class TestPersonView(WebTestCase):
def make_view(self):
return users.UserView(self.request)
def test_get_query(self):
view = self.make_view()
query = view.get_query(session=self.session)
self.assertIsInstance(query, orm.Query)
def test_configure_grid(self):
model = self.app.model
view = self.make_view()
grid = view.make_grid(model_class=model.User)
self.assertFalse(grid.is_linked('person'))
view.configure_grid(grid)
self.assertTrue(grid.is_linked('person'))
def test_configure_form(self):
model = self.app.model
view = self.make_view()
form = view.make_form(model_class=model.Person)
self.assertIsNone(form.is_required('person'))
view.configure_form(form)
self.assertFalse(form.is_required('person'))
def test_unique_username(self):
model = self.app.model
view = self.make_view()
user = model.User(username='foo')
self.session.add(user)
self.session.commit()
with patch.object(users, 'Session', return_value=self.session):
# invalid if same username in data
node = colander.SchemaNode(colander.String(), name='username')
self.assertRaises(colander.Invalid, view.unique_username, node, 'foo')
# but not if username belongs to current user
view.editing = True
self.request.matchdict = {'uuid': user.uuid}
node = colander.SchemaNode(colander.String(), name='username')
self.assertIsNone(view.unique_username(node, 'foo'))