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 @@
{{ "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)
|