use notebookx::{NotebookFormat, Output, StreamName};
use std::path::Path;
const EXAMPLE_NOTEBOOK: &str = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../nb_format_examples/World population.ipynb"
);
#[test]
fn test_parse_real_notebook() {
let path = Path::new(EXAMPLE_NOTEBOOK);
assert!(path.exists(), "Example notebook not found at {:?}", path);
let content = std::fs::read_to_string(path).unwrap();
let notebook = NotebookFormat::Ipynb.parse(&content).unwrap();
assert!(!notebook.cells.is_empty());
assert_eq!(notebook.nbformat, 4);
let kernelspec = notebook.metadata.kernelspec.as_ref().unwrap();
assert_eq!(kernelspec.name, "python3");
assert_eq!(kernelspec.language, "python");
}
#[test]
fn test_real_notebook_cell_types() {
let content = std::fs::read_to_string(EXAMPLE_NOTEBOOK).unwrap();
let notebook = NotebookFormat::Ipynb.parse(&content).unwrap();
let code_count = notebook.cells.iter().filter(|c| c.is_code()).count();
let markdown_count = notebook.cells.iter().filter(|c| c.is_markdown()).count();
assert!(code_count > 0, "Expected code cells");
assert!(markdown_count > 0, "Expected markdown cells");
}
#[test]
fn test_real_notebook_has_outputs() {
let content = std::fs::read_to_string(EXAMPLE_NOTEBOOK).unwrap();
let notebook = NotebookFormat::Ipynb.parse(&content).unwrap();
let cells_with_outputs: Vec<_> = notebook
.cells
.iter()
.filter_map(|c| c.outputs())
.filter(|outputs| !outputs.is_empty())
.collect();
assert!(
!cells_with_outputs.is_empty(),
"Expected cells with outputs"
);
}
#[test]
fn test_real_notebook_output_types() {
let content = std::fs::read_to_string(EXAMPLE_NOTEBOOK).unwrap();
let notebook = NotebookFormat::Ipynb.parse(&content).unwrap();
let mut has_execute_result = false;
let mut has_display_data = false;
let mut has_stream = false;
for cell in ¬ebook.cells {
if let Some(outputs) = cell.outputs() {
for output in outputs {
match output {
Output::ExecuteResult { .. } => has_execute_result = true,
Output::DisplayData { .. } => has_display_data = true,
Output::Stream { name, .. } => {
has_stream = true;
assert!(
matches!(name, StreamName::Stdout | StreamName::Stderr),
"Invalid stream name"
);
}
Output::Error { .. } => {}
}
}
}
}
assert!(has_execute_result, "Expected execute_result outputs");
assert!(has_display_data, "Expected display_data outputs (plots)");
assert!(has_stream, "Expected stream outputs");
}
#[test]
fn test_real_notebook_round_trip() {
let content = std::fs::read_to_string(EXAMPLE_NOTEBOOK).unwrap();
let notebook = NotebookFormat::Ipynb.parse(&content).unwrap();
let serialized = NotebookFormat::Ipynb.serialize(¬ebook).unwrap();
let reparsed = NotebookFormat::Ipynb.parse(&serialized).unwrap();
assert_eq!(notebook.cells.len(), reparsed.cells.len());
for (original, reparsed) in notebook.cells.iter().zip(reparsed.cells.iter()) {
assert_eq!(
original.source_string(),
reparsed.source_string(),
"Cell source mismatch"
);
}
assert_eq!(notebook.metadata.kernelspec, reparsed.metadata.kernelspec);
}
#[test]
fn test_real_notebook_mime_bundle() {
let content = std::fs::read_to_string(EXAMPLE_NOTEBOOK).unwrap();
let notebook = NotebookFormat::Ipynb.parse(&content).unwrap();
let mut found_html = false;
let mut found_plain = false;
let mut found_image = false;
for cell in ¬ebook.cells {
if let Some(outputs) = cell.outputs() {
for output in outputs {
let data = match output {
Output::ExecuteResult { data, .. } => Some(data),
Output::DisplayData { data, .. } => Some(data),
_ => None,
};
if let Some(data) = data {
if data.contains_key("text/html") {
found_html = true;
}
if data.contains_key("text/plain") {
found_plain = true;
}
if data.contains_key("image/png") {
found_image = true;
}
}
}
}
}
assert!(found_plain, "Expected text/plain outputs");
assert!(found_html, "Expected text/html outputs (DataFrames)");
assert!(found_image, "Expected image/png outputs (plots)");
}
#[test]
fn test_real_notebook_preserves_extra_metadata() {
let content = std::fs::read_to_string(EXAMPLE_NOTEBOOK).unwrap();
let notebook = NotebookFormat::Ipynb.parse(&content).unwrap();
assert!(
notebook.metadata.extra.contains_key("jupytext")
|| notebook.metadata.extra.contains_key("toc"),
"Expected extra metadata fields to be preserved"
);
}
#[test]
fn test_format_inference_from_path() {
let path = Path::new("notebook.ipynb");
assert_eq!(NotebookFormat::from_path(path), Some(NotebookFormat::Ipynb));
let path = Path::new("notebook.pct.py");
assert_eq!(
NotebookFormat::from_path(path),
Some(NotebookFormat::Percent)
);
let path = Path::new("regular.py");
assert_eq!(NotebookFormat::from_path(path), None);
}