From 53fabdfaeada6a65ad60eec186a0eb254be28521 Mon Sep 17 00:00:00 2001 From: Kevin Morris Date: Fri, 31 Dec 2021 01:02:40 -0800 Subject: [PATCH] fix(templates): require valid User relationships for usage Previously, when the relationship was None, an would still wrap the None value erroneously. This addresses that for all three user fields. In addition, this commit adds direct testing for the `templates/partials/packages/details.html` template. Signed-off-by: Kevin Morris --- templates/partials/packages/details.html | 12 +- test/test_templates.py | 226 ++++++++++++++++++++++- 2 files changed, 230 insertions(+), 8 deletions(-) diff --git a/templates/partials/packages/details.html b/templates/partials/packages/details.html index 9aeb81ec..6dc8ae77 100644 --- a/templates/partials/packages/details.html +++ b/templates/partials/packages/details.html @@ -96,9 +96,9 @@ {{ "Submitter" | tr }}: - {% if request.user.is_authenticated() %} + {% if request.user.is_authenticated() and pkgbase.Submitter %} - {{ pkgbase.Submitter.Username | default("None" | tr) }} + {{ pkgbase.Submitter.Username }} {% else %} {{ pkgbase.Submitter.Username | default("None" | tr) }} @@ -108,9 +108,9 @@ {{ "Maintainer" | tr }}: - {% if request.user.is_authenticated() %} + {% if request.user.is_authenticated() and pkgbase.Maintainer %} - {{ pkgbase.Maintainer.Username | default("None" | tr) }} + {{ pkgbase.Maintainer.Username }} {% else %} {{ pkgbase.Maintainer.Username | default("None" | tr) }} @@ -120,9 +120,9 @@ {{ "Last Packager" | tr }}: - {% if request.user.is_authenticated() %} + {% if request.user.is_authenticated() and pkgbase.Packager %} - {{ pkgbase.Packager.Username | default("None" | tr) }} + {{ pkgbase.Packager.Username }} {% else %} {{ pkgbase.Packager.Username | default("None" | tr) }} diff --git a/test/test_templates.py b/test/test_templates.py index f7be969b..6104c126 100644 --- a/test/test_templates.py +++ b/test/test_templates.py @@ -1,15 +1,27 @@ import re +from datetime import datetime from typing import Any, Dict import pytest import aurweb.filters # noqa: F401 -from aurweb import config, templates -from aurweb.templates import base_template, register_filter, register_function +from aurweb import config, db, templates +from aurweb.models import Package, PackageBase, User +from aurweb.models.account_type import USER_ID +from aurweb.models.license import License +from aurweb.models.package_license import PackageLicense +from aurweb.models.package_relation import PackageRelation +from aurweb.models.relation_type import PROVIDES_ID, REPLACES_ID +from aurweb.templates import base_template, make_context, register_filter, register_function from aurweb.testing.html import parse_root from aurweb.testing.requests import Request +from aurweb.util import as_timezone, number_format +from aurweb.util import timestamp_to_datetime as to_dt + +GIT_CLONE_URI_ANON = "anon_%s" +GIT_CLONE_URI_PRIV = "priv_%s" @register_filter("func") @@ -22,6 +34,51 @@ def function(): pass +def create_user(username: str) -> User: + with db.begin(): + user = db.create(User, Username=username, + Email=f"{username}@example.org", + Passwd="testPassword", + AccountTypeID=USER_ID) + return user + + +def create_pkgrel(package: Package, reltype_id: int, relname: str) \ + -> PackageRelation: + return db.create(PackageRelation, + Package=package, + RelTypeID=reltype_id, + RelName=relname) + + +@pytest.fixture +def user(db_test) -> User: + user = create_user("test") + yield user + + +@pytest.fixture +def pkgbase(user: User) -> PackageBase: + now = int(datetime.utcnow().timestamp()) + with db.begin(): + pkgbase = db.create(PackageBase, Name="test-pkg", Maintainer=user, + SubmittedTS=now, ModifiedTS=now) + yield pkgbase + + +@pytest.fixture +def package(user: User, pkgbase: PackageBase) -> Package: + with db.begin(): + pkg = db.create(Package, PackageBase=pkgbase, Name=pkgbase.Name) + yield pkg + + +def create_license(pkg: Package, license_name: str) -> PackageLicense: + lic = db.create(License, Name=license_name) + pkglic = db.create(PackageLicense, License=lic, Package=pkg) + return pkglic + + def test_register_filter_exists_key_error(): """ Most instances of register_filter are tested through module imports or template renders, so we only test failures here. """ @@ -114,3 +171,168 @@ def test_pager(): stats = re.sub(r"\s{2,}", " ", stats[0].text.strip()) expected = f"{num_packages} packages found. Page 1 of 2." assert stats == expected + + +def check_package_details(content: str, pkg: Package) -> None: + """ + Perform assertion checks against package details. + """ + pkgbase = pkg.PackageBase + + root = parse_root(content) + pkginfo = root.xpath('//table[@id="pkginfo"]')[0] + rows = pkginfo.xpath("./tr") + + # Check Git Clone URL. + git_clone_uris = rows[0].xpath("./td/a") + anon_uri, priv_uri = git_clone_uris + pkgbasename = pkgbase.Name + assert anon_uri.text.strip() == GIT_CLONE_URI_ANON % pkgbasename + assert priv_uri.text.strip() == GIT_CLONE_URI_PRIV % pkgbasename + + # Check Package Base. + pkgbase_markup = rows[1].xpath("./td/a")[0] + assert pkgbase_markup.text.strip() == pkgbasename + + # Check Description. + desc = rows[2].xpath("./td")[0] + assert desc.text.strip() == str(pkg.Description) + + # Check URL, for which we have none. In this case, no should + # be used since we have nothing to link. + url = rows[3].xpath("./td")[0] + assert url.text.strip() == str(pkg.URL) + + # Check Keywords, which should be empty. + keywords = rows[4].xpath("./td/form/div/input")[0] + assert keywords.attrib["value"] == str() + + i = 4 + licenses = pkg.package_licenses.all() + if licenses: + i += 1 + expected = ", ".join([p.License.Name for p in licenses]) + license_markup = rows[i].xpath("./td")[0] + assert license_markup.text.strip() == expected + else: + assert "Licenses" not in content + + provides = pkg.package_relations.filter( + PackageRelation.RelTypeID == PROVIDES_ID + ).all() + if provides: + i += 1 + expected = ", ".join([p.RelName for p in provides]) + provides_markup = rows[i].xpath("./td")[0] + assert provides_markup.text.strip() == expected + else: + assert "Provides" not in content + + replaces = pkg.package_relations.filter( + PackageRelation.RelTypeID == REPLACES_ID + ).all() + if replaces: + i += 1 + expected = ", ".join([r.RelName for r in replaces]) + replaces_markup = rows[i].xpath("./td")[0] + assert replaces_markup.text.strip() == expected + else: + assert "Replaces" not in content + + # Check Submitter. + selector = "./td" if not pkg.PackageBase.Submitter else "./td/a" + i += 1 + submitter = rows[i].xpath(selector)[0] + assert submitter.text.strip() == str(pkg.PackageBase.Submitter) + + # Check Maintainer. + selector = "./td" if not pkg.PackageBase.Maintainer else "./td/a" + i += 1 + maintainer = rows[i].xpath(selector)[0] + assert maintainer.text.strip() == str(pkg.PackageBase.Maintainer) + + # Check Packager. + selector = "./td" if not pkg.PackageBase.Packager else "./td/a" + i += 1 + packager = rows[i].xpath(selector)[0] + assert packager.text.strip() == str(pkg.PackageBase.Packager) + + # Check Votes. + i += 1 + votes = rows[i].xpath("./td")[0] + assert votes.text.strip() == str(pkg.PackageBase.NumVotes) + + # Check Popularity; for this package, a number_format of 6 places is used. + i += 1 + pop = rows[i].xpath("./td")[0] + assert pop.text.strip() == number_format(0, 6) + + # Check First Submitted + date_fmt = "%Y-%m-%d %H:%M" + i += 1 + first_submitted = rows[i].xpath("./td")[0] + converted_dt = as_timezone(to_dt(pkg.PackageBase.SubmittedTS), "UTC") + expected = converted_dt.strftime(date_fmt) + assert first_submitted.text.strip() == expected + + # Check Last Updated. + i += 1 + last_updated = rows[i].xpath("./td")[0] + converted_dt = as_timezone(to_dt(pkg.PackageBase.ModifiedTS), "UTC") + expected = converted_dt.strftime(date_fmt) + assert last_updated.text.strip() == expected + + +def test_package_details(user: User, package: Package): + """ Test package details with most fields populated, but not all. """ + request = Request(user=user, authenticated=True) + context = make_context(request, "Test Details") + context.update({ + "request": request, + "git_clone_uri_anon": GIT_CLONE_URI_ANON, + "git_clone_uri_priv": GIT_CLONE_URI_PRIV, + "pkgbase": package.PackageBase, + "pkg": package + }) + + base = base_template("partials/packages/details.html") + body = base.render(context, show_package_details=True) + check_package_details(body, package) + + +def test_package_details_filled(user: User, package: Package): + """ Test package details with all fields populated. """ + + pkgbase = package.PackageBase + with db.begin(): + # Setup Submitter and Packager; Maintainer is already set to `user`. + pkgbase.Submitter = pkgbase.Packager = user + + # Create two licenses. + create_license(package, "TPL") # Testing Public License + create_license(package, "TPL2") # Testing Public License 2 + + # Add provides. + create_pkgrel(package, PROVIDES_ID, "test-provider") + + # Add replaces. + create_pkgrel(package, REPLACES_ID, "test-replacement") + + request = Request(user=user, authenticated=True) + context = make_context(request, "Test Details") + context.update({ + "request": request, + "git_clone_uri_anon": GIT_CLONE_URI_ANON, + "git_clone_uri_priv": GIT_CLONE_URI_PRIV, + "pkgbase": package.PackageBase, + "pkg": package, + "licenses": package.package_licenses, + "provides": package.package_relations.filter( + PackageRelation.RelTypeID == PROVIDES_ID), + "replaces": package.package_relations.filter( + PackageRelation.RelTypeID == REPLACES_ID), + }) + + base = base_template("partials/packages/details.html") + body = base.render(context, show_package_details=True) + check_package_details(body, package)