mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-14 07:23:23 -05:00
feat: keep a profile checkable when its forge goes
Four Switch profiles were unverifiable and said so on stderr every pass. yuzu and suyu answer 451, citron's host stopped resolving, and git.eden-emu.dev returns 403 to anything that is not a browser. Each aborted its own report, so nothing could be said about any of them, and the noise repeated on every run over the whole corpus. A withdrawn forge is now a fact rather than a failure. GoneError covers 451, 410 and a host that does not resolve; none is retried, since three attempts with backoff end in the same place. Those profiles land in their own summary bucket, out of the review backlog where nobody could act on them anyway. A 403 stays what it was, a refusal, because small Forgejo instances behind anti-bot filters issue it routinely. A profile can now name a source_mirror, consulted after source and upstream so a live primary always decides attribution. Reaching it took two more changes: a repository that refuses is muted for the rest of the pass instead of ending it, keyed by host as well as slug because a mirror carries the same slug on another forge; and a refused miss is not cached, or the mute would answer for the mirror that was about to be asked. eden now reads from its Codeberg copy, which holds the same head and the pinned commit: 5 refs, all anchored, where the profile could not be checked at all. yuzu, suyu and citron have no mirror that serves content, and now say so once instead of failing loudly.
This commit is contained in:
1 parent
e6d325ed1b
commit
ca307a4ef1
6 files changed
+230
-14
No files matched your search
@@ -7,6 +7,7 @@ import http.client
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import socket
|
||||
import unittest
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
@@ -245,6 +246,73 @@ class TestFetchRetry(unittest.TestCase):
|
||||
self.assertEqual(self.calls, 1)
|
||||
|
||||
|
||||
class TestGoneUpstream(unittest.TestCase):
|
||||
"""A forge that will not come back is a fact, not a failure to retry.
|
||||
|
||||
yuzu and suyu answer 451, citron's host no longer resolves. Retrying
|
||||
those three times with backoff costs a minute per pass and still ends
|
||||
in the same place, and calling them errors buries the profiles that
|
||||
have something to say.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self._orig = (urllib.request.urlopen, upstream._sleep)
|
||||
self.slept: list[float] = []
|
||||
upstream._sleep = self.slept.append
|
||||
self.calls = 0
|
||||
|
||||
def tearDown(self):
|
||||
urllib.request.urlopen, upstream._sleep = self._orig
|
||||
|
||||
def _serve(self, outcome):
|
||||
def opener(req, timeout=None):
|
||||
self.calls += 1
|
||||
raise outcome
|
||||
|
||||
urllib.request.urlopen = opener
|
||||
|
||||
def test_legal_takedown_is_gone_and_not_retried(self):
|
||||
self._serve(_http_error(451))
|
||||
with self.assertRaises(upstream.GoneError):
|
||||
upstream._fetch("https://host/x")
|
||||
self.assertEqual(self.calls, 1)
|
||||
self.assertEqual(self.slept, [])
|
||||
|
||||
def test_http_gone_is_gone(self):
|
||||
self._serve(_http_error(410))
|
||||
with self.assertRaises(upstream.GoneError):
|
||||
upstream._fetch("https://host/x")
|
||||
self.assertEqual(self.calls, 1)
|
||||
|
||||
def test_unresolvable_host_is_gone_and_not_retried(self):
|
||||
self._serve(
|
||||
urllib.error.URLError(socket.gaierror(-2, "Name or service not known"))
|
||||
)
|
||||
with self.assertRaises(upstream.GoneError):
|
||||
upstream._fetch("https://host/x")
|
||||
self.assertEqual(self.calls, 1)
|
||||
self.assertEqual(self.slept, [])
|
||||
|
||||
def test_a_gone_error_is_still_an_upstream_error(self):
|
||||
self._serve(_http_error(451))
|
||||
with self.assertRaises(upstream.UpstreamError):
|
||||
upstream._fetch("https://host/x")
|
||||
|
||||
def test_refused_request_is_not_gone(self):
|
||||
"""403 is a forge refusing a request, which anti-bot filters do."""
|
||||
self._serve(_http_error(403))
|
||||
with self.assertRaises(upstream.UpstreamError) as caught:
|
||||
upstream._fetch("https://host/x")
|
||||
self.assertNotIsInstance(caught.exception, upstream.GoneError)
|
||||
|
||||
def test_a_dropped_connection_is_not_gone(self):
|
||||
self._serve(http.client.RemoteDisconnected("closed"))
|
||||
with self.assertRaises(upstream.UpstreamError) as caught:
|
||||
upstream._fetch("https://host/x")
|
||||
self.assertNotIsInstance(caught.exception, upstream.GoneError)
|
||||
self.assertEqual(self.calls, upstream.RETRIES)
|
||||
|
||||
|
||||
class TestCache(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.TemporaryDirectory()
|
||||
|
||||
Reference in new issue
Block a user