Expand test coverage
This commit is contained in:
parent
386e6b482b
commit
5151c53a2b
20 changed files with 773 additions and 121 deletions
|
|
@ -263,7 +263,7 @@ export CARGO_TERM_COLOR := 'always'
|
|||
|
||||
debug_vars := 'DEBUG=${DEBUG:-} DEBUG_FILTER=${DEBUG_FILTER:-} RUST_BACKTRACE=${RUST_BACKTRACE:-} RUSTFLAGS=${RUSTFLAGS:-}'
|
||||
watch_cmd := "watchexec -qc -r -e rs,toml,html --color always -- "
|
||||
cover_cmd := 'cargo llvm-cov --color always --ignore-filename-regex "main\.rs|dev\.rs"'
|
||||
cover_cmd := 'cargo llvm-cov --color always --ignore-filename-regex "main\.rs|log\.rs"'
|
||||
just_cmd := 'just --timestamp --explain --command-color green'
|
||||
|
||||
set unstable
|
||||
|
|
|
|||
|
|
@ -127,7 +127,6 @@ same_functions_in_if_condition = "warn"
|
|||
semicolon_if_nothing_returned = "warn"
|
||||
set_contains_or_insert = "warn"
|
||||
should_panic_without_expect = "warn"
|
||||
similar_names = "warn"
|
||||
str_split_at_newline = "warn"
|
||||
struct_field_names = "warn"
|
||||
trivially_copy_pass_by_ref = "warn"
|
||||
|
|
|
|||
519
src/graph.rs
519
src/graph.rs
|
|
@ -192,7 +192,7 @@ impl Graph {
|
|||
tlog!(&instant, "Parsed configuration");
|
||||
}
|
||||
|
||||
/// Construct a HashMap with incoming connections (reversed edges)
|
||||
/// Construct a `HashMap` with incoming connections (reversed edges)
|
||||
fn map_incoming(&mut self) {
|
||||
for node in self.nodes.clone().into_values() {
|
||||
for edge in node.connections.clone().values() {
|
||||
|
|
@ -310,7 +310,6 @@ impl Graph {
|
|||
let graph = self.clone();
|
||||
let iterator = self.nodes.iter_mut();
|
||||
for (key, node) in iterator {
|
||||
|
||||
// Parse node text
|
||||
let parse_output = content::rich_parse(&node.text, &graph);
|
||||
node.text
|
||||
|
|
@ -504,7 +503,7 @@ impl std::fmt::Display for Format {
|
|||
match self {
|
||||
Format::TOML => write!(f, "TOML"),
|
||||
Format::JSON => write!(f, "JSON"),
|
||||
Format::Unsupported => write!(f, "Unsupported"),
|
||||
Format::Unsupported => write!(f, "Unsupported format"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -518,7 +517,15 @@ pub struct QueryResult {
|
|||
|
||||
impl std::fmt::Display for QueryResult {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let meta = if self.redirect { "[redirect] " } else { "" };
|
||||
let meta = if self.redirect && self.exact {
|
||||
"[exact redirect] "
|
||||
} else if !self.redirect && self.exact {
|
||||
"[exact] "
|
||||
} else if self.redirect && !self.exact {
|
||||
"[redirect] "
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let node = if let Some(n) = &self.node {
|
||||
n.id.clone()
|
||||
} else {
|
||||
|
|
@ -597,21 +604,30 @@ mod tests {
|
|||
fn bad_deserial_input() {
|
||||
let result = Graph::from_serial("not toml", &Format::TOML);
|
||||
assert!(result.is_err());
|
||||
assert!(matches!(result.unwrap_err().cause, SerialErrorCause::MalformedInput));
|
||||
assert!(matches!(
|
||||
result.unwrap_err().cause,
|
||||
SerialErrorCause::MalformedInput
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_deserial_format() {
|
||||
let result = Graph::from_serial("not toml", &Format::Unsupported);
|
||||
assert!(result.is_err());
|
||||
assert!(matches!(result.unwrap_err().cause, SerialErrorCause::UnsupportedFormat));
|
||||
assert!(matches!(
|
||||
result.unwrap_err().cause,
|
||||
SerialErrorCause::UnsupportedFormat
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_serial_format() {
|
||||
let result = Graph::load().to_serial(&Format::Unsupported);
|
||||
assert!(result.is_err());
|
||||
assert!(matches!(result.unwrap_err().cause, SerialErrorCause::UnsupportedFormat));
|
||||
assert!(matches!(
|
||||
result.unwrap_err().cause,
|
||||
SerialErrorCause::UnsupportedFormat
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -625,12 +641,11 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn title_population_from_id() {
|
||||
let mut graph = Graph::from_serial(concat!(
|
||||
"[nodes.TitlelessNode]\n",
|
||||
r#"text = "Some text""#,
|
||||
),
|
||||
let mut graph = Graph::from_serial(
|
||||
concat!("[nodes.TitlelessNode]\n", r#"text = "Some text""#,),
|
||||
&Format::TOML,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
graph.modulate();
|
||||
|
||||
let node = graph.nodes.get("TitlelessNode");
|
||||
|
|
@ -639,13 +654,16 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn no_title_population_from_id_if_title_set() {
|
||||
let mut graph = Graph::from_serial(concat!(
|
||||
"[nodes.TitlefulNode]\n",
|
||||
r#"title = "A Title""#, "\n",
|
||||
r#"text = "Some text""#,
|
||||
let mut graph = Graph::from_serial(
|
||||
concat!(
|
||||
"[nodes.TitlefulNode]\n",
|
||||
r#"title = "A Title""#,
|
||||
"\n",
|
||||
r#"text = "Some text""#,
|
||||
),
|
||||
&Format::TOML,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
graph.modulate();
|
||||
|
||||
let node = graph.nodes.get("TitlefulNode");
|
||||
|
|
@ -654,32 +672,38 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn detached_edge_is_flagged() {
|
||||
let mut graph = Graph::from_serial(concat!(
|
||||
"[nodes.Node]\n",
|
||||
r#"text = "Some text here""#, "\n\n",
|
||||
"[nodes.Node.connections.Nowhere]\n",
|
||||
let mut graph = Graph::from_serial(
|
||||
concat!(
|
||||
"[nodes.Node]\n",
|
||||
r#"text = "Some text here""#,
|
||||
"\n\n",
|
||||
"[nodes.Node.connections.Nowhere]\n",
|
||||
),
|
||||
&Format::TOML,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
graph.modulate();
|
||||
|
||||
let node = graph.nodes.get("Node").unwrap();
|
||||
let connections = node.connections.as_ref().unwrap();
|
||||
let connection = connections.get("Nowhere").unwrap();
|
||||
let connection = node.connections.get("Nowhere").unwrap();
|
||||
assert!(connection.detached);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn attached_edge_is_not_flagged() {
|
||||
let mut graph = Graph::from_serial(concat!(
|
||||
"[nodes.NodeOne]\n",
|
||||
r#"text = "Some text here""#, "\n\n",
|
||||
"[nodes.NodeOne.connections.NodeTwo]\n\n",
|
||||
"[nodes.NodeTwo]\n",
|
||||
r#"text = "Some other text here""#, "\n\n",
|
||||
let mut graph = Graph::from_serial(
|
||||
concat!(
|
||||
"[nodes.NodeOne]\n",
|
||||
r#"text = "Some text here""#,
|
||||
"\n\n",
|
||||
"[nodes.NodeOne.connections.NodeTwo]\n\n",
|
||||
"[nodes.NodeTwo]\n",
|
||||
r#"text = "Some other text here""#,
|
||||
"\n\n",
|
||||
),
|
||||
&Format::TOML,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
graph.modulate();
|
||||
|
||||
let node = graph.nodes.get("NodeOne").unwrap();
|
||||
|
|
@ -690,14 +714,16 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn to_and_from_population() {
|
||||
let mut graph = Graph::from_serial(concat!(
|
||||
"[nodes.n01]\n",
|
||||
"[nodes.n01.connections.n02]\n\n",
|
||||
"[nodes.n02]\n",
|
||||
"[nodes.n02.connections.n03]\n\n",
|
||||
let mut graph = Graph::from_serial(
|
||||
concat!(
|
||||
"[nodes.n01]\n",
|
||||
"[nodes.n01.connections.n02]\n\n",
|
||||
"[nodes.n02]\n",
|
||||
"[nodes.n02.connections.n03]\n\n",
|
||||
),
|
||||
&Format::TOML,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
graph.modulate();
|
||||
|
||||
let n01 = graph.nodes.get("n01").unwrap();
|
||||
|
|
@ -715,15 +741,19 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn links_become_connections() {
|
||||
let mut graph = Graph::from_serial(concat!(
|
||||
"[nodes.n01]\n",
|
||||
r#"links = [ "n02", "n03", "n04" ]"#, "\n\n",
|
||||
"[nodes.n02]\n",
|
||||
"[nodes.n04]\n",
|
||||
r#"links = [ "n01", "n03" ]"#, "\n\n",
|
||||
let mut graph = Graph::from_serial(
|
||||
concat!(
|
||||
"[nodes.n01]\n",
|
||||
r#"links = [ "n02", "n03", "n04" ]"#,
|
||||
"\n\n",
|
||||
"[nodes.n02]\n",
|
||||
"[nodes.n04]\n",
|
||||
r#"links = [ "n01", "n03" ]"#,
|
||||
"\n\n",
|
||||
),
|
||||
&Format::TOML,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
graph.modulate();
|
||||
|
||||
let n01 = graph.nodes.get("n01").unwrap();
|
||||
|
|
@ -762,14 +792,19 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn detached_count_increments() {
|
||||
let mut graph = Graph::from_serial(concat!(
|
||||
"[nodes.n01]\n",
|
||||
r#"links = [ "n02", "n03", "n04", "n05", "n06", "n10" ]"#, "\n\n",
|
||||
"[nodes.n02]\n",
|
||||
"[nodes.n04]\n",
|
||||
r#"links = [ "n01", "n02", "n03", "n06", "n11", "n15" ]"#, "\n\n"),
|
||||
let mut graph = Graph::from_serial(
|
||||
concat!(
|
||||
"[nodes.n01]\n",
|
||||
r#"links = [ "n02", "n03", "n04", "n05", "n06", "n10" ]"#,
|
||||
"\n\n",
|
||||
"[nodes.n02]\n",
|
||||
"[nodes.n04]\n",
|
||||
r#"links = [ "n01", "n02", "n03", "n06", "n11", "n15" ]"#,
|
||||
"\n\n"
|
||||
),
|
||||
&Format::TOML,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
graph.modulate();
|
||||
|
||||
assert_eq!(graph.stats.detached_total, 8);
|
||||
|
|
@ -778,12 +813,15 @@ mod tests {
|
|||
#[test]
|
||||
fn populated_summary() {
|
||||
let text = "vh18qEUN22X2SxLj6lpOOzMBB4N6S0UG";
|
||||
let mut graph = Graph::from_serial(&format!(
|
||||
"[nodes.n01]\n\
|
||||
let mut graph = Graph::from_serial(
|
||||
&format!(
|
||||
"[nodes.n01]\n\
|
||||
text = \"{text}\"\n\
|
||||
"),
|
||||
"
|
||||
),
|
||||
&Format::TOML,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
graph.modulate();
|
||||
|
||||
assert!(graph.nodes.get("n01").unwrap().summary.contains(text));
|
||||
|
|
@ -794,14 +832,16 @@ mod tests {
|
|||
fn supplied_summary() {
|
||||
let text = "vh18qEUN22X2SxLj6lpOOzMBB4N6S0UG";
|
||||
let summary = "W5dhPgNs7S1Zsq6uPK47MAw8xXyNxwep";
|
||||
let mut graph = Graph::from_serial(&format!(
|
||||
"[nodes.n01]\n\
|
||||
let mut graph = Graph::from_serial(
|
||||
&format!(
|
||||
"[nodes.n01]\n\
|
||||
summary = \"{summary}\"\n\
|
||||
text = \"{text}\"\n\
|
||||
",
|
||||
),
|
||||
&Format::TOML,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
graph.modulate();
|
||||
|
||||
assert_eq!(graph.nodes.get("n01").unwrap().summary, summary);
|
||||
|
|
@ -816,10 +856,11 @@ mod tests {
|
|||
6FokUX o OCEc LzZFfR1nkqa hWIF LdrtD3G. PDQwv Ba2PnZ yEBVpqQdt\n\n\
|
||||
Py6aoPK FV7iU UdrYB vD UeMvvg u 5kbt 9ZW9x7MR"
|
||||
);
|
||||
let mut graph = Graph::from_serial(&format!(
|
||||
"[nodes.n01]\ntext = \"\"\"{text}\"\"\"\n"),
|
||||
let mut graph = Graph::from_serial(
|
||||
&format!("[nodes.n01]\ntext = \"\"\"{text}\"\"\"\n"),
|
||||
&Format::TOML,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
graph.modulate();
|
||||
|
||||
assert_eq!(graph.nodes.get("n01").unwrap().summary, first_sentence);
|
||||
|
|
@ -837,7 +878,8 @@ mod tests {
|
|||
let mut graph = Graph::from_serial(
|
||||
format!("[nodes.n01]\ntext = \"\"\"{text}\"\"\"\n").as_str(),
|
||||
&Format::TOML,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
graph.modulate();
|
||||
|
||||
assert_eq!(graph.nodes.get("n01").unwrap().summary, first_paragraph);
|
||||
|
|
@ -845,23 +887,28 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn summary_from_first_300_chars() {
|
||||
let first_300 = concat!("Primis, quod, cum in rerum natura duo ",
|
||||
let first_300 = concat!(
|
||||
"Primis, quod, cum in rerum natura duo ",
|
||||
"quaerenda sint, unum, quae materia sit, ex qua quaeque res ",
|
||||
"efficiatur, alterum, quae naturales essent nec tamen id, cuius ",
|
||||
"causa haec finxerat, assecutus est: Nam si omnes veri erunt, ut ",
|
||||
"Epicuri ratio docet, tum denique poterit aliquid cognosci et ",
|
||||
"percipi? Quos q");
|
||||
let tail = concat!("uam autem et praeterita grate meminit et ",
|
||||
"percipi? Quos q"
|
||||
);
|
||||
let tail = concat!(
|
||||
"uam autem et praeterita grate meminit et ",
|
||||
"praesentibus ita potitur, ut animadvertat quanta sint ea ",
|
||||
"quamque iucunda, neque pendet ex futuris, sed expectat illa, ",
|
||||
"fruitur praesentibus ab iisque vitii");
|
||||
"fruitur praesentibus ab iisque vitii"
|
||||
);
|
||||
let text = format!("{first_300}{tail}");
|
||||
let summary = format!("{first_300}…");
|
||||
|
||||
let mut graph = Graph::from_serial(
|
||||
format!("[nodes.n01]\ntext = \"\"\"{text}\"\"\"\n").as_str(),
|
||||
&Format::TOML,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
graph.modulate();
|
||||
|
||||
assert_eq!(graph.nodes.get("n01").unwrap().summary, summary);
|
||||
|
|
@ -869,16 +916,18 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn anchors_become_connections() {
|
||||
|
||||
let mut graph = Graph::from_serial("\
|
||||
let mut graph = Graph::from_serial(
|
||||
"\
|
||||
[nodes.n1]
|
||||
text = 'an anchor to |n2|, the existing node'
|
||||
|
||||
[nodes.n2]
|
||||
text = 'an anchor to |n0|, the nonexistent node'
|
||||
|
||||
", &Format::TOML,
|
||||
).unwrap();
|
||||
",
|
||||
&Format::TOML,
|
||||
)
|
||||
.unwrap();
|
||||
graph.modulate();
|
||||
|
||||
let n1_to_n2 = graph.nodes.get("n1").unwrap().connections.get("n2");
|
||||
|
|
@ -891,6 +940,334 @@ mod tests {
|
|||
assert!(!n1_to_n2.unwrap().detached);
|
||||
assert!(n2_to_n0.unwrap().detached);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detached_anchors_increase_counts() {
|
||||
let mut graph = Graph::from_serial(
|
||||
"\
|
||||
[nodes.n1]\n\
|
||||
text = 'this |anchor| is detached, as is |this one|.'\n\
|
||||
",
|
||||
&Format::TOML,
|
||||
)
|
||||
.unwrap();
|
||||
graph.modulate();
|
||||
|
||||
assert_eq!(graph.stats.detached_total, 2);
|
||||
assert_eq!(*graph.stats.detached.get("anchor").unwrap(), 1);
|
||||
assert_eq!(*graph.stats.detached.get("this one").unwrap(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repeated_detached_anchors_increase_counts() {
|
||||
let mut graph = Graph::from_serial(
|
||||
"\
|
||||
[nodes.n1]\n\
|
||||
text = 'this |anchor| is detached, as appears twice: |anchor|.'\n\
|
||||
",
|
||||
&Format::TOML,
|
||||
)
|
||||
.unwrap();
|
||||
graph.modulate();
|
||||
|
||||
assert_eq!(graph.stats.detached_total, 2);
|
||||
assert_eq!(*graph.stats.detached.get("anchor").unwrap(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn find_exact() {
|
||||
let mut graph = Graph::from_serial(
|
||||
"\
|
||||
[nodes.n1]\n\
|
||||
",
|
||||
&Format::TOML,
|
||||
)
|
||||
.unwrap();
|
||||
graph.modulate();
|
||||
|
||||
let query_result = graph.find_node("n1");
|
||||
assert_eq!(query_result.node.unwrap().id, "n1");
|
||||
assert!(query_result.exact);
|
||||
assert!(!query_result.redirect);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn find_inexact() {
|
||||
let mut graph = Graph::from_serial(
|
||||
"\
|
||||
[nodes.n1]\n\
|
||||
",
|
||||
&Format::TOML,
|
||||
)
|
||||
.unwrap();
|
||||
graph.modulate();
|
||||
|
||||
let query_result = graph.find_node("n 1");
|
||||
println!("{query_result}");
|
||||
|
||||
assert_eq!(query_result.node.unwrap().id, "n1");
|
||||
assert!(!query_result.exact);
|
||||
assert!(!query_result.redirect);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn find_inexact_to_redirect() {
|
||||
let mut graph = Graph::from_serial(
|
||||
"\
|
||||
[nodes.n1]\n\
|
||||
redirect = 'n3'
|
||||
\n\
|
||||
[nodes.n3]
|
||||
",
|
||||
&Format::TOML,
|
||||
)
|
||||
.unwrap();
|
||||
graph.modulate();
|
||||
|
||||
let query_result = graph.find_node("n 1");
|
||||
println!("{query_result}");
|
||||
|
||||
assert_eq!(query_result.node.unwrap().id, "n3");
|
||||
assert!(!query_result.exact);
|
||||
assert!(query_result.redirect);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_recursion_to_redirect() {
|
||||
let mut graph = Graph::from_serial(
|
||||
"\
|
||||
[nodes.n1]\n\
|
||||
redirect = 'n2'
|
||||
\n\
|
||||
[nodes.n2]\n\
|
||||
redirect = 'n 3'
|
||||
\n\
|
||||
[nodes.n3]
|
||||
",
|
||||
&Format::TOML,
|
||||
)
|
||||
.unwrap();
|
||||
graph.modulate();
|
||||
|
||||
let query_result = graph.find_node("n 1");
|
||||
println!("{query_result}");
|
||||
|
||||
assert_eq!(query_result.node.unwrap().id, "n3");
|
||||
assert!(!query_result.exact);
|
||||
assert!(query_result.redirect);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn find_redirect_to_inexisting() {
|
||||
let mut graph = Graph::from_serial(
|
||||
"\
|
||||
[nodes.n1]\n\
|
||||
redirect = 'n0'\n\
|
||||
",
|
||||
&Format::TOML,
|
||||
)
|
||||
.unwrap();
|
||||
graph.modulate();
|
||||
|
||||
let query_result = graph.find_node("n1");
|
||||
println!("{query_result}");
|
||||
|
||||
assert!(query_result.node.is_none());
|
||||
assert!(query_result.redirect);
|
||||
assert!(!query_result.exact);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn find_redirect_to_existing() {
|
||||
let mut graph = Graph::from_serial(
|
||||
"\
|
||||
[nodes.n1]\n\
|
||||
redirect = 'n2'\n\
|
||||
\n\
|
||||
[nodes.n2]
|
||||
",
|
||||
&Format::TOML,
|
||||
)
|
||||
.unwrap();
|
||||
graph.modulate();
|
||||
|
||||
let query_result = graph.find_node("n1");
|
||||
assert_eq!(query_result.node.unwrap().id, "n2");
|
||||
assert!(query_result.redirect);
|
||||
assert!(!query_result.exact);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serial_error_display() {
|
||||
let bad_input = SerialErrorCause::MalformedInput;
|
||||
let bad_format = SerialErrorCause::UnsupportedFormat;
|
||||
|
||||
assert_eq!(format!("{bad_input}"), "Malformed Input");
|
||||
assert_eq!(format!("{bad_format}"), "Unsupported Format");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_from_serial_error() {
|
||||
let bad_input_error_message = "denSehpfhCjr05gUd7TgYLb8veJHAMZW";
|
||||
let bad_input_cause = SerialErrorCause::MalformedInput;
|
||||
let bad_input = SerialError {
|
||||
cause: bad_input_cause.clone(),
|
||||
message: bad_input_error_message.to_string(),
|
||||
};
|
||||
|
||||
let bad_format_error_message = "4brcCkWOgLHBvhLk2OcgTOKQgpKrc1bB";
|
||||
let bad_format_cause = SerialErrorCause::UnsupportedFormat;
|
||||
let bad_format = SerialError {
|
||||
cause: bad_format_cause.clone(),
|
||||
message: bad_format_error_message.to_string(),
|
||||
};
|
||||
|
||||
let s_bad_input = String::from(bad_input.clone());
|
||||
let s_bad_format = String::from(bad_format.clone());
|
||||
|
||||
assert!(s_bad_input.contains(bad_input_error_message));
|
||||
assert!(s_bad_input.contains(bad_input.message.as_str()));
|
||||
assert!(s_bad_input.contains(format!("{bad_input_cause}").as_str()));
|
||||
|
||||
assert!(s_bad_format.contains(bad_format_error_message));
|
||||
assert!(s_bad_format.contains(bad_format.message.as_str()));
|
||||
assert!(s_bad_format.contains(format!("{bad_format_cause}").as_str()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_from_str() {
|
||||
let uppercase_toml = Format::from("TOML");
|
||||
let lowercase_toml = Format::from("toml");
|
||||
let mixed_case_toml = Format::from("tOmL");
|
||||
|
||||
assert!(matches!(uppercase_toml, Format::TOML));
|
||||
assert!(matches!(lowercase_toml, Format::TOML));
|
||||
assert!(matches!(mixed_case_toml, Format::TOML));
|
||||
|
||||
let uppercase_json = Format::from("JSON");
|
||||
let lowercase_json = Format::from("json");
|
||||
let mixed_case_json = Format::from("JsoN");
|
||||
|
||||
assert!(matches!(uppercase_json, Format::JSON));
|
||||
assert!(matches!(lowercase_json, Format::JSON));
|
||||
assert!(matches!(mixed_case_json, Format::JSON));
|
||||
|
||||
let unsupported = [
|
||||
Format::from("j son"),
|
||||
Format::from(""),
|
||||
Format::from("strawberry"),
|
||||
Format::from(" "),
|
||||
Format::from("\n"),
|
||||
];
|
||||
|
||||
assert!(unsupported.iter().all(|f| matches!(f, Format::Unsupported)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_display() {
|
||||
let toml = format!("{}", Format::TOML);
|
||||
let json = format!("{}", Format::JSON);
|
||||
let unsupported = format!("{}", Format::Unsupported);
|
||||
|
||||
assert_eq!(toml, "TOML");
|
||||
assert_eq!(json, "JSON");
|
||||
assert_eq!(unsupported, "Unsupported format");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn query_result_display() {
|
||||
let mut node = Node::default();
|
||||
let node_id = "nv00qmO6PDrqJheUHOONlCVpuceefS30";
|
||||
node.id = String::from(node_id);
|
||||
|
||||
let none_exact = QueryResult {
|
||||
node: None,
|
||||
exact: true,
|
||||
redirect: false,
|
||||
};
|
||||
|
||||
assert!(!format!("{none_exact}").contains(node_id));
|
||||
assert!(format!("{none_exact}").contains("No Match"));
|
||||
assert!(format!("{none_exact}").contains("exact"));
|
||||
assert!(!format!("{none_exact}").contains("redirect"));
|
||||
|
||||
let some_exact = QueryResult {
|
||||
node: Some(node.clone()),
|
||||
exact: true,
|
||||
redirect: false,
|
||||
};
|
||||
|
||||
assert!(format!("{some_exact}").contains(node_id));
|
||||
assert!(!format!("{some_exact}").contains("No Match"));
|
||||
assert!(format!("{some_exact}").contains("exact"));
|
||||
assert!(!format!("{some_exact}").contains("redirect"));
|
||||
|
||||
let none_redirect = QueryResult {
|
||||
node: None,
|
||||
exact: false,
|
||||
redirect: true,
|
||||
};
|
||||
|
||||
assert!(!format!("{none_redirect}").contains(node_id));
|
||||
assert!(format!("{none_redirect}").contains("No Match"));
|
||||
assert!(format!("{none_redirect}").contains("redirect"));
|
||||
assert!(!format!("{none_redirect}").contains("exact"));
|
||||
|
||||
let some_redirect = QueryResult {
|
||||
node: Some(node.clone()),
|
||||
exact: false,
|
||||
redirect: true,
|
||||
};
|
||||
|
||||
assert!(format!("{some_redirect}").contains(node_id));
|
||||
assert!(!format!("{some_redirect}").contains("No Match"));
|
||||
assert!(format!("{some_redirect}").contains("redirect"));
|
||||
assert!(!format!("{some_redirect}").contains("exact"));
|
||||
|
||||
let some_exact_redirect = QueryResult {
|
||||
node: Some(node.clone()),
|
||||
exact: true,
|
||||
redirect: true,
|
||||
};
|
||||
|
||||
assert!(format!("{some_exact_redirect}").contains(node_id));
|
||||
assert!(!format!("{some_exact_redirect}").contains("No Match"));
|
||||
assert!(format!("{some_exact_redirect}").contains("redirect"));
|
||||
assert!(format!("{some_exact_redirect}").contains("exact"));
|
||||
|
||||
let none_exact_redirect = QueryResult {
|
||||
node: None,
|
||||
exact: true,
|
||||
redirect: true,
|
||||
};
|
||||
|
||||
assert!(!format!("{none_exact_redirect}").contains(node_id));
|
||||
assert!(format!("{none_exact_redirect}").contains("No Match"));
|
||||
assert!(format!("{none_exact_redirect}").contains("redirect"));
|
||||
assert!(format!("{none_exact_redirect}").contains("exact"));
|
||||
|
||||
let none = QueryResult {
|
||||
node: None,
|
||||
exact: false,
|
||||
redirect: false,
|
||||
};
|
||||
|
||||
assert!(!format!("{none}").contains(node_id));
|
||||
assert!(format!("{none}").contains("No Match"));
|
||||
assert!(!format!("{none}").contains("redirect"));
|
||||
assert!(!format!("{none}").contains("exact"));
|
||||
|
||||
let some = QueryResult {
|
||||
node: Some(node.clone()),
|
||||
exact: false,
|
||||
redirect: false,
|
||||
};
|
||||
|
||||
assert!(format!("{some}").contains(node_id));
|
||||
assert!(!format!("{some}").contains("No Match"));
|
||||
assert!(!format!("{some}").contains("redirect"));
|
||||
assert!(!format!("{some}").contains("exact"));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
113
src/log/level.rs
113
src/log/level.rs
|
|
@ -59,28 +59,26 @@ impl From<u16> for Level {
|
|||
|
||||
impl From<&str> for Level {
|
||||
fn from(s: &str) -> Level {
|
||||
if s == "0" || s == "SILENT" || s == "silent" {
|
||||
if s == "0" || s.to_uppercase() == "SILENT" {
|
||||
Level::SILENT
|
||||
} else if s == "1" || s == "FATAL" || s == "fatal" {
|
||||
} else if s == "1" || s.to_uppercase() == "FATAL" {
|
||||
Level::FATAL
|
||||
} else if s == "2" || s == "ERROR" || s == "error" {
|
||||
} else if s == "2" || s.to_uppercase() == "ERROR" {
|
||||
Level::ERROR
|
||||
} else if s == "3"
|
||||
|| s == "WARN"
|
||||
|| s == "warn"
|
||||
|| s == "WARNING"
|
||||
|| s == "warning"
|
||||
|| s.to_uppercase() == "WARN"
|
||||
|| s.to_uppercase() == "WARNING"
|
||||
{
|
||||
Level::WARN
|
||||
} else if s == "4" || s == "INFO" || s == "info" {
|
||||
} else if s == "4" || s.to_uppercase() == "INFO" {
|
||||
Level::INFO
|
||||
} else if s == "5" || s == "DEBUG" || s == "debug" {
|
||||
} else if s == "5" || s.to_uppercase() == "DEBUG" {
|
||||
Level::DEBUG
|
||||
} else if s == "6" || s == "VERBOSE" || s == "verbose" {
|
||||
} else if s == "6" || s.to_uppercase() == "VERBOSE" {
|
||||
Level::VERBOSE
|
||||
} else if s == "7" || s == "TRACE" || s == "trace" {
|
||||
} else if s == "7" || s.to_uppercase() == "TRACE" {
|
||||
Level::TRACE
|
||||
} else if s == "37" || s == "META" || s == "meta" {
|
||||
} else if s == "37" || s.to_uppercase() == "META" {
|
||||
Level::META
|
||||
} else {
|
||||
super::ENV_DEFAULT
|
||||
|
|
@ -103,3 +101,94 @@ impl std::fmt::Display for Level {
|
|||
write!(f, "{s}")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn u16_from_level() {
|
||||
assert_eq!(u16::from(Level::SILENT), 0);
|
||||
assert_eq!(u16::from(Level::FATAL), 1);
|
||||
assert_eq!(u16::from(Level::ERROR), 2);
|
||||
assert_eq!(u16::from(Level::WARN), 3);
|
||||
assert_eq!(u16::from(Level::INFO), 4);
|
||||
assert_eq!(u16::from(Level::DEBUG), 5);
|
||||
assert_eq!(u16::from(Level::VERBOSE), 6);
|
||||
assert_eq!(u16::from(Level::TRACE), 7);
|
||||
assert_eq!(u16::from(Level::META), 37);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn level_from_u16() {
|
||||
assert_eq!(Level::from(0), Level::SILENT);
|
||||
assert_eq!(Level::from(1), Level::FATAL);
|
||||
assert_eq!(Level::from(2), Level::ERROR);
|
||||
assert_eq!(Level::from(3), Level::WARN);
|
||||
assert_eq!(Level::from(4), Level::INFO);
|
||||
assert_eq!(Level::from(5), Level::DEBUG);
|
||||
assert_eq!(Level::from(6), Level::VERBOSE);
|
||||
assert_eq!(Level::from(7), Level::TRACE);
|
||||
assert_eq!(Level::from(37), Level::META);
|
||||
assert_eq!(Level::from(99), Level::WARN);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn level_from_str() {
|
||||
assert_eq!(Level::from("0"), Level::SILENT);
|
||||
assert_eq!(Level::from("SILENT"), Level::SILENT);
|
||||
assert_eq!(Level::from("silent"), Level::SILENT);
|
||||
assert_eq!(Level::from("SiLEnT"), Level::SILENT);
|
||||
|
||||
assert_eq!(Level::from("1"), Level::FATAL);
|
||||
assert_eq!(Level::from("FATAL"), Level::FATAL);
|
||||
assert_eq!(Level::from("fatal"), Level::FATAL);
|
||||
assert_eq!(Level::from("FaTaL"), Level::FATAL);
|
||||
|
||||
assert_eq!(Level::from("3"), Level::WARN);
|
||||
assert_eq!(Level::from("WARN"), Level::WARN);
|
||||
assert_eq!(Level::from("warn"), Level::WARN);
|
||||
assert_eq!(Level::from("WaRn"), Level::WARN);
|
||||
assert_eq!(Level::from("WARNING"), Level::WARN);
|
||||
assert_eq!(Level::from("warning"), Level::WARN);
|
||||
assert_eq!(Level::from("WaRninG"), Level::WARN);
|
||||
|
||||
assert_eq!(Level::from("4"), Level::INFO);
|
||||
assert_eq!(Level::from("INFO"), Level::INFO);
|
||||
assert_eq!(Level::from("info"), Level::INFO);
|
||||
assert_eq!(Level::from("iNFo"), Level::INFO);
|
||||
|
||||
assert_eq!(Level::from("5"), Level::DEBUG);
|
||||
assert_eq!(Level::from("DEBUG"), Level::DEBUG);
|
||||
assert_eq!(Level::from("debug"), Level::DEBUG);
|
||||
assert_eq!(Level::from("deBuG"), Level::DEBUG);
|
||||
|
||||
assert_eq!(Level::from("6"), Level::VERBOSE);
|
||||
assert_eq!(Level::from("VERBOSE"), Level::VERBOSE);
|
||||
assert_eq!(Level::from("verbose"), Level::VERBOSE);
|
||||
assert_eq!(Level::from("VerBosE"), Level::VERBOSE);
|
||||
|
||||
assert_eq!(Level::from("7"), Level::TRACE);
|
||||
assert_eq!(Level::from("TRACE"), Level::TRACE);
|
||||
assert_eq!(Level::from("trace"), Level::TRACE);
|
||||
assert_eq!(Level::from("trAcE"), Level::TRACE);
|
||||
|
||||
assert_eq!(Level::from("37"), Level::META);
|
||||
assert_eq!(Level::from("META"), Level::META);
|
||||
assert_eq!(Level::from("meta"), Level::META);
|
||||
assert_eq!(Level::from("mETa"), Level::META);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_level() {
|
||||
assert_eq!(format!("{}", Level::SILENT), "SILENT");
|
||||
assert_eq!(format!("{}", Level::FATAL), "FATAL");
|
||||
assert_eq!(format!("{}", Level::ERROR), "ERROR");
|
||||
assert_eq!(format!("{}", Level::WARN), "WARNING");
|
||||
assert_eq!(format!("{}", Level::INFO), "INFO");
|
||||
assert_eq!(format!("{}", Level::DEBUG), "DEBUG");
|
||||
assert_eq!(format!("{}", Level::VERBOSE), "VERBOSE");
|
||||
assert_eq!(format!("{}", Level::TRACE), "TRACE");
|
||||
assert_eq!(format!("{}", Level::META), "META");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -168,4 +168,13 @@ mod tests {
|
|||
== "application/json"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn not_found() {
|
||||
let state = GlobalState {
|
||||
graph: Graph::default(),
|
||||
};
|
||||
let response = file(Path("/k/j/m".to_string()), State(state)).await;
|
||||
assert!(response.status() == StatusCode::NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,18 +17,11 @@ pub async fn node(
|
|||
let instant = now();
|
||||
let result = state.graph.find_node(&id);
|
||||
let found = result.node.is_some();
|
||||
let node = result
|
||||
.node
|
||||
.unwrap_or(Node::new(Some(format!("Could not find node ID {id}."))));
|
||||
let node = result.node.unwrap_or(Node::not_found(Some(format!(
|
||||
"Could not find node ID {id}."
|
||||
))));
|
||||
|
||||
if !node.redirect.is_empty() {
|
||||
return Redirect::permanent(
|
||||
format!("/node/{}", node.redirect).as_str(),
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
|
||||
if found && !result.exact {
|
||||
if found && (!result.exact || result.redirect) {
|
||||
return Redirect::permanent(format!("/node/{}", node.id).as_str())
|
||||
.into_response();
|
||||
}
|
||||
|
|
@ -60,7 +53,7 @@ mod tests {
|
|||
http::{HeaderName, StatusCode},
|
||||
};
|
||||
|
||||
use crate::graph::Graph;
|
||||
use crate::graph::{Format, Graph};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
|
@ -108,4 +101,21 @@ mod tests {
|
|||
let response = wrap_node("docs").await;
|
||||
assert_eq!(response.status(), StatusCode::PERMANENT_REDIRECT);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn standalone_graph_redirect() {
|
||||
let toml = "\
|
||||
[nodes.n1]\n\
|
||||
redirect = 'n2'\n\
|
||||
\n\
|
||||
[nodes.n2]\n\
|
||||
text = 'n2 text'\n\
|
||||
";
|
||||
let graph = Graph::from_serial(toml, &Format::TOML).unwrap();
|
||||
let state = GlobalState { graph };
|
||||
let response =
|
||||
node(Path("n1".to_string()), axum::extract::State(state)).await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::PERMANENT_REDIRECT);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,3 +92,52 @@ impl Mime {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
let m = Mime::guess("/home/jane/top/inner/kitty.png");
|
||||
assert_eq!(String::from(m), "image/png");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all() {
|
||||
let pairs = [
|
||||
("file.txt", "text/plain"),
|
||||
("file.csv", "text/csv"),
|
||||
("file.css", "text/css"),
|
||||
("file.ttf", "font/ttf"),
|
||||
("file.otf", "font/otf"),
|
||||
("file.woff", "font/woff"),
|
||||
("file.woff2", "font/woff2"),
|
||||
("file.svg", "image/svg+xml"),
|
||||
("file.ico", "image/x-icon"),
|
||||
("file.jpeg", "image/jpeg"),
|
||||
("file.png", "image/png"),
|
||||
("file.apng", "image/apng"),
|
||||
("caddy.gif", "image/gif"),
|
||||
("file.webp", "image/webp"),
|
||||
("file.avif", "image/avif"),
|
||||
("file.toml", "application/toml"),
|
||||
("file.xml", "application/xml"),
|
||||
("file.json", "application/json"),
|
||||
("file.js", "text/javascript"),
|
||||
("file.pdf", "application/pdf"),
|
||||
("book.epub", "application/epub+zip"),
|
||||
("weird.xzx", "application/octet-stream"),
|
||||
];
|
||||
|
||||
for (file, mime) in pairs {
|
||||
assert_eq!(String::from(Mime::guess(file)), mime);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown() {
|
||||
let u = Mime::guess("x");
|
||||
assert!(matches!(u, Mime::Unknown));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
use std::mem::discriminant;
|
||||
|
||||
use parser::{Token, Lexeme};
|
||||
|
||||
use crate::graph::Graph;
|
||||
|
|
@ -21,6 +23,26 @@ pub struct TokenOutput {
|
|||
pub format_tokens: Vec<Token>,
|
||||
}
|
||||
|
||||
impl TokenOutput {
|
||||
pub fn only(&self, kind: &Token) -> Vec<Token> {
|
||||
let filter = |tokens: &[Token], k: &Token| -> Vec<Token> {
|
||||
tokens
|
||||
.iter()
|
||||
.filter(|&t| discriminant(t) == discriminant(k))
|
||||
.cloned()
|
||||
.collect::<Vec<Token>>()
|
||||
};
|
||||
|
||||
let filtered_tokens = filter(&self.tokens, kind);
|
||||
let filtered_format_tokens = filter(&self.format_tokens, kind);
|
||||
|
||||
[filtered_tokens, filtered_format_tokens]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<Token>>()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(text: &str, graph: &Graph) -> String {
|
||||
parser::read(text, graph)
|
||||
}
|
||||
|
|
@ -28,3 +50,21 @@ pub fn parse(text: &str, graph: &Graph) -> String {
|
|||
pub fn rich_parse(text: &str, graph: &Graph) -> TokenOutput {
|
||||
parser::rich_read(text, graph)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::syntax::content::parser::token::{Bold, Oblique};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn only() {
|
||||
let graph = Graph::default();
|
||||
let output = rich_parse("*four* *bold* and _two_ italic", &graph);
|
||||
let bold_tokens = output.only(&Token::Bold(Bold::new(true)));
|
||||
let italic_tokens = output.only(&Token::Oblique(Oblique::new(true)));
|
||||
println!("{bold_tokens:?}");
|
||||
assert_eq!(bold_tokens.len(), 4);
|
||||
assert_eq!(italic_tokens.len(), 2);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::syntax::content::Parseable as _;
|
||||
use crate::syntax::content::Parseable;
|
||||
|
||||
pub mod anchor;
|
||||
pub mod bold;
|
||||
|
|
|
|||
|
|
@ -15,29 +15,6 @@ pub struct Anchor {
|
|||
}
|
||||
|
||||
impl Anchor {
|
||||
pub fn new(
|
||||
text: &str,
|
||||
destination: &str,
|
||||
node: Option<Node>,
|
||||
node_id: Option<String>,
|
||||
leading: bool,
|
||||
external: bool,
|
||||
balanced: bool,
|
||||
) -> Anchor {
|
||||
let mut anchor = Anchor {
|
||||
text: text.to_owned(),
|
||||
destination: Some(String::from(destination)),
|
||||
node,
|
||||
node_id,
|
||||
leading,
|
||||
external,
|
||||
balanced,
|
||||
};
|
||||
|
||||
anchor.route();
|
||||
anchor
|
||||
}
|
||||
|
||||
pub fn text(&self) -> String {
|
||||
self.text.clone()
|
||||
}
|
||||
|
|
@ -287,4 +264,18 @@ mod tests {
|
|||
anchor.route(); // set_destination also called this
|
||||
assert!(anchor.destination().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_node_id() {
|
||||
let payload = "kxBDJ0EoDVaygxpZ8NgNdQrUIBsGimTs";
|
||||
let mut anchor = Anchor::default();
|
||||
anchor.set_node_id(payload);
|
||||
assert_eq!(anchor.node_id.unwrap(), payload);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_no_destination() {
|
||||
let anchor = Anchor::default();
|
||||
assert_eq!(format!("{anchor}"), "Anchor <empty> -> <unknown>");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,4 +76,10 @@ mod tests {
|
|||
"Tk:Bold [closed]"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flatten() {
|
||||
let bold = Bold::new(false);
|
||||
assert_eq!(bold.flatten(), "");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,4 +75,10 @@ mod tests {
|
|||
"Tk:CheckBox [empty]"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flatten() {
|
||||
let checkbox = CheckBox::new(false);
|
||||
assert_eq!(checkbox.flatten(), "");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -300,4 +300,30 @@ mod tests {
|
|||
"Tk:Header [closed L2]"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_unknown_open_state() {
|
||||
let header = Header {
|
||||
open: None,
|
||||
dom_id: None,
|
||||
level: Level::One,
|
||||
};
|
||||
|
||||
assert_eq!(format!("{header}"), "Header [unknown L1]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_dom_id() {
|
||||
let payload = "WIV0h1wCeY7Pp3FkjgIftJHX1I6YvnSc";
|
||||
let header = Header {
|
||||
open: None,
|
||||
dom_id: Some(payload.to_string()),
|
||||
level: Level::One,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
format!("{header}"),
|
||||
format!("Header [unknown L1 DOM ID {payload}]")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,4 +89,10 @@ mod tests {
|
|||
"Tk:Item [<unknown>] dRMy4"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flatten() {
|
||||
let item = Item::new("", None);
|
||||
assert_eq!(item.flatten(), "");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,4 +189,23 @@ mod tests {
|
|||
let lexeme = Lexeme::new("SL6PX", "6xsNB", "oeAHa");
|
||||
List::lex(&lexeme);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ordered_list() {
|
||||
let mut list = List::new(true);
|
||||
list.items = vec![
|
||||
Item::new("a", Some(0)),
|
||||
Item::new("b", Some(0)),
|
||||
Item::new("c", Some(0)),
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
list.render(),
|
||||
"\n<ol>\n\
|
||||
<li>a</li>\n\
|
||||
<li>b</li>\n\
|
||||
<li>c</li>\n\
|
||||
</ol>\n\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,4 +79,10 @@ mod tests {
|
|||
"Tk:Oblique [closed]"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flatten() {
|
||||
let oblique = Oblique::new(false);
|
||||
assert_eq!(oblique.flatten(), "");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,4 +99,10 @@ mod tests {
|
|||
"Tk:PreFormat [unknown]"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flatten() {
|
||||
let preformat = PreFormat::new(false);
|
||||
assert_eq!(preformat.flatten(), "");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,4 +76,10 @@ mod tests {
|
|||
"Tk:Strike [closed]"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flatten() {
|
||||
let strike = Strike::new(false);
|
||||
assert_eq!(strike.flatten(), "");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,4 +79,10 @@ mod tests {
|
|||
"Tk:Underline [closed]"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flatten() {
|
||||
let underline = Underline::new(false);
|
||||
assert_eq!(underline.flatten(), "");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -320,6 +320,7 @@ td, th {
|
|||
|
||||
summary {
|
||||
margin-bottom: 15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
hr {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue