From 5aa358fb41ce325af310da985f5a15d525715db9 Mon Sep 17 00:00:00 2001 From: Shihao Date: Thu, 1 Oct 2026 21:49:54 -0600 Subject: [PATCH] Search pgsql-bugs too when attaching a thread The "Attach thread" search only looked at pgsql-hackers, so a patch for a bug report could only be attached by pasting a message id. Search every list in ARCHIVES_SEARCH_LISTS, which is pgsql-hackers and pgsql-bugs by default, and merge the results. Fixes #117 --- pgcommitfest/commitfest/ajax.py | 12 ++- .../commitfest/tests/test_get_threads.py | 73 +++++++++++++++++++ pgcommitfest/settings.py | 2 + 3 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 pgcommitfest/commitfest/tests/test_get_threads.py diff --git a/pgcommitfest/commitfest/ajax.py b/pgcommitfest/commitfest/ajax.py index 8137dfdf..fa1eac69 100644 --- a/pgcommitfest/commitfest/ajax.py +++ b/pgcommitfest/commitfest/ajax.py @@ -87,13 +87,19 @@ def getThreads(request): else: attachonly = 0 - # Make a JSON api call to the archives server + # Make a JSON api call to the archives server for each list. A message + # that was sent to more than one list is only returned once. params = {"n": 100, "a": attachonly} if search: params["s"] = search - r = _archivesAPI("/list/pgsql-hackers/latest.json", params) - return sorted(r, key=lambda x: x["date"], reverse=True) + messages = {} + for listname in settings.ARCHIVES_SEARCH_LISTS: + for m in _archivesAPI("/list/%s/latest.json" % listname, params): + messages.setdefault(m["msgid"], m) + + r = sorted(messages.values(), key=lambda x: x["date"], reverse=True) + return r[: params["n"]] def getMessages(request): diff --git a/pgcommitfest/commitfest/tests/test_get_threads.py b/pgcommitfest/commitfest/tests/test_get_threads.py new file mode 100644 index 00000000..df240976 --- /dev/null +++ b/pgcommitfest/commitfest/tests/test_get_threads.py @@ -0,0 +1,73 @@ +from django.test import RequestFactory + +from unittest.mock import patch + +from pgcommitfest.commitfest.ajax import getThreads + + +def _msg(msgid, date, subj): + return {"msgid": msgid, "date": date, "from": "a@example.com", "subj": subj} + + +HACKERS = [ + _msg("h1@example.com", "2026-01-03T00:00:00", "hackers thread"), + _msg("both@example.com", "2026-01-01T00:00:00", "sent to both lists"), +] +BUGS = [ + _msg("b1@example.com", "2026-01-02T00:00:00", "BUG #1: bugs thread"), + _msg("both@example.com", "2026-01-01T00:00:00", "sent to both lists"), +] + + +def _fake_archives(suburl, params=None): + return { + "/list/pgsql-hackers/latest.json": HACKERS, + "/list/pgsql-bugs/latest.json": BUGS, + }[suburl] + + +def test_get_threads_searches_hackers_and_bugs(): + """The thread search should return threads from pgsql-bugs too.""" + request = RequestFactory().get("/ajax/getThreads/", {"s": "thread", "a": "1"}) + + with patch( + "pgcommitfest.commitfest.ajax._archivesAPI", side_effect=_fake_archives + ) as api: + result = getThreads(request) + + # Both lists are searched with the same parameters + assert [c.args for c in api.call_args_list] == [ + ("/list/pgsql-hackers/latest.json", {"n": 100, "a": 1, "s": "thread"}), + ("/list/pgsql-bugs/latest.json", {"n": 100, "a": 1, "s": "thread"}), + ] + + # Results are merged newest first, and the message sent to both lists + # shows up only once + assert [m["msgid"] for m in result] == [ + "h1@example.com", + "b1@example.com", + "both@example.com", + ] + + +def test_get_threads_limits_merged_result(): + """Merging two lists should not return more than one list would.""" + hackers = [ + _msg("h%d@example.com" % i, "2026-01-01T00:%02d:00" % (i % 60), "h") + for i in range(100) + ] + bugs = [ + _msg("b%d@example.com" % i, "2026-01-02T00:%02d:00" % (i % 60), "b") + for i in range(100) + ] + + def fake(suburl, params=None): + return hackers if "pgsql-hackers" in suburl else bugs + + request = RequestFactory().get("/ajax/getThreads/") + with patch("pgcommitfest.commitfest.ajax._archivesAPI", side_effect=fake): + result = getThreads(request) + + assert len(result) == 100 + # The bugs messages are all newer, so they are the ones that are kept + assert all(m["msgid"].startswith("b") for m in result) diff --git a/pgcommitfest/settings.py b/pgcommitfest/settings.py index 07e4adaf..cdcd3121 100644 --- a/pgcommitfest/settings.py +++ b/pgcommitfest/settings.py @@ -157,6 +157,8 @@ ARCHIVES_PORT = "8001" ARCHIVES_HOST = "archives.postgresql.org" # Host: header to send ARCHIVES_APIKEY = None +# Mailing lists searched when attaching a thread to a patch +ARCHIVES_SEARCH_LISTS = ["pgsql-hackers", "pgsql-bugs"] # Email address to pgsql-hackers. Set to something local to test maybe? HACKERS_EMAIL = "pgsql-hackers-testing@localhost"