From f23acb858b53cf2c09c0ef8233c6f5bdb0ef967d Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Sun, 6 Sep 2026 16:54:51 +0200 Subject: [PATCH 01/15] public: Replace `.items()` with `.values()` --- public/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/utils.py b/public/utils.py index 840806396..71fe255c5 100644 --- a/public/utils.py +++ b/public/utils.py @@ -49,7 +49,7 @@ def package_links(self): by_arch = defaultdict(list) for package in self.others: by_arch[package.arch].append(package) - for _, packages in by_arch.items(): + for packages in by_arch.values(): if len(packages) == 1: yield packages[0] else: From 138331ade8f4ed2e59eecc13c1e6f2155b594f08 Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Sun, 6 Sep 2026 16:57:00 +0200 Subject: [PATCH 02/15] reporead: Prefer list comprehensions --- devel/management/commands/reporead.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/devel/management/commands/reporead.py b/devel/management/commands/reporead.py index d56356866..bbe70d82f 100644 --- a/devel/management/commands/reporead.py +++ b/devel/management/commands/reporead.py @@ -206,9 +206,8 @@ def create_multivalued(dbpkg, repopkg, db_attr, repo_attr): collection = getattr(dbpkg, db_attr) collection.all().delete() model = collection.model - new_items = [] - for name in getattr(repopkg, repo_attr): - new_items.append(model(pkg=dbpkg, name=name)) + new_items = [model(pkg=dbpkg, name=name) for name in getattr(repopkg, repo_attr)] + if new_items: model.objects.bulk_create(new_items) From 78ed05e35bbe69afa968d489fb0338dd6836a913 Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Sun, 6 Sep 2026 16:58:07 +0200 Subject: [PATCH 03/15] treewide: Don't pass None to `dict.get()` The default value is `None` so its redundant to pass. --- devel/management/commands/generate_keyring.py | 2 +- devel/management/commands/pgp_import.py | 2 +- .../management/commands/read_rebuilderd_status.py | 2 +- devel/management/commands/readlinks.py | 2 +- devel/management/commands/rematch_developers.py | 2 +- devel/reports.py | 2 +- devel/views.py | 14 +++++++------- main/utils.py | 2 +- mirrors/management/commands/mirrorcheck.py | 2 +- mirrors/views/mirrorlist.py | 2 +- packages/management/commands/populate_signoffs.py | 2 +- todolists/utils.py | 2 +- 12 files changed, 18 insertions(+), 18 deletions(-) diff --git a/devel/management/commands/generate_keyring.py b/devel/management/commands/generate_keyring.py index 93bb01a7e..0ce5f3e7e 100644 --- a/devel/management/commands/generate_keyring.py +++ b/devel/management/commands/generate_keyring.py @@ -24,7 +24,7 @@ def add_arguments(self, parser): parser.add_argument('args', nargs='*', help=' ') def handle(self, *args, **options): - v = int(options.get('verbosity', None)) + v = int(options.get('verbosity')) if v == 0: logger.level = logging.ERROR elif v == 1: diff --git a/devel/management/commands/pgp_import.py b/devel/management/commands/pgp_import.py index 044e45f60..70c2b8030 100644 --- a/devel/management/commands/pgp_import.py +++ b/devel/management/commands/pgp_import.py @@ -28,7 +28,7 @@ def add_arguments(self, parser): parser.add_argument('args', nargs='*', help=' ') def handle(self, *args, **options): - v = int(options.get('verbosity', None)) + v = int(options.get('verbosity')) if v == 0: logger.level = logging.ERROR elif v == 1: diff --git a/devel/management/commands/read_rebuilderd_status.py b/devel/management/commands/read_rebuilderd_status.py index afc963d84..56bd03d11 100644 --- a/devel/management/commands/read_rebuilderd_status.py +++ b/devel/management/commands/read_rebuilderd_status.py @@ -30,7 +30,7 @@ class Command(BaseCommand): help = "Import reproducible status from rebuilderd." def handle(self, *args, **options): - v = int(options.get('verbosity', None)) + v = int(options.get('verbosity')) if v == 0: logger.level = logging.ERROR elif v == 1: diff --git a/devel/management/commands/readlinks.py b/devel/management/commands/readlinks.py index f5a27b74b..92df5cdf3 100644 --- a/devel/management/commands/readlinks.py +++ b/devel/management/commands/readlinks.py @@ -25,7 +25,7 @@ def handle(self, filename=None, **options): if not os.path.exists(filename) or not os.path.isfile(filename): raise CommandError('Specified links database file does not exist.') - v = int(options.get('verbosity', None)) + v = int(options.get('verbosity')) if v == 0: logger.level = logging.ERROR elif v == 1: diff --git a/devel/management/commands/rematch_developers.py b/devel/management/commands/rematch_developers.py index e1cddaa78..ffb613d47 100644 --- a/devel/management/commands/rematch_developers.py +++ b/devel/management/commands/rematch_developers.py @@ -26,7 +26,7 @@ class Command(BaseCommand): help = "Match and map objects in database to developer emails" def handle(self, **options): - v = int(options.get('verbosity', None)) + v = int(options.get('verbosity')) if v == 0: logger.level = logging.ERROR elif v == 1: diff --git a/devel/reports.py b/devel/reports.py index 8d67eb330..7ff9c2ac0 100644 --- a/devel/reports.py +++ b/devel/reports.py @@ -150,7 +150,7 @@ def mismatched_signature(packages): for package in packages: bad = False sig = package.signature - dev_key = known_keys.get(sig.key_id, None) + dev_key = known_keys.get(sig.key_id) if dev_key: package.sig_by = dev_key.owner if dev_key.owner_id != package.packager_id: diff --git a/devel/views.py b/devel/views.py index fd5231735..2a04a3706 100644 --- a/devel/views.py +++ b/devel/views.py @@ -153,11 +153,11 @@ def clock(request): for dev in devs: dates = [ - latest_news.get(dev.id, None), - latest_package.get(dev.id, None), - latest_signoff.get(dev.id, None), - latest_flagreq.get(dev.id, None), - latest_log.get(dev.id, None), + latest_news.get(dev.id), + latest_package.get(dev.id), + latest_signoff.get(dev.id), + latest_flagreq.get(dev.id), + latest_log.get(dev.id), dev.last_login, ] dates = [d for d in dates if d is not None] @@ -287,7 +287,7 @@ def get_report_packages(report, packages, username): @login_required def report_pkgbases(request, report_name: str, username: str | None = None) -> HttpResponse: - report = {report.slug: report for report in available_reports()}.get(report_name, None) + report = {report.slug: report for report in available_reports()}.get(report_name) if report is None: raise Http404 @@ -306,7 +306,7 @@ def report_pkgbases(request, report_name: str, username: str | None = None) -> H @login_required def report(request, report_name, username=None): available = {report.slug: report for report in available_reports()} - report = available.get(report_name, None) + report = available.get(report_name) if report is None: raise Http404 diff --git a/main/utils.py b/main/utils.py index c484672d6..d986e87bf 100644 --- a/main/utils.py +++ b/main/utils.py @@ -142,7 +142,7 @@ def groupby_preserve_order(iterable, keyfunc): for item in iterable: key = keyfunc(item) - group = seen_keys.get(key, None) + group = seen_keys.get(key) if group is None: group = [] seen_keys[key] = group diff --git a/mirrors/management/commands/mirrorcheck.py b/mirrors/management/commands/mirrorcheck.py index ee7ee1e4b..1585c3834 100644 --- a/mirrors/management/commands/mirrorcheck.py +++ b/mirrors/management/commands/mirrorcheck.py @@ -64,7 +64,7 @@ def handle(self, **options): urls = MirrorUrl.objects.select_related('protocol').filter( active=True, mirror__active=True, mirror__public=True) - location = options.get('location', None) + location = options.get('location') if location: location = CheckLocation.objects.get(id=location) family = location.family diff --git a/mirrors/views/mirrorlist.py b/mirrors/views/mirrorlist.py index 1fb4e48b8..61748ac9a 100644 --- a/mirrors/views/mirrorlist.py +++ b/mirrors/views/mirrorlist.py @@ -65,7 +65,7 @@ def status_filter(original_urls): scores = {u.id: u.score for u in status_info['urls']} urls = [] for u in original_urls: - u.score = scores.get(u.id, None) + u.score = scores.get(u.id) # also include mirrors that don't have an up to date score # (as opposed to those that have been set with no score) if (u.id not in scores) or (u.score and u.score < 100.0): diff --git a/packages/management/commands/populate_signoffs.py b/packages/management/commands/populate_signoffs.py index 7accb408b..57b0f00eb 100644 --- a/packages/management/commands/populate_signoffs.py +++ b/packages/management/commands/populate_signoffs.py @@ -28,7 +28,7 @@ class Command(BaseCommand): is signoff-eligible and does not have an existing comment attached""" def handle(self, **options): - v = int(options.get('verbosity', None)) + v = int(options.get('verbosity')) if v == 0: logger.level = logging.ERROR elif v == 1: diff --git a/todolists/utils.py b/todolists/utils.py index eee4c07d8..28628f6b0 100644 --- a/todolists/utils.py +++ b/todolists/utils.py @@ -51,7 +51,7 @@ def attach_staging(packages, list_id): annotated = [] for package in packages: - in_staging = lookup.get((package.pkgname, package.arch), None) + in_staging = lookup.get((package.pkgname, package.arch)) package.staging = in_staging return annotated From 4c3903335c66411c0eedccb067fb1c00add8408a Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Sun, 6 Sep 2026 16:59:37 +0200 Subject: [PATCH 04/15] treewide: Position constants on the right hand side Also known as yoda conditions, more readable. --- mirrors/tests/test_mirrorcheck.py | 2 +- mirrors/tests/test_mirrorlocations.py | 4 ++-- news/tests/test_crud.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mirrors/tests/test_mirrorcheck.py b/mirrors/tests/test_mirrorcheck.py index 895d09011..c888cabcb 100644 --- a/mirrors/tests/test_mirrorcheck.py +++ b/mirrors/tests/test_mirrorcheck.py @@ -127,7 +127,7 @@ def test_socket_error(urlopen, Request, mirrorurl): def test_checklocation_fail(db): with pytest.raises(CheckLocation.DoesNotExist) as e: call_command('mirrorcheck', '-l', '1') - assert 'CheckLocation matching query does not exist.' == str(e.value) + assert str(e.value) == 'CheckLocation matching query does not exist.' def test_checklocation_model(checklocation): diff --git a/mirrors/tests/test_mirrorlocations.py b/mirrors/tests/test_mirrorlocations.py index ffd68ed12..28a5a45ab 100644 --- a/mirrors/tests/test_mirrorlocations.py +++ b/mirrors/tests/test_mirrorlocations.py @@ -5,6 +5,6 @@ def test_mirrorlocations_json(client, checklocation): response = client.get('/mirrors/locations/json/') assert response.status_code == 200 data = response.json() - assert 1 == data['version'] + assert data['version'] == 1 location = data['locations'][0]['country_code'] - assert COUNTRY == location + assert location == COUNTRY diff --git a/news/tests/test_crud.py b/news/tests/test_crud.py index 836716000..6f7a59436 100644 --- a/news/tests/test_crud.py +++ b/news/tests/test_crud.py @@ -50,4 +50,4 @@ def test_send_announce(db, admin_client): def test_preview(db, admin_client): response = admin_client.post('/news/preview/', {'data': '**body**'}, follow=True) assert response.status_code == 200 - assert '

