[FIX] test long cache : ne compter vue qu'une URL vraiment livrée

Un même paquet vit sous deux chemins selon le miroir, et la clé du magasin
porte le chemin : la première VM peut recevoir « 504 » sur l'un — amont jugé
muet — puis être servie du disque par l'autre. Rien n'entre alors au magasin
sous le premier chemin, et le téléchargement honnête de la seconde y était
compté en faute, faisant échouer une mesure où le cache avait tout servi. Une
URL n'est « déjà vue » que si la première VM en a obtenu les octets ; un
statut absent vaut livré, les journaux d'avant ne l'écrivant pas toujours.
Vérifié : trois tests, deux mutations attrapées.

--- EN ---

One package lives under two paths depending on the mirror, and the store key
carries the path: the first VM can get « 504 » on one — upstream judged mute —
then be served from disk by the other. Nothing enters the store under the
first path, and the second VM's honest download was counted a fault there,
failing a measurement where the cache had served everything. A URL is « already
seen » only if the first VM obtained its bytes; a missing status counts as
delivered, older logs not always writing it. Checked: three tests, two
mutations caught.

Assisted-by: Claude Opus 5
This commit is contained in:
Mathieu Benoit 2026-09-17 07:14:52 -04:00
parent 96ae77485c
commit 416032fe7b
2 changed files with 56 additions and 8 deletions

View file

@ -826,11 +826,28 @@ def paquets_seulement(lignes):
return [l for l in lignes if l.get("class") == "immutable"]
def livre(ligne):
"""La réponse a-t-elle livré le fichier ?
Un statut absent vaut oui : les journaux d'avant ne l'écrivaient pas
toujours, et le prendre pour un échec ferait disparaître des mesures
entières. Tout ce qui n'est pas une livraison — refus d'un miroir, « 504 »
d'un amont jugé muet — ne compte ni comme faute ni comme fichier déjà vu.
"""
statut = ligne.get("status")
return not isinstance(statut, int) or 200 <= statut < 400
def verdict(premier, second, journal):
"""Le critère, puis la manchette. Rend True si le cache a servi."""
p1 = paquets_seulement(premier)
p2 = paquets_seulement(second)
vues1 = {l["url"] for l in p1}
# « Déjà vue » suppose que la PREMIÈRE VM en a obtenu les octets. Un même
# paquet vit sous deux chemins selon le miroir, et la clé porte le chemin :
# la première peut recevoir « 504 » sur l'un — amont jugé muet — puis être
# servie du disque par l'autre. Le téléchargement honnête de la seconde sur
# le premier chemin n'est pas une faute : rien n'était rangé sous cette clé.
vues1 = {l["url"] for l in p1 if livre(l)}
# Le critère : ce que les DEUX ont demandé ne doit pas être ressorti.
#
@ -839,13 +856,7 @@ def verdict(premier, second, journal):
# miroir suivant, et le cache sert celui-là du disque. Il n'a rien livré
# que le cache aurait dû garder — un 404 figé masquerait le fichier publié
# ensuite. Le compter en faute fait échouer une mesure où tout a été servi.
refus = [
l
for l in p2
if l.get("upstream")
and isinstance(l.get("status"), int)
and l["status"] >= 400
]
refus = [l for l in p2 if l.get("upstream") and not livre(l)]
fautes = [
l
for l in p2

View file

@ -620,5 +620,42 @@ class TestLeGainSeCalculeParCondition(unittest.TestCase):
self.assertNotIn("gain", texte)
class UneUrlQueLaPremiereNaJamaisObtenue(unittest.TestCase):
"""Deux miroirs rangent le même paquet sous deux chemins, et la clé porte
le chemin. La première VM reçoit « 504 » sur l'un — amont jugé muet — puis
est servie du disque par l'autre ; rien n'entre au magasin sous le premier
chemin. Le téléchargement de la seconde n'y est donc pas une faute.
"""
AUTRE = PAQUET_A.replace("miroir.example", "autre-miroir.example")
def test_un_504_chez_la_premiere_ne_rend_pas_la_seconde_fautive(self):
premier = [
ligne(
self.AUTRE, True, issue="offline-miss", octets=0, statut=504
),
ligne(PAQUET_A, False, issue="hit", statut=200),
]
second = [ligne(self.AUTRE, True, issue="stored", statut=200)]
self.assertTrue(
QC.verdict(premier, second, None),
"un 504 de la première VM fait accuser le cache d'un fichier"
" qu'il n'a jamais eu à garder",
)
def test_un_fichier_livre_deux_fois_reste_une_faute(self):
premier = [ligne(PAQUET_A, True, statut=200)]
second = [ligne(PAQUET_A, True, statut=200)]
self.assertFalse(QC.verdict(premier, second, None))
def test_un_statut_absent_vaut_livre(self):
"""Les journaux d'avant ne l'écrivaient pas toujours : le prendre pour
un échec effacerait des mesures entières."""
self.assertTrue(QC.livre({"url": PAQUET_A}))
self.assertTrue(QC.livre({"url": PAQUET_A, "status": 206}))
self.assertFalse(QC.livre({"url": PAQUET_A, "status": 504}))
self.assertFalse(QC.livre({"url": PAQUET_A, "status": 404}))
if __name__ == "__main__":
unittest.main()