Adjusting to make the database better tested.
This commit is contained in:
@ -26,3 +26,4 @@ tokio = { version = "1", features = ["full"] }
|
|||||||
default = []
|
default = []
|
||||||
database = ["sqlx"]
|
database = ["sqlx"]
|
||||||
publisher = ["database"]
|
publisher = ["database"]
|
||||||
|
tester = ["publisher"]
|
||||||
|
|||||||
@ -1,51 +1,66 @@
|
|||||||
use std::path::{Path, PathBuf};
|
#![cfg(feature = "publisher")]
|
||||||
|
|
||||||
use chrono::NaiveDate;
|
use chrono::NaiveDate;
|
||||||
use tavern::{Adventurer, Database, Legend, Lore, Tale, Tavern};
|
|
||||||
|
use tavern::{Adventurer, Legend, Lore, Story, Tale, Tavern};
|
||||||
|
use tavern::Database;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(feature = "publisher")]
|
/// This will generate a tavern that we can create a Toml file from.
|
||||||
fn generate_tavern() -> Tavern
|
fn generate_tavern() -> Tavern
|
||||||
{
|
{
|
||||||
let legend: Legend = Legend
|
let legend: Legend = Legend
|
||||||
{
|
{
|
||||||
profile:
|
profile: String::from("https://cybermages.tech/about/myrddin"),
|
||||||
String::from("https://cybermages.tech/about/myrddin"),
|
image: String::from("https://cybermages.tech/about/myrddin/pic"),
|
||||||
image:
|
blurb: String::from("I love code!")
|
||||||
String::from("https://cybermages.tech/about/myrddin/pic"),
|
};
|
||||||
blurb: String::from("I love code!") };
|
|
||||||
|
|
||||||
let author: Adventurer =
|
let author: Adventurer = Adventurer
|
||||||
Adventurer { name: String::from("Jason Smith"),
|
|
||||||
handle: String::from("myrddin"),
|
|
||||||
legend };
|
|
||||||
|
|
||||||
let lore: Lore =
|
|
||||||
Lore { title: String::from("Test post"),
|
|
||||||
slug: String::from("test_post"),
|
|
||||||
author: author.handle.clone(),
|
|
||||||
summary: String::from("The Moon is made of cheese!"),
|
|
||||||
tags: vec![String::from("Space"),
|
|
||||||
String::from("Cheese")],
|
|
||||||
publish_date:
|
|
||||||
NaiveDate::from_ymd_opt(2025, 12, 25).unwrap()
|
|
||||||
.and_hms_opt(13,
|
|
||||||
10, 41)
|
|
||||||
.unwrap() };
|
|
||||||
|
|
||||||
let tale: Tale = Tale { lore,
|
|
||||||
story: PathBuf::from("posts/test_post.md") };
|
|
||||||
|
|
||||||
|
|
||||||
// Create a dummy posts directory and file for this example to work
|
|
||||||
if !Path::new("posts").exists()
|
|
||||||
{
|
{
|
||||||
std::fs::create_dir("posts").unwrap();
|
name: String::from("Jason Smith"),
|
||||||
}
|
handle: String::from("myrddin"),
|
||||||
std::fs::write("posts/the-rustacean.md",
|
legend: legend
|
||||||
"# Hello, Rust!\n\nThis is a **test** post.").unwrap();
|
};
|
||||||
|
|
||||||
|
let lore: Lore = Lore
|
||||||
|
{
|
||||||
|
title: String::from("Test post"),
|
||||||
|
slug: String::from("test_post"),
|
||||||
|
author: author.handle.clone(),
|
||||||
|
summary: String::from("The Moon is made of cheese!"),
|
||||||
|
tags: vec![String::from("Space"), String::from("Cheese")],
|
||||||
|
publish_date:
|
||||||
|
NaiveDate::from_ymd_opt(2025, 12, 25).unwrap()
|
||||||
|
.and_hms_opt(13, 10, 41)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let tale: Tale = Tale
|
||||||
|
{
|
||||||
|
lore: lore,
|
||||||
|
story: Story::Html("#Test Post\nThis is a test post.\n\n##Subsection\nMini post.\n* Test 1\n* Test 2\n* Test 3".to_string())
|
||||||
|
};
|
||||||
|
|
||||||
|
let lore2: Lore = Lore
|
||||||
|
{
|
||||||
|
title: String::from("Test Tale"),
|
||||||
|
slug: String::from("test_tale"),
|
||||||
|
author: author.handle.clone(),
|
||||||
|
summary: String::from("The Moon is made of rocks!"),
|
||||||
|
tags: vec![String::from("Space"), String::from("Rocks")],
|
||||||
|
publish_date:
|
||||||
|
NaiveDate::from_ymd_opt(2025, 12, 25).unwrap()
|
||||||
|
.and_hms_opt(13, 10, 41)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let tale2: Tale = Tale
|
||||||
|
{
|
||||||
|
lore: lore2,
|
||||||
|
story: Story::Html("#Test Tale\nThis is a test tale.\n\n##Subsection\nMini tale.\n* Test 1\n* Test 2\n* Test 3".to_string())
|
||||||
|
};
|
||||||
|
|
||||||
Tavern { title: String::from("Runes & Ramblings"),
|
Tavern { title: String::from("Runes & Ramblings"),
|
||||||
description: String::from("Join software engineer Jason Smith \
|
description: String::from("Join software engineer Jason Smith \
|
||||||
@ -53,14 +68,52 @@ fn generate_tavern() -> Tavern
|
|||||||
Explore program design, tech \
|
Explore program design, tech \
|
||||||
stacks, and more on this blog from \
|
stacks, and more on this blog from \
|
||||||
CybeMages, LLC."),
|
CybeMages, LLC."),
|
||||||
tales: vec![tale],
|
tales: vec![tale, tale2],
|
||||||
authors: vec![author] }
|
authors: vec![author] }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(feature = "publisher")]
|
fn create_temp_file<P>(filename: P) -> std::path::PathBuf
|
||||||
|
where P: AsRef<std::path::Path>
|
||||||
|
{
|
||||||
|
let mut path = std::env::temp_dir();
|
||||||
|
path.push(filename);
|
||||||
|
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cleanup_temp_file<P>(path: P)
|
||||||
|
where P: AsRef<std::path::Path>
|
||||||
|
{
|
||||||
|
match path.as_ref().try_exists()
|
||||||
|
{
|
||||||
|
Ok(exists) =>
|
||||||
|
{
|
||||||
|
if exists
|
||||||
|
{
|
||||||
|
let _ = std::fs::remove_file(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(e) =>
|
||||||
|
{
|
||||||
|
eprintln!("{}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_to_file<P>(tavern: Tavern, config_file: P) -> Result<(), Box<dyn std::error::Error>>
|
||||||
|
where P: AsRef<std::path::Path>
|
||||||
|
{
|
||||||
|
let toml_string = toml::to_string_pretty(&tavern)?;
|
||||||
|
|
||||||
|
std::fs::write(&config_file, &toml_string)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn read_from_file<P>(config_file: P) -> Tavern
|
fn read_from_file<P>(config_file: P) -> Tavern
|
||||||
where P: AsRef<Path>
|
where P: AsRef<std::path::Path>
|
||||||
{
|
{
|
||||||
// Read the previously written TOML file
|
// Read the previously written TOML file
|
||||||
let toml_data =
|
let toml_data =
|
||||||
@ -71,65 +124,36 @@ fn read_from_file<P>(config_file: P) -> Tavern
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(feature = "publisher")]
|
|
||||||
async fn create_database() -> Result<(), Box<dyn std::error::Error>>
|
async fn create_database() -> Result<(), Box<dyn std::error::Error>>
|
||||||
{
|
{
|
||||||
|
// First we need to generate a TOML file to work with.
|
||||||
|
let config_path = create_temp_file("tavern.toml");
|
||||||
|
cleanup_temp_file(&config_path);
|
||||||
|
|
||||||
|
write_to_file(generate_tavern(), &config_path)?;
|
||||||
|
|
||||||
// This part would be the entry point of your CI/CD script
|
// This part would be the entry point of your CI/CD script
|
||||||
// It would load your data and then save it to the database
|
// It would load your data and then save it to the database
|
||||||
|
|
||||||
// Create a Tavern object
|
// Create a Tavern object
|
||||||
let _tavern = read_from_file("Tavern.toml");
|
let tavern = read_from_file(&config_path);
|
||||||
// let tavern = generate_tavern();
|
|
||||||
|
|
||||||
// Open the database and save the Tavern content
|
// Open the database and save the Tavern content
|
||||||
let _db = Database::open("/home/myrddin/cybermages/blog/tavern.db").await?;
|
let db_path = create_temp_file("tavern.db");
|
||||||
//db.insert_tavern(&tavern.title, &tavern.description)?;
|
cleanup_temp_file(&db_path);
|
||||||
//println!("Saved site settings: Title='{}', Description='{}'",
|
|
||||||
//tavern.title, tavern.description);
|
let db = Database::open(db_path).await?;
|
||||||
|
db.insert_tavern(&tavern).await?;
|
||||||
//for author in &tavern.authors
|
|
||||||
// {
|
|
||||||
// db.insert_adventurer(author)?;
|
|
||||||
// println!("Saved adventurer: {}", author.name);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// for tale in &tavern.tales
|
|
||||||
// {
|
|
||||||
// db.insert_tale(tale)?;
|
|
||||||
// println!("Saved tale: {}", tale.title);
|
|
||||||
// }
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "publisher")]
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
pub async fn main()
|
pub async fn main()
|
||||||
{
|
{
|
||||||
match std::env::set_current_dir("/home/myrddin/cybermages/blog/")
|
|
||||||
{
|
|
||||||
Ok(_) =>
|
|
||||||
{
|
|
||||||
println!("Successfully changed working directory.");
|
|
||||||
}
|
|
||||||
Err(e) =>
|
|
||||||
{
|
|
||||||
eprintln!("Failed to change directory: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match create_database().await
|
match create_database().await
|
||||||
{
|
{
|
||||||
Ok(_) =>
|
Ok(_) => {}
|
||||||
{}
|
Err(e) => { eprintln!("Error: {}", e); }
|
||||||
Err(e) =>
|
|
||||||
{
|
|
||||||
eprintln!("Error: {}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "publisher"))]
|
|
||||||
pub fn main()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
///
|
///
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct Legend
|
pub struct Legend
|
||||||
{
|
{
|
||||||
/// A link to the adventurer's profile (e.g., personal website, GitHub,
|
/// A link to the adventurer's profile (e.g., personal website, GitHub,
|
||||||
@ -20,7 +20,7 @@ pub struct Legend
|
|||||||
///
|
///
|
||||||
/// An `Adventurer` contains identifying and descriptive information
|
/// An `Adventurer` contains identifying and descriptive information
|
||||||
/// such as their name, handle, profile URL, image, and a short blurb.
|
/// such as their name, handle, profile URL, image, and a short blurb.
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct Adventurer
|
pub struct Adventurer
|
||||||
{
|
{
|
||||||
/// The full name of the adventurer.
|
/// The full name of the adventurer.
|
||||||
@ -34,7 +34,3 @@ pub struct Adventurer
|
|||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub legend: Legend
|
pub legend: Legend
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
impl Adventurer {}
|
|
||||||
|
|||||||
@ -4,20 +4,20 @@ use std::str::FromStr;
|
|||||||
use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqlitePoolOptions};
|
use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqlitePoolOptions};
|
||||||
use sqlx::{Error, Result};
|
use sqlx::{Error, Result};
|
||||||
|
|
||||||
#[cfg(not(feature = "publisher"))]
|
#[cfg(any(not(feature = "publisher"), feature = "tester"))]
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
|
|
||||||
use crate::adventurer::Adventurer;
|
use crate::adventurer::Adventurer;
|
||||||
use crate::tale::Tale;
|
use crate::tale::{Story, Tale};
|
||||||
|
|
||||||
#[cfg(feature = "publisher")]
|
#[cfg(feature = "publisher")]
|
||||||
use crate::converter::Converter;
|
use crate::converter::Converter;
|
||||||
#[cfg(feature = "publisher")]
|
#[cfg(feature = "publisher")]
|
||||||
use crate::tavern::Tavern;
|
use crate::tavern::Tavern;
|
||||||
|
|
||||||
#[cfg(not(feature = "publisher"))]
|
#[cfg(any(not(feature = "publisher"), feature = "tester"))]
|
||||||
use crate::tale::Lore;
|
use crate::tale::Lore;
|
||||||
#[cfg(not(feature = "publisher"))]
|
#[cfg(any(not(feature = "publisher"), feature = "tester"))]
|
||||||
use crate::adventurer::Legend;
|
use crate::adventurer::Legend;
|
||||||
|
|
||||||
|
|
||||||
@ -140,13 +140,24 @@ impl Database
|
|||||||
-> Result<()>
|
-> Result<()>
|
||||||
{
|
{
|
||||||
// Convert the tales content from Markdown to HTML.
|
// Convert the tales content from Markdown to HTML.
|
||||||
let markdown_content = std::fs::read_to_string(&tale.story)?;
|
let html_content: std::borrow::Cow<'_, str> = match &tale.story
|
||||||
let html_content: String = Converter::markdown_to_html(&markdown_content);
|
{
|
||||||
|
Story::Html(story) => { std::borrow::Cow::Borrowed(story) }
|
||||||
|
|
||||||
|
Story::File(path) =>
|
||||||
|
{
|
||||||
|
let markdown = std::fs::read_to_string(path)?;
|
||||||
|
std::borrow::Cow::Owned(Converter::markdown_to_html(&markdown))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Start a transaction.
|
// Start a transaction.
|
||||||
let mut tx = self.pool.begin().await?;
|
let mut tx = self.pool.begin().await?;
|
||||||
|
|
||||||
// Store the tale.
|
// Store the tale.
|
||||||
|
// Pull the HTML content out first so that the str will last long enough.
|
||||||
|
// This get's around the macro lifetime issue.
|
||||||
|
let html_str = html_content.as_ref();
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"INSERT OR REPLACE INTO tales (
|
"INSERT OR REPLACE INTO tales (
|
||||||
slug, title, author, summary, publish_date, content
|
slug, title, author, summary, publish_date, content
|
||||||
@ -156,7 +167,7 @@ impl Database
|
|||||||
tale.lore.author,
|
tale.lore.author,
|
||||||
tale.lore.summary,
|
tale.lore.summary,
|
||||||
tale.lore.publish_date,
|
tale.lore.publish_date,
|
||||||
html_content
|
html_str
|
||||||
).execute(&mut *tx) // Pass mutable reference to the transaction
|
).execute(&mut *tx) // Pass mutable reference to the transaction
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@ -263,7 +274,7 @@ impl Database
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "publisher"))]
|
#[cfg(any(not(feature = "publisher"), feature = "tester"))]
|
||||||
pub async fn get_tales_summary(&self, categories: &[String])
|
pub async fn get_tales_summary(&self, categories: &[String])
|
||||||
-> Result<Vec<Lore>>
|
-> Result<Vec<Lore>>
|
||||||
{
|
{
|
||||||
@ -312,7 +323,7 @@ impl Database
|
|||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let date_str: String = row.try_get("publish_date")?;
|
let date_str: String = row.try_get("publish_date")?;
|
||||||
let publish_date = chrono::NaiveDateTime::parse_from_str(&date_str, "%Y-%m-%dT%H:%M:%S")
|
let publish_date = chrono::NaiveDateTime::parse_from_str(&date_str, "%Y-%m-%d %H:%M:%S")
|
||||||
.map_err(|e| sqlx::Error::Decode(e.into()))?;
|
.map_err(|e| sqlx::Error::Decode(e.into()))?;
|
||||||
|
|
||||||
tales.push(Lore { title: row.try_get("title")?,
|
tales.push(Lore { title: row.try_get("title")?,
|
||||||
@ -328,7 +339,7 @@ impl Database
|
|||||||
Ok(tales)
|
Ok(tales)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "publisher"))]
|
#[cfg(any(not(feature = "publisher"), feature = "tester"))]
|
||||||
pub async fn get_tale_by_slug(&self, slug: &str) -> Result<Option<Tale>>
|
pub async fn get_tale_by_slug(&self, slug: &str) -> Result<Option<Tale>>
|
||||||
{
|
{
|
||||||
let mut tx = self.pool.begin().await?;
|
let mut tx = self.pool.begin().await?;
|
||||||
@ -356,7 +367,7 @@ impl Database
|
|||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let date_str: String = row.try_get("publish_date")?;
|
let date_str: String = row.try_get("publish_date")?;
|
||||||
let publish_date = chrono::NaiveDateTime::parse_from_str(&date_str, "%Y-%m-%dT%H:%M:%S")
|
let publish_date = chrono::NaiveDateTime::parse_from_str(&date_str, "%Y-%m-%d %H:%M:%S")
|
||||||
.map_err(|e| Error::Decode(e.into()))?;
|
.map_err(|e| Error::Decode(e.into()))?;
|
||||||
|
|
||||||
let lore = Lore { title: row.try_get("title")?,
|
let lore = Lore { title: row.try_get("title")?,
|
||||||
@ -367,7 +378,7 @@ impl Database
|
|||||||
tags };
|
tags };
|
||||||
|
|
||||||
Ok(Some(Tale { lore,
|
Ok(Some(Tale { lore,
|
||||||
story: row.try_get("content")? }))
|
story: Story::Html(row.try_get("content")?) }))
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -375,7 +386,7 @@ impl Database
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "publisher"))]
|
#[cfg(any(not(feature = "publisher"), feature = "tester"))]
|
||||||
pub async fn get_adventurer(&self, handle: &str)
|
pub async fn get_adventurer(&self, handle: &str)
|
||||||
-> Result<Option<Adventurer>>
|
-> Result<Option<Adventurer>>
|
||||||
{
|
{
|
||||||
|
|||||||
@ -17,5 +17,5 @@ pub use crate::adventurer::{Adventurer, Legend};
|
|||||||
#[cfg(feature = "database")]
|
#[cfg(feature = "database")]
|
||||||
pub use crate::database::Database;
|
pub use crate::database::Database;
|
||||||
pub use crate::info::{get_name, get_version};
|
pub use crate::info::{get_name, get_version};
|
||||||
pub use crate::tale::{Lore, Tale};
|
pub use crate::tale::{Story, Lore, Tale};
|
||||||
pub use crate::tavern::Tavern;
|
pub use crate::tavern::Tavern;
|
||||||
|
|||||||
@ -1,17 +1,15 @@
|
|||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// A type alias representing the path to a Markdown file.
|
#[derive(Clone, PartialEq, Deserialize, Serialize)]
|
||||||
/// This type is used to point to the location of the content of a `Tale`.
|
pub enum Story
|
||||||
#[cfg(feature = "publisher")]
|
{
|
||||||
pub type Markdown = std::path::PathBuf;
|
File(std::path::PathBuf),
|
||||||
|
Html(String)
|
||||||
|
}
|
||||||
/// A type alias representing the HTML content of the tale.
|
|
||||||
#[cfg(not(feature = "publisher"))]
|
|
||||||
pub type Markdown = String;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -19,7 +17,7 @@ pub type Markdown = String;
|
|||||||
///
|
///
|
||||||
/// This includes details such as the title, author, summary, and
|
/// This includes details such as the title, author, summary, and
|
||||||
/// associated tags.
|
/// associated tags.
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct Lore
|
pub struct Lore
|
||||||
{
|
{
|
||||||
/// The title of the tale.
|
/// The title of the tale.
|
||||||
@ -44,13 +42,14 @@ pub struct Lore
|
|||||||
|
|
||||||
|
|
||||||
/// Represents a post or story in the application.
|
/// Represents a post or story in the application.
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct Tale
|
pub struct Tale
|
||||||
{
|
{
|
||||||
/// Metadata of the post.
|
/// Metadata of the post.
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub lore: Lore,
|
pub lore: Lore,
|
||||||
|
|
||||||
/// The file path to the Markdown content of the tale.
|
/// The story content for this tale..
|
||||||
pub story: Markdown
|
#[serde(flatten)]
|
||||||
|
pub story: Story
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ use crate::tale::Tale;
|
|||||||
/// A `Tavern` contains a list of all tales (posts) and their respective
|
/// A `Tavern` contains a list of all tales (posts) and their respective
|
||||||
/// authors. It serves as the central structure for organizing and serializing
|
/// authors. It serves as the central structure for organizing and serializing
|
||||||
/// the site's content.
|
/// the site's content.
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Clone, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct Tavern
|
pub struct Tavern
|
||||||
{
|
{
|
||||||
///
|
///
|
||||||
|
|||||||
139
tavern/tests/database.rs
Normal file
139
tavern/tests/database.rs
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
#![cfg(feature = "tester")]
|
||||||
|
|
||||||
|
use chrono::NaiveDate;
|
||||||
|
use tavern::{Adventurer, Database, Legend, Lore, Story, Tale, Tavern};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn generate_tavern() -> Tavern
|
||||||
|
{
|
||||||
|
let legend: Legend = Legend
|
||||||
|
{
|
||||||
|
profile: String::from("https://cybermages.tech/about/myrddin"),
|
||||||
|
image: String::from("https://cybermages.tech/about/myrddin/pic"),
|
||||||
|
blurb: String::from("I love code!")
|
||||||
|
};
|
||||||
|
|
||||||
|
let author: Adventurer = Adventurer
|
||||||
|
{
|
||||||
|
name: String::from("Jason Smith"),
|
||||||
|
handle: String::from("myrddin"),
|
||||||
|
legend: legend
|
||||||
|
};
|
||||||
|
|
||||||
|
let lore: Lore = Lore
|
||||||
|
{
|
||||||
|
title: String::from("Test post"),
|
||||||
|
slug: String::from("test_post"),
|
||||||
|
author: author.handle.clone(),
|
||||||
|
summary: String::from("The Moon is made of cheese!"),
|
||||||
|
tags: vec![String::from("Space"), String::from("Cheese")],
|
||||||
|
publish_date:
|
||||||
|
NaiveDate::from_ymd_opt(2025, 12, 25).unwrap()
|
||||||
|
.and_hms_opt(13, 10, 41)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let tale: Tale = Tale
|
||||||
|
{
|
||||||
|
lore: lore,
|
||||||
|
story: Story::Html("#Test Post\nThis is a test post.\n\n##Subsection\nMini post.\n* Test 1\n* Test 2\n* Test 3".to_string())
|
||||||
|
};
|
||||||
|
|
||||||
|
let lore2: Lore = Lore
|
||||||
|
{
|
||||||
|
title: String::from("Test Tale"),
|
||||||
|
slug: String::from("test_tale"),
|
||||||
|
author: author.handle.clone(),
|
||||||
|
summary: String::from("The Moon is made of rocks!"),
|
||||||
|
tags: vec![String::from("Space"), String::from("Rocks")],
|
||||||
|
publish_date:
|
||||||
|
NaiveDate::from_ymd_opt(2025, 12, 25).unwrap()
|
||||||
|
.and_hms_opt(13, 10, 41)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let tale2: Tale = Tale
|
||||||
|
{
|
||||||
|
lore: lore2,
|
||||||
|
story: Story::Html("#Test Tale\nThis is a test tale.\n\n##Subsection\nMini tale.\n* Test 1\n* Test 2\n* Test 3".to_string())
|
||||||
|
};
|
||||||
|
|
||||||
|
Tavern { title: String::from("Runes & Ramblings"),
|
||||||
|
description: String::from("Join software engineer Jason Smith \
|
||||||
|
on his Rust programming journey. \
|
||||||
|
Explore program design, tech \
|
||||||
|
stacks, and more on this blog from \
|
||||||
|
CybeMages, LLC."),
|
||||||
|
tales: vec![tale, tale2],
|
||||||
|
authors: vec![author] }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn create_temp_file<S>(filename: S) -> std::path::PathBuf
|
||||||
|
where S: AsRef<std::path::Path>
|
||||||
|
{
|
||||||
|
let mut path = std::env::temp_dir();
|
||||||
|
path.push(filename);
|
||||||
|
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn cleanup_temp_file(path: &std::path::PathBuf)
|
||||||
|
{
|
||||||
|
if path.exists()
|
||||||
|
{
|
||||||
|
let _ = std::fs::remove_file(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn tavern_tale() -> Result<(), Box<dyn std::error::Error>>
|
||||||
|
{
|
||||||
|
let tavern = generate_tavern();
|
||||||
|
|
||||||
|
// Save the TOML to a temporary file.
|
||||||
|
let path = create_temp_file("tavern_test_out.db");
|
||||||
|
cleanup_temp_file(&path);
|
||||||
|
|
||||||
|
let db = Database::open(&path).await?;
|
||||||
|
db.insert_tavern(&tavern).await?;
|
||||||
|
|
||||||
|
// Write a Tale to the Tavern.
|
||||||
|
let lore: Lore = Lore
|
||||||
|
{
|
||||||
|
title: String::from("About Tavern"),
|
||||||
|
slug: String::from("about_tavern"),
|
||||||
|
author: String::from("myrddin"),
|
||||||
|
summary: String::from("Myrddin is awesome and wrote a blog system!"),
|
||||||
|
tags: vec![String::from("Blog"), String::from("Silly")],
|
||||||
|
publish_date:
|
||||||
|
NaiveDate::from_ymd_opt(2025, 12, 25).unwrap()
|
||||||
|
.and_hms_opt(13, 10, 41)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let tale: Tale = Tale
|
||||||
|
{
|
||||||
|
lore: lore,
|
||||||
|
story: Story::Html("#Tavern Blog System\nThis is a great blogging system.\n\n##Subsection\nWritten by Myrddin himself!.\n* Test 1\n* Test 2\n* Test 3".to_string())
|
||||||
|
};
|
||||||
|
db.insert_tale(&tale).await?;
|
||||||
|
|
||||||
|
// Read the Tale from the Tavern.
|
||||||
|
let tale = db.get_tale_by_slug(&tale.lore.slug).await?;
|
||||||
|
match tale
|
||||||
|
{
|
||||||
|
None => { panic!("Do something to say this test failed.") }
|
||||||
|
Some(tale) =>
|
||||||
|
{
|
||||||
|
assert_eq!("Myrddin is awesome and wrote a blog system!", tale.lore.summary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup_temp_file(&path);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@ -1,37 +1,44 @@
|
|||||||
#![cfg(feature = "publisher")]
|
#![cfg(feature = "publisher")]
|
||||||
|
|
||||||
use chrono::NaiveDate;
|
use chrono::NaiveDate;
|
||||||
use tavern::{Adventurer, FrontMatter, Tale, Tavern};
|
use tavern::{Adventurer, Legend, Lore, Story, Tale, Tavern};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fn generate_tavern() -> Tavern
|
fn generate_tavern() -> Tavern
|
||||||
{
|
{
|
||||||
let author: Adventurer =
|
let legend: Legend = Legend
|
||||||
Adventurer { name: String::from("Jason Smith"),
|
{
|
||||||
handle: String::from("myrddin"),
|
profile: String::from("https://cybermages.tech/about/myrddin"),
|
||||||
profile:
|
image: String::from("https://cybermages.tech/about/myrddin/pic"),
|
||||||
String::from("https://cybermages.tech/about/myrddin"),
|
blurb: String::from("I love code!")
|
||||||
image:
|
|
||||||
String::from("https://cybermages.tech/about/myrddin/pic"),
|
|
||||||
blurb: String::from("I love code!") };
|
|
||||||
|
|
||||||
|
|
||||||
let fm: FrontMatter = FrontMatter {
|
|
||||||
title: String::from("Test post"),
|
|
||||||
slug: String::from("test_post"),
|
|
||||||
author: author.handle.clone(),
|
|
||||||
summary: String::from("The Moon is made of cheese!"),
|
|
||||||
tags: vec![String::from("Space"), String::from("Cheese")],
|
|
||||||
publish_date:
|
|
||||||
NaiveDate::from_ymd_opt(2025, 12, 25).unwrap()
|
|
||||||
.and_hms_opt(13, 10, 41)
|
|
||||||
.unwrap()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let tale: Tale = Tale {
|
let author: Adventurer = Adventurer
|
||||||
front_matter: fm,
|
{
|
||||||
content: std::path::PathBuf::from("posts/test_post.md") };
|
name: String::from("Jason Smith"),
|
||||||
|
handle: String::from("myrddin"),
|
||||||
|
legend: legend
|
||||||
|
};
|
||||||
|
|
||||||
|
let lore: Lore = Lore
|
||||||
|
{
|
||||||
|
title: String::from("Test post"),
|
||||||
|
slug: String::from("test_post"),
|
||||||
|
author: author.handle.clone(),
|
||||||
|
summary: String::from("The Moon is made of cheese!"),
|
||||||
|
tags: vec![String::from("Space"), String::from("Cheese")],
|
||||||
|
publish_date:
|
||||||
|
NaiveDate::from_ymd_opt(2025, 12, 25).unwrap()
|
||||||
|
.and_hms_opt(13, 10, 41)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let tale: Tale = Tale
|
||||||
|
{
|
||||||
|
lore: lore,
|
||||||
|
story: Story::File(std::path::PathBuf::from("posts/test_post.md"))
|
||||||
|
};
|
||||||
|
|
||||||
Tavern { title: String::from("Runes & Ramblings"),
|
Tavern { title: String::from("Runes & Ramblings"),
|
||||||
description: String::from("Join software engineer Jason Smith \
|
description: String::from("Join software engineer Jason Smith \
|
||||||
@ -43,6 +50,14 @@ fn generate_tavern() -> Tavern
|
|||||||
authors: vec![author] }
|
authors: vec![author] }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_temp_file<S>(filename: S) -> std::path::PathBuf
|
||||||
|
where S: AsRef<std::path::Path>
|
||||||
|
{
|
||||||
|
let mut path = std::env::temp_dir();
|
||||||
|
path.push(filename);
|
||||||
|
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
fn cleanup_temp_file(path: &std::path::PathBuf)
|
fn cleanup_temp_file(path: &std::path::PathBuf)
|
||||||
{
|
{
|
||||||
@ -63,8 +78,7 @@ fn to_file()
|
|||||||
succeed");
|
succeed");
|
||||||
|
|
||||||
// Save the TOML to a temporary file.
|
// Save the TOML to a temporary file.
|
||||||
let mut path = std::env::temp_dir();
|
let path = create_temp_file("tavern_test_to.toml");
|
||||||
path.push("tavern_test_out.toml");
|
|
||||||
std::fs::write(&path, &toml_string).expect("Failed to write TOML to file");
|
std::fs::write(&path, &toml_string).expect("Failed to write TOML to file");
|
||||||
|
|
||||||
cleanup_temp_file(&path);
|
cleanup_temp_file(&path);
|
||||||
@ -80,8 +94,7 @@ fn from_file()
|
|||||||
succeed");
|
succeed");
|
||||||
|
|
||||||
// Save the TOML to a temporary file.
|
// Save the TOML to a temporary file.
|
||||||
let mut path = std::env::temp_dir();
|
let path = create_temp_file("tavern_test_from.toml");
|
||||||
path.push("tavern_test_in.toml");
|
|
||||||
std::fs::write(&path, &toml_string).expect("Failed to write TOML to file");
|
std::fs::write(&path, &toml_string).expect("Failed to write TOML to file");
|
||||||
|
|
||||||
// Read the previously written TOML file
|
// Read the previously written TOML file
|
||||||
@ -94,9 +107,9 @@ fn from_file()
|
|||||||
|
|
||||||
// Assert some known values to make this a real test
|
// Assert some known values to make this a real test
|
||||||
let tale = &tavern.tales[0];
|
let tale = &tavern.tales[0];
|
||||||
assert_eq!(tale.front_matter.title, "Test post");
|
assert_eq!(tale.lore.title, "Test post");
|
||||||
assert_eq!(tale.front_matter.slug, "test_post");
|
assert_eq!(tale.lore.slug, "test_post");
|
||||||
assert_eq!(tale.front_matter.author, "myrddin");
|
assert_eq!(tale.lore.author, "myrddin");
|
||||||
|
|
||||||
cleanup_temp_file(&path);
|
cleanup_temp_file(&path);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user