pagetop/tests/route.rs
Manuel Cillero 747e64ce34 (auth): Añade permisos tipados
Sustituye las claves de permiso `&str` sueltas por el trait `Permission`
(clave, etiqueta y grupo), y añade `require_permission()` para cortar un
handler con `ErrorPage::AccessDenied` antes de construir nada.

Para que `require_permission()` pueda fallar sin un `Context` previo,
`ErrorPage` pasa a envolver `Option<HttpRequest>`, `Context::new()`
recibe ahora `HttpRequest` sin `Option`, y se añade `Context::admin()`
para la plantilla de administración.
2026-07-27 22:46:41 +02:00

124 lines
4 KiB
Rust

use pagetop::prelude::*;
// Forces an effective language different from the default negotiated one (en-US, with no `?lang` in
// the request), so that `Context::route()` decides to propagate `?lang=...` in local routes.
fn cx_with_lang(lang: &str) -> Context {
Context::default().with_langid(&Locale::resolve(lang))
}
// **< Route - automatic detection of external URLs >***********************************************
#[pagetop::test]
async fn route_from_str_detects_external_urls_by_prefix() {
let cx = cx_with_lang("es-ES");
for url in [
"http://example.com",
"https://example.com",
"//example.com",
"mailto:user@example.com",
"tel:+123456789",
] {
let route: Route = url.into();
assert_eq!(
route.resolve(&cx).to_string(),
url,
"expected {url:?} to be detected as external and left unmodified"
);
}
}
#[pagetop::test]
async fn route_from_string_behaves_like_from_str() {
let cx = cx_with_lang("es-ES");
let external: Route = String::from("https://example.com").into();
assert_eq!(external.resolve(&cx).to_string(), "https://example.com");
let local: Route = String::from("/local/path").into();
assert_eq!(local.resolve(&cx).to_string(), "/local/path?lang=es-ES");
}
#[pagetop::test]
async fn route_local_path_goes_through_context_route_and_preserves_lang() {
let cx = cx_with_lang("es-ES");
let route: Route = "/local/path".into();
assert_eq!(route.resolve(&cx).to_string(), "/local/path?lang=es-ES");
}
#[pagetop::test]
async fn route_unrecognized_scheme_is_not_detected_as_external() {
// `ftp://` is not among the prefixes recognized by the heuristic, so the implicit conversion
// treats it as a local path and goes through `Context::route()`; for a URL like this,
// `Route::external()` is still necessary (see the test below).
let cx = cx_with_lang("es-ES");
let route: Route = "ftp://files.example.com".into();
assert_eq!(
route.resolve(&cx).to_string(),
"ftp://files.example.com?lang=es-ES"
);
}
// **< Route::external() / From<RoutePath> (share the same fixed resolution) >**********************
#[pagetop::test]
async fn route_external_ignores_the_rendering_context() {
let route = Route::external("/fixed/path");
let cx_es = cx_with_lang("es-ES");
let cx_en = cx_with_lang("en-US");
assert_eq!(route.resolve(&cx_es).to_string(), "/fixed/path");
assert_eq!(route.resolve(&cx_en).to_string(), "/fixed/path");
}
#[pagetop::test]
async fn route_from_route_path_ignores_the_rendering_context() {
let route: Route = RoutePath::new("/fixed/path").with_param("q", "rust").into();
let cx_es = cx_with_lang("es-ES");
let cx_en = cx_with_lang("en-US");
assert_eq!(route.resolve(&cx_es).to_string(), "/fixed/path?q=rust");
assert_eq!(route.resolve(&cx_en).to_string(), "/fixed/path?q=rust");
}
#[pagetop::test]
async fn route_external_never_adds_lang_even_for_unrecognized_schemes() {
let cx = cx_with_lang("es-ES");
let route = Route::external("ftp://files.example.com");
assert_eq!(route.resolve(&cx).to_string(), "ftp://files.example.com");
}
// **< Route::with() >******************************************************************************
#[pagetop::test]
async fn route_with_captures_dynamic_values_from_the_environment() {
let cx = cx_with_lang("es-ES");
let user_id = 42;
let route = Route::with(move |cx| cx.route(format!("/users/{user_id}")));
assert_eq!(route.resolve(&cx).to_string(), "/users/42?lang=es-ES");
}
#[pagetop::test]
async fn route_default_resolves_to_an_empty_path() {
let cx = Context::default();
assert_eq!(Route::default().resolve(&cx).to_string(), "");
}
// **< Context::route() - direct protection against external URLs >*********************************
#[pagetop::test]
async fn context_route_never_adds_lang_to_an_external_looking_url() {
let cx = cx_with_lang("es-ES");
assert_eq!(
cx.route("https://example.com/help").to_string(),
"https://example.com/help"
);
}