//! Blog workflow: create project -> create post -> publish -> public page -> RSS use crate::harness::TestHarness; use serde_json::Value; #[tokio::test] async fn blog_post_lifecycle() { let mut h = TestHarness::new().await; // Setup: creator with project let user_id = h.signup("blogger", "blogger@example.com", "password123").await; h.grant_creator(user_id).await; h.client.post_form("/logout", "").await; h.login("blogger", "password123").await; let resp = h .client .post_form("/api/projects", "slug=my-blog&title=My+Blog") .await; let project: Value = resp.json(); let project_id = project["id"].as_str().unwrap(); // Make project public h.client .put_json( &format!("/api/projects/{}", project_id), r#"{"is_public": true}"#, ) .await; // Create blog post let resp = h .client .post_json( &format!("/api/projects/{}/blog", project_id), r#"{"title": "First Post", "body_markdown": "Hello **world**!", "is_published": false}"#, ) .await; assert!( resp.status.is_success(), "Create blog post failed: {} {}", resp.status, resp.text ); let post: Value = resp.json(); let post_id = post["id"].as_str().unwrap(); let post_slug = post["slug"].as_str().unwrap(); // Publish the post let resp = h .client .put_json( &format!("/api/blog/{}", post_id), &format!( r#"{{"title": "First Post", "slug": "{}", "body_markdown": "Hello **world**!", "is_published": true}}"#, post_slug ), ) .await; assert!( resp.status.is_success(), "Publish blog post failed: {} {}", resp.status, resp.text ); // Check blog page is accessible let blog_url = format!("/p/my-blog/blog/{}", post_slug); let resp = h.client.get(&blog_url).await; assert_eq!( resp.status, 200, "Blog post page should be accessible at {}", blog_url ); assert!( resp.text.contains("First Post"), "Blog post page should contain the title" ); // Check RSS feed let resp = h.client.get("/p/my-blog/blog/feed.xml").await; assert_eq!(resp.status, 200, "RSS feed should be accessible"); assert!( resp.text.contains("