body

' == response.content.decode() + assert response.content.decode() == '

body

' From f3a8a84c81df9b35b0a4ca6357c46433b0ef03db Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Sun, 6 Sep 2026 17:05:32 +0200 Subject: [PATCH 05/15] planet: Return fixture directly There is no need to yield as there is no automatic cleanup in this fixture. --- planet/tests/test_command.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/planet/tests/test_command.py b/planet/tests/test_command.py index fe35989eb..7367af044 100644 --- a/planet/tests/test_command.py +++ b/planet/tests/test_command.py @@ -10,7 +10,7 @@ @pytest.fixture def command(): - yield Command() + return Command() @pytest.fixture From b5bcb3b55c6e4e0013241adfb120fb316c86a650 Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Sun, 6 Sep 2026 17:10:59 +0200 Subject: [PATCH 06/15] mirrors: Port test_mirrorsrsync to pytest --- mirrors/tests/test_mirrorrsync.py | 39 ++++++++++++++++--------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/mirrors/tests/test_mirrorrsync.py b/mirrors/tests/test_mirrorrsync.py index 3d39d8170..e34338008 100644 --- a/mirrors/tests/test_mirrorrsync.py +++ b/mirrors/tests/test_mirrorrsync.py @@ -1,4 +1,4 @@ -from django.test import TransactionTestCase +import pytest from mirrors.models import Mirror, MirrorRsync @@ -6,25 +6,26 @@ TEST_IPV4 = "8.8.8.8" -class MirrorRsyncTest(TransactionTestCase): - def setUp(self): - self.mirror = Mirror.objects.create(name='rmirror', - admin_email='foo@bar.com') +@pytest.fixture +def mirror(transactional_db): + mirror = Mirror.objects.create(name='rmirror', admin_email='foo@bar.com') + yield mirror + mirror.delete() - def tearDown(self): - self.mirror.delete() - def test_ipv6(self): - mirrorrsync = MirrorRsync.objects.create(ip=TEST_IPV6, mirror=self.mirror) - self.assertEqual(str(mirrorrsync), TEST_IPV6) - mirrorrsync.delete() +def test_ipv6(mirror): + mirrorrsync = MirrorRsync.objects.create(ip=TEST_IPV6, mirror=mirror) + assert str(mirrorrsync) == TEST_IPV6 + mirrorrsync.delete() - def test_ipv4(self): - mirrorrsync = MirrorRsync.objects.create(ip=TEST_IPV4, mirror=self.mirror) - self.assertEqual(str(mirrorrsync), TEST_IPV4) - mirrorrsync.delete() - def test_invalid(self): - with self.assertRaises(ValueError) as e: - MirrorRsync.objects.create(ip="8.8.8.8.8", mirror=self.mirror) - self.assertIn('IPv4 Address with more than 4 bytes', str(e.exception)) +def test_ipv4(mirror): + mirrorrsync = MirrorRsync.objects.create(ip=TEST_IPV4, mirror=mirror) + assert str(mirrorrsync) == TEST_IPV4 + mirrorrsync.delete() + + +def test_invalid(mirror): + with pytest.raises(ValueError) as e: + MirrorRsync.objects.create(ip="8.8.8.8.8", mirror=mirror) + assert 'IPv4 Address with more than 4 bytes' in str(e) From 9e6f7f2b3bc84da22950b5acf0b549046c31f34c Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Sun, 6 Sep 2026 17:16:09 +0200 Subject: [PATCH 07/15] packages: Port test_populate_signoffs to pytest --- packages/tests/test_populate_signoffs.py | 53 ++++++++++++------------ 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/packages/tests/test_populate_signoffs.py b/packages/tests/test_populate_signoffs.py index db55d2ba9..4224e2dbc 100644 --- a/packages/tests/test_populate_signoffs.py +++ b/packages/tests/test_populate_signoffs.py @@ -1,43 +1,42 @@ from datetime import datetime, timezone from unittest import mock +import pytest from django.core.management import call_command -from django.test import TransactionTestCase import packages.management.commands.populate_signoffs # noqa from main.models import Arch, Repo from packages.models import Package, SignoffSpecification -class RematchDeveloperTest(TransactionTestCase): - fixtures = ['main/fixtures/arches.json', 'main/fixtures/repos.json'] +@pytest.fixture +def package(transactional_db, arches, repos): + repo = Repo.objects.get(name='Extra-Testing') + arch = Arch.objects.get(name__iexact='any') + now = datetime.now(tz=timezone.utc) + package = Package.objects.create(arch=arch, repo=repo, pkgname='systemd', + pkgbase='systemd', pkgver='0.1', + pkgrel='1', pkgdesc='Linux kernel', + compressed_size=10, installed_size=20, + last_update=now, created=now) + yield package + package.delete() - def setUp(self): - repo = Repo.objects.get(name='Extra-Testing') - arch = Arch.objects.get(name__iexact='any') - now = datetime.now(tz=timezone.utc) - self.package = Package.objects.create(arch=arch, repo=repo, pkgname='systemd', - pkgbase='systemd', pkgver='0.1', - pkgrel='1', pkgdesc='Linux kernel', - compressed_size=10, installed_size=20, - last_update=now, created=now) - def tearDown(self): - self.package.delete() +def test_basic(package): + with mock.patch('packages.management.commands.populate_signoffs.get_tag_info') as get_tag_info: + comment = 'upgpkg: 0.1-1: rebuild' + get_tag_info.return_value = {'message': f'{comment}\n', 'author': 'foo@archlinux.org'} + call_command('populate_signoffs') - def test_basic(self): - with mock.patch('packages.management.commands.populate_signoffs.get_tag_info') as get_tag_info: - comment = 'upgpkg: 0.1-1: rebuild' - get_tag_info.return_value = {'message': f'{comment}\n', 'author': 'foo@archlinux.org'} - call_command('populate_signoffs') + signoff_spec = SignoffSpecification.objects.first() + assert signoff_spec.comments == comment + assert signoff_spec.pkgbase == package.pkgbase - signoff_spec = SignoffSpecification.objects.first() - assert signoff_spec.comments == comment - assert signoff_spec.pkgbase == self.package.pkgbase - def test_invalid(self): - with mock.patch('packages.management.commands.populate_signoffs.get_tag_info') as get_tag_info: - get_tag_info.return_value = None - call_command('populate_signoffs') +def test_invalid(package): + with mock.patch('packages.management.commands.populate_signoffs.get_tag_info') as get_tag_info: + get_tag_info.return_value = None + call_command('populate_signoffs') - assert SignoffSpecification.objects.count() == 0 + assert SignoffSpecification.objects.count() == 0 From febb1661b4d090d65fbcc268eb8fa275ef5334a0 Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Sun, 6 Sep 2026 17:38:19 +0200 Subject: [PATCH 08/15] devel: Port test_user to pytest --- devel/tests/test_user.py | 276 +++++++++++++++++++++------------------ 1 file changed, 147 insertions(+), 129 deletions(-) diff --git a/devel/tests/test_user.py b/devel/tests/test_user.py index aec02c0df..a76fec7a8 100644 --- a/devel/tests/test_user.py +++ b/devel/tests/test_user.py @@ -1,137 +1,155 @@ +import pytest from django.contrib.auth.models import User -from django.test import TestCase from devel.models import UserProfile from devel.utils import UserFinder -class DevelTest(TestCase): - def test_index(self): - response = self.client.get('/devel/') - self.assertEqual(response.status_code, 302) - self.assertEqual(response.has_header('Location'), True) - self.assertEqual(response['location'], - '/login/?next=/devel/') - - def test_profile(self): - response = self.client.get('/devel/profile/') - self.assertEqual(response.status_code, 302) - self.assertEqual(response.has_header('Location'), True) - self.assertEqual(response['location'], - '/login/?next=/devel/profile/') - - def test_newuser(self): - response = self.client.get('/devel/newuser/') - self.assertEqual(response.status_code, 302) - self.assertEqual(response.has_header('Location'), True) - self.assertEqual(response['location'], - '/login/?next=/devel/newuser/') - - def test_mirrors(self): - response = self.client.get('/mirrors/') - self.assertEqual(response.status_code, 200) - - def test_admin_log(self): - User.objects.create_superuser('admin', 'admin@archlinux.org', 'admin') - response = self.client.post('/login/', {'username': 'admin', 'password': 'admin'}) - response = self.client.get('/devel/admin_log', follow=True) - self.assertEqual(response.status_code, 200) - - -class FindUserTest(TestCase): - - def setUp(self): - self.finder = UserFinder() - - self.user1 = User.objects.create( - username="joeuser", first_name="Joe", last_name="User", email="user1@example.com") - self.user2 = User.objects.create( - username="john", first_name="John", last_name="", email="user2@example.com") - self.user3 = User.objects.create( - username="bjones", first_name="Bob", last_name="Jones", email="user3@example.com") - - for user in (self.user1, self.user2, self.user3): - email_addr = "%s@awesome.com" % user.username - UserProfile.objects.create(user=user, public_email=email_addr) - - self.user4 = User.objects.create( - username="tim1", first_name="Tim", last_name="One", email="tim@example.com") - self.user5 = User.objects.create( - username="tim2", first_name="Tim", last_name="Two", email="timtwo@example.com") - - def test_not_matching(self): - self.assertIsNone(self.finder.find(None)) - self.assertIsNone(self.finder.find("")) - self.assertIsNone(self.finder.find("Bogus")) - self.assertIsNone(self.finder.find("Bogus ")) - self.assertIsNone(self.finder.find("")) - self.assertIsNone(self.finder.find("bogus@example.com")) - self.assertIsNone(self.finder.find("Unknown Packager")) - - def test_by_email(self): - self.assertEqual( - self.user1, self.finder.find("XXX YYY ")) - self.assertEqual( - self.user2, self.finder.find("YYY ZZZ ")) - - def test_by_profile_email(self): - self.assertEqual( - self.user1, self.finder.find("XXX ")) - self.assertEqual( - self.user2, self.finder.find("YYY ")) - self.assertEqual( - self.user3, self.finder.find("ZZZ ")) - - def test_by_name(self): - self.assertEqual( - self.user1, self.finder.find("Joe User ")) - self.assertEqual( - self.user1, self.finder.find("Joe User")) - self.assertEqual( - self.user2, self.finder.find("John ")) - self.assertEqual( - self.user2, self.finder.find("John")) - self.assertEqual( - self.user3, self.finder.find("Bob Jones ")) - - def test_by_invalid(self): - self.assertEqual( - self.user1, self.finder.find("Joe User ")) - self.assertEqual( - self.user3, self.finder.find("Bob Jones ")) - - def test_ambiguous(self): - self.assertEqual( - self.user4, self.finder.find("Tim One ")) - self.assertEqual( - self.user5, self.finder.find("Tim Two ")) - self.assertIsNone(self.finder.find("Tim ")) - - def test_find_by_username(self): - self.assertEqual(self.finder.find_by_username(None), None) - self.assertEqual(self.finder.find_by_username('noone'), None) - self.assertEqual(self.finder.find_by_username(self.user1.username), self.user1) - # Test cache - self.assertEqual(self.finder.find_by_username(self.user1.username), self.user1) - - def test_find_by_email(self): - self.assertEqual(self.finder.find_by_email(None), None) - self.assertEqual(self.finder.find_by_email('bar@bar.com'), None) - self.assertEqual(self.finder.find_by_email(self.user1.email), self.user1) - # Test cache - self.assertEqual(self.finder.find_by_email(self.user1.email), self.user1) +def test_index(client): + response = client.get('/devel/') + assert response.status_code == 302 + assert response.has_header('Location') + assert response['location'] == '/login/?next=/devel/' + + +def test_profile(client): + response = client.get('/devel/profile/') + assert response.status_code == 302 + assert response.has_header('Location') + assert response['location'] == '/login/?next=/devel/profile/' + + +def test_newuser(client): + response = client.get('/devel/newuser/') + assert response.status_code == 302 + assert response.has_header('Location') + assert response['location'] == '/login/?next=/devel/newuser/' + + +def test_mirrors(db, client): + response = client.get('/mirrors/') + assert response.status_code == 200 + + +def test_admin_log(db, client): + User.objects.create_superuser('admin', 'admin@archlinux.org', 'admin') + response = client.post('/login/', {'username': 'admin', 'password': 'admin'}) + response = client.get('/devel/admin_log', follow=True) + assert response.status_code == 200 + + +@pytest.fixture +def finder(): + return UserFinder() + + +@pytest.fixture +def users(transactional_db): + users = [] + user_profiles = [] + + user1 = User.objects.create( + username="joeuser", first_name="Joe", last_name="User", email="user1@example.com") + users.append(user1) + user2 = User.objects.create( + username="john", first_name="John", last_name="", email="user2@example.com") + users.append(user2) + user3 = User.objects.create( + username="bjones", first_name="Bob", last_name="Jones", email="user3@example.com") + users.append(user3) + + for user in (user1, user2, user3): + email_addr = "%s@awesome.com" % user.username + user_profiles.append(UserProfile.objects.create(user=user, public_email=email_addr)) + + user4 = User.objects.create( + username="tim1", first_name="Tim", last_name="One", email="tim@example.com") + users.append(user4) + user5 = User.objects.create( + username="tim2", first_name="Tim", last_name="Two", email="timtwo@example.com") + users.append(user5) + + yield users + + for user_profile in user_profiles: + user_profile.delete() + + for user in users: + user.delete() + + +def test_not_matching(finder, users): + assert finder.find(None) is None + assert finder.find("") is None + assert finder.find("Bogus") is None + assert finder.find("Bogus ") is None + assert finder.find("") is None + assert finder.find("bogus@example.com") is None + assert finder.find("Unknown Packager") is None + + +def test_by_email(finder, users): + user1, user2, *_ = users + assert finder.find("XXX YYY ") == user1 + assert finder.find("YYY ZZZ ") == user2 + + +def test_by_profile_email(finder, users): + user1, user2, user3, *_ = users + assert finder.find("XXX ") == user1 + assert finder.find("YYY ") == user2 + assert finder.find("ZZZ ") == user3 + + +def test_by_name(finder, users): + user1, user2, user3, *_ = users + assert finder.find("Joe User ") == user1 + assert finder.find("Joe User") == user1 + assert finder.find("John ") == user2 + assert finder.find("John") == user2 + assert finder.find("Bob Jones ") == user3 + + +def test_by_invalid(finder, users): + user1, *_ = users + assert finder.find("Joe User ") == user1 + assert finder.find("Bob Jones ") == user3 + + +def test_ambiguous(finder, users): + _user1, _user2, _user3, user4, user5 = users + assert finder.find("Tim One ") == user4 + assert finder.find("Tim Two ") == user5 + assert finder.find("Tim ") is None + + +def test_find_by_username(finder, users): + user1, *_ = users + assert finder.find_by_username(None) is None + assert finder.find_by_username('noone') is None + assert finder.find_by_username(user1.username) == user1 + # Test cache + assert finder.find_by_username(user1.username) == user1 + + +def test_find_by_email(finder, users): + user1, *_ = users + assert finder.find_by_email(None) is None + assert finder.find_by_email('bar@bar.com') is None + assert finder.find_by_email(user1.email) == user1 + # Test cache + assert finder.find_by_email(user1.email) == user1 # vim: set ts=4 sw=4 et: From ebf0949dfe66071ebd46478842082bbb6610a766 Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Sun, 6 Sep 2026 17:39:42 +0200 Subject: [PATCH 09/15] devel: Port test_templatetags to pytest --- devel/tests/test_templatetags.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/devel/tests/test_templatetags.py b/devel/tests/test_templatetags.py index e5d9782af..dbef240e7 100644 --- a/devel/tests/test_templatetags.py +++ b/devel/tests/test_templatetags.py @@ -1,11 +1,10 @@ from django.contrib.auth.models import User -from django.test import TestCase from devel.templatetags.group import in_group -class DevelTemplatetagsTest(TestCase): - def test_in_group(self): - user = User.objects.create(username="joeuser", first_name="Joe", - last_name="User", email="user1@example.com") - self.assertEqual(in_group(user, 'none'), False) +def test_in_group(db): + user = User.objects.create(username="joeuser", first_name="Joe", + last_name="User", email="user1@example.com") + assert in_group(user, 'none') is False + user.delete() From e29e9760934131644cd25e548c6870c889c85b24 Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Sun, 6 Sep 2026 20:26:08 +0200 Subject: [PATCH 10/15] devel: Port test_pgp_import to pytest --- devel/tests/test_pgp_import.py | 51 +++++++++++++++++----------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/devel/tests/test_pgp_import.py b/devel/tests/test_pgp_import.py index 376ec0b0b..54f8d0a86 100644 --- a/devel/tests/test_pgp_import.py +++ b/devel/tests/test_pgp_import.py @@ -1,8 +1,8 @@ from unittest.mock import patch +import pytest from django.core.management import call_command from django.core.management.base import CommandError -from django.test import TransactionTestCase CREATED = 1541685162 USER = 'John Doe ' @@ -18,28 +18,27 @@ ] -class PGPImportTest(TransactionTestCase): - fixtures = ['main/fixtures/arches.json', 'main/fixtures/repos.json'] - - def test_pgp_import_error(self): - with self.assertRaises(CommandError) as e: - call_command('pgp_import') - self.assertIn('keyring_path', str(e.exception)) - - @patch('devel.management.commands.pgp_import.call_gpg') - def test_pgp_import_garbage_data(self, mock_call_gpg): - mock_call_gpg.return_value = 'barf' - with patch('devel.management.commands.pgp_import.logger') as logger: - call_command('pgp_import', '/tmp') - logger.info.assert_called() - logger.info.assert_any_call('created %d, updated %d signatures', 0, 0) - logger.info.assert_any_call('created %d, updated %d keys', 0, 0) - - @patch('devel.management.commands.pgp_import.call_gpg') - def test_pgp_import(self, mock_call_gpg): - mock_call_gpg.return_value = '\n'.join(SIG_DATA) - with patch('devel.management.commands.pgp_import.logger') as logger: - call_command('pgp_import', '/tmp') - logger.info.assert_called() - logger.info.assert_any_call('created %d, updated %d signatures', 0, 0) - logger.info.assert_any_call('created %d, updated %d keys', 1, 0) +def test_pgp_import_error(): + with pytest.raises(CommandError) as e: + call_command('pgp_import') + assert 'keyring_path' in str(e) + + +@patch('devel.management.commands.pgp_import.call_gpg') +def test_pgp_import_garbage_data(mock_call_gpg, arches, repos): + mock_call_gpg.return_value = 'barf' + with patch('devel.management.commands.pgp_import.logger') as logger: + call_command('pgp_import', '/tmp') + logger.info.assert_called() + logger.info.assert_any_call('created %d, updated %d signatures', 0, 0) + logger.info.assert_any_call('created %d, updated %d keys', 0, 0) + + +@patch('devel.management.commands.pgp_import.call_gpg') +def test_pgp_import(mock_call_gpg, arches, repos): + mock_call_gpg.return_value = '\n'.join(SIG_DATA) + with patch('devel.management.commands.pgp_import.logger') as logger: + call_command('pgp_import', '/tmp') + logger.info.assert_called() + logger.info.assert_any_call('created %d, updated %d signatures', 0, 0) + logger.info.assert_any_call('created %d, updated %d keys', 1, 0) From d4c3cb4546c1aae58d8d7a134fe133f256aa00c6 Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Sun, 6 Sep 2026 20:27:12 +0200 Subject: [PATCH 11/15] devel: Port test_rematch_developers to pytest --- devel/tests/test_rematch_developers.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/devel/tests/test_rematch_developers.py b/devel/tests/test_rematch_developers.py index da1aeb5b4..8dbbdfdff 100644 --- a/devel/tests/test_rematch_developers.py +++ b/devel/tests/test_rematch_developers.py @@ -1,13 +1,9 @@ from unittest.mock import patch from django.core.management import call_command -from django.test import TransactionTestCase -class RematchDeveloperTest(TransactionTestCase): - fixtures = ['main/fixtures/arches.json', 'main/fixtures/repos.json'] - - def test_rematch_developers(self): - with patch('devel.management.commands.rematch_developers.logger') as logger: - call_command('rematch_developers') - logger.info.assert_called() +def test_rematch_developers(arches, repos): + with patch('devel.management.commands.rematch_developers.logger') as logger: + call_command('rematch_developers') + logger.info.assert_called() From eb8fa97de8e55f76a773d1844645c215d92bc5f9 Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Sun, 6 Sep 2026 20:31:10 +0200 Subject: [PATCH 12/15] devel: Port test_devel to pytest --- devel/tests/test_devel.py | 76 +++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/devel/tests/test_devel.py b/devel/tests/test_devel.py index 3b2fd4fe9..b55531c91 100644 --- a/devel/tests/test_devel.py +++ b/devel/tests/test_devel.py @@ -1,43 +1,43 @@ +import pytest from django.contrib.auth.models import Group, User -from django.test import TransactionTestCase from devel.models import UserProfile -class DevelView(TransactionTestCase): - fixtures = ['main/fixtures/arches.json', 'main/fixtures/repos.json', - 'main/fixtures/package.json'] - - def setUp(self): - password = 'test' - self.user = User.objects.create_superuser('admin', - 'admin@archlinux.org', - password) - for name in ['Developers', 'Retired Developers']: - Group.objects.create(name=name) - self.user.groups.add(Group.objects.get(name='Developers')) - self.user.save() - self.profile = UserProfile.objects.create(user=self.user, - public_email=f"{self.user.username}@awesome.com") - self.client.post('/login/', { - 'username': self.user.username, - 'password': password - }) - - def tearDown(self): - self.profile.delete() - self.user.delete() - Group.objects.all().delete() - - def test_clock(self): - response = self.client.get('/devel/clock/') - self.assertEqual(response.status_code, 200) - - def test_profile(self): - response = self.client.get('/devel/profile/') - self.assertEqual(response.status_code, 200) - # Test changing - - def test_stats(self): - response = self.client.get('/devel/stats/') - self.assertEqual(response.status_code, 200) +@pytest.fixture +def devel_client(client, arches, repos, package): + password = 'test' + user = User.objects.create_superuser('admin', + 'admin@archlinux.org', + password) + for name in ['Developers', 'Retired Developers']: + Group.objects.create(name=name) + user.groups.add(Group.objects.get(name='Developers')) + user.save() + profile = UserProfile.objects.create(user=user, + public_email=f"{user.username}@awesome.com") + client.post('/login/', { + 'username': user.username, + 'password': password + }) + + yield client + + profile.delete() + user.delete() + Group.objects.all().delete() + + +def test_clock(devel_client): + response = devel_client.get('/devel/clock/') + assert response.status_code == 200 + + +def test_profile(devel_client): + response = devel_client.get('/devel/profile/') + assert response.status_code == 200 + + +def test_stats(devel_client): + response = devel_client.get('/devel/stats/') + assert response.status_code == 200 From d33cb119d181a77e47629da594839864176f02fc Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Sun, 6 Sep 2026 20:42:47 +0200 Subject: [PATCH 13/15] devel: Port test_retire_user to pytest --- devel/tests/test_retire_user.py | 101 +++++++++++++++++--------------- 1 file changed, 55 insertions(+), 46 deletions(-) diff --git a/devel/tests/test_retire_user.py b/devel/tests/test_retire_user.py index 8ff3fe050..06fb7c73d 100644 --- a/devel/tests/test_retire_user.py +++ b/devel/tests/test_retire_user.py @@ -1,66 +1,75 @@ +import pytest from django.contrib.auth.models import Group, User from django.core.management import call_command from django.core.management.base import CommandError -from django.test import TransactionTestCase from devel.models import UserProfile from main.models import Repo -class RetireUsertest(TransactionTestCase): - fixtures = ['main/fixtures/arches.json', 'main/fixtures/repos.json'] +@pytest.fixture +def groups(): + for name in ['Developers', 'Retired Developers']: + Group.objects.create(name=name) - def setUp(self): - self.username = 'joe' - self.user = User.objects.create(username=self.username, first_name="Joe", - last_name="User", email="user1@example.com") - self.profile = UserProfile.objects.create(user=self.user, - public_email=f"{self.user.username}@awesome.com") - for name in ['Developers', 'Retired Developers']: - Group.objects.create(name=name) +@pytest.fixture +def test_user(arches, repos, groups): + username = 'joe' + user = User.objects.create(username=username, first_name="Joe", + last_name="User", email="user1@example.com") - def tearDown(self): - self.profile.delete() - self.user.delete() + profile = UserProfile.objects.create(user=user, + public_email=f"{user.username}@awesome.com") - def test_invalid_args(self): - with self.assertRaises(CommandError) as e: - call_command('retire_user') - self.assertIn('missing argument user.', str(e.exception)) + yield user - def test_user_not_found(self): - with self.assertRaises(CommandError) as e: - call_command('retire_user', 'user1') - self.assertIn("Failed to find User 'user1'", str(e.exception)) + profile.delete() + user.delete() - def test_userprofile_missing(self): - user = User.objects.create(username='user2', first_name="Jane", - last_name="User2", email="user2@example.com") - with self.assertRaises(CommandError) as e: - call_command('retire_user', user.username) - self.assertIn("Failed to find UserProfile", str(e.exception)) - user.delete() +def test_invalid_args(): + with pytest.raises(CommandError) as e: + call_command('retire_user') + assert 'missing argument user.' in str(e) - def test_user_inactive(self): - call_command('retire_user', self.username) - user = User.objects.get(username=self.username) - self.assertEqual(user.is_active, False) - def test_user_moved_groups(self): - self.user.groups.add(Group.objects.get(name='Developers')) - self.user.save() +def test_user_not_found(db): + with pytest.raises(CommandError) as e: + call_command('retire_user', 'user1') + assert "Failed to find User 'user1'" in str(e) - call_command('retire_user', self.username) - user = User.objects.get(username=self.username) - groups = [Group.objects.get(name='Retired Developers')] - self.assertEqual(list(user.groups.all()), groups) - def test_user_repos(self): - self.profile.allowed_repos.add(Repo.objects.get(name='Core')) - self.profile.save() +def test_userprofile_missing(db): + user = User.objects.create(username='user2', first_name="Jane", + last_name="User2", email="user2@example.com") - call_command('retire_user', self.username) - profile = UserProfile.objects.get(user=self.user) - self.assertEqual(len(profile.allowed_repos.all()), 0) + with pytest.raises(CommandError) as e: + call_command('retire_user', user.username) + assert "Failed to find UserProfile" in str(e) + user.delete() + + +def test_user_inactive(test_user): + call_command('retire_user', test_user.username) + user = User.objects.get(username=test_user.username) + assert not user.is_active + + +def test_user_moved_groups(test_user): + test_user.groups.add(Group.objects.get(name='Developers')) + test_user.save() + + call_command('retire_user', test_user.username) + user = User.objects.get(username=test_user.username) + groups = [Group.objects.get(name='Retired Developers')] + assert list(user.groups.all()) == groups + + +def test_user_repos(test_user): + test_user.userprofile.allowed_repos.add(Repo.objects.get(name='Core')) + test_user.userprofile.save() + + call_command('retire_user', test_user.username) + profile = UserProfile.objects.get(user=test_user) + assert len(profile.allowed_repos.all()) == 0 From 78755ec7c05016b0108dff318aa87e43606afec6 Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Sun, 6 Sep 2026 20:50:43 +0200 Subject: [PATCH 14/15] devel: Port test_reports to pytest --- devel/tests/test_reports.py | 207 +++++++++++++++++++----------------- 1 file changed, 112 insertions(+), 95 deletions(-) diff --git a/devel/tests/test_reports.py b/devel/tests/test_reports.py index 62729deb7..c942c5dd3 100644 --- a/devel/tests/test_reports.py +++ b/devel/tests/test_reports.py @@ -1,104 +1,121 @@ +import pytest from django.contrib.auth.models import User -from django.test import TransactionTestCase from devel.reports import Linkify from packages.models import PackageRelation -class DeveloperReport(TransactionTestCase): - fixtures = ['main/fixtures/arches.json', 'main/fixtures/repos.json', - 'main/fixtures/package.json'] - - def setUp(self): - password = 'test' - self.user = User.objects.create_superuser('admin', - 'admin@archlinux.org', - password) - self.client.post('/login/', { - 'username': self.user.username, - 'password': password - }) - - def tearDown(self): - self.user.delete() - - def test_overview(self): - response = self.client.get('/devel/') - self.assertEqual(response.status_code, 200) - - def test_reports_old(self): - response = self.client.get('/devel/reports/old', follow=True) - self.assertEqual(response.status_code, 200) - - def test_reports_outofdate(self): - response = self.client.get('/devel/reports/long-out-of-date', follow=True) - self.assertEqual(response.status_code, 200) - - def test_reports_big(self): - response = self.client.get('/devel/reports/big', follow=True) - self.assertEqual(response.status_code, 200) - - def test_reports_badcompression(self): - response = self.client.get('/devel/reports/badcompression', follow=True) - self.assertEqual(response.status_code, 200) - - def test_reports_uncompressed_man(self): - response = self.client.get('/devel/reports/uncompressed-man', follow=True) - self.assertEqual(response.status_code, 200) - - def test_reports_uncompressed_info(self): - response = self.client.get('/devel/reports/uncompressed-info', follow=True) - self.assertEqual(response.status_code, 200) - - def test_reports_unneeded_orphans(self): - response = self.client.get('/devel/reports/unneeded-orphans', follow=True) - self.assertEqual(response.status_code, 200) - - def test_reports_mismatched_signature(self): - response = self.client.get('/devel/reports/mismatched-signature', follow=True) - self.assertEqual(response.status_code, 200) - - def test_reports_signature_time(self): - response = self.client.get('/devel/reports/signature-time', follow=True) - self.assertEqual(response.status_code, 200) - - def test_reports_pkgbases(self): - response = self.client.get('/devel/reports/old/pkgbases/') - self.assertEqual(response.status_code, 200) - self.assertEqual(response['Content-Type'], 'text/plain') - - def test_reports_pkgbases_with_username(self): - response = self.client.get( - f'/devel/reports/uncompressed-man/{self.user.username}/pkgbases/') - self.assertEqual(response.status_code, 200) - self.assertEqual(response['Content-Type'], 'text/plain') - - def test_reports_pkgbases_invalid_report(self): - response = self.client.get('/devel/reports/nonexistent/pkgbases/') - self.assertEqual(response.status_code, 404) - - def test_report_filtered_by_maintainer(self): - PackageRelation.objects.create( - pkgbase='linux', - user=self.user, - type=PackageRelation.MAINTAINER, - ) - response = self.client.get( - f'/devel/reports/old/{self.user.username}/', follow=True) - self.assertEqual(response.status_code, 200) - pkgbases = {pkg.pkgbase for pkg in response.context['packages']} - self.assertEqual(pkgbases, {'linux'}) - - def test_report_pkgbases_filtered_by_maintainer(self): - PackageRelation.objects.create( - pkgbase='linux', - user=self.user, - type=PackageRelation.MAINTAINER, - ) - response = self.client.get( - f'/devel/reports/old/{self.user.username}/pkgbases/') - self.assertEqual(response.status_code, 200) - self.assertEqual(response.content.decode().strip(), 'linux') +@pytest.fixture +def devel_client(client, arches, repos, package): + password = 'test' + user = User.objects.create_superuser('admin', + 'admin@archlinux.org', + password) + client.post('/login/', { + 'username': user.username, + 'password': password + }) + + yield client + + user.delete() + + +def test_overview(devel_client): + response = devel_client.get('/devel/') + assert response.status_code == 200 + + +def test_reports_old(devel_client): + response = devel_client.get('/devel/reports/old', follow=True) + assert response.status_code == 200 + + +def test_reports_outofdate(devel_client): + response = devel_client.get('/devel/reports/long-out-of-date', follow=True) + assert response.status_code == 200 + + +def test_reports_big(devel_client): + response = devel_client.get('/devel/reports/big', follow=True) + assert response.status_code == 200 + + +def test_reports_badcompression(devel_client): + response = devel_client.get('/devel/reports/badcompression', follow=True) + assert response.status_code == 200 + + +def test_reports_uncompressed_man(devel_client): + response = devel_client.get('/devel/reports/uncompressed-man', follow=True) + assert response.status_code == 200 + + +def test_reports_uncompressed_info(devel_client): + response = devel_client.get('/devel/reports/uncompressed-info', follow=True) + assert response.status_code == 200 + + +def test_reports_unneeded_orphans(devel_client): + response = devel_client.get('/devel/reports/unneeded-orphans', follow=True) + assert response.status_code == 200 + + +def test_reports_mismatched_signature(devel_client): + response = devel_client.get('/devel/reports/mismatched-signature', follow=True) + assert response.status_code == 200 + + +def test_reports_signature_time(devel_client): + response = devel_client.get('/devel/reports/signature-time', follow=True) + assert response.status_code == 200 + + +def test_reports_pkgbases(devel_client): + response = devel_client.get('/devel/reports/old/pkgbases/') + assert response.status_code == 200 + assert response['Content-Type'] == 'text/plain' + + +def test_reports_pkgbases_with_username(devel_client): + response = devel_client.get('/devel/reports/uncompressed-man/admin/pkgbases/') + assert response.status_code == 200 + assert response['Content-Type'] == 'text/plain' + + +def test_reports_pkgbases_invalid_report(devel_client): + response = devel_client.get('/devel/reports/nonexistent/pkgbases/') + assert response.status_code == 404 + + +def test_report_filtered_by_maintainer(devel_client): + user = User.objects.get(username='admin') + PackageRelation.objects.create( + pkgbase='linux', + user=user, + type=PackageRelation.MAINTAINER, + ) + + response = devel_client.get( + f'/devel/reports/old/{user.username}/', follow=True) + assert response.status_code == 200 + + pkgbases = {pkg.pkgbase for pkg in response.context['packages']} + assert pkgbases == {'linux'} + + +def test_report_pkgbases_filtered_by_maintainer(devel_client): + user = User.objects.get(username='admin') + PackageRelation.objects.create( + pkgbase='linux', + user=user, + type=PackageRelation.MAINTAINER, + ) + + response = devel_client.get( + f'/devel/reports/old/{user.username}/pkgbases/') + assert response.status_code == 200 + assert response.content.decode().strip() == 'linux' def test_linkify_escapes_html(): From f36af27c2ba1db54409d1a752e646cdbfc2a1561 Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Sun, 6 Sep 2026 21:03:09 +0200 Subject: [PATCH 15/15] devel: Port test_reporead to pytest --- devel/tests/test_reporead.py | 149 +++++++++++++++++++---------------- 1 file changed, 80 insertions(+), 69 deletions(-) diff --git a/devel/tests/test_reporead.py b/devel/tests/test_reporead.py index 810388cc4..ab463cb65 100644 --- a/devel/tests/test_reporead.py +++ b/devel/tests/test_reporead.py @@ -2,19 +2,19 @@ from datetime import datetime, timezone from unittest.mock import patch +import pytest from django.core.management import call_command from django.core.management.base import CommandError -from django.test import TransactionTestCase from main.models import Arch, Package, Repo from packages.models import FlagRequest -# Django's TestCase is wrapped in transaction, therefore use TransactionTestCase -class RepoReadTest(TransactionTestCase): - fixtures = ['main/fixtures/arches.json', 'main/fixtures/repos.json'] +@pytest.fixture +def create_pkg(arches, repos): + packages = [] - def create_pkg(self, repo=None, pkgver='4.10.1', pkgrel='1'): + def _create_pkg(repo=None, pkgver='4.10.1', pkgrel='1'): if not repo: repo = Repo.objects.get(name__iexact='core') @@ -26,67 +26,78 @@ def create_pkg(self, repo=None, pkgver='4.10.1', pkgrel='1'): compressed_size=10, installed_size=20, last_update=now, created=now) - def test_invalid_args(self): - with self.assertRaises(CommandError) as e: - call_command('reporead') - self.assertIn('missing arch and file.', str(e.exception)) - - with self.assertRaises(CommandError) as e: - call_command('reporead', 'x86_64') - self.assertIn('Package database file is required.', str(e.exception)) - - with self.assertRaises(CommandError) as e: - call_command('reporead', 'x86_64', 'nothing.db.tar.gz') - self.assertIn('Specified package database file does not exist.', str(e.exception)) - - def test_invalid_arch(self): - with self.assertRaises(CommandError) as e: - call_command('reporead', 'armv64', 'devel/fixtures/core.db.tar.gz') - self.assertEqual('Specified architecture armv64 is not currently known.', str(e.exception)) - - def test_read_packages(self): - with patch('devel.management.commands.reporead.logger') as logger: - call_command('reporead', 'x86_64', 'devel/fixtures/core.db.tar.gz') - logger.info.assert_called() - - # Verify contents - with tarfile.open('devel/fixtures/core.db.tar.gz') as tar: - files = [name.replace('core.db/', '') for name in tar.getnames() - if name != 'core.db' and 'desc' not in name] - - packages = Package.objects.all() - import_packages = [f"{pkg.pkgname}-{pkg.pkgver}-{pkg.pkgrel}" for pkg in packages] - self.assertCountEqual(files, import_packages) - - def test_flagoutofdate(self): - pkg = self.create_pkg() - FlagRequest.objects.create(pkgbase=pkg.pkgbase, repo=pkg.repo, - pkgver=pkg.pkgver, epoch=pkg.epoch, - ip_address='1.1.1.1') - - with patch('devel.management.commands.reporead.logger') as logger: - call_command('reporead', 'x86_64', 'devel/fixtures/core.db.tar.gz') - logger.info.assert_called() - - self.assertEqual(len(FlagRequest.objects.all()), 0) - - def test_flagoutofdate_staging(self): - staging = Repo.objects.get(name__iexact='staging') - - pkg = self.create_pkg() - staging_pkg = self.create_pkg(repo=staging, pkgrel='2') - - FlagRequest.objects.create(pkgbase=pkg.pkgbase, repo=pkg.repo, - pkgver=pkg.pkgver, epoch=pkg.epoch, - ip_address='1.1.1.1') - FlagRequest.objects.create(pkgbase=staging_pkg.pkgbase, repo=staging_pkg.repo, - pkgver=staging_pkg.pkgver, epoch=staging_pkg.epoch, - ip_address='1.1.1.1') - - with patch('devel.management.commands.reporead.logger') as logger: - call_command('reporead', 'x86_64', 'devel/fixtures/core.db.tar.gz') - logger.info.assert_called() - - objects = FlagRequest.objects.all() - self.assertEqual(len(objects), 1) - self.assertEqual(objects[0].pkgver, staging_pkg.pkgver) + yield _create_pkg + + for pkg in packages: + pkg.delete() + + +def test_invalid_arch(arches): + with pytest.raises(CommandError) as exc_info: + call_command('reporead', 'armv64', 'devel/fixtures/core.db.tar.gz') + assert exc_info.value.args[0] == 'Specified architecture armv64 is not currently known.' + + +def test_invalid_args(arches): + with pytest.raises(CommandError) as exc_info: + call_command('reporead') + assert 'missing arch and file' in str(exc_info) + + with pytest.raises(CommandError) as exc_info: + call_command('reporead', 'x86_64') + assert 'Package database file is required' in str(exc_info) + + with pytest.raises(CommandError) as exc_info: + call_command('reporead', 'x86_64', 'nothing.db.tar.gz') + assert 'Specified package database file does not exist.' in str(exc_info) + + +def test_read_packages(transactional_db, arches, repos): + # TODO: use pytest-pacman for generating the database + with patch('devel.management.commands.reporead.logger') as logger: + call_command('reporead', 'x86_64', 'devel/fixtures/core.db.tar.gz') + logger.info.assert_called() + + # Verify contents + with tarfile.open('devel/fixtures/core.db.tar.gz') as tar: + files = [name.replace('core.db/', '') for name in tar.getnames() + if name != 'core.db' and 'desc' not in name] + + packages = Package.objects.all() + import_packages = [f"{pkg.pkgname}-{pkg.pkgver}-{pkg.pkgrel}" for pkg in packages] + assert len(files) == len(import_packages) + + +def test_flagoutofdate(transactional_db, arches, repos, create_pkg): + pkg = create_pkg() + FlagRequest.objects.create(pkgbase=pkg.pkgbase, repo=pkg.repo, + pkgver=pkg.pkgver, epoch=pkg.epoch, + ip_address='1.1.1.1') + + with patch('devel.management.commands.reporead.logger') as logger: + call_command('reporead', 'x86_64', 'devel/fixtures/core.db.tar.gz') + logger.info.assert_called() + + assert len(FlagRequest.objects.all()) == 0 + + +def test_flagoutofdate_staging(transactional_db, arches, repos, create_pkg): + staging = Repo.objects.get(name__iexact='staging') + + pkg = create_pkg() + staging_pkg = create_pkg(repo=staging, pkgrel='2') + + FlagRequest.objects.create(pkgbase=pkg.pkgbase, repo=pkg.repo, + pkgver=pkg.pkgver, epoch=pkg.epoch, + ip_address='1.1.1.1') + FlagRequest.objects.create(pkgbase=staging_pkg.pkgbase, repo=staging_pkg.repo, + pkgver=staging_pkg.pkgver, epoch=staging_pkg.epoch, + ip_address='1.1.1.1') + + with patch('devel.management.commands.reporead.logger') as logger: + call_command('reporead', 'x86_64', 'devel/fixtures/core.db.tar.gz') + logger.info.assert_called() + + objects = FlagRequest.objects.all() + assert len(objects) == 1 + assert objects[0].pkgver == staging_pkg.pkgver