3
0
Fork 0
wuttaweb/tests/test_diffs.py
Lance Edgar d08ba5fe51 fix: render human-friendly datetime for such fields in version diff
still render the "raw" value but include human-friendly for convenience
2026-03-14 16:19:17 -05:00

229 lines
8.5 KiB
Python

# -*- coding: utf-8; -*-
import datetime
import re
from unittest.mock import patch
import sqlalchemy as sa
from sqlalchemy import orm
from wuttjamaican.util import get_timezone_by_name
from wuttaweb import diffs as mod
from wuttaweb.testing import WebTestCase, VersionWebTestCase
class TestWebDiff(WebTestCase):
def make_diff(self, *args, **kwargs):
return mod.WebDiff(self.config, *args, **kwargs)
def test_render_html(self):
old_data = {"foo": "bar"}
new_data = {"foo": "baz"}
diff = self.make_diff(old_data, new_data)
html = diff.render_html()
self.assertIn("<table", html)
self.assertIn("<tr>", html)
self.assertIn("&#39;bar&#39;", html)
self.assertIn(f'style="background-color: {diff.old_color}"', html)
self.assertIn("&#39;baz&#39;", html)
self.assertIn(f'style="background-color: {diff.new_color}"', html)
self.assertIn("</tr>", html)
self.assertIn("</table>", html)
class TestVersionDiff(VersionWebTestCase):
def make_diff(self, *args, **kwargs):
return mod.VersionDiff(self.config, *args, **kwargs)
def test_constructor(self):
import sqlalchemy_continuum as continuum
model = self.app.model
user = model.User(username="fred")
self.session.add(user)
self.session.commit()
user.username = "freddie"
self.session.commit()
self.session.delete(user)
self.session.commit()
txncls = continuum.transaction_class(model.User)
vercls = continuum.version_class(model.User)
versions = self.session.query(vercls).order_by(vercls.transaction_id).all()
self.assertEqual(len(versions), 3)
version = versions[0]
diff = self.make_diff(version)
self.assertEqual(diff.nature, "create")
self.assertEqual(
diff.fields,
["active", "person_uuid", "prevent_edit", "username", "uuid"],
)
version = versions[1]
diff = self.make_diff(version)
self.assertEqual(diff.nature, "update")
self.assertEqual(
diff.fields,
["active", "person_uuid", "prevent_edit", "username", "uuid"],
)
version = versions[2]
diff = self.make_diff(version)
self.assertEqual(diff.nature, "delete")
self.assertEqual(
diff.fields,
["active", "person_uuid", "prevent_edit", "username", "uuid"],
)
def test_render_version_value_with_objref(self):
import sqlalchemy_continuum as continuum
model = self.app.model
person = model.Person(full_name="Fred Flintstone")
self.session.add(person)
# create, update, delete user
user = model.User(username="fred", person=person)
self.session.add(user)
self.session.commit()
user.username = "freddie"
self.session.commit()
self.session.delete(user)
self.session.commit()
txncls = continuum.transaction_class(model.User)
vercls = continuum.version_class(model.User)
versions = self.session.query(vercls).order_by(vercls.transaction_id).all()
self.assertEqual(len(versions), 3)
# create (1st version)
version = versions[0]
diff = self.make_diff(version)
self.assertEqual(diff.nature, "create")
self.assertEqual(diff.render_old_value("username"), "")
self.assertIn("fred", diff.render_new_value("username"))
self.assertNotIn("freddie", diff.render_new_value("username"))
self.assertEqual(diff.render_old_value("person_uuid"), "")
# rendered person_uuid includes display name
html = diff.render_new_value("person_uuid")
self.assertIn(str(person.uuid), html)
self.assertIn("Fred Flintstone", html)
# update (2nd version)
version = versions[1]
diff = self.make_diff(version)
self.assertEqual(diff.nature, "update")
self.assertIn("fred", diff.render_old_value("username"))
self.assertNotIn("freddie", diff.render_old_value("username"))
self.assertIn("freddie", diff.render_new_value("username"))
# rendered person_uuid includes display name
html = diff.render_old_value("person_uuid")
self.assertIn(str(person.uuid), html)
self.assertIn("Fred Flintstone", html)
html = diff.render_new_value("person_uuid")
self.assertIn(str(person.uuid), html)
self.assertIn("Fred Flintstone", html)
# delete (3rd version)
version = versions[2]
diff = self.make_diff(version)
self.assertEqual(diff.nature, "delete")
self.assertIn("freddie", diff.render_old_value("username"))
self.assertEqual(diff.render_new_value("username"), "")
# rendered person_uuid includes display name
html = diff.render_old_value("person_uuid")
self.assertIn(str(person.uuid), html)
self.assertIn("Fred Flintstone", html)
self.assertEqual(diff.render_new_value("person_uuid"), "")
def test_render_version_value_with_datetime(self):
import sqlalchemy_continuum as continuum
tzlocal = get_timezone_by_name("America/Los_Angeles")
with patch.object(self.app, "get_timezone", return_value=tzlocal):
# make one person
model = self.app.model
person = model.Person(full_name="Fred Flintstone")
self.session.add(person)
self.session.commit()
# get its one version record
txncls = continuum.transaction_class(model.Person)
vercls = continuum.version_class(model.Person)
version = self.session.query(vercls).order_by(vercls.transaction_id).one()
# make a diff, but we have to mock some things up for the test
# coverage, since we don't currently have a versioned datetime
# field in the base model.
diff = self.make_diff(version)
mock_column = sa.Column("timestamp", sa.DateTime())
mock_property = orm.ColumnProperty(column=mock_column)
class MockMapper:
def __init__(self, mapper):
self.mapper = mapper
def __getattr__(self, name):
if hasattr(self.mapper, name):
return getattr(self.mapper, name)
raise AttributeError(f"attr not found: {name}")
def has_property(self, field):
if field == "timestamp":
return True
return self.mapper.has_property(field)
def get_property(self, field):
if field == "timestamp":
return mock_property
return self.mapper.get_property(field)
mock_mapper = MockMapper(diff.mapper)
with patch.object(diff, "mapper", new=mock_mapper):
# zone-aware local time
dt = datetime.datetime(2026, 3, 14, 14, 0, tzinfo=tzlocal)
html = diff.render_version_value(version, "timestamp", dt)
self.assertTrue(
re.search(
r'<span style="[^"]+"><span title="[^"]+">\d{4}-\d{2}-\d{2} \d{2}:\d{2}-\d{4}</span></span>',
html,
)
)
self.assertTrue(
re.search(
r'<span style="[^"]+"><span title="[^"]+">2026-03-14 14:00-0700</span></span>',
html,
)
)
# zone-naive (presumed UTC)
dt = datetime.datetime(2026, 3, 14, 21, 0)
html = diff.render_version_value(version, "timestamp", dt)
self.assertTrue(
re.search(
r'<span style="[^"]+"><span title="[^"]+">\d{4}-\d{2}-\d{2} \d{2}:\d{2}-\d{4}</span></span>',
html,
)
)
self.assertTrue(
re.search(
r'<span style="[^"]+"><span title="[^"]+">2026-03-14 14:00-0700</span></span>',
html,
)
)
# null
html = diff.render_version_value(version, "timestamp", None)
self.assertFalse(
re.search(
r'<span style="[^"]+"><span title="[^"]+">\d{4}-\d{2}-\d{2} \d{2}:\d{2}-\d{4}</span></span>',
html,
)
)