testing the components???

This commit is contained in:
Hamcha 2025-01-26 13:25:24 +01:00
parent 7a4f3b15b7
commit f6b35d05e7
Signed by: hamcha
GPG key ID: 1669C533B8CF6D89
10 changed files with 338 additions and 40 deletions
Cargo.lockCargo.toml
src
inbound/renderer
main.rs
outbound
repository
services

77
Cargo.lock generated
View file

@ -38,6 +38,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "anstyle"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]] [[package]]
name = "askama_escape" name = "askama_escape"
version = "0.10.3" version = "0.10.3"
@ -869,6 +875,12 @@ version = "0.15.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
[[package]]
name = "downcast"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1"
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
version = "0.8.35" version = "0.8.35"
@ -967,6 +979,12 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "fragile"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa"
[[package]] [[package]]
name = "futures" name = "futures"
version = "0.3.31" version = "0.3.31"
@ -1540,6 +1558,7 @@ dependencies = [
"dioxus-cli-config", "dioxus-cli-config",
"dioxus-logger", "dioxus-logger",
"dotenvy", "dotenvy",
"mockall",
"serde", "serde",
"thiserror 2.0.11", "thiserror 2.0.11",
"tokio", "tokio",
@ -1628,6 +1647,32 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "mockall"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39a6bfcc6c8c7eed5ee98b9c3e33adc726054389233e201c95dab2d41a3839d2"
dependencies = [
"cfg-if",
"downcast",
"fragile",
"mockall_derive",
"predicates",
"predicates-tree",
]
[[package]]
name = "mockall_derive"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898"
dependencies = [
"cfg-if",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "multer" name = "multer"
version = "3.1.0" version = "3.1.0"
@ -1806,6 +1851,32 @@ dependencies = [
"zerocopy", "zerocopy",
] ]
[[package]]
name = "predicates"
version = "3.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573"
dependencies = [
"anstyle",
"predicates-core",
]
[[package]]
name = "predicates-core"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa"
[[package]]
name = "predicates-tree"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c"
dependencies = [
"predicates-core",
"termtree",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.93" version = "1.0.93"
@ -2319,6 +2390,12 @@ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
[[package]]
name = "termtree"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683"
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.69" version = "1.0.69"

View file

@ -10,13 +10,14 @@ dioxus = { version = "0.6", features = ["fullstack", "router"] }
dioxus-cli-config = "0.6" dioxus-cli-config = "0.6"
dioxus-logger = "0.6" dioxus-logger = "0.6"
dotenvy = { version = "0.15", optional = true } dotenvy = { version = "0.15", optional = true }
mockall = { version = "0.13", optional = true }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
thiserror = "2" thiserror = "2"
tokio = { version = "1", features = ["full"], optional = true } tokio = { version = "1", features = ["full"], optional = true }
[features] [features]
default = [] default = []
server = ["dioxus/server", "tokio", "dotenvy"] server = ["dioxus/server", "tokio", "dotenvy", "mockall"]
web = ["dioxus/web"] web = ["dioxus/web"]
tokio = ["dep:tokio"] tokio = ["dep:tokio"]

View file

@ -11,6 +11,9 @@ mod meta;
mod page; mod page;
mod server; mod server;
#[cfg(test)]
mod testing;
#[derive(Clone, Routable, Debug, PartialEq, serde::Serialize, serde::Deserialize)] #[derive(Clone, Routable, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
#[rustfmt::skip] #[rustfmt::skip]
enum Route { enum Route {

View file

@ -71,3 +71,121 @@ pub fn BlockElement(block: Block) -> Element {
}, },
} }
} }
#[cfg(test)]
mod tests {
use meta::SiteContext;
use crate::{
domain::entities::{
cursor::Paginated,
site::{Image, SiteInfo},
},
outbound::services::site::{MockSiteService, SiteServiceProvider},
};
use super::*;
#[test]
fn block_renders_text() {
let text = "Hello, world!";
let block = Block::Text {
text: text.to_string(),
};
let element = rsx! {
BlockElement { block }
};
let elem_str = dioxus::ssr::render_element(element);
assert!(elem_str.contains(text));
}
#[test]
fn block_gallery_renders_images() {
let images = vec![
Image {
src: "https://example.com/image1.jpg".to_string(),
caption: "Image 1".to_string(),
},
Image {
src: "https://example.com/image2.jpg".to_string(),
caption: "Image 2".to_string(),
},
];
let block = Block::Gallery { images };
let element = rsx! {
BlockElement { block }
};
let elem_str = dioxus::ssr::render_element(element);
assert!(elem_str.contains("<img"));
assert!(elem_str.contains("https://example.com/image1.jpg"));
assert!(elem_str.contains("https://example.com/image2.jpg"));
}
#[test]
fn post_renders_blocks() {
let blocks = vec![
Block::Text {
text: "Hello, world!".to_string(),
},
Block::Text {
text: "Something else!".to_string(),
},
];
let post = Post { blocks };
let element = rsx! {
PostElement { post }
};
let elem_str = dioxus::ssr::render_element(element);
assert!(elem_str.contains("Hello, world!"));
assert!(elem_str.contains("Something else!"));
}
#[test]
fn collection_renders_posts() {
let mut app = VirtualDom::new(|| {
rsx! {
Collection { collection_id: "".to_string() }
}
});
app.provide_root_context(SiteContext {
info: SiteInfo {
title: "test".to_string(),
domain: "test".to_string(),
pages: vec![],
},
});
let mut mock_service = MockSiteService::new();
mock_service
.expect_get_posts()
.times(1)
.returning(move |_, _, _| {
Box::pin(async {
Ok(Paginated {
data: vec![
Post {
blocks: vec![Block::Text {
text: "Hello, world!".to_string(),
}],
},
Post {
blocks: vec![Block::Text {
text: "Something else!".to_string(),
}],
},
],
next: None,
})
})
});
server_context().insert(SiteServiceProvider::with(mock_service));
app.rebuild_in_place();
let elem_str = dioxus::ssr::render(&app);
println!("elem_str: {elem_str}");
assert!(elem_str.contains("Hello, world!"));
assert!(elem_str.contains("Something else!"));
}
}

View file

@ -7,8 +7,8 @@ use crate::domain::entities::{
#[server] #[server]
pub async fn get_site_info() -> Result<SiteInfo, ServerFnError> { pub async fn get_site_info() -> Result<SiteInfo, ServerFnError> {
use crate::outbound::services::site::SiteService; use crate::outbound::services::site::SiteServiceProvider;
let FromContext::<SiteService>(service) = extract().await?; let FromContext(SiteServiceProvider { service }) = extract().await?;
let headers = server_context().request_parts().headers.clone(); let headers = server_context().request_parts().headers.clone();
let domain = match headers.get("host").and_then(|h| h.to_str().ok()) { let domain = match headers.get("host").and_then(|h| h.to_str().ok()) {
@ -26,8 +26,8 @@ pub async fn get_site_info() -> Result<SiteInfo, ServerFnError> {
#[server] #[server]
pub async fn get_page(domain: String, page: String) -> Result<Page, ServerFnError> { pub async fn get_page(domain: String, page: String) -> Result<Page, ServerFnError> {
use crate::outbound::services::site::SiteService; use crate::outbound::services::site::SiteServiceProvider;
let FromContext::<SiteService>(service) = extract().await?; let FromContext(SiteServiceProvider { service }) = extract().await?;
Ok(service.get_page(&domain, &page).await?) Ok(service.get_page(&domain, &page).await?)
} }
@ -37,8 +37,9 @@ pub async fn get_posts(
domain: String, domain: String,
collection_id: String, collection_id: String,
) -> Result<Paginated<Post, String>, ServerFnError> { ) -> Result<Paginated<Post, String>, ServerFnError> {
use crate::outbound::services::site::SiteService; println!("server_context: {:#?}", server_context().request_parts());
let FromContext::<SiteService>(service) = extract().await?; use crate::outbound::services::site::SiteServiceProvider;
let FromContext(SiteServiceProvider { service }) = extract().await?;
Ok(service.get_posts(&domain, &collection_id, None).await?) Ok(service.get_posts(&domain, &collection_id, None).await?)
} }
@ -49,8 +50,8 @@ pub async fn get_post(
collection_id: String, collection_id: String,
id: String, id: String,
) -> Result<Post, ServerFnError> { ) -> Result<Post, ServerFnError> {
use crate::outbound::services::site::SiteService; use crate::outbound::services::site::SiteServiceProvider;
let FromContext::<SiteService>(service) = extract().await?; let FromContext(SiteServiceProvider { service }) = extract().await?;
Ok(service.get_post(&domain, &collection_id, &id).await?) Ok(service.get_post(&domain, &collection_id, &id).await?)
} }

View file

@ -0,0 +1,7 @@
use dioxus::prelude::*;
#[component]
pub fn UseSiteContext() -> Element {
use_context_provider(|| Signal::new(0));
rsx! {}
}

View file

@ -15,10 +15,10 @@ fn main() {
let mut builder = dioxus::LaunchBuilder::new(); let mut builder = dioxus::LaunchBuilder::new();
server_only! { server_only! {
use outbound::services::site::SiteService; use outbound::services::site::{SiteServiceProvider, SiteServiceImpl};
let store = tokio::runtime::Runtime::new().unwrap().block_on(setup_inmem_store()); let store = tokio::runtime::Runtime::new().unwrap().block_on(setup_inmem_store());
builder = builder.with_context_provider(move || { builder = builder.with_context_provider(move || {
Box::new(SiteService::new(store.clone())) Box::new(SiteServiceProvider::with(SiteServiceImpl::new(store.clone())))
}) })
} }

View file

@ -175,6 +175,16 @@ impl SiteRepository for InMemoryStore {
Ok(()) Ok(())
} }
async fn delete_site(&self, domain: &str) -> Result<()> {
self.data
.lock()
.await
.sites
.remove(domain)
.ok_or(repository::site::Error::NotFound)
.map(|_| ())
}
async fn get_page(&self, domain: &str, page: &str) -> Result<Page> { async fn get_page(&self, domain: &str, page: &str) -> Result<Page> {
self.data self.data
.lock() .lock()
@ -246,7 +256,7 @@ impl SiteRepository for InMemoryStore {
// Skip posts before and including the cursor post_id // Skip posts before and including the cursor post_id
posts = posts posts = posts
.iter() .iter()
.skip_while(|(id, _)| id.to_owned() <= &after) .skip_while(|(id, _)| **id <= after)
.cloned() .cloned()
.collect(); .collect();
} }
@ -524,4 +534,27 @@ mod tests {
repository::site::Error::Conflict repository::site::Error::Conflict
)); ));
} }
#[tokio::test]
async fn deletes_a_site() {
let store = InMemoryStore::with_test_data().await;
let _ = store.delete_site("example.com").await.unwrap();
let result = store.get_site_by_domain("example.com").await;
assert!(result.is_err());
assert!(matches!(
result.err().unwrap(),
repository::site::Error::NotFound
));
}
#[tokio::test]
async fn deletes_a_nonexistent_site() {
let store = InMemoryStore::new();
let result = store.delete_site("example.com").await;
assert!(result.is_err());
assert!(matches!(
result.err().unwrap(),
repository::site::Error::NotFound
));
}
} }

