feat: basic support for displaying version history
this is not terribly feature-rich yet, just the basics
This commit is contained in:
parent
6d2eccd0ea
commit
f33448f64a
18 changed files with 1323 additions and 66 deletions
|
|
@ -16,7 +16,8 @@ from wuttaweb.views import master as mod
|
|||
from wuttaweb.views import View
|
||||
from wuttaweb.progress import SessionProgress
|
||||
from wuttaweb.subscribers import new_request_set_user
|
||||
from wuttaweb.testing import WebTestCase
|
||||
from wuttaweb.testing import WebTestCase, VersionWebTestCase
|
||||
from wuttaweb.grids import Grid
|
||||
|
||||
|
||||
class TestMasterView(WebTestCase):
|
||||
|
|
@ -1841,3 +1842,247 @@ class TestMasterView(WebTestCase):
|
|||
# class may specify
|
||||
with patch.object(view, "rows_title", create=True, new="Mock Rows"):
|
||||
self.assertEqual(view.get_rows_title(), "Mock Rows")
|
||||
|
||||
|
||||
class TestVersionedMasterView(VersionWebTestCase):
|
||||
|
||||
def make_view(self):
|
||||
return mod.MasterView(self.request)
|
||||
|
||||
def test_defaults(self):
|
||||
model = self.app.model
|
||||
|
||||
with patch.multiple(mod.MasterView, model_class=model.User, has_versions=True):
|
||||
mod.MasterView.defaults(self.pyramid_config)
|
||||
|
||||
def test_get_model_version_class(self):
|
||||
model = self.app.model
|
||||
with patch.object(mod.MasterView, "model_class", new=model.User):
|
||||
view = self.make_view()
|
||||
vercls = view.get_model_version_class()
|
||||
self.assertEqual(vercls.__name__, "UserVersion")
|
||||
|
||||
def test_should_expose_versions(self):
|
||||
model = self.app.model
|
||||
with patch.multiple(mod.MasterView, model_class=model.User, has_versions=True):
|
||||
|
||||
# fully enabled for root user
|
||||
with patch.object(self.request, "is_root", new=True):
|
||||
view = self.make_view()
|
||||
self.assertTrue(view.should_expose_versions())
|
||||
|
||||
# but not if user has no access
|
||||
view = self.make_view()
|
||||
self.assertFalse(view.should_expose_versions())
|
||||
|
||||
# again, works for root user
|
||||
with patch.object(self.request, "is_root", new=True):
|
||||
view = self.make_view()
|
||||
self.assertTrue(view.should_expose_versions())
|
||||
|
||||
# but not if config disables versioning
|
||||
with patch.object(view.app, "continuum_is_enabled", return_value=False):
|
||||
self.assertFalse(view.should_expose_versions())
|
||||
|
||||
def test_get_version_grid_key(self):
|
||||
model = self.app.model
|
||||
with patch.object(mod.MasterView, "model_class", new=model.User):
|
||||
|
||||
# default
|
||||
view = self.make_view()
|
||||
self.assertEqual(view.get_version_grid_key(), "users.history")
|
||||
|
||||
# custom
|
||||
with patch.object(
|
||||
mod.MasterView,
|
||||
"version_grid_key",
|
||||
new="users_custom_history",
|
||||
create=True,
|
||||
):
|
||||
view = self.make_view()
|
||||
self.assertEqual(view.get_version_grid_key(), "users_custom_history")
|
||||
|
||||
def test_get_version_grid_columns(self):
|
||||
model = self.app.model
|
||||
with patch.object(mod.MasterView, "model_class", new=model.User):
|
||||
|
||||
# default
|
||||
view = self.make_view()
|
||||
self.assertEqual(
|
||||
view.get_version_grid_columns(),
|
||||
["id", "issued_at", "user", "remote_addr"],
|
||||
)
|
||||
|
||||
# custom
|
||||
with patch.object(
|
||||
mod.MasterView,
|
||||
"version_grid_columns",
|
||||
new=["issued_at", "user"],
|
||||
create=True,
|
||||
):
|
||||
view = self.make_view()
|
||||
self.assertEqual(view.get_version_grid_columns(), ["issued_at", "user"])
|
||||
|
||||
def test_get_version_grid_data(self):
|
||||
model = self.app.model
|
||||
|
||||
user = model.User(username="fred")
|
||||
self.session.add(user)
|
||||
self.session.commit()
|
||||
user.username = "freddie"
|
||||
self.session.commit()
|
||||
|
||||
with patch.object(mod.MasterView, "model_class", new=model.User):
|
||||
view = self.make_view()
|
||||
query = view.get_version_grid_data(user)
|
||||
self.assertIsInstance(query, orm.Query)
|
||||
transactions = query.all()
|
||||
self.assertEqual(len(transactions), 2)
|
||||
|
||||
def test_configure_version_grid(self):
|
||||
import sqlalchemy_continuum as continuum
|
||||
|
||||
model = self.app.model
|
||||
txncls = continuum.transaction_class(model.User)
|
||||
|
||||
with patch.object(mod.MasterView, "model_class", new=model.User):
|
||||
view = self.make_view()
|
||||
|
||||
# this is mostly just for coverage, but we at least can
|
||||
# confirm something does change
|
||||
grid = view.make_grid(model_class=txncls)
|
||||
self.assertNotIn("issued_at", grid.linked_columns)
|
||||
view.configure_version_grid(grid)
|
||||
self.assertIn("issued_at", grid.linked_columns)
|
||||
|
||||
def test_make_version_grid(self):
|
||||
model = self.app.model
|
||||
|
||||
user = model.User(username="fred")
|
||||
self.session.add(user)
|
||||
self.session.commit()
|
||||
user.username = "freddie"
|
||||
self.session.commit()
|
||||
|
||||
with patch.object(mod.MasterView, "model_class", new=model.User):
|
||||
with patch.object(mod.MasterView, "Session", return_value=self.session):
|
||||
with patch.dict(self.request.matchdict, uuid=user.uuid):
|
||||
view = self.make_view()
|
||||
grid = view.make_version_grid()
|
||||
self.assertIsInstance(grid, Grid)
|
||||
self.assertIsInstance(grid.data, orm.Query)
|
||||
self.assertEqual(len(grid.data.all()), 2)
|
||||
|
||||
def test_view_versions(self):
|
||||
self.pyramid_config.add_route("home", "/")
|
||||
self.pyramid_config.add_route("login", "/auth/login")
|
||||
self.pyramid_config.add_route("users", "/users/")
|
||||
self.pyramid_config.add_route("users.view", "/users/{uuid}")
|
||||
self.pyramid_config.add_route("users.version", "/users/{uuid}/versions/{txnid}")
|
||||
model = self.app.model
|
||||
|
||||
user = model.User(username="fred")
|
||||
self.session.add(user)
|
||||
self.session.commit()
|
||||
user.username = "freddie"
|
||||
self.session.commit()
|
||||
|
||||
with patch.object(mod.MasterView, "model_class", new=model.User):
|
||||
with patch.object(mod.MasterView, "Session", return_value=self.session):
|
||||
with patch.dict(self.request.matchdict, uuid=user.uuid):
|
||||
view = self.make_view()
|
||||
|
||||
# normal, full page
|
||||
response = view.view_versions()
|
||||
self.assertEqual(response.content_type, "text/html")
|
||||
self.assertIn("<b-table", response.text)
|
||||
|
||||
# partial page
|
||||
with patch.dict(self.request.params, partial="1"):
|
||||
response = view.view_versions()
|
||||
self.assertEqual(response.content_type, "application/json")
|
||||
self.assertIn("data", response.json)
|
||||
self.assertEqual(len(response.json["data"]), 2)
|
||||
|
||||
def test_get_relevant_versions(self):
|
||||
import sqlalchemy_continuum as continuum
|
||||
|
||||
model = self.app.model
|
||||
txncls = continuum.transaction_class(model.User)
|
||||
vercls = continuum.version_class(model.User)
|
||||
|
||||
user = model.User(username="fred")
|
||||
self.session.add(user)
|
||||
self.session.commit()
|
||||
|
||||
txn = (
|
||||
self.session.query(txncls)
|
||||
.join(vercls, vercls.transaction_id == txncls.id)
|
||||
.order_by(txncls.id)
|
||||
.first()
|
||||
)
|
||||
|
||||
with patch.object(mod.MasterView, "model_class", new=model.User):
|
||||
with patch.object(mod.MasterView, "Session", return_value=self.session):
|
||||
view = self.make_view()
|
||||
versions = view.get_relevant_versions(txn, user)
|
||||
self.assertEqual(len(versions), 1)
|
||||
version = versions[0]
|
||||
self.assertIsInstance(version, vercls)
|
||||
|
||||
def test_view_version(self):
|
||||
import sqlalchemy_continuum as continuum
|
||||
|
||||
self.pyramid_config.add_route("home", "/")
|
||||
self.pyramid_config.add_route("login", "/auth/login")
|
||||
self.pyramid_config.add_route("users", "/users/")
|
||||
self.pyramid_config.add_route("users.view", "/users/{uuid}")
|
||||
self.pyramid_config.add_route("users.versions", "/users/{uuid}/versions/")
|
||||
self.pyramid_config.add_route("users.version", "/users/{uuid}/versions/{txnid}")
|
||||
model = self.app.model
|
||||
txncls = continuum.transaction_class(model.User)
|
||||
vercls = continuum.version_class(model.User)
|
||||
|
||||
user = model.User(username="fred")
|
||||
self.session.add(user)
|
||||
self.session.commit()
|
||||
user.username = "freddie"
|
||||
self.session.commit()
|
||||
|
||||
transactions = (
|
||||
self.session.query(txncls)
|
||||
.join(vercls, vercls.transaction_id == txncls.id)
|
||||
.order_by(txncls.id)
|
||||
.all()
|
||||
)
|
||||
self.assertEqual(len(transactions), 2)
|
||||
|
||||
with patch.object(mod.MasterView, "model_class", new=model.User):
|
||||
with patch.object(mod.MasterView, "Session", return_value=self.session):
|
||||
|
||||
# invalid txnid
|
||||
with patch.dict(self.request.matchdict, uuid=user.uuid, txnid=999999):
|
||||
view = self.make_view()
|
||||
self.assertRaises(HTTPNotFound, view.view_version)
|
||||
|
||||
# first txn
|
||||
first = transactions[0]
|
||||
with patch.dict(self.request.matchdict, uuid=user.uuid, txnid=first.id):
|
||||
view = self.make_view()
|
||||
response = view.view_version()
|
||||
self.assertIn(
|
||||
'<table class="table is-fullwidth is-bordered is-narrow">',
|
||||
response.text,
|
||||
)
|
||||
|
||||
# second txn
|
||||
second = transactions[1]
|
||||
with patch.dict(
|
||||
self.request.matchdict, uuid=user.uuid, txnid=second.id
|
||||
):
|
||||
view = self.make_view()
|
||||
response = view.view_version()
|
||||
self.assertIn(
|
||||
'<table class="table is-fullwidth is-bordered is-narrow">',
|
||||
response.text,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue