From d3d022d09b0fade909d482ab46b3c3e6b5fc90b1 Mon Sep 17 00:00:00 2001 From: Miriam Date: Sat, 9 May 2026 12:15:05 +0200 Subject: [PATCH] feat(#590): add "Testing Version" column to todo lists; include new fixtures for todolist dev(#590): add missing fixtures for todo lists cleanup ci/cd: fix ruff address-review: refactor for cleaner code --- README.md | 1 + templates/todolists/view.html | 4 + todolists/fixtures/todolist.json | 158 +++++++++++++++++++++++++++++++ todolists/tests/test_utils.py | 103 ++++++++++++++++++++ todolists/utils.py | 35 ++++--- todolists/views.py | 3 +- 6 files changed, 292 insertions(+), 12 deletions(-) create mode 100644 todolists/fixtures/todolist.json create mode 100644 todolists/tests/test_utils.py diff --git a/README.md b/README.md index 69de8329..253f5c7c 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ probably want the following: uv run ./manage.py loaddata devel/fixtures/*.json uv run ./manage.py loaddata mirrors/fixtures/*.json uv run ./manage.py loaddata releng/fixtures/*.json + uv run ./manage.py loaddata todolists/fixtures/*.json 7. Use the following commands to start a service instance uv run ./manage.py runserver diff --git a/templates/todolists/view.html b/templates/todolists/view.html index 5b08ab47..26ff6549 100644 --- a/templates/todolists/view.html +++ b/templates/todolists/view.html @@ -75,6 +75,7 @@

Filter Todo List Packages

Name Current Version Staging Version + Testing Version Maintainers Status Last Touched By @@ -96,6 +97,9 @@

Filter Todo List Packages

{% with staging=pkg.staging %} {% if staging %}{% pkg_details_link staging staging.full_version %}{% endif %} {% endwith %} + {% with testing=pkg.testing %} + {% if testing %}{% pkg_details_link testing testing.full_version %}{% endif %} + {% endwith %} {{ pkg.maintainers|join:', ' }} {% if perms.todolists.change_todolistpackage %} diff --git a/todolists/fixtures/todolist.json b/todolists/fixtures/todolist.json new file mode 100644 index 00000000..9802f097 --- /dev/null +++ b/todolists/fixtures/todolist.json @@ -0,0 +1,158 @@ +[ + { + "model": "auth.user", + "pk": 1, + "fields": { + "username": "developer", + "first_name": "Test", + "last_name": "Developer", + "email": "developer@archlinux.org", + "password": "pbkdf2_sha256$1000000$5mOVHaWggfkqKGsKsVpWH8$kS2tNunBSv+rrDohZXeuP9L/kX8oDe09vzab+m3WQXo=", + "is_staff": true, + "is_active": true, + "is_superuser": true, + "date_joined": "2024-01-01T00:00:00Z", + "last_login": null, + "groups": [], + "user_permissions": [] + } + }, + { + "model": "main.package", + "pk": 6, + "fields": { + "repo": 3, + "arch": 3, + "pkgname": "linux", + "pkgbase": "linux", + "pkgver": "6.9.1", + "pkgrel": "2", + "epoch": 0, + "pkgdesc": "The Linux kernel and modules", + "url": "https://www.kernel.org/", + "filename": "linux-6.9.1-2-x86_64.pkg.tar.zst", + "compressed_size": 72000000, + "installed_size": 91000000, + "build_date": "2024-05-20T10:00:00Z", + "last_update": "2024-05-20T10:00:00Z", + "files_last_update": null, + "created": "2024-05-20T10:00:00Z", + "packager_str": "Test Developer ", + "packager": null, + "signature_bytes": null, + "flag_date": null + } + }, + { + "model": "main.package", + "pk": 7, + "fields": { + "repo": 16, + "arch": 3, + "pkgname": "systemd", + "pkgbase": "systemd", + "pkgver": "256.1", + "pkgrel": "1", + "epoch": 0, + "pkgdesc": "system and service manager", + "url": "https://www.github.com/systemd/systemd", + "filename": "systemd-256.1-1-x86_64.pkg.tar.zst", + "compressed_size": 4100000, + "installed_size": 19000000, + "build_date": "2024-05-22T08:00:00Z", + "last_update": "2024-05-22T08:00:00Z", + "files_last_update": null, + "created": "2024-05-22T08:00:00Z", + "packager_str": "Test Developer ", + "packager": null, + "signature_bytes": null, + "flag_date": null + } + }, + { + "model": "todolists.todolist", + "pk": 1, + "fields": { + "slug": "glibc-2-40-rebuild", + "name": "glibc 2.40 rebuild", + "description": "Rebuild of packages affected by the glibc 2.40 soname bump.", + "creator": 1, + "created": "2024-05-20T09:00:00Z", + "kind": 0, + "last_modified": "2024-05-22T08:00:00Z", + "raw": "linux\ncoreutils\npacman\nsystemd" + } + }, + { + "model": "todolists.todolistpackage", + "pk": 1, + "fields": { + "todolist": 1, + "pkg": 1, + "pkgname": "linux", + "pkgbase": "linux", + "arch": 3, + "repo": 1, + "created": "2024-05-20T09:00:00Z", + "last_modified": "2024-05-20T09:00:00Z", + "removed": null, + "status": 0, + "user": null, + "comments": null + } + }, + { + "model": "todolists.todolistpackage", + "pk": 2, + "fields": { + "todolist": 1, + "pkg": 2, + "pkgname": "coreutils", + "pkgbase": "coreutils", + "arch": 3, + "repo": 1, + "created": "2024-05-20T09:00:00Z", + "last_modified": "2024-05-21T14:30:00Z", + "removed": null, + "status": 1, + "user": 1, + "comments": null + } + }, + { + "model": "todolists.todolistpackage", + "pk": 3, + "fields": { + "todolist": 1, + "pkg": 4, + "pkgname": "pacman", + "pkgbase": "pacman", + "arch": 3, + "repo": 1, + "created": "2024-05-20T09:00:00Z", + "last_modified": "2024-05-22T10:00:00Z", + "removed": null, + "status": 2, + "user": 1, + "comments": null + } + }, + { + "model": "todolists.todolistpackage", + "pk": 4, + "fields": { + "todolist": 1, + "pkg": 5, + "pkgname": "systemd", + "pkgbase": "systemd", + "arch": 3, + "repo": 1, + "created": "2024-05-20T09:00:00Z", + "last_modified": "2024-05-20T09:00:00Z", + "removed": null, + "status": 0, + "user": null, + "comments": null + } + } +] diff --git a/todolists/tests/test_utils.py b/todolists/tests/test_utils.py new file mode 100644 index 00000000..0f84dcfe --- /dev/null +++ b/todolists/tests/test_utils.py @@ -0,0 +1,103 @@ +import pytest +from django.utils.timezone import now + +from main.models import Package, Repo +from todolists.models import TodolistPackage +from todolists.utils import attach_staging, attach_testing + + +def make_package(pkgname, repo, arch): + return Package.objects.create( + pkgname=pkgname, + pkgbase=pkgname, + pkgver='1.0', + pkgrel='1', + epoch=0, + pkgdesc='Test package', + url='https://example.com', + filename=f'{pkgname}-1.0-1-x86_64.pkg.tar.zst', + compressed_size=1000, + installed_size=2000, + build_date=now(), + last_update=now(), + created=now(), + packager_str='Test Packager', + repo=repo, + arch=arch, + ) + + +@pytest.fixture +def stable_pkg(repos, package): + return Package.objects.get(pkgname='linux') + + +@pytest.fixture +def todolist_with_linux(todolist, stable_pkg, user): + arch = stable_pkg.arch + repo = stable_pkg.repo + tlpkg = TodolistPackage.objects.create( + todolist=todolist, + pkg=stable_pkg, + pkgname=stable_pkg.pkgname, + pkgbase=stable_pkg.pkgbase, + arch=arch, + repo=repo, + user=user, + ) + yield todolist + tlpkg.delete() + + +def test_attach_testing_no_testing_pkg(todolist_with_linux): + todolist = todolist_with_linux + attach_testing(todolist.packages(), todolist.pk) + for pkg in todolist.packages(): + assert pkg.testing is None + + +def test_attach_testing_finds_testing_pkg(todolist_with_linux): + todolist = todolist_with_linux + stable_pkg = Package.objects.get(pkgname='linux', repo__testing=False, repo__staging=False) + testing_repo = Repo.objects.get(name='Core-Testing') + + testing_pkg = make_package('linux', testing_repo, stable_pkg.arch) + try: + attach_testing(todolist.packages(), todolist.pk) + for pkg in todolist.packages(): + if pkg.pkgname == 'linux': + assert pkg.testing is not None + assert pkg.testing.repo.testing is True + finally: + testing_pkg.delete() + + +def test_attach_testing_ignores_staging_repos(todolist_with_linux): + todolist = todolist_with_linux + stable_pkg = Package.objects.get(pkgname='linux', repo__testing=False, repo__staging=False) + staging_repo = Repo.objects.get(name='Core-Staging') + + staging_pkg = make_package('linux', staging_repo, stable_pkg.arch) + try: + attach_testing(todolist.packages(), todolist.pk) + for pkg in todolist.packages(): + if pkg.pkgname == 'linux': + assert pkg.testing is None + finally: + staging_pkg.delete() + + +def test_attach_staging_still_works(todolist_with_linux): + todolist = todolist_with_linux + stable_pkg = Package.objects.get(pkgname='linux', repo__testing=False, repo__staging=False) + staging_repo = Repo.objects.get(name='Core-Staging') + + staging_pkg = make_package('linux', staging_repo, stable_pkg.arch) + try: + attach_staging(todolist.packages(), todolist.pk) + for pkg in todolist.packages(): + if pkg.pkgname == 'linux': + assert pkg.staging is not None + assert pkg.staging.repo.staging is True + finally: + staging_pkg.delete() diff --git a/todolists/utils.py b/todolists/utils.py index eee4c07d..11836500 100644 --- a/todolists/utils.py +++ b/todolists/utils.py @@ -39,21 +39,34 @@ def get_annotated_todolists(incomplete_only=False): return lists -def attach_staging(packages, list_id): - '''Look for any staging version of the packages provided and attach them - to the 'staging' attribute on each package if found.''' +def _attach_repo_version(packages, list_id, kind): + '''Look for related packages in repos flagged by kind and attach them. + + kind is a Repo boolean field name used as repo__{kind}=True. Only + 'staging' and 'testing' are used; they set package.staging or + package.testing for the todolist templates. + ''' pkgnames = TodolistPackage.objects.filter( todolist_id=list_id).values('pkgname') - staging_pkgs = Package.objects.normal().filter(repo__staging=True, - pkgname__in=pkgnames) - # now build a lookup dict to attach to the correct package - lookup = {(p.pkgname, p.arch): p for p in staging_pkgs} + related_pkgs = Package.objects.normal().filter( + **{f'repo__{kind}': True}, + pkgname__in=pkgnames, + ) + lookup = {(p.pkgname, p.arch): p for p in related_pkgs} - annotated = [] for package in packages: - in_staging = lookup.get((package.pkgname, package.arch), None) - package.staging = in_staging + setattr(package, kind, lookup.get((package.pkgname, package.arch))) + + +def attach_staging(packages, list_id): + '''Look for any staging version of the packages provided and attach them + to the 'staging' attribute on each package if found.''' + _attach_repo_version(packages, list_id, 'staging') + - return annotated +def attach_testing(packages, list_id): + '''Look for any testing version of the packages provided and attach them + to the 'testing' attribute on each package if found.''' + _attach_repo_version(packages, list_id, 'testing') # vim: set ts=4 sw=4 et: diff --git a/todolists/views.py b/todolists/views.py index 5009ff23..d51c177e 100644 --- a/todolists/views.py +++ b/todolists/views.py @@ -16,7 +16,7 @@ from packages.utils import PackageJSONEncoder, attach_maintainers from .models import Todolist, TodolistPackage -from .utils import attach_staging, get_annotated_todolists +from .utils import attach_staging, attach_testing, get_annotated_todolists class TodoListForm(forms.ModelForm): @@ -65,6 +65,7 @@ def view(request, slug): # so accessing maintainers in the template is now cheap attach_maintainers(todolist.packages()) attach_staging(todolist.packages(), todolist.pk) + attach_testing(todolist.packages(), todolist.pk) arches = {tp.arch for tp in todolist.packages()} repos = {tp.repo for tp in todolist.packages()} context = {