#![allow(clippy::unwrap_used)]
use serde_json::json;
use super::*;
#[test]
fn xlsx_content_type_constant() {
assert_eq!(
XLSX_CONTENT_TYPE,
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
);
}
#[test]
fn accepts_xlsx_true_for_exact_match() {
let mut headers = HeaderMap::new();
headers.insert("accept", HeaderValue::from_static(XLSX_CONTENT_TYPE));
assert!(accepts_xlsx(&headers));
}
#[test]
fn accepts_xlsx_true_in_list() {
let mut headers = HeaderMap::new();
let value = format!("application/json, {XLSX_CONTENT_TYPE}");
headers.insert("accept", HeaderValue::from_str(&value).unwrap());
assert!(accepts_xlsx(&headers));
}
#[test]
fn accepts_xlsx_false_for_json() {
let mut headers = HeaderMap::new();
headers.insert("accept", HeaderValue::from_static("application/json"));
assert!(!accepts_xlsx(&headers));
}
#[test]
fn accepts_xlsx_false_for_csv() {
let mut headers = HeaderMap::new();
headers.insert("accept", HeaderValue::from_static("text/csv"));
assert!(!accepts_xlsx(&headers));
}
#[test]
fn accepts_xlsx_false_when_missing() {
let headers = HeaderMap::new();
assert!(!accepts_xlsx(&headers));
}
#[test]
fn accepts_xlsx_case_insensitive() {
let mut headers = HeaderMap::new();
let upper = XLSX_CONTENT_TYPE.to_ascii_uppercase();
headers.insert("accept", HeaderValue::from_str(&upper).unwrap());
assert!(accepts_xlsx(&headers));
}
#[test]
fn accepts_xlsx_ignores_quality_params() {
let mut headers = HeaderMap::new();
let value = format!("{XLSX_CONTENT_TYPE};q=0.9, application/json;q=0.8");
headers.insert("accept", HeaderValue::from_str(&value).unwrap());
assert!(accepts_xlsx(&headers));
}
#[test]
fn accepts_xlsx_does_not_match_plain_xml() {
let mut headers = HeaderMap::new();
headers.insert("accept", HeaderValue::from_static("application/xml"));
assert!(!accepts_xlsx(&headers));
}
#[test]
fn sanitize_filename_keeps_safe_chars() {
assert_eq!(sanitize_filename("users"), "users");
assert_eq!(sanitize_filename("user_profile"), "user_profile");
assert_eq!(sanitize_filename("user-list"), "user-list");
assert_eq!(sanitize_filename("Order99"), "Order99");
}
#[test]
fn sanitize_filename_strips_unsafe_chars() {
assert_eq!(sanitize_filename("admin/secrets"), "adminsecrets");
assert_eq!(sanitize_filename("../etc/passwd"), "etcpasswd");
assert_eq!(sanitize_filename("user list"), "userlist");
assert_eq!(sanitize_filename("a\"b"), "ab");
}
#[test]
fn sanitize_filename_empty_for_all_unsafe() {
assert_eq!(sanitize_filename(""), "");
assert_eq!(sanitize_filename("///"), "");
}
fn render_workbook(columns: &[String], rows: &[serde_json::Value]) -> Vec<u8> {
let mut workbook = Workbook::new();
let worksheet = workbook.add_worksheet();
write_header_row(worksheet, columns).unwrap();
for (idx, row) in rows.iter().enumerate() {
#[allow(clippy::cast_possible_truncation)] let row_idx = (idx + 1) as u32;
write_data_row(worksheet, row_idx, columns, row).unwrap();
}
workbook.save_to_buffer().unwrap()
}
#[test]
fn render_workbook_produces_valid_zip_magic() {
let cols = vec!["id".to_string(), "name".to_string()];
let rows = vec![json!({"id": 1, "name": "Alice"})];
let bytes = render_workbook(&cols, &rows);
assert_eq!(&bytes[..4], b"PK\x03\x04");
assert!(bytes.len() > 1024, "workbook bytes too small: {}", bytes.len());
}
#[test]
fn render_workbook_empty_rows_still_valid() {
let cols = vec!["id".to_string()];
let bytes = render_workbook(&cols, &[]);
assert_eq!(&bytes[..4], b"PK\x03\x04");
}
#[test]
fn render_workbook_handles_all_scalar_types() {
let cols = vec![
"s".into(),
"n".into(),
"b".into(),
"z".into(),
"arr".into(),
"obj".into(),
];
let rows = vec![json!({
"s": "hello",
"n": 42.5,
"b": true,
"z": null,
"arr": [1, 2, 3],
"obj": {"k": "v"},
})];
let bytes = render_workbook(&cols, &rows);
assert_eq!(&bytes[..4], b"PK\x03\x04");
}
#[test]
fn render_workbook_handles_missing_field_as_blank() {
let cols = vec!["id".into(), "name".into()];
let rows = vec![json!({"id": 7})];
let bytes = render_workbook(&cols, &rows);
assert_eq!(&bytes[..4], b"PK\x03\x04");
}
#[test]
fn truncate_for_xlsx_short_string_unchanged() {
assert_eq!(truncate_for_xlsx("hello"), "hello");
assert_eq!(truncate_for_xlsx(""), "");
}
#[test]
fn truncate_for_xlsx_long_string_capped_with_ellipsis() {
let long = "a".repeat(XLSX_MAX_CELL_CHARS + 100);
let out = truncate_for_xlsx(&long);
assert_eq!(out.chars().count(), XLSX_MAX_CELL_CHARS);
assert!(out.ends_with('…'));
}
#[test]
fn truncate_for_xlsx_at_limit_unchanged() {
let exact = "a".repeat(XLSX_MAX_CELL_CHARS);
assert_eq!(truncate_for_xlsx(&exact), exact);
}
#[test]
fn too_many_rows_error_carries_413_and_mentions_csv() {
let err = too_many_rows_error(100_000);
assert_eq!(err.status, StatusCode::PAYLOAD_TOO_LARGE);
assert_eq!(err.code, "XLSX_ROW_LIMIT_EXCEEDED");
assert!(err.message.contains("100000"));
assert!(err.message.contains("text/csv"));
}
#[test]
fn create_temp_file_default_dir_succeeds() {
let tf = create_temp_file(None).unwrap();
assert!(tf.path().exists(), "temp file should exist on disk");
}
#[test]
fn create_temp_file_custom_dir_uses_that_dir() {
let dir = tempfile::tempdir().unwrap();
let tf = create_temp_file(Some(dir.path())).unwrap();
assert!(
tf.path().starts_with(dir.path()),
"temp file {:?} should live under custom dir {:?}",
tf.path(),
dir.path(),
);
}