about summary refs log tree commit diff
path: root/templates
diff options
context:
space:
mode:
authorVika <vika@fireburn.ru>2022-05-23 08:54:42 +0300
committerVika <vika@fireburn.ru>2022-05-23 08:54:42 +0300
commit84f9508813ac368bdce302db7f3a15bc6fce3fee (patch)
treedadab1c11dd954c6fcfac4952b19ed2160e65ae2 /templates
parent6c1ebd115fff3e4dcda7c051ca71ccfec90342ec (diff)
templates: introduce unit tests
These unit tests generate a random MF2-JSON post, convert it to
MF2-HTML using the template and then read it back using the
`microformats` crate.

The only problem is that it has a nasty bug with overstuffing implied
properties. This is being worked on:
https://gitlab.com/maxburon/microformats-parser/-/issues/7

For now the tests marked as ignored because they fail. But the
function itself that generates them should remain here for
documentation and potential code sharing with the `microformats`
crate, potentially even migrating to a subcrate there.
Diffstat (limited to 'templates')
-rw-r--r--templates/Cargo.toml7
-rw-r--r--templates/src/lib.rs154
2 files changed, 161 insertions, 0 deletions
diff --git a/templates/Cargo.toml b/templates/Cargo.toml
index c4c7f46..fe8ac19 100644
--- a/templates/Cargo.toml
+++ b/templates/Cargo.toml
@@ -5,6 +5,13 @@ edition = "2021"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
+[dev-dependencies]
+faker_rand = "^0.1.1"
+rand = "^0.8.5"
+test-logger = "^0.1.0"
+[dev-dependencies.microformats]
+version="^0.2.0"
+
 [dependencies]
 ellipse = "^0.2.0"           # Truncate and ellipsize strings in a human-friendly way
 http = "^0.2.7"              # Hyper's strong HTTP types
diff --git a/templates/src/lib.rs b/templates/src/lib.rs
index c7b03ea..01c69ab 100644
--- a/templates/src/lib.rs
+++ b/templates/src/lib.rs
@@ -4,3 +4,157 @@ mod onboarding;
 pub use onboarding::OnboardingPage;
 mod login;
 pub use login::LoginPage;
+
+#[cfg(test)]
+mod tests {
+    use serde_json::json;
+    use microformats::types::Document;
+
+    enum PostType {
+        Note,
+        Article,
+        ReplyTo(serde_json::Value),
+        ReplyToLink(String),
+        LikeOf(serde_json::Value),
+        LikeOfLink(String)
+    }
+
+    fn gen_hcard(domain: &str) -> serde_json::Value {
+        use faker_rand::en_us::names::FirstName;
+
+        json!({
+            "type": ["h-card"],
+            "properties": {
+                "name": [rand::random::<FirstName>().to_string()],
+                "photo": [format!("https://{domain}/media/me.png")],
+                "uid": [format!("https://{domain}/")],
+                "url": [format!("https://{domain}/")]
+            }
+        })
+    }
+    
+    fn gen_random_post(domain: &str, kind: PostType) -> serde_json::Value {
+        use faker_rand::lorem::{Paragraph, Word, Sentence};
+
+        fn html(content: Paragraph) -> serde_json::Value {
+            json!({
+                "html": format!("<p>{}</p>", content),
+                "value": content.to_string()
+            })
+        }
+            
+        let uid = format!(
+            "https://{domain}/posts/{}-{}-{}",
+            rand::random::<Word>(), rand::random::<Word>(), rand::random::<Word>()
+        );
+        let dt = chrono::offset::Local::now()
+            .to_rfc3339_opts(chrono::SecondsFormat::Secs, true);
+
+        match kind {
+            PostType::Note => {
+                let content = rand::random::<Paragraph>();
+
+                json!({
+                    "type": ["h-entry"],
+                    "properties": {
+                        "content": [html(content)],
+                        "published": [dt],
+                        "uid": [&uid],
+                        "url": [&uid],
+                        "author": [gen_hcard(domain)]
+                    }
+                })
+            }
+            PostType::Article => {
+                let content = rand::random::<Paragraph>();
+                let name = rand::random::<Sentence>();
+
+                json!({
+                    "type": ["h-entry"],
+                    "properties": {
+                        "content": [html(content)],
+                        "published": [dt],
+                        "uid": [&uid],
+                        "url": [&uid],
+                        "author": [gen_hcard(domain)],
+                        "name": [name.to_string()]
+                    }
+                })
+            }
+            _ => todo!()
+        }
+    }
+
+    #[test]
+    #[ignore = "see https://gitlab.com/maxburon/microformats-parser/-/issues/7"]
+    fn test_note() {
+        use microformats::types::PropertyValue;
+        use faker_rand::en_us::internet::Domain;
+
+        test_logger::ensure_env_logger_initialized();
+
+        let mf2 = gen_random_post(
+            &rand::random::<Domain>().to_string(),
+            PostType::Note
+        );
+
+        let html = crate::templates::Entry {
+            post: &mf2
+        }.to_string();
+        println!("\n```html\n{}\n```", &html);
+        let url: microformats::types::Url = mf2["properties"]["uid"][0].as_str()
+            .unwrap()
+            .parse()
+            .unwrap();
+        let parsed: Document = microformats::from_html(&html, url.clone()).unwrap();
+
+        let item = parsed.get_item_by_url(&url).unwrap();
+        println!("\n```json\n{}\n```", serde_json::to_string_pretty(&item).unwrap());
+        if let PropertyValue::Item(item) = item {
+            let _item = item.borrow();
+            let props = _item.properties.borrow();
+
+            if let PropertyValue::Fragment(content) = props.get("content").and_then(|v| v.first()).unwrap() {
+                assert_eq!(content.html, mf2["properties"]["content"][0]["html"].as_str().unwrap());
+            } else {
+                unreachable!()
+            }
+
+            assert!(props.contains_key("published"));
+            use microformats::types::temporal::Value as TemporalValue;
+            if let Some(PropertyValue::Temporal(
+                TemporalValue::Timestamp(item)
+            )) = props.get("published")
+                .and_then(|v| v.first())
+            {
+                use chrono::{DateTime, FixedOffset, NaiveDateTime};
+
+                let offset = item.as_offset().unwrap().data;
+                let ndt: NaiveDateTime = item.as_date().unwrap().data
+                    .and_time(item.as_time().unwrap().data)
+                    - offset;
+                let dt = DateTime::<FixedOffset>::from_utc(ndt, offset);
+
+                let expected: DateTime<FixedOffset> = chrono::DateTime::parse_from_rfc3339(
+                    mf2["properties"]["published"][0].as_str().unwrap()
+                ).unwrap();
+
+                assert_eq!(dt, expected);
+            } else {
+                panic!("Failed to find datetime in properties!");
+            }
+                
+            assert!(props.contains_key("uid"));
+            assert!(props.contains_key("url"));
+            assert!(props.get("url")
+                    .unwrap()
+                    .iter()
+                    .any(|i| i == props.get("uid").and_then(|v| v.first()).unwrap()));
+            // XXX: fails because of https://gitlab.com/maxburon/microformats-parser/-/issues/7
+            assert!(!props.contains_key("name"));
+
+        } else {
+            unreachable!()
+        }
+    }
+}