Expand test coverage

This commit is contained in:
Juno Takano 2026-01-10 05:42:36 -03:00
commit 5958f1551b
27 changed files with 593 additions and 109 deletions

View file

@ -117,6 +117,7 @@ mod tests {
"/",
"/about",
"/tree",
"/data",
"/node/Syntax",
"/static/style.css",
"/static/favicon.svg",

View file

@ -88,4 +88,10 @@ mod tests {
let response = node(Path("syntax".to_string())).await;
assert_eq!(response.status(), StatusCode::PERMANENT_REDIRECT);
}
#[tokio::test]
async fn docs_redirect() {
let response = node(Path("docs".to_string())).await;
assert_eq!(response.status(), StatusCode::PERMANENT_REDIRECT);
}
}

View file

@ -76,4 +76,13 @@ mod tests {
let response = search(query).await;
assert!(response.status_code() == StatusCode::PERMANENT_REDIRECT);
}
#[tokio::test]
async fn dedicated_redirect_endpoint() {
let query = Form(Query {
node: String::from("syNTaX"),
});
let response = redirect(query).await;
assert!(response.status_code() == StatusCode::PERMANENT_REDIRECT);
}
}

View file

@ -47,3 +47,16 @@ pub fn close(state: &State, tokens: &mut Vec<Token>) {
Block::None => (),
}
}
#[cfg(test)]
mod tests {
use crate::syntax::content::parser::{context::Block, state::State};
#[test]
#[should_panic(expected = "End of input with open list")]
fn open_list_eoi() {
let mut state = State::default();
state.context.block = Block::List;
super::close(&state, &mut vec![]);
}
}

View file