View file

@ -1,4 +1,3 @@
use async_trait::async_trait;
use thiserror::Error; use thiserror::Error;
use crate::domain::entities::{ use crate::domain::entities::{
@ -12,13 +11,13 @@ pub struct SiteMetadata {
pub title: String, pub title: String,
} }
#[async_trait] #[async_trait::async_trait]
pub trait SiteRepository: Send + Sync + 'static { pub trait SiteRepository: Send + Sync + 'static {
async fn get_site_by_domain(&self, domain: &str) -> Result<SiteMetadata>; async fn get_site_by_domain(&self, domain: &str) -> Result<SiteMetadata>;
async fn get_pages_for_site(&self, domain: &str) -> Result<Vec<PageInfo>>;
async fn create_site(&self, site: SiteMetadata) -> Result<()>; async fn create_site(&self, site: SiteMetadata) -> Result<()>;
async fn delete_site(&self, domain: &str) -> Result<()>;
async fn get_pages_for_site(&self, domain: &str) -> Result<Vec<PageInfo>>;
async fn get_page(&self, domain: &str, name: &str) -> Result<Page>; async fn get_page(&self, domain: &str, name: &str) -> Result<Page>;
async fn set_page(&self, domain: &str, page: Page) -> Result<()>; async fn set_page(&self, domain: &str, page: Page) -> Result<()>;
async fn delete_page(&self, domain: &str, page: &str) -> Result<()>; async fn delete_page(&self, domain: &str, page: &str) -> Result<()>;

View file

@ -14,18 +14,51 @@ use crate::{
}; };
#[derive(Clone)] #[derive(Clone)]
pub struct SiteService { pub struct SiteServiceProvider {
pub service: Arc<dyn SiteService>,
}
impl SiteServiceProvider {
pub fn with(service: impl SiteService) -> Self {
Self {
service: Arc::new(service),
}
}
}
#[async_trait::async_trait]
#[mockall::automock]
pub trait SiteService: Send + Sync + 'static {
async fn get_site(&self, domain: &str) -> Result<SiteInfo>;
async fn create_site(&self, site: SiteMetadata) -> Result<()>;
async fn delete_site(&self, domain: &str) -> Result<()>;
async fn get_page(&self, domain: &str, name: &str) -> Result<Page>;
async fn set_page(&self, domain: &str, page: Page) -> Result<()>;
async fn delete_page(&self, domain: &str, name: &str) -> Result<()>;
async fn get_post(&self, domain: &str, collection_id: &str, id: &str) -> Result<Post>;
async fn get_posts(
&self,
domain: &str,
collection_id: &str,
cursor: Option<String>,
) -> Result<Paginated<Post, String>>;
}
pub struct SiteServiceImpl {
site_repository: Arc<dyn SiteRepository>, site_repository: Arc<dyn SiteRepository>,
} }
impl SiteService { impl SiteServiceImpl {
pub fn new(site_repository: impl SiteRepository) -> Self { pub fn new(site_repository: impl SiteRepository) -> Self {
Self { Self {
site_repository: Arc::new(site_repository), site_repository: Arc::new(site_repository),
} }
} }
}
pub async fn get_site(&self, domain: &str) -> Result<SiteInfo> { #[async_trait::async_trait]
impl SiteService for SiteServiceImpl {
async fn get_site(&self, domain: &str) -> Result<SiteInfo> {
let info = self.site_repository.get_site_by_domain(domain).await?; let info = self.site_repository.get_site_by_domain(domain).await?;
let pages = self let pages = self
.site_repository .site_repository
@ -39,31 +72,37 @@ impl SiteService {
}) })
} }
pub async fn create_site(&self, site: SiteMetadata) -> Result<()> { async fn create_site(&self, site: SiteMetadata) -> Result<()> {
self.site_repository.create_site(site).await?; self.site_repository.create_site(site).await?;
Ok(()) Ok(())
} }
pub async fn get_page(&self, domain: &str, name: &str) -> Result<Page> { async fn delete_site(&self, domain: &str) -> Result<()> {
self.site_repository.delete_site(domain).await?;
Ok(())
}
async fn get_page(&self, domain: &str, name: &str) -> Result<Page> {
let page = self.site_repository.get_page(domain, name).await?; let page = self.site_repository.get_page(domain, name).await?;
Ok(page) Ok(page)
} }
pub async fn set_page(&self, domain: &str, page: Page) -> Result<()> { async fn set_page(&self, domain: &str, page: Page) -> Result<()> {
self.site_repository.set_page(domain, page).await?; self.site_repository.set_page(domain, page).await?;
Ok(()) Ok(())
} }
pub async fn delete_page(&self, domain: &str, page: &str) -> Result<()> { async fn delete_page(&self, domain: &str, page: &str) -> Result<()> {
self.site_repository.delete_page(domain, page).await?; self.site_repository.delete_page(domain, page).await?;
Ok(()) Ok(())
} }
pub async fn get_post(&self, domain: &str, collection_id: &str, id: &str) -> Result<Post> { async fn get_post(&self, domain: &str, collection_id: &str, id: &str) -> Result<Post> {
let post = self let post = self
.site_repository .site_repository
.get_post(domain, collection_id, id) .get_post(domain, collection_id, id)
@ -72,7 +111,7 @@ impl SiteService {
Ok(post) Ok(post)
} }
pub async fn get_posts( async fn get_posts(
&self, &self,
domain: &str, domain: &str,
collection_id: &str, collection_id: &str,
@ -119,12 +158,15 @@ mod tests {
use super::{Error, SiteService}; use super::{Error, SiteService};
use crate::{ use crate::{
domain::entities::site::{Block, Page, PageContent, PageInfo, Post}, domain::entities::site::{Block, Page, PageContent, PageInfo, Post},
outbound::repository::{adapters::memory::InMemoryStore, site::SiteMetadata}, outbound::{
repository::{adapters::memory::InMemoryStore, site::SiteMetadata},
services::site::SiteServiceImpl,
},
}; };
#[tokio::test] #[tokio::test]
async fn gets_site_info() { async fn gets_site_info() {
let service = SiteService::new(InMemoryStore::with_test_data().await); let service = SiteServiceImpl::new(InMemoryStore::with_test_data().await);
let info = service.get_site("example.com").await.unwrap(); let info = service.get_site("example.com").await.unwrap();
assert_eq!(info.domain, "example.com"); assert_eq!(info.domain, "example.com");
assert_eq!(info.title, "Test site"); assert_eq!(info.title, "Test site");
@ -133,7 +175,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn gets_nonexistent_site() { async fn gets_nonexistent_site() {
let service = SiteService::new(InMemoryStore::with_test_data().await); let service = SiteServiceImpl::new(InMemoryStore::with_test_data().await);
let result = service.get_site("nonexistent.com").await; let result = service.get_site("nonexistent.com").await;
assert!(result.is_err()); assert!(result.is_err());
assert!(matches!(result.err().unwrap(), Error::NotFound)); assert!(matches!(result.err().unwrap(), Error::NotFound));
@ -141,7 +183,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn gets_page_data() { async fn gets_page_data() {
let service = SiteService::new(InMemoryStore::with_test_data().await); let service = SiteServiceImpl::new(InMemoryStore::with_test_data().await);
let page = service.get_page("example.com", "/").await.unwrap(); let page = service.get_page("example.com", "/").await.unwrap();
assert_eq!(page.info.title, "Home"); assert_eq!(page.info.title, "Home");
@ -161,7 +203,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn gets_nonexistent_page() { async fn gets_nonexistent_page() {
let service = SiteService::new(InMemoryStore::with_test_data().await); let service = SiteServiceImpl::new(InMemoryStore::with_test_data().await);
let result = service.get_page("example.com", "nonexistent").await; let result = service.get_page("example.com", "nonexistent").await;
assert!(result.is_err()); assert!(result.is_err());
assert!(matches!(result.err().unwrap(), Error::NotFound)); assert!(matches!(result.err().unwrap(), Error::NotFound));
@ -169,7 +211,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn gets_a_single_post() { async fn gets_a_single_post() {
let service = SiteService::new(InMemoryStore::with_test_data().await); let service = SiteServiceImpl::new(InMemoryStore::with_test_data().await);
let post = service let post = service
.get_post("example.com", "home_posts", "post_id") .get_post("example.com", "home_posts", "post_id")
.await .await
@ -186,7 +228,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn gets_nonexistent_post() { async fn gets_nonexistent_post() {
let service = SiteService::new(InMemoryStore::with_test_data().await); let service = SiteServiceImpl::new(InMemoryStore::with_test_data().await);
let result = service let result = service
.get_post("example.com", "home_posts", "nonexistent") .get_post("example.com", "home_posts", "nonexistent")
.await; .await;
@ -196,7 +238,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn gets_posts() { async fn gets_posts() {
let service = SiteService::new(InMemoryStore::with_test_data().await); let service = SiteServiceImpl::new(InMemoryStore::with_test_data().await);
let posts = service let posts = service
.get_posts("example.com", "home_posts", None) .get_posts("example.com", "home_posts", None)
.await .await
@ -207,7 +249,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn gets_posts_with_cursor() { async fn gets_posts_with_cursor() {
let service = SiteService::new(InMemoryStore::with_test_data().await); let service = SiteServiceImpl::new(InMemoryStore::with_test_data().await);
let posts = service let posts = service
.get_posts("example.com", "home_posts", Some("post_id".to_string())) .get_posts("example.com", "home_posts", Some("post_id".to_string()))
.await .await
@ -218,7 +260,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn gets_nonexistent_posts() { async fn gets_nonexistent_posts() {
let service = SiteService::new(InMemoryStore::with_test_data().await); let service = SiteServiceImpl::new(InMemoryStore::with_test_data().await);
let result = service.get_posts("example.com", "nonexistent", None).await; let result = service.get_posts("example.com", "nonexistent", None).await;
assert!(result.is_err()); assert!(result.is_err());
assert!(matches!(result.err().unwrap(), Error::NotFound)); assert!(matches!(result.err().unwrap(), Error::NotFound));
@ -234,7 +276,7 @@ mod tests {
}, },
}; };
let service = SiteService::new(InMemoryStore::with_test_data().await); let service = SiteServiceImpl::new(InMemoryStore::with_test_data().await);
service service
.set_page( .set_page(
"example.com", "example.com",
@ -258,7 +300,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn deletes_a_page() { async fn deletes_a_page() {
let service = SiteService::new(InMemoryStore::with_test_data().await); let service = SiteServiceImpl::new(InMemoryStore::with_test_data().await);
service.delete_page("example.com", "about").await.unwrap(); service.delete_page("example.com", "about").await.unwrap();
let result = service.get_page("example.com", "about").await; let result = service.get_page("example.com", "about").await;
assert!(result.is_err()); assert!(result.is_err());
@ -267,7 +309,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn deletes_a_nonexistent_page() { async fn deletes_a_nonexistent_page() {
let service = SiteService::new(InMemoryStore::with_test_data().await); let service = SiteServiceImpl::new(InMemoryStore::with_test_data().await);
let result = service.delete_page("example.com", "nonexistent").await; let result = service.delete_page("example.com", "nonexistent").await;
assert!(result.is_err()); assert!(result.is_err());
assert!(matches!(result.err().unwrap(), Error::NotFound)); assert!(matches!(result.err().unwrap(), Error::NotFound));
@ -275,7 +317,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn creates_a_site() { async fn creates_a_site() {
let service = SiteService::new(InMemoryStore::with_test_data().await); let service = SiteServiceImpl::new(InMemoryStore::with_test_data().await);
service service
.create_site(SiteMetadata { .create_site(SiteMetadata {
domain: "new.com".to_string(), domain: "new.com".to_string(),
@ -287,7 +329,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn doesnt_create_a_site_that_already_exists() { async fn doesnt_create_a_site_that_already_exists() {
let service = SiteService::new(InMemoryStore::with_test_data().await); let service = SiteServiceImpl::new(InMemoryStore::with_test_data().await);
let result = service let result = service
.create_site(SiteMetadata { .create_site(SiteMetadata {
domain: "example.com".to_string(), domain: "example.com".to_string(),
@ -300,4 +342,21 @@ mod tests {
Error::RepositoryError(crate::outbound::repository::site::Error::Conflict) Error::RepositoryError(crate::outbound::repository::site::Error::Conflict)
)); ));
} }
#[tokio::test]
async fn deletes_a_site() {
let service = SiteServiceImpl::new(InMemoryStore::with_test_data().await);
service.delete_site("example.com").await.unwrap();
let result = service.get_site("example.com").await;
assert!(result.is_err());
assert!(matches!(result.err().unwrap(), Error::NotFound));
}
#[tokio::test]
async fn deletes_a_nonexistent_site() {
let service = SiteServiceImpl::new(InMemoryStore::with_test_data().await);
let result = service.delete_site("nonexistent").await;
assert!(result.is_err());
assert!(matches!(result.err().unwrap(), Error::NotFound));
}
} }