use serve_net::testing::respond;
use web_net::{App, Context, ErrorPage, Guest, Open, Output, Render, RenderError, TemplateId, get};
struct Page<'a> {
title: &'a str,
items: &'a [String],
}
struct Pages;
impl Render for Pages {
type View<'a> = Page<'a>;
fn template(&self, name: &str) -> Option<TemplateId> {
(name == "list").then_some(TemplateId(7))
}
fn render(
&self,
t: TemplateId,
page: &Page<'_>,
out: &mut Output<'_>,
) -> Result<(), RenderError> {
if t != TemplateId(7) {
return Err(RenderError::new("pages.unknown"));
}
let buf = out.buffer();
buf.extend_from_slice(b"<h1>");
buf.extend_from_slice(page.title.replace('<', "<").as_bytes());
buf.extend_from_slice(b"</h1>");
for i in page.items {
buf.extend_from_slice(b"<li>");
buf.extend_from_slice(i.as_bytes());
}
Ok(())
}
fn error_page(&self, page: &ErrorPage<'_>, out: &mut Output<'_>) -> Result<bool, RenderError> {
out.write_str(&format!("[{} {}]", page.status, page.title))?;
Ok(true)
}
}
#[test]
fn test_typed_engine_renders_borrowed_views() {
let app = App::with_gate(Open)
.render(Pages)
.page_limit(64)
.route(
get("/", |cx: &mut Context<'_, Guest, Pages>| {
let items = vec!["a".to_owned(), "b".to_owned()];
let title = String::from("Hi <you>");
cx.render(&Page {
title: &title,
items: &items,
})
})
.public()
.template("list"),
)
.route(
get("/big", |cx: &mut Context<'_, Guest, Pages>| {
let items = vec!["x".repeat(100)];
cx.render(&Page {
title: "big",
items: &items,
})
})
.public()
.template("list"),
)
.build()
.unwrap();
let r = respond(&app, b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n");
assert_eq!(r.text(), "<h1>Hi <you></h1><li>a<li>b");
let r = respond(&app, b"GET /big HTTP/1.1\r\nHost: localhost\r\n\r\n");
assert_eq!(r.status, 500);
let r = respond(&app, b"GET /nope HTTP/1.1\r\nHost: localhost\r\n\r\n");
assert_eq!((r.status, r.text()), (404, "[404 Not Found]"));
}
#[test]
fn test_engine_after_routes_is_a_build_error() {
let e = App::builder()
.route(get("/", |cx| cx.text(200, "x")).public())
.render(Pages)
.build()
.unwrap_err();
assert!(e.problems().iter().any(|p| p.subject == "render"), "{e}");
}
struct Themed;
impl Render for Themed {
type View<'a> = str;
fn template(&self, _: &str) -> Option<TemplateId> {
None
}
fn render(&self, _: TemplateId, _: &str, _: &mut Output<'_>) -> Result<(), RenderError> {
Ok(())
}
fn error_page(&self, page: &ErrorPage<'_>, out: &mut Output<'_>) -> Result<bool, RenderError> {
let css = page.url("/static/site.css")?;
let home = page.url("/")?;
out.write_str(&format!("<link href=\"{css}\"><a href=\"{home}\">home</a>"))?;
Ok(true)
}
}
#[test]
fn test_error_page_links_inside_the_base_path() {
for (base, css, home) in [
("/blog", "/blog/static/site.css", "/blog/"),
("", "/static/site.css", "/"),
] {
let mut b = App::builder().render(Themed);
if !base.is_empty() {
b = b.base(base);
}
let app = b.build().unwrap();
let r = serve_net::testing::respond(
&app,
format!("GET {base}/missing HTTP/1.1\r\nHost: localhost\r\n\r\n").as_bytes(),
);
assert_eq!(r.status, 404);
assert_eq!(
r.text(),
format!("<link href=\"{css}\"><a href=\"{home}\">home</a>"),
"base {base:?}"
);
}
}