@ -207,6 +207,43 @@ mod tests {
);
}
#[test]
fn leading_multiword_anchor() {
assert_eq!(
read("interactions are |basic elements| of systems"),
r#"<p>interactions are <a href="/node/basic elements">basic elements</a> of systems</p>"#
);
}
#[test]
fn explicit_end_of_destination() {
assert_eq!(
read("interactions are |basic elements|BasicElements| of systems"),
r#"<p>interactions are <a href="/node/BasicElements">basic elements</a> of systems</p>"#
);
}
#[test]
fn explicit_end_of_external_destination() {
assert_eq!(
read("this |anchor example|https://example.com| is external"),
r#"<p>this <a href="https://example.com">anchor example</a> is external</p>"#
);
}
#[test]
fn anchor_destination_at_eoi() {
assert_eq!(read("a |b c|d"), r#"<p>a <a href="/node/d">b c</a></p>"#);
}
#[test]
fn external_anchor_destination_at_eoi() {
assert_eq!(
read("a b|https://example.com"),
r#"<p>a <a href="https://example.com">b</a></p>"#
);
}
#[test]
fn nonleading_plural_anchor_at_eoi() {
assert_eq!(

View file

@ -85,7 +85,12 @@ pub fn parse(
#[cfg(test)]
mod tests {
use crate::{syntax::content::parser, types::Graph};
use crate::{
syntax::content::parser::{
self, context::list::parse, lexeme::Lexeme, state::State,
},
types::{Config, Graph},
};
fn read(input: &str) -> String {
parser::read(input, &Graph::new(None).meta.config)
@ -260,4 +265,21 @@ mod tests {
</ul>\n\n"
);
}
#[test]
#[should_panic(
expected = "List context parser called to handle non-list context"
)]
fn bad_context() {
let mut state = State::default();
let lexemes = Lexeme::collect(&["a", "b", "c"].map(str::to_string));
let config = Config::default();
parse(
&Lexeme::default(),
&mut state,
&mut vec![],
&mut lexemes.iter().peekable(),
&config,
);
}
}

View file

@ -196,7 +196,7 @@ impl Lexeme {
text: last.clone(),
next: String::default(),
third: String::default(),
first: false,
first: segments.is_empty(),
last: true,
};
@ -241,12 +241,12 @@ impl fmt::Display for Lexeme {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use crate::dev::wrap;
let properties = if self.first {
"[F] "
let properties = if self.last && self.first {
"[S] "
} else if self.last {
"[L] "
} else if self.last && self.first {
"[FL] "
} else if self.first {
"[F] "
} else {
""
};
@ -254,11 +254,11 @@ impl fmt::Display for Lexeme {
let next_display = if self.last {
" <EOI>"
} else if self.third.is_empty() {
&format!("-> {} -! EOI", wrap(&self.next))
&format!(" -> {} <EOI>", wrap(&self.next))
} else {
&format!("-> {} -> {}", wrap(&self.next), wrap(&self.third))
&format!(" -> {} -> {}", wrap(&self.next), wrap(&self.third))
};
write!(f, "Lx {}{} {}", properties, wrap(&self.text), next_display)
write!(f, "Lx {}{}{}", properties, wrap(&self.text), next_display)
}
}
@ -319,4 +319,114 @@ mod tests {
let lexeme = Lexeme::new(payload, "", "");
assert_eq!(lexeme.count_char('j'), 3);
}
#[test]
fn mutate_text() {
let mut lexeme = Lexeme::new("b71Je", "I6y3i", "LC8na");
lexeme.mutate_text("qkjjK2");
assert_eq!(lexeme.text(), "qkjjK2");
}
#[test]
fn third_as_char() {
let lexeme_a = Lexeme::new("1", "2", "3");
assert_eq!(lexeme_a.third_as_char().unwrap(), '3');
let lexeme_c = Lexeme::new("a", "b", "");
assert!(lexeme_c.third_as_char().is_none());
}
#[test]
fn match_third_char() {
let lexeme = Lexeme::new("1", "2", "3");
assert!(lexeme.match_third_char('3'));
}
#[test]
fn match_next_either_char() {
let lexeme = Lexeme::new("1", "2", "3");
assert!(lexeme.match_next_either_char('x', '2'));
assert!(lexeme.match_next_either_char('2', 'x'));
}
#[test]
fn match_triple() {
let lexeme = Lexeme::new("1", "2", "3");
assert!(lexeme.match_char_triple('1', '2', '3'));
}
#[test]
fn is_punctuation() {
let delimiters = Delimiters::default();
let mut lexemes: Vec<Lexeme> = vec![];
for p in delimiters.punctuation {
lexemes.push(Lexeme::new(&p.to_string(), "", ""));
}
for lexeme in lexemes {
assert!(lexeme.is_punctuation());
}
}
#[test]
fn is_next_punctuation() {
let delimiters = Delimiters::default();
let mut lexemes: Vec<Lexeme> = vec![];
for p in delimiters.punctuation {
lexemes.push(Lexeme::new("", &p.to_string(), ""));
}
for lexeme in lexemes {
assert!(lexeme.is_next_punctuation());
}
}
#[test]
fn match_last_char() {
let lexeme = Lexeme::new("qYBWuNX", "", "");
assert!(lexeme.match_last_char('X'));
}
#[test]
fn match_next_last_char() {
let lexeme = Lexeme::new("", "teDAqVx", "");
assert!(lexeme.match_next_first_char('t'));
}
#[test]
fn display() {
let input = ["pcdA", "o32X", "kz2i", "79Lz"].map(str::to_string);
let lexemes = Lexeme::collect(&input);
let first = lexemes.first().unwrap();
let second = lexemes.get(1).unwrap();
let third = lexemes.get(2).unwrap();
let last = lexemes.last().unwrap();
assert_eq!(
format!("{first}"),
String::from("Lx [F] pcdA -> o32X -> kz2i"),
"first"
);
assert_eq!(
format!("{second}"),
String::from("Lx o32X -> kz2i -> 79Lz"),
"second"
);
assert_eq!(
format!("{third}"),
String::from("Lx kz2i -> 79Lz <EOI>"),
"third"
);
assert_eq!(
format!("{last}"),
String::from("Lx [L] 79Lz <EOI>"),
"last"
);
let input_single = ["9fOC"].map(str::to_string);
let lexemes_single = Lexeme::collect(&input_single);
let single = lexemes_single.first().unwrap();
println!("{single:#?}");
assert!(input_single.to_vec().len() == 1);
assert_eq!(format!("{single}"), "Lx [S] 9fOC <EOI>");
}
}

View file

@ -103,4 +103,15 @@ mod tests {
fn trailing_oblique_with_newline() {
assert_eq!(read("see _acks_\n"), "<p>see <em>acks</em></p>");
}
#[test]
fn underline() {
assert_eq!(read("__u__"), "<p><u>u</u></p>");
}
#[test]
fn checkbox() {
assert_eq!(read("[ ]"), r#"<p><input type="checkbox"/></p>"#);
assert_eq!(read("[x]"), r#"<p><input type="checkbox" checked /></p>"#);
}
}

View file

@ -94,3 +94,56 @@ impl Default for State {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn anchor_buffer_display() {
let buffer = AnchorBuffer::default();
println!("{buffer:#?}");
println!("{buffer}");
assert_eq!(
format!("{buffer}"),
"AnchorBuffer [] >> Anchor <empty> -> <unknown>"
);
}
#[test]
fn anchor_buffer_display_with_text_set() {
let mut buffer = AnchorBuffer::default();
buffer.text = String::from("mX8Z7yWmsK");
println!("{buffer:#?}");
println!("{buffer}");
assert_eq!(
format!("{buffer}"),
r#"AnchorBuffer [text: "mX8Z7yWmsK"] >> Anchor <empty> -> <unknown>"#
);
}
#[test]
fn anchor_buffer_display_with_destination_set() {
let mut buffer = AnchorBuffer::default();
buffer.destination = String::from("VP2aqGngAq");
println!("{buffer:#?}");
println!("{buffer}");
assert_eq!(
format!("{buffer}"),
r#"AnchorBuffer [, dest: "VP2aqGngAq"] >> Anchor <empty> -> <unknown>"#
);
}
#[test]
fn anchor_buffer_display_with_text_and_destination_set() {
let mut buffer = AnchorBuffer::default();
buffer.text = String::from("ECJrzgkBHg");
buffer.destination = String::from("9dy6gQ2g3E");
println!("{buffer:#?}");
println!("{buffer}");
assert_eq!(
format!("{buffer}"),
r#"AnchorBuffer [text: "ECJrzgkBHg", dest: "9dy6gQ2g3E"] >> Anchor <empty> -> <unknown>"#
);
}
}

View file

@ -31,7 +31,6 @@ pub enum Token {
Oblique(oblique::Oblique),
Paragraph(paragraph::Paragraph),
PreFormat(preformat::PreFormat),
Span(span::Span),
Underline(underline::Underline),
}
@ -51,7 +50,6 @@ impl Token {
Token::Oblique(ref d) => d.render(),
Token::Paragraph(ref d) => d.render(),
Token::PreFormat(ref d) => d.render(),
Token::Span(ref d) => d.render(),
Token::Underline(ref d) => d.render(),
}
}
@ -73,7 +71,6 @@ impl std::fmt::Display for Token {
Token::Oblique(ref d) => format!("{d}"),
Token::Paragraph(ref d) => format!("{d}"),
Token::PreFormat(ref d) => format!("{d}"),
Token::Span(ref d) => format!("{d}"),
Token::Underline(ref d) => format!("{d}"),
};

View file

@ -45,15 +45,22 @@ impl std::fmt::Display for Anchor {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use crate::dev::wrap;
let wrapped_text = wrap(&self.text);
let display_text = if wrapped_text.is_empty() {
"<empty>"
} else {
wrapped_text.as_str()
};
let display_destination = match self.destination {
Some(ref destination) => {
if destination.is_empty() {
"<empty>"
String::from("<empty>")
} else {
destination
format!("{destination:?}")
}
},
None => "<unknown>",
None => String::from("<unknown>"),
};
let mut tail = String::default();
@ -68,13 +75,7 @@ impl std::fmt::Display for Anchor {
tail.push_str(" +External");
}
write!(
f,
"Anchor {:?} -> {:?}{}",
wrap(&self.text),
display_destination,
tail
)
write!(f, "Anchor {display_text} -> {display_destination}{tail}")
}
}
@ -107,6 +108,8 @@ impl Anchor {
#[cfg(test)]
mod tests {
use crate::syntax::content::parser::token::Token;
use super::*;
#[test]
@ -133,4 +136,36 @@ mod tests {
let anchor = Anchor::default();
drop(anchor.render());
}
#[test]
fn token_display() {
let mut anchor = Anchor::default();
assert_eq!(
format!("{}", Token::Anchor(anchor.clone())),
"Tk:Anchor <empty> -> <unknown>",
);
anchor.text = String::from("FsJAt RTggA");
assert_eq!(
format!("{}", Token::Anchor(anchor.clone())),
"Tk:Anchor 'FsJAt RTggA' -> <unknown>",
);
anchor.text = String::from("wPVo1 0OmYm");
anchor.destination = Some(String::from("M1UEp 1gbfr"));
assert_eq!(
format!("{}", Token::Anchor(anchor.clone())),
r#"Tk:Anchor 'wPVo1 0OmYm' -> "M1UEp 1gbfr""#,
);
anchor.balanced = true;
anchor.leading = true;
anchor.external = true;
assert_eq!(
format!("{}", Token::Anchor(anchor.clone())),
"Tk:Anchor 'wPVo1 0OmYm' -> \"M1UEp 1gbfr\" \
+Leading +Balanced +External",
);
}
}

View file

@ -40,6 +40,8 @@ impl std::fmt::Display for Bold {
#[cfg(test)]
mod tests {
use crate::syntax::content::parser::token::Token;
use super::*;
#[test]
@ -58,4 +60,16 @@ mod tests {
fn lex() {
Bold::lex(&Lexeme::default());
}
#[test]
fn token_display() {
let mut bold = Bold::new(true);
assert_eq!(format!("{}", Token::Bold(bold.clone())), "Tk:Bold [open]");
bold.open = false;
assert_eq!(
format!("{}", Token::Bold(bold.clone())),
"Tk:Bold [closed]"
);
}
}

View file

@ -44,6 +44,8 @@ impl std::fmt::Display for CheckBox {
#[cfg(test)]
mod tests {
use crate::syntax::content::parser::token::Token;
use super::*;
#[test]
@ -54,4 +56,19 @@ mod tests {
let code_closed = CheckBox::new(false);
assert_eq!(code_closed.render(), r#"<input type="checkbox"/>"#);
}
#[test]
fn token_display() {
let mut checkbox = CheckBox::new(true);
assert_eq!(
format!("{}", Token::CheckBox(checkbox.clone())),
"Tk:CheckBox [checked]"
);
checkbox.checked = false;
assert_eq!(
format!("{}", Token::CheckBox(checkbox.clone())),
"Tk:CheckBox [empty]"
);
}
}

View file

@ -40,6 +40,8 @@ impl std::fmt::Display for Code {
#[cfg(test)]
mod tests {
use crate::syntax::content::parser::token::Token;
use super::*;
#[test]
@ -58,4 +60,16 @@ mod tests {
fn lex() {
Code::lex(&Lexeme::default());
}
#[test]
fn token_display() {
let mut code = Code::new(true);
assert_eq!(format!("{}", Token::Code(code.clone())), "Tk:Code [open]");
code.open = false;
assert_eq!(
format!("{}", Token::Code(code.clone())),
"Tk:Code [closed]"
);
}
}

View file

@ -191,6 +191,8 @@ impl Display for Level {
#[cfg(test)]
mod tests {
use crate::syntax::content::parser::token::Token;
use super::*;
#[test]
@ -285,4 +287,13 @@ mod tests {
header.render();
}
#[test]
fn token_display() {
let header = Header::from_u8(2, false, None);
assert_eq!(
format!("{}", Token::Header(header)),
"Tk:Header [closed L2]"
);
}
}

View file

@ -43,3 +43,46 @@ impl std::fmt::Display for Item {
)
}
}
#[cfg(test)]
mod tests {
use crate::syntax::content::parser::token::Token;
use super::*;
#[test]
#[should_panic(
expected = "Items should only be rendered by a list's render method"
)]
fn render() {
let item = Item::new("aCNuZwwzrt", None);
item.render();
}
#[test]
fn probe() {
let lexeme = Lexeme::new("bOa", "2R6", "4Mp");
assert!(!Item::probe(&lexeme));
}
#[test]
#[should_panic(expected = "Attempt to lex an item directly from a lexeme")]
fn lex() {
let lexeme = Lexeme::new("8kbX", "Qzqu", "iDpg");
Item::lex(&lexeme);
}
#[test]
fn token_display() {
let mut item = Item::new("dRMy4", Some(4));
assert_eq!(
format!("{}", Token::Item(item.clone())),
"Tk:Item [D4] dRMy4"
);
item.depth = None;
assert_eq!(
format!("{}", Token::Item(item.clone())),
"Tk:Item [<unknown>] dRMy4"
);
}
}

View file

@ -24,3 +24,16 @@ impl std::fmt::Display for LineBreak {
write!(f, "LineBreak")
}
}
#[cfg(test)]
mod tests {
use crate::syntax::content::parser::token::Token;
use super::*;
#[test]
fn token_display() {
let linebreak = LineBreak::default();
assert_eq!(format!("{}", Token::LineBreak(linebreak)), "Tk:LineBreak");
}
}

View file

@ -90,6 +90,8 @@ impl std::fmt::Display for List {
#[cfg(test)]
mod tests {
use crate::syntax::content::parser::token::Token;
use super::*;
#[test]
@ -164,4 +166,20 @@ mod tests {
</ul>\n\n"
);
}
#[test]
fn token_display() {
let list = List::new(false);
assert_eq!(
format!("{}", Token::List(list.clone())),
"Tk:List [0 unordered items]"
);
}
#[test]
#[should_panic(expected = "Attempt to lex a List directly from a lexeme")]
fn lex() {
let lexeme = Lexeme::new("SL6PX", "6xsNB", "oeAHa");
List::lex(&lexeme);
}
}

View file

@ -26,3 +26,27 @@ impl std::fmt::Display for Literal {
write!(f, "Literal {}", crate::dev::wrap(&self.text))
}
}
#[cfg(test)]
mod tests {
use crate::syntax::content::parser::token::Token;
use super::*;
#[test]
fn token_display() {
let mut literal = Literal {
text: String::from("MYpDT"),
};
assert_eq!(
format!("{}", Token::Literal(literal.clone())),
"Tk:Literal MYpDT"
);
literal.text = String::from("TjY02");
assert_eq!(
format!("{}", Token::Literal(literal.clone())),
"Tk:Literal TjY02"
);
}
}

View file

@ -40,6 +40,8 @@ impl std::fmt::Display for Oblique {
#[cfg(test)]
mod tests {
use crate::syntax::content::parser::token::Token;
use super::*;
#[test]
@ -58,4 +60,19 @@ mod tests {
fn lex() {
Oblique::lex(&Lexeme::default());
}
#[test]
fn token_display() {
let mut oblique = Oblique::new(true);
assert_eq!(
format!("{}", Token::Oblique(oblique.clone())),
"Tk:Oblique [open]"
);
oblique.open = false;
assert_eq!(
format!("{}", Token::Oblique(oblique.clone())),
"Tk:Oblique [closed]"
);
}
}

View file

@ -58,6 +58,8 @@ impl std::fmt::Display for Paragraph {
#[cfg(test)]
mod tests {
use crate::syntax::content::parser::token::Token;
use super::*;
#[test]
@ -74,4 +76,25 @@ mod tests {
let p = Paragraph::lex(&Lexeme::default());
drop(p.render());
}
#[test]
fn token_display() {
let mut paragraph = Paragraph::new(true);
assert_eq!(
format!("{}", Token::Paragraph(paragraph.clone())),
"Tk:Paragraph [open]"
);
paragraph.open = Some(false);
assert_eq!(
format!("{}", Token::Paragraph(paragraph.clone())),
"Tk:Paragraph [closed]"
);
paragraph.open = None;
assert_eq!(
format!("{}", Token::Paragraph(paragraph.clone())),
"Tk:Paragraph [unknown]"
);
}
}

View file

@ -50,6 +50,8 @@ impl Parseable for PreFormat {
#[cfg(test)]
mod tests {
use crate::syntax::content::parser::token::Token;
use super::*;
#[test]
@ -72,4 +74,25 @@ mod tests {
let from_non_empty_lexeme = PreFormat::lex(&Lexeme::default());
from_non_empty_lexeme.render();
}
#[test]
fn token_display() {
let mut preformat = PreFormat::new(true);
assert_eq!(
format!("{}", Token::PreFormat(preformat.clone())),
"Tk:PreFormat [open]"
);
preformat.open = Some(false);
assert_eq!(
format!("{}", Token::PreFormat(preformat.clone())),
"Tk:PreFormat [closed]"
);
preformat.open = None;
assert_eq!(
format!("{}", Token::PreFormat(preformat.clone())),
"Tk:PreFormat [unknown]"
);
}
}

View file

@ -1,85 +1 @@
use crate::syntax::content::{Parseable, parser::lexeme::Lexeme};
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Span {
open: Option<bool>,
}
impl Span {
pub fn new(open: bool) -> Span {
Span { open: Some(open) }
}
}
impl Parseable for Span {
fn probe(_lexeme: &Lexeme) -> bool {
// there is no lexeme for span
false
}
fn lex(_lexeme: &Lexeme) -> Span {
Span { open: None }
}
fn render(&self) -> String {
if let Some(open) = self.open {
if open {
"<span>".to_owned()
} else {
"</span>".to_owned()
}
} else {
panic!("Attempt to render a span tag while open state is unknown")
}
}
}
impl std::fmt::Display for Span {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let display_open_state = match self.open {
Some(ref open_state) => {
if *open_state {
"open"
} else {
"closed"
}
},
None => "unknown",
};
write!(f, "Span [{display_open_state}]")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn probe() {
assert!(!Span::probe(&Lexeme::default()));
}
#[test]
fn lex() {
let span = Span::lex(&Lexeme::default());
assert!(span.open.is_none());
}
#[test]
fn render() {
let open_span = Span::new(true);
assert_eq!(open_span.render(), "<span>");
let closed_span = Span::new(false);
assert_eq!(closed_span.render(), "</span>");
}
#[test]
#[should_panic(
expected = "Attempt to render a span tag while open state is unknown"
)]
fn render_unknown_open_state() {
let open_span = Span::lex(&Lexeme::default());
drop(open_span.render());
}
}

View file

@ -37,6 +37,8 @@ impl std::fmt::Display for Strike {
#[cfg(test)]
mod tests {
use crate::syntax::content::parser::token::Token;
use super::*;
#[test]
@ -55,4 +57,19 @@ mod tests {
fn lex() {
Strike::lex(&Lexeme::default());
}
#[test]
fn token_display() {
let mut strike = Strike::new(true);
assert_eq!(
format!("{}", Token::Strike(strike.clone())),
"Tk:Strike [open]"
);
strike.open = false;
assert_eq!(
format!("{}", Token::Strike(strike.clone())),
"Tk:Strike [closed]"
);
}
}

View file

@ -40,6 +40,8 @@ impl std::fmt::Display for Underline {
#[cfg(test)]
mod tests {
use crate::syntax::content::parser::token::Token;
use super::*;
#[test]
@ -58,4 +60,19 @@ mod tests {
fn lex() {
Underline::lex(&Lexeme::default());
}
#[test]
fn token_display() {
let mut underline = Underline::new(true);
assert_eq!(
format!("{}", Token::Underline(underline.clone())),
"Tk:Underline [open]"
);
underline.open = false;
assert_eq!(
format!("{}", Token::Underline(underline.clone())),
"Tk:Underline [closed]"
);
}
}