♻️ Splitting tests for file server into their own tests.rs
parent
aad7f8e729
commit
47c59447f3
|
@ -1,6 +1,6 @@
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive (Error, Debug)]
|
#[derive (Debug, Error)]
|
||||||
pub enum ConfigError {
|
pub enum ConfigError {
|
||||||
#[error ("I/O error")]
|
#[error ("I/O error")]
|
||||||
Io (#[from] std::io::Error),
|
Io (#[from] std::io::Error),
|
||||||
|
@ -23,13 +23,13 @@ pub enum ConfigError {
|
||||||
|
|
||||||
// I'm not sure how important this is, but it was already in the code
|
// I'm not sure how important this is, but it was already in the code
|
||||||
|
|
||||||
#[derive (Error, Debug)]
|
#[derive (Debug, Error)]
|
||||||
pub enum ShuttingDownError {
|
pub enum ShuttingDownError {
|
||||||
#[error ("Relay is shutting down")]
|
#[error ("Relay is shutting down")]
|
||||||
ShuttingDown,
|
ShuttingDown,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive (Error, Debug)]
|
#[derive (Debug, Error)]
|
||||||
pub enum HandleHttpResponseError {
|
pub enum HandleHttpResponseError {
|
||||||
#[error ("HTTP error")]
|
#[error ("HTTP error")]
|
||||||
Http (#[from] http::Error),
|
Http (#[from] http::Error),
|
||||||
|
@ -50,7 +50,7 @@ pub enum HandleHttpResponseError {
|
||||||
RelayingTaskPanicked (#[from] tokio::task::JoinError),
|
RelayingTaskPanicked (#[from] tokio::task::JoinError),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive (Error, Debug)]
|
#[derive (Debug, Error)]
|
||||||
pub enum RequestError {
|
pub enum RequestError {
|
||||||
#[error ("HTTP error")]
|
#[error ("HTTP error")]
|
||||||
Http (#[from] http::Error),
|
Http (#[from] http::Error),
|
||||||
|
@ -68,7 +68,7 @@ pub enum RequestError {
|
||||||
Mysterious,
|
Mysterious,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive (Error, Debug)]
|
#[derive (Debug, Error)]
|
||||||
pub enum RelayError {
|
pub enum RelayError {
|
||||||
#[error ("Handlebars template file error")]
|
#[error ("Handlebars template file error")]
|
||||||
TemplateFile (#[from] handlebars::TemplateFileError),
|
TemplateFile (#[from] handlebars::TemplateFileError),
|
||||||
|
|
|
@ -22,6 +22,7 @@ reqwest = { version = "0.10.8", features = ["stream"] }
|
||||||
rmp-serde = "0.14.4"
|
rmp-serde = "0.14.4"
|
||||||
serde = {version = "1.0.117", features = ["derive"]}
|
serde = {version = "1.0.117", features = ["derive"]}
|
||||||
structopt = "0.3.20"
|
structopt = "0.3.20"
|
||||||
|
thiserror = "1.0.22"
|
||||||
tokio = { version = "0.2.22", features = ["full"] }
|
tokio = { version = "0.2.22", features = ["full"] }
|
||||||
tracing = "0.1.21"
|
tracing = "0.1.21"
|
||||||
tracing-futures = "0.2.4"
|
tracing-futures = "0.2.4"
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive (Debug, Error)]
|
||||||
|
pub enum ServerError {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive (Debug, Error)]
|
||||||
|
pub enum FileServerError {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -46,6 +46,8 @@ use ptth_core::{
|
||||||
prefix_match,
|
prefix_match,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mod errors;
|
||||||
|
|
||||||
#[derive (Debug, Serialize)]
|
#[derive (Debug, Serialize)]
|
||||||
pub struct ServerInfo {
|
pub struct ServerInfo {
|
||||||
pub server_name: String,
|
pub server_name: String,
|
||||||
|
@ -680,221 +682,4 @@ fn pretty_print_bytes (b: u64) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg (test)]
|
#[cfg (test)]
|
||||||
mod tests {
|
mod tests;
|
||||||
use std::{
|
|
||||||
ffi::OsStr,
|
|
||||||
path::{
|
|
||||||
Component,
|
|
||||||
Path,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
use maplit::*;
|
|
||||||
use tokio::runtime::Runtime;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn icons () {
|
|
||||||
let video = "🎞️";
|
|
||||||
let picture = "📷";
|
|
||||||
let file = "📄";
|
|
||||||
|
|
||||||
for (input, expected) in vec! [
|
|
||||||
("copying_is_not_theft.mp4", video),
|
|
||||||
("copying_is_not_theft.avi", video),
|
|
||||||
("copying_is_not_theft.mkv", video),
|
|
||||||
("copying_is_not_theft.webm", video),
|
|
||||||
("lolcats.jpg", picture),
|
|
||||||
("lolcats.jpeg", picture),
|
|
||||||
("lolcats.png", picture),
|
|
||||||
("lolcats.bmp", picture),
|
|
||||||
("ptth.log", file),
|
|
||||||
("README.md", file),
|
|
||||||
("todo.txt", file),
|
|
||||||
].into_iter () {
|
|
||||||
assert_eq! (super::get_icon (input), expected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_range_header () {
|
|
||||||
for (input, expected) in vec! [
|
|
||||||
("", (None, None)),
|
|
||||||
("bytes=0-", (Some (0), None)),
|
|
||||||
("bytes=0-999", (Some (0), Some (1000))),
|
|
||||||
("bytes=111-999", (Some (111), Some (1000))),
|
|
||||||
].into_iter () {
|
|
||||||
let actual = super::parse_range_header (input);
|
|
||||||
assert_eq! (actual, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
use super::ParsedRange::*;
|
|
||||||
|
|
||||||
for (header, file_len, expected) in vec! [
|
|
||||||
(None, 0, Ok (0..0)),
|
|
||||||
(None, 1024, Ok (0..1024)),
|
|
||||||
|
|
||||||
(Some (""), 0, RangeNotSatisfiable (0)),
|
|
||||||
(Some (""), 1024, PartialContent (0..1024)),
|
|
||||||
|
|
||||||
(Some ("bytes=0-"), 1024, PartialContent (0..1024)),
|
|
||||||
(Some ("bytes=0-999"), 1024, PartialContent (0..1000)),
|
|
||||||
(Some ("bytes=0-1023"), 1024, PartialContent (0..1024)),
|
|
||||||
(Some ("bytes=111-999"), 1024, PartialContent (111..1000)),
|
|
||||||
(Some ("bytes=111-1023"), 1024, PartialContent (111..1024)),
|
|
||||||
(Some ("bytes=200-100"), 1024, RangeNotSatisfiable (1024)),
|
|
||||||
|
|
||||||
(Some ("bytes=0-"), 512, PartialContent (0..512)),
|
|
||||||
(Some ("bytes=0-1023"), 512, RangeNotSatisfiable (512)),
|
|
||||||
(Some ("bytes=1000-1023"), 512, RangeNotSatisfiable (512)),
|
|
||||||
].into_iter () {
|
|
||||||
let actual = super::check_range (header, file_len);
|
|
||||||
assert_eq! (actual, expected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn pretty_print_bytes () {
|
|
||||||
for (input_after, expected_before, expected_after) in vec! [
|
|
||||||
(1, "0 B", "1 B"),
|
|
||||||
(1024, "1023 B", "1 KiB"),
|
|
||||||
(1024 + 512, "1 KiB", "2 KiB"),
|
|
||||||
(1023 * 1024 + 512, "1023 KiB", "1 MiB"),
|
|
||||||
((1024 + 512) * 1024, "1 MiB", "2 MiB"),
|
|
||||||
(1023 * 1024 * 1024 + 512 * 1024, "1023 MiB", "1 GiB"),
|
|
||||||
((1024 + 512) * 1024 * 1024, "1 GiB", "2 GiB"),
|
|
||||||
|
|
||||||
].into_iter () {
|
|
||||||
let actual = super::pretty_print_bytes (input_after - 1);
|
|
||||||
assert_eq! (&actual, expected_before);
|
|
||||||
|
|
||||||
let actual = super::pretty_print_bytes (input_after);
|
|
||||||
assert_eq! (&actual, expected_after);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn i_hate_paths () {
|
|
||||||
let mut components = Path::new ("/home/user").components ();
|
|
||||||
|
|
||||||
assert_eq! (components.next (), Some (Component::RootDir));
|
|
||||||
assert_eq! (components.next (), Some (Component::Normal (OsStr::new ("home"))));
|
|
||||||
assert_eq! (components.next (), Some (Component::Normal (OsStr::new ("user"))));
|
|
||||||
assert_eq! (components.next (), None);
|
|
||||||
|
|
||||||
let mut components = Path::new ("./home/user").components ();
|
|
||||||
|
|
||||||
assert_eq! (components.next (), Some (Component::CurDir));
|
|
||||||
assert_eq! (components.next (), Some (Component::Normal (OsStr::new ("home"))));
|
|
||||||
assert_eq! (components.next (), Some (Component::Normal (OsStr::new ("user"))));
|
|
||||||
assert_eq! (components.next (), None);
|
|
||||||
|
|
||||||
let mut components = Path::new (".").components ();
|
|
||||||
|
|
||||||
assert_eq! (components.next (), Some (Component::CurDir));
|
|
||||||
assert_eq! (components.next (), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn file_server () {
|
|
||||||
use ptth_core::{
|
|
||||||
http_serde::Method,
|
|
||||||
};
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
tracing_subscriber::fmt ().try_init ().ok ();
|
|
||||||
let mut rt = Runtime::new ().unwrap ();
|
|
||||||
|
|
||||||
rt.block_on (async {
|
|
||||||
let file_server_root = PathBuf::from ("./");
|
|
||||||
let headers = Default::default ();
|
|
||||||
|
|
||||||
{
|
|
||||||
use InternalResponse::*;
|
|
||||||
|
|
||||||
let bad_passwords_path = "/files/src/bad_passwords.txt";
|
|
||||||
|
|
||||||
for (uri_path, expected) in vec! [
|
|
||||||
("/", Root),
|
|
||||||
("/files", Redirect ("files/".to_string ())),
|
|
||||||
("/files/?", InvalidQuery),
|
|
||||||
("/files/src", Redirect ("src/".to_string ())),
|
|
||||||
("/files/src/?", InvalidQuery),
|
|
||||||
(bad_passwords_path, ServeFile (ServeFileParams {
|
|
||||||
send_body: true,
|
|
||||||
range: 0..1_048_576,
|
|
||||||
range_requested: false,
|
|
||||||
file: AlwaysEqual::testing_blank (),
|
|
||||||
})),
|
|
||||||
("/files/test/test.md", ServeFile (ServeFileParams {
|
|
||||||
send_body: true,
|
|
||||||
range: 0..144,
|
|
||||||
range_requested: false,
|
|
||||||
file: AlwaysEqual::testing_blank (),
|
|
||||||
})),
|
|
||||||
("/ ", InvalidUri),
|
|
||||||
].into_iter () {
|
|
||||||
let resp = internal_serve_all (
|
|
||||||
&file_server_root,
|
|
||||||
Method::Get,
|
|
||||||
uri_path,
|
|
||||||
&headers,
|
|
||||||
None
|
|
||||||
).await;
|
|
||||||
|
|
||||||
assert_eq! (resp, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
let resp = internal_serve_all (
|
|
||||||
&file_server_root,
|
|
||||||
Method::Get,
|
|
||||||
bad_passwords_path,
|
|
||||||
&hashmap! {
|
|
||||||
"range".into () => b"bytes=0-2000000".to_vec (),
|
|
||||||
},
|
|
||||||
None
|
|
||||||
).await;
|
|
||||||
|
|
||||||
assert_eq! (resp, RangeNotSatisfiable (1_048_576));
|
|
||||||
|
|
||||||
let resp = internal_serve_all (
|
|
||||||
&file_server_root,
|
|
||||||
Method::Head,
|
|
||||||
bad_passwords_path,
|
|
||||||
&headers,
|
|
||||||
None
|
|
||||||
).await;
|
|
||||||
|
|
||||||
assert_eq! (resp, ServeFile (ServeFileParams {
|
|
||||||
send_body: false,
|
|
||||||
range: 0..1_048_576,
|
|
||||||
range_requested: false,
|
|
||||||
file: AlwaysEqual::testing_blank (),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_uri () {
|
|
||||||
use hyper::Uri;
|
|
||||||
|
|
||||||
assert! (Uri::from_maybe_shared ("/").is_ok ());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn markdown () {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
for (input, expected) in vec! [
|
|
||||||
("", ""),
|
|
||||||
(
|
|
||||||
"Hello world, this is a ~~complicated~~ *very simple* example.",
|
|
||||||
"<p>Hello world, this is a <del>complicated</del> <em>very simple</em> example.</p>\n"
|
|
||||||
),
|
|
||||||
].into_iter () {
|
|
||||||
let mut out = String::default ();
|
|
||||||
render_markdown (input.as_bytes (), &mut out).unwrap ();
|
|
||||||
assert_eq! (expected, &out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,216 @@
|
||||||
|
use std::{
|
||||||
|
ffi::OsStr,
|
||||||
|
path::{
|
||||||
|
Component,
|
||||||
|
Path,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use maplit::*;
|
||||||
|
use tokio::runtime::Runtime;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn icons () {
|
||||||
|
let video = "🎞️";
|
||||||
|
let picture = "📷";
|
||||||
|
let file = "📄";
|
||||||
|
|
||||||
|
for (input, expected) in vec! [
|
||||||
|
("copying_is_not_theft.mp4", video),
|
||||||
|
("copying_is_not_theft.avi", video),
|
||||||
|
("copying_is_not_theft.mkv", video),
|
||||||
|
("copying_is_not_theft.webm", video),
|
||||||
|
("lolcats.jpg", picture),
|
||||||
|
("lolcats.jpeg", picture),
|
||||||
|
("lolcats.png", picture),
|
||||||
|
("lolcats.bmp", picture),
|
||||||
|
("ptth.log", file),
|
||||||
|
("README.md", file),
|
||||||
|
("todo.txt", file),
|
||||||
|
].into_iter () {
|
||||||
|
assert_eq! (super::get_icon (input), expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_range_header () {
|
||||||
|
for (input, expected) in vec! [
|
||||||
|
("", (None, None)),
|
||||||
|
("bytes=0-", (Some (0), None)),
|
||||||
|
("bytes=0-999", (Some (0), Some (1000))),
|
||||||
|
("bytes=111-999", (Some (111), Some (1000))),
|
||||||
|
].into_iter () {
|
||||||
|
let actual = super::parse_range_header (input);
|
||||||
|
assert_eq! (actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
use super::ParsedRange::*;
|
||||||
|
|
||||||
|
for (header, file_len, expected) in vec! [
|
||||||
|
(None, 0, Ok (0..0)),
|
||||||
|
(None, 1024, Ok (0..1024)),
|
||||||
|
|
||||||
|
(Some (""), 0, RangeNotSatisfiable (0)),
|
||||||
|
(Some (""), 1024, PartialContent (0..1024)),
|
||||||
|
|
||||||
|
(Some ("bytes=0-"), 1024, PartialContent (0..1024)),
|
||||||
|
(Some ("bytes=0-999"), 1024, PartialContent (0..1000)),
|
||||||
|
(Some ("bytes=0-1023"), 1024, PartialContent (0..1024)),
|
||||||
|
(Some ("bytes=111-999"), 1024, PartialContent (111..1000)),
|
||||||
|
(Some ("bytes=111-1023"), 1024, PartialContent (111..1024)),
|
||||||
|
(Some ("bytes=200-100"), 1024, RangeNotSatisfiable (1024)),
|
||||||
|
|
||||||
|
(Some ("bytes=0-"), 512, PartialContent (0..512)),
|
||||||
|
(Some ("bytes=0-1023"), 512, RangeNotSatisfiable (512)),
|
||||||
|
(Some ("bytes=1000-1023"), 512, RangeNotSatisfiable (512)),
|
||||||
|
].into_iter () {
|
||||||
|
let actual = super::check_range (header, file_len);
|
||||||
|
assert_eq! (actual, expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pretty_print_bytes () {
|
||||||
|
for (input_after, expected_before, expected_after) in vec! [
|
||||||
|
(1, "0 B", "1 B"),
|
||||||
|
(1024, "1023 B", "1 KiB"),
|
||||||
|
(1024 + 512, "1 KiB", "2 KiB"),
|
||||||
|
(1023 * 1024 + 512, "1023 KiB", "1 MiB"),
|
||||||
|
((1024 + 512) * 1024, "1 MiB", "2 MiB"),
|
||||||
|
(1023 * 1024 * 1024 + 512 * 1024, "1023 MiB", "1 GiB"),
|
||||||
|
((1024 + 512) * 1024 * 1024, "1 GiB", "2 GiB"),
|
||||||
|
|
||||||
|
].into_iter () {
|
||||||
|
let actual = super::pretty_print_bytes (input_after - 1);
|
||||||
|
assert_eq! (&actual, expected_before);
|
||||||
|
|
||||||
|
let actual = super::pretty_print_bytes (input_after);
|
||||||
|
assert_eq! (&actual, expected_after);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn i_hate_paths () {
|
||||||
|
let mut components = Path::new ("/home/user").components ();
|
||||||
|
|
||||||
|
assert_eq! (components.next (), Some (Component::RootDir));
|
||||||
|
assert_eq! (components.next (), Some (Component::Normal (OsStr::new ("home"))));
|
||||||
|
assert_eq! (components.next (), Some (Component::Normal (OsStr::new ("user"))));
|
||||||
|
assert_eq! (components.next (), None);
|
||||||
|
|
||||||
|
let mut components = Path::new ("./home/user").components ();
|
||||||
|
|
||||||
|
assert_eq! (components.next (), Some (Component::CurDir));
|
||||||
|
assert_eq! (components.next (), Some (Component::Normal (OsStr::new ("home"))));
|
||||||
|
assert_eq! (components.next (), Some (Component::Normal (OsStr::new ("user"))));
|
||||||
|
assert_eq! (components.next (), None);
|
||||||
|
|
||||||
|
let mut components = Path::new (".").components ();
|
||||||
|
|
||||||
|
assert_eq! (components.next (), Some (Component::CurDir));
|
||||||
|
assert_eq! (components.next (), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn file_server () {
|
||||||
|
use ptth_core::{
|
||||||
|
http_serde::Method,
|
||||||
|
};
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
tracing_subscriber::fmt ().try_init ().ok ();
|
||||||
|
let mut rt = Runtime::new ().unwrap ();
|
||||||
|
|
||||||
|
rt.block_on (async {
|
||||||
|
let file_server_root = PathBuf::from ("./");
|
||||||
|
let headers = Default::default ();
|
||||||
|
|
||||||
|
{
|
||||||
|
use InternalResponse::*;
|
||||||
|
|
||||||
|
let bad_passwords_path = "/files/src/bad_passwords.txt";
|
||||||
|
|
||||||
|
for (uri_path, expected) in vec! [
|
||||||
|
("/", Root),
|
||||||
|
("/files", Redirect ("files/".to_string ())),
|
||||||
|
("/files/?", InvalidQuery),
|
||||||
|
("/files/src", Redirect ("src/".to_string ())),
|
||||||
|
("/files/src/?", InvalidQuery),
|
||||||
|
(bad_passwords_path, ServeFile (ServeFileParams {
|
||||||
|
send_body: true,
|
||||||
|
range: 0..1_048_576,
|
||||||
|
range_requested: false,
|
||||||
|
file: AlwaysEqual::testing_blank (),
|
||||||
|
})),
|
||||||
|
("/files/test/test.md", ServeFile (ServeFileParams {
|
||||||
|
send_body: true,
|
||||||
|
range: 0..144,
|
||||||
|
range_requested: false,
|
||||||
|
file: AlwaysEqual::testing_blank (),
|
||||||
|
})),
|
||||||
|
("/ ", InvalidUri),
|
||||||
|
].into_iter () {
|
||||||
|
let resp = internal_serve_all (
|
||||||
|
&file_server_root,
|
||||||
|
Method::Get,
|
||||||
|
uri_path,
|
||||||
|
&headers,
|
||||||
|
None
|
||||||
|
).await;
|
||||||
|
|
||||||
|
assert_eq! (resp, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
let resp = internal_serve_all (
|
||||||
|
&file_server_root,
|
||||||
|
Method::Get,
|
||||||
|
bad_passwords_path,
|
||||||
|
&hashmap! {
|
||||||
|
"range".into () => b"bytes=0-2000000".to_vec (),
|
||||||
|
},
|
||||||
|
None
|
||||||
|
).await;
|
||||||
|
|
||||||
|
assert_eq! (resp, RangeNotSatisfiable (1_048_576));
|
||||||
|
|
||||||
|
let resp = internal_serve_all (
|
||||||
|
&file_server_root,
|
||||||
|
Method::Head,
|
||||||
|
bad_passwords_path,
|
||||||
|
&headers,
|
||||||
|
None
|
||||||
|
).await;
|
||||||
|
|
||||||
|
assert_eq! (resp, ServeFile (ServeFileParams {
|
||||||
|
send_body: false,
|
||||||
|
range: 0..1_048_576,
|
||||||
|
range_requested: false,
|
||||||
|
file: AlwaysEqual::testing_blank (),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_uri () {
|
||||||
|
use hyper::Uri;
|
||||||
|
|
||||||
|
assert! (Uri::from_maybe_shared ("/").is_ok ());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn markdown () {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
for (input, expected) in vec! [
|
||||||
|
("", ""),
|
||||||
|
(
|
||||||
|
"Hello world, this is a ~~complicated~~ *very simple* example.",
|
||||||
|
"<p>Hello world, this is a <del>complicated</del> <em>very simple</em> example.</p>\n"
|
||||||
|
),
|
||||||
|
].into_iter () {
|
||||||
|
let mut out = String::default ();
|
||||||
|
render_markdown (input.as_bytes (), &mut out).unwrap ();
|
||||||
|
assert_eq! (expected, &out);
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ use ptth_core::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub mod errors;
|
||||||
pub mod file_server;
|
pub mod file_server;
|
||||||
pub mod load_toml;
|
pub mod load_toml;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue