Skip to content
This repository was archived by the owner on Apr 16, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions conman/nav_tree/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

class NodeTest(TestCase):
def test_fields(self):
"""Check the Node model has the expected fields."""
expected = (
'id',
'noderedirect',
Expand Down Expand Up @@ -59,6 +60,7 @@ def test_create_leaf_without_slug(self):

class NodeUniqueness(TestCase):
def test_unique_slug_per_parent(self):
"""Two Nodes cannot share the same slug and parent Node."""
slug = 'slug'
root_node = RootNodeFactory.create()
NodeFactory.create(slug=slug, parent=root_node)
Expand All @@ -67,6 +69,7 @@ def test_unique_slug_per_parent(self):
NodeFactory.create(slug=slug, parent=root_node)

def test_unique_root_url(self):
"""Only one Node can exist with an empty slug."""
Node.objects.create(slug='')

with self.assertRaises(IntegrityError):
Expand Down Expand Up @@ -201,13 +204,15 @@ class NodeManagerBestMatchForPathTest(TestCase):
LIMIT 1
"""
def test_get_root(self):
"""Check a Root Node matches a simple '/' path."""
root = RootNodeFactory.create()
with self.assertNumQueries(1):
node = Node.objects.best_match_for_path('/')

self.assertEqual(node, root)

def test_get_leaf(self):
"""Check a Node with a slug matches a path of that slug."""
leaf = ChildNodeFactory.create(slug='leaf')

with self.assertNumQueries(1):
Expand All @@ -216,6 +221,7 @@ def test_get_leaf(self):
self.assertEqual(node, leaf)

def test_get_leaf_on_branch(self):
"""Check a Node matches a path containing its slug and parent's slug."""
branch = ChildNodeFactory.create(slug='branch')
leaf = NodeFactory.create(slug='leaf', parent=branch)

Expand All @@ -225,6 +231,7 @@ def test_get_leaf_on_branch(self):
self.assertEqual(node, leaf)

def test_get_branch_with_leaf(self):
"""Check a Branch Node matches a path of its slug even if a Leaf exists."""
branch = ChildNodeFactory.create(slug='branch')
NodeFactory.create(slug='leaf', parent=branch)

Expand Down Expand Up @@ -255,11 +262,13 @@ class NodeManagerBestMatchForBrokenPathTest(TestCase):
LIMIT 1
"""
def test_throw_error_without_match(self):
"""Check Node.DoesNotExist is raised if no Root Node exists."""
with self.assertNumQueries(1):
with self.assertRaises(Node.DoesNotExist):
Node.objects.best_match_for_path('/')

def test_fall_back_to_root(self):
"""Check the Root Node matches when no better Node is available."""
root = RootNodeFactory.create()

with self.assertNumQueries(1):
Expand All @@ -268,6 +277,7 @@ def test_fall_back_to_root(self):
self.assertEqual(node, root)

def test_fall_back_to_branch(self):
"""Check a Branch Node matches when no Leaf Node matches."""
branch = ChildNodeFactory.create(slug='branch')

with self.assertNumQueries(1):
Expand All @@ -278,6 +288,7 @@ def test_fall_back_to_branch(self):

class NodeGetHandlerClassTest(TestCase):
def test_get_handler_class(self):
"""A Node's handler is looked up from the handler's path."""
handler_class = handlers.BaseHandler
node = NodeFactory.build()
node.handler = handler_class.path()
Expand Down Expand Up @@ -311,6 +322,9 @@ def test_get_handler_again(self):

class NodeHandleTest(TestCase):
def test_handle(self):
"""
Node delegates a request to its handler after stripping its url from the path.
"""
node = NodeFactory.build(url='/branch/')
node.get_handler_class = mock.MagicMock()
request = mock.Mock()
Expand Down Expand Up @@ -338,17 +352,20 @@ def test_child_str(self):

class NodeCheckTest(TestCase):
def test_node_class(self):
"""The Node class does not require a handler attribute."""
errors = Node.check()
self.assertEqual(errors, [])

def test_subclass_with_handler(self):
"""A subclass of Node must have a handler attribute."""
class NodeWithHandler(Node):
handler = 'has.been.set'

errors = NodeWithHandler.check()
self.assertEqual(errors, [])

def test_subclass_without_handler(self):
"""A subclass of Node without a handler fails Node.check."""
class NodeWithoutHandler(Node):
pass # handler not set

Expand Down
5 changes: 5 additions & 0 deletions conman/nav_tree/tests/test_urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,22 @@ def test_double_slash_url(self):
self.assert_url_uses_router('//')

def test_root_url(self):
"""The root url is resolved using views.node_router."""
self.assert_url_uses_router('/')

def test_child_url(self):
"""A child url is resolved using views.node_router."""
self.assert_url_uses_router('/slug/')

def test_nested_child_url(self):
"""A nested child url is resolved using views.node_router."""
self.assert_url_uses_router('/foo/bar/')

def test_numerical_url(self):
"""A numeric url is resolved using views.node_router."""
self.assert_url_uses_router('/meanings/42/')

def test_without_trailing_slash(self):
"""A url without a trailing slash is not resolved by views.node_router."""
with self.assertRaises(Resolver404):
self.assert_url_uses_router('/fail')
8 changes: 8 additions & 0 deletions conman/nav_tree/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

class TestSplitPath(TestCase):
def test_split_path(self):
"""split_path returns a list of all sub-paths of a url path."""
paths = utils.split_path('/a/path/with/many/parts/')
expected = [
'/',
Expand All @@ -17,16 +18,19 @@ def test_split_path(self):
self.assertCountEqual(paths, expected) # Order does not matter

def test_split_empty_path(self):
"""An empty path has sub-path '/'."""
paths = utils.split_path('')
expected = ['/']
self.assertCountEqual(paths, expected)

def test_split_root_path(self):
"""The root path '/' has sub-path '/'."""
paths = utils.split_path('/')
expected = ['/']
self.assertCountEqual(paths, expected)

def test_split_path_with_dots(self):
"""split_path does no special processing on a path containing dots."""
paths = utils.split_path('/path/../')
expected = [
'/',
Expand All @@ -38,18 +42,22 @@ def test_split_path_with_dots(self):

class TestImportFromDottedPath(TestCase):
def test_empty(self):
"""An empty path cannot be imported."""
with self.assertRaises(ValueError):
utils.import_from_dotted_path('')

def test_too_short(self):
"""A path with only one component cannot be imported."""
with self.assertRaises(ValueError):
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have expected ImportError. Is there a particular reason ValueError is more appropriate?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a peculiarity of implementation, I suppose. Perhaps we should not have this limitation (though I cannot see when we would need a top-level package here.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. Thinking about it a bit more, maybe ValueError is appropriate - the function is designed to work only with dotted paths. I think it should have a custom error message though.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ValueError is appropriate - the function is designed to work only with dotted paths

Indeed.

I think it should have a custom error message though.

👍

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#28

utils.import_from_dotted_path('antigravity')

def test_import_module(self):
"""A module can be imported by dotted path."""
result = utils.import_from_dotted_path('conman.nav_tree.utils')
self.assertEqual(result, utils)

def test_import_class(self):
"""A class can be imported by dotted path."""
this_test = 'conman.nav_tree.tests.test_utils.TestImportFromDottedPath'
result = utils.import_from_dotted_path(this_test)
self.assertEqual(result, self.__class__)
4 changes: 4 additions & 0 deletions conman/nav_tree/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
class RouterTest(TestCase):
"""Test that `node_router` correctly deals with the urls handed to it."""
def test_root(self):
"""node_router delegates to the Root Node's handler for an empty url."""
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests seem complicated - they were difficult to condense into docstring summaries. Perhaps they can be simplified?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They do. Not very DRY either. Perhaps if they were made dry, then it would look easier to have one assert per test here.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#29

url = ''
factories.NodeFactory.create()
request = mock.MagicMock()
Expand All @@ -21,6 +22,7 @@ def test_root(self):
self.assertEqual(response, handle(request, '/' + url))

def test_complex_url(self):
"""node_router finds the url's best match and delegates to its handler."""
url = 'slug/42/foo/bar/'
factories.NodeFactory.create()
request = mock.MagicMock()
Expand All @@ -35,6 +37,7 @@ def test_complex_url(self):
class RouterIntegrationTest(TestCase):
"""Test that `node_router` is correctly handed urls."""
def test_root_url(self):
"""The Root Node handler is passed the correct request and root url."""
url = '/'
factories.NodeFactory.create()
handle_path = 'conman.nav_tree.models.Node.handle'
Expand All @@ -45,6 +48,7 @@ def test_root_url(self):
handle.assert_called_with(response.wsgi_request, url)

def test_complex_url(self):
"""The correct request and url is passed through to the node's handler."""
url = '/slug/42/foo/bar/'
factories.NodeFactory.create()
handle_path = 'conman.nav_tree.models.Node.handle'
Expand Down
2 changes: 2 additions & 0 deletions conman/pages/tests/test_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@

class TestPageHandler(TestCase):
def test_heritage(self):
"""PageHandler subclasses SimpleHandler."""
self.assertTrue(issubclass(handlers.PageHandler, SimpleHandler))

def test_view(self):
"""PageHandler uses the PageDetail view."""
view = handlers.PageHandler.view
expected = views.PageDetail.as_view()

Expand Down
1 change: 1 addition & 0 deletions conman/pages/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

class PageTest(TestCase):
def test_fields(self):
"""Check Page has Node's fields and a few of its own."""
expected = (
'id',
'node_ptr',
Expand Down
2 changes: 2 additions & 0 deletions conman/pages/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

class TestPageDetail(RequestTestCase):
def test_get_object(self):
"""PageDetail displays the page instance passed in the node kwarg."""
request = self.create_request()
page = factories.PageFactory.create()

Expand All @@ -20,6 +21,7 @@ class TestPageDetailIntegration(TestCase):
view = views.PageDetail

def test_get(self):
"""A page's content is rendered at its url."""
page = factories.PageFactory.create(content='This is a test')
response = self.client.get(page.url)
self.assertIn(page.content, response.rendered_content)
2 changes: 2 additions & 0 deletions conman/redirects/tests/test_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@

class TestNodeRedirectHandler(TestCase):
def test_heritage(self):
"""NodeRedirectHandler sublcasses SimpleHandler."""
self.assertTrue(issubclass(NodeRedirectHandler, SimpleHandler))

def test_view(self):
"""NodeRedirectHandler uses the NodeRedirectView."""
view = NodeRedirectHandler.view
expected = NodeRedirectView.as_view()

Expand Down
2 changes: 2 additions & 0 deletions conman/redirects/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

class NodeRedirectTest(TestCase):
def test_fields(self):
"""NodeRedirect has Node's fields and some specific to redirects."""
expected = (
'id',
'node_ptr',
Expand All @@ -22,6 +23,7 @@ def test_fields(self):
class NodeRedirectUnicodeMethodTest(TestCase):
"""We should get something nice when RedirectNode is cast to string"""
def test_str(self):
"""The str of a NodeRedirect identifies it by class and url."""
leaf = ChildNodeRedirectFactory.create(slug='leaf')

self.assertEqual(str(leaf), 'NodeRedirect @ /leaf/')
40 changes: 14 additions & 26 deletions conman/redirects/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,48 +12,36 @@ def setUp(self):
self.request = self.create_request()
self.view = views.NodeRedirectView.as_view()

def test_target(self):
"""NodeRedirectView redirects to the target's url."""
node = ChildNodeRedirectFactory.create(target=self.target)
response = self.view(self.request, node=node)

self.assertEqual(response['Location'], self.target.url)

def test_permanent(self):
node = ChildNodeRedirectFactory.create(
target=self.target,
permanent=True,
)
"""A permanent redirect has status_code 301."""
node = ChildNodeRedirectFactory.create(permanent=True)
response = self.view(self.request, node=node)

self.assertEqual(response.status_code, 301)
self.assertEqual(response['Location'], self.target.url)

def test_temporary(self):
node = ChildNodeRedirectFactory.create(
target=self.target,
permanent=False,
)
"""A temporary redirect has status_code 302."""
node = ChildNodeRedirectFactory.create(permanent=False)
response = self.view(self.request, node=node)

self.assertEqual(response.status_code, 302)
self.assertEqual(response['Location'], self.target.url)


class TestNodeRedirectViewIntegration(TestCase):
def setUp(self):
self.target = ChildNodeFactory.create()
self.expected = 'http://testserver' + self.target.url

def test_permanent(self):
node = ChildNodeRedirectFactory.create(
target=self.target,
permanent=True,
)
response = self.client.get(node.url)

self.assertEqual(response.status_code, 301)
self.assertEqual(response['Location'], self.expected)

def test_temporary(self):
node = ChildNodeRedirectFactory.create(
target=self.target,
permanent=False,
)
def test_access_redirect(self):
"""Accessing a NodeRedirect's url redirects to its target's url."""
node = ChildNodeRedirectFactory.create(target=self.target)
response = self.client.get(node.url)

self.assertEqual(response.status_code, 302)
self.assertEqual(response['Location'], self.expected)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this refactor is an improvement, but if you disagree or think it shouldn't be in this pull-request I can easily remove it.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this refactor is an improvement

So do I, thanks :)