diff --git a/Cargo.lock b/Cargo.lock
index f824cef..f2c382b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -38,6 +38,12 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "anstyle"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
+
 [[package]]
 name = "askama_escape"
 version = "0.10.3"
@@ -869,6 +875,12 @@ version = "0.15.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
 
+[[package]]
+name = "downcast"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1"
+
 [[package]]
 name = "encoding_rs"
 version = "0.8.35"
@@ -967,6 +979,12 @@ dependencies = [
  "percent-encoding",
 ]
 
+[[package]]
+name = "fragile"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa"
+
 [[package]]
 name = "futures"
 version = "0.3.31"
@@ -1540,6 +1558,7 @@ dependencies = [
  "dioxus-cli-config",
  "dioxus-logger",
  "dotenvy",
+ "mockall",
  "serde",
  "thiserror 2.0.11",
  "tokio",
@@ -1628,6 +1647,32 @@ dependencies = [
  "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]]
 name = "multer"
 version = "3.1.0"
@@ -1806,6 +1851,32 @@ dependencies = [
  "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]]
 name = "proc-macro2"
 version = "1.0.93"
@@ -2319,6 +2390,12 @@ dependencies = [
  "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]]
 name = "thiserror"
 version = "1.0.69"
diff --git a/Cargo.toml b/Cargo.toml
index bea5917..3d870f7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,13 +10,14 @@ dioxus = { version = "0.6", features = ["fullstack", "router"] }
 dioxus-cli-config = "0.6"
 dioxus-logger = "0.6"
 dotenvy = { version = "0.15", optional = true }
+mockall = { version = "0.13", optional = true }
 serde = { version = "1", features = ["derive"] }
 thiserror = "2"
 tokio = { version = "1", features = ["full"], optional = true }
 
 [features]
 default = []
-server = ["dioxus/server", "tokio", "dotenvy"]
+server = ["dioxus/server", "tokio", "dotenvy", "mockall"]
 web = ["dioxus/web"]
 tokio = ["dep:tokio"]
 
diff --git a/src/inbound/renderer/mod.rs b/src/inbound/renderer/mod.rs
index ada7c5e..4e3213e 100644
--- a/src/inbound/renderer/mod.rs
+++ b/src/inbound/renderer/mod.rs
@@ -11,6 +11,9 @@ mod meta;
 mod page;
 mod server;
 
+#[cfg(test)]
+mod testing;
+
 #[derive(Clone, Routable, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
 #[rustfmt::skip]
 enum Route {
diff --git a/src/inbound/renderer/page.rs b/src/inbound/renderer/page.rs
index 4122d52..0148041 100644
--- a/src/inbound/renderer/page.rs
+++ b/src/inbound/renderer/page.rs
@@ -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!"));
+    }
+}
diff --git a/src/inbound/renderer/server.rs b/src/inbound/renderer/server.rs
index bd921c8..aa705e7 100644
--- a/src/inbound/renderer/server.rs
+++ b/src/inbound/renderer/server.rs
@@ -7,8 +7,8 @@ use crate::domain::entities::{
 
 #[server]
 pub async fn get_site_info() -> Result<SiteInfo, ServerFnError> {
-    use crate::outbound::services::site::SiteService;
-    let FromContext::<SiteService>(service) = extract().await?;
+    use crate::outbound::services::site::SiteServiceProvider;
+    let FromContext(SiteServiceProvider { service }) = extract().await?;
 
     let headers = server_context().request_parts().headers.clone();
     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]
 pub async fn get_page(domain: String, page: String) -> Result<Page, ServerFnError> {
-    use crate::outbound::services::site::SiteService;
-    let FromContext::<SiteService>(service) = extract().await?;
+    use crate::outbound::services::site::SiteServiceProvider;
+    let FromContext(SiteServiceProvider { service }) = extract().await?;
 
     Ok(service.get_page(&domain, &page).await?)
 }
@@ -37,8 +37,9 @@ pub async fn get_posts(
     domain: String,
     collection_id: String,
 ) -> Result<Paginated<Post, String>, ServerFnError> {
-    use crate::outbound::services::site::SiteService;
-    let FromContext::<SiteService>(service) = extract().await?;
+    println!("server_context: {:#?}", server_context().request_parts());
+    use crate::outbound::services::site::SiteServiceProvider;
+    let FromContext(SiteServiceProvider { service }) = extract().await?;
 
     Ok(service.get_posts(&domain, &collection_id, None).await?)
 }
@@ -49,8 +50,8 @@ pub async fn get_post(
     collection_id: String,
     id: String,
 ) -> Result<Post, ServerFnError> {
-    use crate::outbound::services::site::SiteService;
-    let FromContext::<SiteService>(service) = extract().await?;
+    use crate::outbound::services::site::SiteServiceProvider;
+    let FromContext(SiteServiceProvider { service }) = extract().await?;
 
     Ok(service.get_post(&domain, &collection_id, &id).await?)
 }
diff --git a/src/inbound/renderer/testing/mod.rs b/src/inbound/renderer/testing/mod.rs
new file mode 100644
index 0000000..8a52814
--- /dev/null
+++ b/src/inbound/renderer/testing/mod.rs
@@ -0,0 +1,7 @@
+use dioxus::prelude::*;
+
+#[component]
+pub fn UseSiteContext() -> Element {
+    use_context_provider(|| Signal::new(0));
+    rsx! {}
+}
diff --git a/src/main.rs b/src/main.rs
index 57e951b..67eab0b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -15,10 +15,10 @@ fn main() {
 
     let mut builder = dioxus::LaunchBuilder::new();
     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());
         builder = builder.with_context_provider(move || {
-            Box::new(SiteService::new(store.clone()))
+            Box::new(SiteServiceProvider::with(SiteServiceImpl::new(store.clone())))
         })
     }
 
diff --git a/src/outbound/repository/adapters/memory.rs b/src/outbound/repository/adapters/memory.rs
index 81974a0..8d89a57 100644
--- a/src/outbound/repository/adapters/memory.rs
+++ b/src/outbound/repository/adapters/memory.rs
@@ -175,6 +175,16 @@ impl SiteRepository for InMemoryStore {
         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> {
         self.data
             .lock()
@@ -246,7 +256,7 @@ impl SiteRepository for InMemoryStore {
             // Skip posts before and including the cursor post_id
             posts = posts
                 .iter()
-                .skip_while(|(id, _)| id.to_owned() <= &after)
+                .skip_while(|(id, _)| **id <= after)
                 .cloned()
                 .collect();
         }
@@ -524,4 +534,27 @@ mod tests {
             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
+        ));
+    }
 }
diff --git a/src/outbound/repository/site.rs b/src/outbound/repository/site.rs
index 735ff5d..ebad384 100644
--- a/src/outbound/repository/site.rs
+++ b/src/outbound/repository/site.rs
@@ -1,4 +1,3 @@
-use async_trait::async_trait;
 use thiserror::Error;
 
 use crate::domain::entities::{
@@ -12,13 +11,13 @@ pub struct SiteMetadata {
     pub title: String,
 }
 
-#[async_trait]
+#[async_trait::async_trait]
 pub trait SiteRepository: Send + Sync + 'static {
     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 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 set_page(&self, domain: &str, page: Page) -> Result<()>;
     async fn delete_page(&self, domain: &str, page: &str) -> Result<()>;
diff --git a/src/outbound/services/site.rs b/src/outbound/services/site.rs
index 3300755..3214833 100644
--- a/src/outbound/services/site.rs
+++ b/src/outbound/services/site.rs
@@ -14,18 +14,51 @@ use crate::{
 };
 
 #[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>,
 }
 
-impl SiteService {
+impl SiteServiceImpl {
     pub fn new(site_repository: impl SiteRepository) -> Self {
         Self {
             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 pages = self
             .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?;
 
         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?;
 
         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?;
 
         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?;
 
         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
             .site_repository
             .get_post(domain, collection_id, id)
@@ -72,7 +111,7 @@ impl SiteService {
         Ok(post)
     }
 
-    pub async fn get_posts(
+    async fn get_posts(
         &self,
         domain: &str,
         collection_id: &str,
@@ -119,12 +158,15 @@ mod tests {
     use super::{Error, SiteService};
     use crate::{
         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]
     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();
         assert_eq!(info.domain, "example.com");
         assert_eq!(info.title, "Test site");
@@ -133,7 +175,7 @@ mod tests {
 
     #[tokio::test]
     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;
         assert!(result.is_err());
         assert!(matches!(result.err().unwrap(), Error::NotFound));
@@ -141,7 +183,7 @@ mod tests {
 
     #[tokio::test]
     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();
 
         assert_eq!(page.info.title, "Home");
@@ -161,7 +203,7 @@ mod tests {
 
     #[tokio::test]
     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;
         assert!(result.is_err());
         assert!(matches!(result.err().unwrap(), Error::NotFound));
@@ -169,7 +211,7 @@ mod tests {
 
     #[tokio::test]
     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
             .get_post("example.com", "home_posts", "post_id")
             .await
@@ -186,7 +228,7 @@ mod tests {
 
     #[tokio::test]
     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
             .get_post("example.com", "home_posts", "nonexistent")
             .await;
@@ -196,7 +238,7 @@ mod tests {
 
     #[tokio::test]
     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
             .get_posts("example.com", "home_posts", None)
             .await
@@ -207,7 +249,7 @@ mod tests {
 
     #[tokio::test]
     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
             .get_posts("example.com", "home_posts", Some("post_id".to_string()))
             .await
@@ -218,7 +260,7 @@ mod tests {
 
     #[tokio::test]
     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;
         assert!(result.is_err());
         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
             .set_page(
                 "example.com",
@@ -258,7 +300,7 @@ mod tests {
 
     #[tokio::test]
     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();
         let result = service.get_page("example.com", "about").await;
         assert!(result.is_err());
@@ -267,7 +309,7 @@ mod tests {
 
     #[tokio::test]
     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;
         assert!(result.is_err());
         assert!(matches!(result.err().unwrap(), Error::NotFound));
@@ -275,7 +317,7 @@ mod tests {
 
     #[tokio::test]
     async fn creates_a_site() {
-        let service = SiteService::new(InMemoryStore::with_test_data().await);
+        let service = SiteServiceImpl::new(InMemoryStore::with_test_data().await);
         service
             .create_site(SiteMetadata {
                 domain: "new.com".to_string(),
@@ -287,7 +329,7 @@ mod tests {
 
     #[tokio::test]
     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
             .create_site(SiteMetadata {
                 domain: "example.com".to_string(),
@@ -300,4 +342,21 @@ mod tests {
             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));
+    }
 }