370 lines
15 KiB
Python
370 lines
15 KiB
Python
# -*- coding: utf-8; -*-
|
|
|
|
import os
|
|
import sys
|
|
from unittest.mock import patch
|
|
|
|
from wuttaweb.testing import WebTestCase
|
|
from wuttaweb.views import views as mod
|
|
from wuttaweb.views.users import UserView
|
|
|
|
|
|
class TestMasterViewView(WebTestCase):
|
|
|
|
def make_view(self):
|
|
return mod.MasterViewView(self.request)
|
|
|
|
def test_includeme(self):
|
|
self.pyramid_config.include("wuttaweb.views.views")
|
|
|
|
def test_get_grid_data(self):
|
|
view = self.make_view()
|
|
|
|
# empty by default, since nothing registered in test setup
|
|
data = view.get_grid_data()
|
|
self.assertIsInstance(data, list)
|
|
self.assertEqual(len(data), 0)
|
|
|
|
# so let's register one and try again
|
|
self.pyramid_config.add_wutta_master_view(UserView)
|
|
data = view.get_grid_data()
|
|
self.assertGreater(len(data), 0)
|
|
master = data[0]
|
|
self.assertIsInstance(master, dict)
|
|
self.assertEqual(master["model_title_plural"], "Users")
|
|
self.assertEqual(master["model_name"], "User")
|
|
self.assertEqual(master["url_prefix"], "/users")
|
|
|
|
def test_configure_grid(self):
|
|
self.pyramid_config.add_route("users", "/users/")
|
|
self.pyramid_config.add_wutta_master_view(UserView)
|
|
view = self.make_view()
|
|
|
|
# sanity / coverage check
|
|
grid = view.make_grid(
|
|
columns=["model_title_plural", "url_prefix"], data=view.get_grid_data()
|
|
)
|
|
view.configure_grid(grid)
|
|
|
|
# nb. must invoke this to exercise the url logic
|
|
grid.get_vue_context()
|
|
|
|
def test_get_template_context(self):
|
|
view = self.make_view()
|
|
with patch.object(view, "Session", return_value=self.session):
|
|
|
|
# normal view gets no extra context
|
|
context = view.get_template_context({})
|
|
self.assertIsInstance(context, dict)
|
|
self.assertNotIn("app_models", context)
|
|
self.assertNotIn("view_module_dirs", context)
|
|
self.assertNotIn("view_module_dir", context)
|
|
self.assertNotIn("menu_path", context)
|
|
self.assertNotIn("roles", context)
|
|
self.assertNotIn("listing_roles", context)
|
|
self.assertNotIn("creating_roles", context)
|
|
self.assertNotIn("viewing_roles", context)
|
|
self.assertNotIn("editing_roles", context)
|
|
self.assertNotIn("deleting_roles", context)
|
|
|
|
# but 'create' view gets extra context
|
|
with patch.object(view, "creating", new=True):
|
|
context = view.get_template_context({})
|
|
self.assertIsInstance(context, dict)
|
|
self.assertIn("app_models", context)
|
|
self.assertIn("view_module_dirs", context)
|
|
self.assertIn("view_module_dir", context)
|
|
self.assertIn("menu_path", context)
|
|
self.assertIn("roles", context)
|
|
self.assertIn("listing_roles", context)
|
|
self.assertIn("creating_roles", context)
|
|
self.assertIn("viewing_roles", context)
|
|
self.assertIn("editing_roles", context)
|
|
self.assertIn("deleting_roles", context)
|
|
|
|
# try that again but this time make sure there is only
|
|
# one possibility for view module path, which is auto
|
|
# selected by default
|
|
with patch.object(
|
|
view, "get_view_module_options", return_value=["wuttaweb.views"]
|
|
):
|
|
context = view.get_template_context({})
|
|
self.assertEqual(context["view_module_dir"], "wuttaweb.views")
|
|
|
|
def test_get_view_module_options(self):
|
|
view = self.make_view()
|
|
|
|
# register one master view, which should be reflected in options
|
|
self.pyramid_config.add_wutta_master_view(UserView)
|
|
options = view.get_view_module_options()
|
|
self.assertEqual(len(options), 1)
|
|
self.assertEqual(options[0], "wuttaweb.views")
|
|
|
|
def test_suggest_details(self):
|
|
view = self.make_view()
|
|
|
|
# first test uses model_class
|
|
sample = {
|
|
"action": "suggest_details",
|
|
"model_option": "model_class",
|
|
"model_name": "Person",
|
|
}
|
|
with patch.object(self.request, "json_body", new=sample, create=True):
|
|
result = view.wizard_action()
|
|
self.assertEqual(result["class_file_name"], "people.py")
|
|
self.assertEqual(result["class_name"], "PersonView")
|
|
self.assertEqual(result["model_name"], "Person")
|
|
self.assertEqual(result["model_title"], "Person")
|
|
self.assertEqual(result["model_title_plural"], "People")
|
|
self.assertEqual(result["route_prefix"], "people")
|
|
self.assertEqual(result["permission_prefix"], "people")
|
|
self.assertEqual(result["url_prefix"], "/people")
|
|
self.assertEqual(result["template_prefix"], "/people")
|
|
self.assertIn("grid_columns", result)
|
|
self.assertIsInstance(result["grid_columns"], str)
|
|
self.assertIn("form_fields", result)
|
|
self.assertIsInstance(result["form_fields"], str)
|
|
|
|
# second test uses model_name
|
|
sample = {
|
|
"action": "suggest_details",
|
|
"model_option": "model_name",
|
|
"model_name": "acme_brick",
|
|
}
|
|
with patch.object(self.request, "json_body", new=sample, create=True):
|
|
result = view.wizard_action()
|
|
self.assertEqual(result["class_file_name"], "acme_bricks.py")
|
|
self.assertEqual(result["class_name"], "AcmeBrickView")
|
|
self.assertEqual(result["model_name"], "acme_brick")
|
|
self.assertEqual(result["model_title"], "Acme Brick")
|
|
self.assertEqual(result["model_title_plural"], "Acme Bricks")
|
|
self.assertEqual(result["route_prefix"], "acme_bricks")
|
|
self.assertEqual(result["permission_prefix"], "acme_bricks")
|
|
self.assertEqual(result["url_prefix"], "/acme-bricks")
|
|
self.assertEqual(result["template_prefix"], "/acme-bricks")
|
|
self.assertEqual(result["grid_columns"], "")
|
|
self.assertEqual(result["form_fields"], "")
|
|
|
|
def test_write_view_file(self):
|
|
view = self.make_view()
|
|
view_file_path = self.write_file("silly_things.py", "")
|
|
wutta_file_path = os.path.join(
|
|
os.path.dirname(sys.modules["wuttaweb.views"].__file__),
|
|
"silly_things.py",
|
|
)
|
|
self.assertEqual(os.path.getsize(view_file_path), 0)
|
|
|
|
# first test w/ Upgrade model_class and target file path
|
|
sample = {
|
|
"action": "write_view_file",
|
|
"view_location": None,
|
|
"view_file_path": view_file_path,
|
|
"overwrite": False,
|
|
"class_name": "UpgradeView",
|
|
"model_option": "model_class",
|
|
"model_name": "Upgrade",
|
|
"model_title": "Upgrade",
|
|
"model_title_plural": "Upgrades",
|
|
"route_prefix": "upgrades",
|
|
"permission_prefix": "upgrades",
|
|
"url_prefix": "/upgrades",
|
|
"template_prefix": "/upgrades",
|
|
"listable": True,
|
|
"creatable": True,
|
|
"viewable": True,
|
|
"editable": True,
|
|
"deletable": True,
|
|
"grid_columns": ["description", "created_by"],
|
|
"form_fields": ["description", "created_by"],
|
|
}
|
|
with patch.object(self.request, "json_body", new=sample, create=True):
|
|
|
|
# does not overwrite by default
|
|
result = view.wizard_action()
|
|
self.assertIn("error", result)
|
|
self.assertEqual(result["error"], "File already exists")
|
|
self.assertEqual(os.path.getsize(view_file_path), 0)
|
|
|
|
# but can overwrite if requested
|
|
with patch.dict(sample, {"overwrite": True}):
|
|
result = view.wizard_action()
|
|
self.assertNotIn("error", result)
|
|
self.assertGreater(os.path.getsize(view_file_path), 1000)
|
|
self.assertEqual(result["view_file_path"], view_file_path)
|
|
self.assertEqual(
|
|
result["view_module_path"], "poser.web.views.silly_things"
|
|
)
|
|
|
|
# reset file
|
|
with open(view_file_path, "wb") as f:
|
|
pass
|
|
self.assertEqual(os.path.getsize(view_file_path), 0)
|
|
|
|
# second test w/ silly_thing model_name and target module path
|
|
sample = {
|
|
"action": "write_view_file",
|
|
"view_location": "wuttaweb.views",
|
|
"view_file_name": "silly_things.py",
|
|
"overwrite": False,
|
|
"class_name": "SillyThingView",
|
|
"model_option": "model_name",
|
|
"model_name": "silly_thing",
|
|
"model_title": "Silly Thing",
|
|
"model_title_plural": "Silly Things",
|
|
"route_prefix": "silly_things",
|
|
"permission_prefix": "silly_things",
|
|
"url_prefix": "/silly-things",
|
|
"template_prefix": "/silly-things",
|
|
"listable": True,
|
|
"creatable": True,
|
|
"viewable": True,
|
|
"editable": True,
|
|
"deletable": True,
|
|
"grid_columns": ["id", "name", "description"],
|
|
"form_fields": ["id", "name", "description"],
|
|
}
|
|
with patch.object(self.request, "json_body", new=sample, create=True):
|
|
|
|
# file does not yet exist, so will be written
|
|
result = view.wizard_action()
|
|
self.assertNotIn("error", result)
|
|
self.assertEqual(result["view_file_path"], wutta_file_path)
|
|
self.assertGreater(os.path.getsize(wutta_file_path), 1000)
|
|
self.assertEqual(os.path.getsize(view_file_path), 0)
|
|
self.assertEqual(result["view_module_path"], "wuttaweb.views.silly_things")
|
|
|
|
# once file exists, will not overwrite by default
|
|
result = view.wizard_action()
|
|
self.assertIn("error", result)
|
|
self.assertEqual(result["error"], "File already exists")
|
|
self.assertEqual(os.path.getsize(view_file_path), 0)
|
|
|
|
# reset file
|
|
with open(wutta_file_path, "wb") as f:
|
|
pass
|
|
self.assertEqual(os.path.getsize(wutta_file_path), 0)
|
|
|
|
# can still overrwite explicitly
|
|
with patch.dict(sample, {"overwrite": True}):
|
|
result = view.wizard_action()
|
|
self.assertNotIn("error", result)
|
|
self.assertEqual(result["view_file_path"], wutta_file_path)
|
|
self.assertGreater(os.path.getsize(wutta_file_path), 1000)
|
|
self.assertEqual(os.path.getsize(view_file_path), 0)
|
|
self.assertEqual(
|
|
result["view_module_path"], "wuttaweb.views.silly_things"
|
|
)
|
|
|
|
# nb. must be sure to deleta that file!
|
|
os.remove(wutta_file_path)
|
|
|
|
def test_check_route(self):
|
|
self.pyramid_config.add_route("people", "/people/")
|
|
view = self.make_view()
|
|
sample = {
|
|
"action": "check_route",
|
|
"route": "people",
|
|
}
|
|
|
|
with patch.object(self.request, "json_body", new=sample, create=True):
|
|
|
|
# should get url and path
|
|
result = view.wizard_action()
|
|
self.assertEqual(result["url"], "http://example.com/people/")
|
|
self.assertEqual(result["path"], "/people/")
|
|
self.assertNotIn("problem", result)
|
|
|
|
# unless we check a bad route
|
|
with patch.dict(sample, {"route": "invalid_nothing_burger"}):
|
|
result = view.wizard_action()
|
|
self.assertIn("problem", result)
|
|
self.assertNotIn("url", result)
|
|
self.assertNotIn("path", result)
|
|
|
|
def test_apply_permissions(self):
|
|
model = self.app.model
|
|
auth = self.app.get_auth_handler()
|
|
admin = auth.get_role_administrator(self.session)
|
|
known = auth.get_role_authenticated(self.session)
|
|
|
|
manager = model.Role(name="Manager")
|
|
self.session.add(manager)
|
|
|
|
worker = model.Role(name="worker")
|
|
self.session.add(worker)
|
|
|
|
fred = model.User(username="fred")
|
|
fred.roles.append(manager)
|
|
fred.roles.append(worker)
|
|
self.session.add(fred)
|
|
|
|
self.session.commit()
|
|
|
|
self.assertFalse(auth.has_permission(self.session, fred, "people.list"))
|
|
self.assertFalse(auth.has_permission(self.session, fred, "people.create"))
|
|
self.assertFalse(auth.has_permission(self.session, fred, "people.view"))
|
|
self.assertFalse(auth.has_permission(self.session, fred, "people.edit"))
|
|
self.assertFalse(auth.has_permission(self.session, fred, "people.delete"))
|
|
|
|
view = self.make_view()
|
|
with patch.object(view, "Session", return_value=self.session):
|
|
|
|
sample = {
|
|
"action": "apply_permissions",
|
|
"permission_prefix": "people",
|
|
"listing_roles": {known.uuid.hex: True},
|
|
"creating_roles": {worker.uuid.hex: True},
|
|
"viewing_roles": {known.uuid.hex: True},
|
|
"editing_roles": {manager.uuid.hex: True},
|
|
"deleting_roles": {manager.uuid.hex: True},
|
|
}
|
|
with patch.object(self.request, "json_body", new=sample, create=True):
|
|
|
|
# nb. empty result is normal
|
|
result = view.wizard_action()
|
|
self.assertEqual(result, {})
|
|
|
|
self.assertTrue(auth.has_permission(self.session, fred, "people.list"))
|
|
self.assertTrue(
|
|
auth.has_permission(self.session, fred, "people.create")
|
|
)
|
|
self.assertTrue(auth.has_permission(self.session, fred, "people.view"))
|
|
self.assertTrue(auth.has_permission(self.session, fred, "people.edit"))
|
|
self.assertTrue(
|
|
auth.has_permission(self.session, fred, "people.delete")
|
|
)
|
|
|
|
def test_wizard_action(self):
|
|
view = self.make_view()
|
|
|
|
# missing action
|
|
with patch.object(self.request, "json_body", create=True, new={}):
|
|
result = view.wizard_action()
|
|
self.assertIn("error", result)
|
|
self.assertEqual(result["error"], "Must specify the action to perform.")
|
|
|
|
# unknown action
|
|
with patch.object(
|
|
self.request, "json_body", create=True, new={"action": "nothing"}
|
|
):
|
|
result = view.wizard_action()
|
|
self.assertIn("error", result)
|
|
self.assertEqual(result["error"], "Unknown action requested: nothing")
|
|
|
|
# error invoking action
|
|
with patch.object(
|
|
self.request, "json_body", create=True, new={"action": "check_route"}
|
|
):
|
|
with patch.object(view, "check_route", side_effect=RuntimeError("whoa")):
|
|
result = view.wizard_action()
|
|
self.assertIn("error", result)
|
|
self.assertEqual(result["error"], "Unexpected error occurred: whoa")
|
|
|
|
def test_configure(self):
|
|
self.pyramid_config.add_route("home", "/")
|
|
self.pyramid_config.add_route("login", "/auth/login")
|
|
self.pyramid_config.add_route("master_views", "/views/master")
|
|
view = self.make_view()
|
|
|
|
# sanity/coverage
|
|
view.configure()
|