grcov: increase coverage with rustdoc tests

axum-login-upgrade
Manos Pitsidianakis 2023-05-09 16:36:23 +03:00
parent 98b1aa6e06
commit 828bbfe071
Signed by: Manos Pitsidianakis
GPG Key ID: 7729C7707F7E09D0
4 changed files with 352 additions and 107 deletions

View File

@ -38,8 +38,8 @@ jobs:
args: --all --all-features --no-fail-fast
env:
CARGO_INCREMENTAL: '0'
RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests'
RUSTDOCFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests'
RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests -Cinstrument-coverage'
RUSTDOCFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests -Cinstrument-coverage'
- uses: actions-rs/grcov@v0.1
with:
config: .github/grcov.yml

View File

@ -141,7 +141,7 @@ impl Connection {
///
/// # Example
///
/// ```rust
/// ```rust,no_run
/// use mailpot::{Connection, Configuration};
/// use melib::smtp::{SmtpServerConf, SmtpAuth, SmtpSecurity};
/// #
@ -807,3 +807,39 @@ pub mod transaction {
Panic,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_connection() {
use melib::smtp::{SmtpAuth, SmtpSecurity, SmtpServerConf};
use tempfile::TempDir;
use crate::SendMail;
let tmp_dir = TempDir::new().unwrap();
let db_path = tmp_dir.path().join("mpot.db");
let data_path = tmp_dir.path().to_path_buf();
let config = Configuration {
send_mail: SendMail::Smtp(SmtpServerConf {
hostname: "127.0.0.1".into(),
port: 25,
envelope_from: "foo-chat@example.com".into(),
auth: SmtpAuth::None,
security: SmtpSecurity::None,
extensions: Default::default(),
}),
db_path,
data_path,
administrators: vec![],
};
assert_eq!(
&Connection::open_db(config.clone()).unwrap_err().to_string(),
"Database doesn't exist"
);
_ = Connection::open_or_create_db(config).unwrap();
}
}

View File

@ -189,44 +189,6 @@ impl minijinja::value::StructObject for MailingList {
/// Return a vector of weeks, with each week being a vector of 7 days and
/// corresponding sum of posts per day.
///
///
/// # Example
///
/// ```rust
/// # use mailpot_web::minijinja_utils::calendarize;
/// # use minijinja::Environment;
/// # use minijinja::value::Value;
/// # use std::collections::HashMap;
///
/// let mut env = Environment::new();
/// env.add_function("calendarize", calendarize);
///
/// let month = "2001-09";
/// let mut hist = [0usize; 31];
/// hist[15] = 5;
/// hist[1] = 1;
/// hist[0] = 512;
/// hist[30] = 30;
/// assert_eq!(
/// &env.render_str(
/// "{% set c=calendarize(month, hists) %}Month: {{ c.month }} Month Name: {{ \
/// c.month_name }} Month Int: {{ c.month_int }} Year: {{ c.year }} Sum: {{ c.sum }} {% \
/// for week in c.weeks %}{% for day in week %}{% set num = c.hist[day-1] %}({{ day }}, \
/// {{ num }}){% endfor %}{% endfor %}",
/// minijinja::context! {
/// month,
/// hists => vec![(month.to_string(), hist)].into_iter().collect::<HashMap<String, [usize;
/// 31]>>(),
/// }
/// )
/// .unwrap(),
/// "Month: 2001-09 Month Name: September Month Int: 9 Year: 2001 Sum: 548 (0, 30)(0, 30)(0, \
/// 30)(0, 30)(0, 30)(1, 512)(2, 1)(3, 0)(4, 0)(5, 0)(6, 0)(7, 0)(8, 0)(9, 0)(10, 0)(11, \
/// 0)(12, 0)(13, 0)(14, 0)(15, 0)(16, 5)(17, 0)(18, 0)(19, 0)(20, 0)(21, 0)(22, 0)(23, \
/// 0)(24, 0)(25, 0)(26, 0)(27, 0)(28, 0)(29, 0)(30, 0)"
/// );
/// ```
pub fn calendarize(
_state: &minijinja::State,
args: Value,
@ -294,7 +256,7 @@ pub fn calendarize(
///
/// # Examples
///
/// ```rust
/// ```rust,no_run
/// # use mailpot_web::pluralize;
/// # use minijinja::Environment;
///
@ -436,26 +398,6 @@ pub fn pluralize(
/// `strip_carets` filter for [`minijinja`].
///
/// Removes `[<>]` from message ids.
///
/// # Examples
///
/// ```rust
/// # use mailpot_web::strip_carets;
/// # use minijinja::Environment;
///
/// let mut env = Environment::new();
/// env.add_filter("strip_carets", strip_carets);
/// assert_eq!(
/// &env.render_str(
/// "{{ msg_id | strip_carets }}",
/// minijinja::context! {
/// msg_id => "<hello1@example.com>",
/// }
/// )
/// .unwrap(),
/// "hello1@example.com",
/// );
/// ```
pub fn strip_carets(_state: &minijinja::State, arg: Value) -> std::result::Result<Value, Error> {
Ok(Value::from(
arg.as_str()
@ -475,7 +417,7 @@ pub fn strip_carets(_state: &minijinja::State, arg: Value) -> std::result::Resul
///
/// # Examples
///
/// ```rust
/// ```rust,no_run
/// # use mailpot_web::urlize;
/// # use minijinja::Environment;
/// # use minijinja::value::Value;
@ -505,7 +447,7 @@ pub fn urlize(state: &minijinja::State, arg: Value) -> std::result::Result<Value
/// Make an html heading: `h1, h2, h3` etc.
///
/// # Example
/// ```
/// ```rust,no_run
/// use mailpot_web::minijinja_utils::heading;
/// use minijinja::value::Value;
///
@ -601,3 +543,227 @@ pub fn heading(level: Value, text: Value, id: Option<Value>) -> std::result::Res
)))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pluralize() {
let mut env = Environment::new();
env.add_filter("pluralize", pluralize);
for (num, s) in [
(0, "You have 0 messages."),
(1, "You have 1 message."),
(10, "You have 10 messages."),
] {
assert_eq!(
&env.render_str(
"You have {{ num_messages }} message{{ num_messages|pluralize }}.",
minijinja::context! {
num_messages => num,
}
)
.unwrap(),
s
);
}
for (num, s) in [
(0, "You have 0 walruses."),
(1, "You have 1 walrus."),
(10, "You have 10 walruses."),
] {
assert_eq!(
&env.render_str(
r#"You have {{ num_walruses }} walrus{{ num_walruses|pluralize(None, "es") }}."#,
minijinja::context! {
num_walruses => num,
}
)
.unwrap(),
s
);
}
for (num, s) in [
(0, "You have 0 cherries."),
(1, "You have 1 cherry."),
(10, "You have 10 cherries."),
] {
assert_eq!(
&env.render_str(
r#"You have {{ num_cherries }} cherr{{ num_cherries|pluralize("y", "ies") }}."#,
minijinja::context! {
num_cherries => num,
}
)
.unwrap(),
s
);
}
assert_eq!(
&env.render_str(
r#"You have {{ num_cherries|length }} cherr{{ num_cherries|pluralize("y", "ies") }}."#,
minijinja::context! {
num_cherries => vec![(); 5],
}
)
.unwrap(),
"You have 5 cherries."
);
assert_eq!(
&env.render_str(
r#"You have {{ num_cherries }} cherr{{ num_cherries|pluralize("y", "ies") }}."#,
minijinja::context! {
num_cherries => "5",
}
)
.unwrap(),
"You have 5 cherries."
);
assert_eq!(
&env.render_str(
r#"You have 1 cherr{{ num_cherries|pluralize("y", "ies") }}."#,
minijinja::context! {
num_cherries => true,
}
)
.unwrap(),
"You have 1 cherry.",
);
assert_eq!(
&env.render_str(
r#"You have {{ num_cherries }} cherr{{ num_cherries|pluralize("y", "ies") }}."#,
minijinja::context! {
num_cherries => 0.5f32,
}
)
.unwrap_err()
.to_string(),
"invalid operation: Pluralize argument is not an integer, or a sequence / object with \
a length but of type number (in <string>:1)",
);
}
#[test]
fn test_urlize() {
let mut env = Environment::new();
env.add_function("urlize", urlize);
env.add_global(
"root_url_prefix",
Value::from_safe_string("/lists/prefix/".to_string()),
);
assert_eq!(
&env.render_str(
"<a href=\"{{ urlize(\"path/index.html\") }}\">link</a>",
minijinja::context! {}
)
.unwrap(),
"<a href=\"/lists/prefix/path/index.html\">link</a>",
);
}
#[test]
fn test_heading() {
assert_eq!(
"<h1 id=\"bl-bfa-b-ah-b-asdb-hadas-d\">bl bfa B AH bAsdb hadas d<a \
class=\"self-link\" href=\"#bl-bfa-b-ah-b-asdb-hadas-d\"></a></h1>",
&heading(1.into(), "bl bfa B AH bAsdb hadas d".into(), None)
.unwrap()
.to_string()
);
assert_eq!(
"<h2 id=\"short\">bl bfa B AH bAsdb hadas d<a class=\"self-link\" \
href=\"#short\"></a></h2>",
&heading(
2.into(),
"bl bfa B AH bAsdb hadas d".into(),
Some("short".into())
)
.unwrap()
.to_string()
);
assert_eq!(
r#"invalid operation: first heading() argument must be an unsigned integer less than 7 and positive"#,
&heading(
0.into(),
"bl bfa B AH bAsdb hadas d".into(),
Some("short".into())
)
.unwrap_err()
.to_string()
);
assert_eq!(
r#"invalid operation: first heading() argument must be an unsigned integer less than 7 and positive"#,
&heading(
8.into(),
"bl bfa B AH bAsdb hadas d".into(),
Some("short".into())
)
.unwrap_err()
.to_string()
);
assert_eq!(
r#"invalid operation: first heading() argument is not an integer < 7 but of type sequence"#,
&heading(
Value::from(vec![Value::from(1)]),
"bl bfa B AH bAsdb hadas d".into(),
Some("short".into())
)
.unwrap_err()
.to_string()
);
}
#[test]
fn test_strip_carets() {
let mut env = Environment::new();
env.add_filter("strip_carets", strip_carets);
assert_eq!(
&env.render_str(
"{{ msg_id | strip_carets }}",
minijinja::context! {
msg_id => "<hello1@example.com>",
}
)
.unwrap(),
"hello1@example.com",
);
}
#[test]
fn test_calendarize() {
use std::collections::HashMap;
let mut env = Environment::new();
env.add_function("calendarize", calendarize);
let month = "2001-09";
let mut hist = [0usize; 31];
hist[15] = 5;
hist[1] = 1;
hist[0] = 512;
hist[30] = 30;
assert_eq!(
&env.render_str(
"{% set c=calendarize(month, hists) %}Month: {{ c.month }} Month Name: {{ \
c.month_name }} Month Int: {{ c.month_int }} Year: {{ c.year }} Sum: {{ c.sum }} {% \
for week in c.weeks %}{% for day in week %}{% set num = c.hist[day-1] %}({{ day }}, \
{{ num }}){% endfor %}{% endfor %}",
minijinja::context! {
month,
hists => vec![(month.to_string(), hist)].into_iter().collect::<HashMap<String, [usize;
31]>>(),
}
)
.unwrap(),
"Month: 2001-09 Month Name: September Month Int: 9 Year: 2001 Sum: 548 (0, 30)(0, 30)(0, \
30)(0, 30)(0, 30)(1, 512)(2, 1)(3, 0)(4, 0)(5, 0)(6, 0)(7, 0)(8, 0)(9, 0)(10, 0)(11, \
0)(12, 0)(13, 0)(14, 0)(15, 0)(16, 5)(17, 0)(18, 0)(19, 0)(20, 0)(21, 0)(22, 0)(23, \
0)(24, 0)(25, 0)(26, 0)(27, 0)(28, 0)(29, 0)(30, 0)"
);
}
}

View File

@ -66,7 +66,7 @@ impl Message {
///
/// # Example
///
/// ```rust
/// ```no_run
/// # use mailpot_web::utils::{Message, Level, SessionMessages};
/// struct Session(Vec<Message>);
///
@ -135,17 +135,6 @@ impl SessionMessages for WritableSession {
/// Deserialize a string integer into `i64`, because POST parameters are
/// strings.
///
/// ```
/// # use mailpot_web::utils::IntPOST;
/// # use mailpot::serde_json::{self, json};
/// assert_eq!(
/// IntPOST(5),
/// serde_json::from_str::<IntPOST>("\"5\"").unwrap()
/// );
/// assert_eq!(IntPOST(5), serde_json::from_str::<IntPOST>("5").unwrap());
/// assert_eq!(&json! { IntPOST(5) }.to_string(), "5");
/// ```
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Hash)]
#[repr(transparent)]
pub struct IntPOST(pub i64);
@ -201,20 +190,6 @@ impl<'de> serde::Deserialize<'de> for IntPOST {
/// Deserialize a string integer into `bool`, because POST parameters are
/// strings.
///
/// ```
/// # use mailpot_web::utils::BoolPOST;
/// # use mailpot::serde_json::{self, json};
/// assert_eq!(
/// BoolPOST(true),
/// serde_json::from_str::<BoolPOST>("true").unwrap()
/// );
/// assert_eq!(
/// BoolPOST(true),
/// serde_json::from_str::<BoolPOST>("\"true\"").unwrap()
/// );
/// assert_eq!(&json! { BoolPOST(false) }.to_string(), "false");
/// ```
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, PartialOrd, Hash)]
#[repr(transparent)]
pub struct BoolPOST(pub bool);
@ -261,23 +236,6 @@ impl<'de> serde::Deserialize<'de> for BoolPOST {
}
}
/// ```
/// use axum::response::Redirect;
/// use mailpot_web::Next;
///
/// let next = Next {
/// next: Some("foo".to_string()),
/// };
/// assert_eq!(
/// format!("{:?}", Redirect::to("foo")),
/// format!("{:?}", next.or_else(|| "bar".to_string()))
/// );
/// let next = Next { next: None };
/// assert_eq!(
/// format!("{:?}", Redirect::to("bar")),
/// format!("{:?}", next.or_else(|| "bar".to_string()))
/// );
/// ```
#[derive(Debug, Clone, serde::Deserialize)]
pub struct Next {
#[serde(default, deserialize_with = "empty_string_as_none")]
@ -530,3 +488,88 @@ pub fn thread_roots(
ret.sort_by_key(|(_, _, key)| std::cmp::Reverse(*key));
ret
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_session() {
struct Session(Vec<Message>);
impl SessionMessages for Session {
type Error = std::convert::Infallible;
fn drain_messages(&mut self) -> Vec<Message> {
std::mem::take(&mut self.0)
}
fn add_message(&mut self, m: Message) -> Result<(), std::convert::Infallible> {
self.0.push(m);
Ok(())
}
}
let mut s = Session(vec![]);
s.add_message(Message {
message: "foo".into(),
level: Level::default(),
})
.unwrap();
s.add_message(Message {
message: "bar".into(),
level: Level::Error,
})
.unwrap();
assert_eq!(
s.drain_messages().as_slice(),
[
Message {
message: "foo".into(),
level: Level::default(),
},
Message {
message: "bar".into(),
level: Level::Error
}
]
.as_slice()
);
assert!(s.0.is_empty());
}
#[test]
fn test_post_serde() {
use mailpot::serde_json::{self, json};
assert_eq!(
IntPOST(5),
serde_json::from_str::<IntPOST>("\"5\"").unwrap()
);
assert_eq!(IntPOST(5), serde_json::from_str::<IntPOST>("5").unwrap());
assert_eq!(&json! { IntPOST(5) }.to_string(), "5");
assert_eq!(
BoolPOST(true),
serde_json::from_str::<BoolPOST>("true").unwrap()
);
assert_eq!(
BoolPOST(true),
serde_json::from_str::<BoolPOST>("\"true\"").unwrap()
);
assert_eq!(&json! { BoolPOST(false) }.to_string(), "false");
}
#[test]
fn test_next() {
let next = Next {
next: Some("foo".to_string()),
};
assert_eq!(
format!("{:?}", Redirect::to("foo")),
format!("{:?}", next.or_else(|| "bar".to_string()))
);
let next = Next { next: None };
assert_eq!(
format!("{:?}", Redirect::to("bar")),
format!("{:?}", next.or_else(|| "bar".to_string()))
);
}
}