testing the components???
This commit is contained in:
parent
7a4f3b15b7
commit
f6b35d05e7
10 changed files with 338 additions and 40 deletions
77
Cargo.lock
generated
77
Cargo.lock
generated
|
@ -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"
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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!"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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?)
|
||||||
}
|
}
|
||||||
|
|
7
src/inbound/renderer/testing/mod.rs
Normal file
7
src/inbound/renderer/testing/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn UseSiteContext() -> Element {
|
||||||
|
use_context_provider(|| Signal::new(0));
|
||||||
|
rsx! {}
|
||||||
|
}
|
|
@ -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())))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<()>;
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue