admin_handler_pagination.rs 87 lignes · 2933 octets
//! RFC 0022 — anti-régression pagination admin.
//!
//! Le template `templates/admin/users_list.html` utilisait
//! `{% elif p >= page - 2 %}` et `{% elif p == page - 3 %}`, qui
//! paniquent en `attempt to subtract with overflow` sur `u64` dès que
//! `page < 3`. Toute première visite `/admin/users?page=1` (la valeur
//! par défaut) crashait le worker tokio sans log d'erreur.
//!
//! Le fix utilise les formes additives équivalentes
//! `p + 2 >= page && p <= page + 2` et `p + 3 == page || p == page + 3`
//! qui n'underflow jamais sur `u64`. Ce fichier vérifie ces formes
//! pour tout `(page, p) ∈ [0..10]²` et empêche un retour silencieux
//! aux soustractions naïves.

/// Mirroir de la condition « afficher la page p si elle est dans la
/// fenêtre [page-2 ; page+2] » utilisée dans `users_list.html` ligne 211.
#[inline]
fn in_window_central(p: u64, page: u64) -> bool {
    p + 2 >= page && p <= page + 2
}

/// Mirroir de la condition « afficher l'ellipse … si p est exactement
/// page-3 ou page+3 » utilisée dans `users_list.html` ligne 214.
#[inline]
fn is_ellipsis_marker(p: u64, page: u64) -> bool {
    p + 3 == page || p == page + 3
}

#[test]
fn central_window_does_not_underflow_for_low_pages() {
    for page in 0u64..=4 {
        for p in 0u64..=10 {
            // Ne doit pas paniquer.
            let _ = in_window_central(p, page);
            let _ = is_ellipsis_marker(p, page);
        }
    }
}

#[test]
fn central_window_matches_naive_subtraction_when_safe() {
    for page in 3u64..=10 {
        for p in 0u64..=15 {
            let additive = in_window_central(p, page);
            // Comparaison à la version naïve (sûre uniquement quand page >= 2).
            let naive = p >= page - 2 && p <= page + 2;
            assert_eq!(
                additive, naive,
                "additive form must equal naive form for page={page}, p={p}",
            );
        }
    }
}

#[test]
fn ellipsis_marker_matches_naive_when_safe() {
    for page in 3u64..=10 {
        for p in 0u64..=15 {
            let additive = is_ellipsis_marker(p, page);
            // page >= 3 garantit pas d'underflow.
            let naive = p == page - 3 || p == page + 3;
            assert_eq!(
                additive, naive,
                "additive ellipsis form must equal naive for page={page}, p={p}",
            );
        }
    }
}

#[test]
fn central_window_rejects_far_outside_pages() {
    // page=1 : fenêtre centrale = [0, 0..3]. p=4 doit être hors fenêtre.
    assert!(!in_window_central(4, 1));
    // page=10 : fenêtre = [8, 12]. p=7 hors fenêtre.
    assert!(!in_window_central(7, 10));
}

#[test]
fn central_window_includes_self() {
    // Pour toute page, page elle-même est toujours dans la fenêtre.
    for page in 0u64..=20 {
        assert!(
            in_window_central(page, page),
            "page {page} must always include itself",
        );
    }
}