use super::PQC_NOTE_LINES;
#[must_use]
pub(super) fn render(headers: &[(String, String)]) -> String {
let mut out = String::new();
out.push_str("# ssg edge-headers: Netlify _headers\n");
out.push_str("# ----------------------------------\n");
for line in PQC_NOTE_LINES {
out.push_str(&format!("# {line}\n"));
}
out.push('\n');
out.push_str("/*\n");
for (key, value) in headers {
out.push_str(&format!(" {key}: {value}\n"));
}
out
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
use crate::postprocess::edge_headers::merged_headers;
use std::collections::BTreeMap;
#[test]
fn render_includes_pqc_docstring() {
let body = render(&merged_headers(&BTreeMap::new()));
assert!(body.contains("X25519+ML-KEM-768"));
assert!(body.contains("netlify.com"));
}
#[test]
fn render_uses_wildcard_route_group() {
let body = render(&merged_headers(&BTreeMap::new()));
let route_line = body
.lines()
.find(|l| !l.starts_with('#') && !l.trim().is_empty());
assert_eq!(route_line, Some("/*"));
}
#[test]
fn render_indents_headers_under_route() {
let body = render(&merged_headers(&BTreeMap::new()));
assert!(body.contains(" Strict-Transport-Security:"));
assert!(body.contains(" Content-Security-Policy:"));
assert!(body.contains(" X-Content-Type-Options: nosniff"));
assert!(
body.contains(" Referrer-Policy: strict-origin-when-cross-origin")
);
assert!(body.contains(" Permissions-Policy:"));
}
#[test]
fn render_has_no_duplicate_csp_header() {
let body = render(&merged_headers(&BTreeMap::new()));
let csp_count = body
.lines()
.filter(|l| l.contains("Content-Security-Policy:"))
.count();
assert_eq!(csp_count, 1);
}
#[test]
fn render_with_empty_headers_still_emits_route_group() {
let body = render(&[]);
assert!(body.contains("/*\n"));
assert!(body.starts_with("# ssg edge-headers: Netlify _headers"));
}
#[test]
fn render_preserves_input_header_order() {
let headers = vec![
("First".to_string(), "1".to_string()),
("Second".to_string(), "2".to_string()),
("Third".to_string(), "3".to_string()),
];
let body = render(&headers);
let i_first = body.find("First:").unwrap();
let i_second = body.find("Second:").unwrap();
let i_third = body.find("Third:").unwrap();
assert!(i_first < i_second && i_second < i_third);
}
#[test]
fn render_starts_with_docstring_then_blank_line_then_route() {
let body = render(&[]);
let lines: Vec<&str> = body.lines().collect();
assert!(lines[0].starts_with('#'));
let blank_idx = lines
.iter()
.position(|l| l.is_empty())
.expect("expected a blank line between docstring and route");
assert_eq!(lines[blank_idx + 1], "/*");
}
#[test]
fn render_emits_pqc_note_lines_as_comment_block() {
let body = render(&[]);
for line in PQC_NOTE_LINES {
let formatted = format!("# {line}");
assert!(body.contains(&formatted), "missing PQC line: {formatted}");
}
}
#[test]
fn render_includes_link_to_netlify_docs() {
let body = render(&[]);
assert!(body.contains("docs.netlify.com"));
}
#[test]
fn render_with_override_value_appears_in_output() {
let mut overrides: BTreeMap<String, String> = BTreeMap::new();
let _ = overrides.insert(
"Permissions-Policy".to_string(),
"geolocation=(self)".to_string(),
);
let body = render(&merged_headers(&overrides));
assert!(body.contains("Permissions-Policy: geolocation=(self)"));
}
}