brewlog/tests/server/timeline.rs
2025-11-24 19:15:45 +00:00

146 lines
4.2 KiB
Rust

use crate::helpers::{create_roaster_with_payload, spawn_app};
use brewlog::domain::roasters::NewRoaster;
use brewlog::domain::roasts::NewRoast;
use reqwest::Client;
use tokio::time::{Duration, sleep};
async fn create_roast(app: &crate::helpers::TestApp, roaster_id: &str, name: &str) {
let client = Client::new();
let roast = NewRoast {
roaster_id: roaster_id.to_string(),
name: name.to_string(),
origin: "Ethiopia".to_string(),
region: "Yirgacheffe".to_string(),
producer: "Chelbesa Cooperative".to_string(),
tasting_notes: vec!["Blueberry".to_string(), "Jasmine".to_string()],
process: "Washed".to_string(),
};
let response = client
.post(app.api_url("/roasts"))
.json(&roast)
.send()
.await
.expect("failed to create roast");
assert_eq!(response.status(), 201);
}
#[tokio::test]
async fn timeline_page_returns_a_200_with_empty_state() {
// Arrange
let app = spawn_app().await;
let client = Client::new();
// Act
let response = client
.get(format!("{}/timeline", app.address))
.send()
.await
.expect("failed to fetch timeline");
// Assert
assert_eq!(response.status(), 200);
let body = response.text().await.expect("failed to read response body");
assert!(
body.contains("No events yet"),
"Expected empty timeline state message, got: {body}"
);
}
#[tokio::test]
async fn creating_a_roaster_surfaces_on_the_timeline() {
// Arrange
let app = spawn_app().await;
let client = Client::new();
let roaster_name = "Timeline Roasters";
let roaster = create_roaster_with_payload(
&app,
NewRoaster {
name: roaster_name.to_string(),
country: "UK".to_string(),
city: Some("Bristol".to_string()),
homepage: Some("https://example.com".to_string()),
notes: None,
},
)
.await;
let roaster_id = roaster.id.clone();
// Give the database a brief moment to commit timestamps
sleep(Duration::from_millis(10)).await;
// Act
let response = client
.get(format!("{}/timeline", app.address))
.send()
.await
.expect("failed to fetch timeline");
// Assert
assert_eq!(response.status(), 200);
let body = response.text().await.expect("failed to read response body");
assert!(
body.contains("Roaster Added"),
"Expected 'Roaster Added' badge in timeline HTML, got: {body}"
);
assert!(
body.contains(roaster_name),
"Expected roaster name to appear in timeline HTML, got: {body}"
);
assert!(
body.contains(&format!("/roasters/{}", roaster_id)),
"Expected roaster detail link in timeline HTML, got: {body}"
);
}
#[tokio::test]
async fn creating_a_roast_surfaces_on_the_timeline() {
// Arrange
let app = spawn_app().await;
let client = Client::new();
let roaster_id = create_roaster_with_payload(
&app,
NewRoaster {
name: "Timeline Roast Roasters".to_string(),
country: "UK".to_string(),
city: Some("Bristol".to_string()),
homepage: Some("https://example.com".to_string()),
notes: None,
},
)
.await
.id;
// Ensure the roast event occurs after the roaster event to make ordering deterministic
sleep(Duration::from_millis(5)).await;
let roast_name = "Timeline Natural";
create_roast(&app, &roaster_id, roast_name).await;
// Act
let response = client
.get(format!("{}/timeline", app.address))
.send()
.await
.expect("failed to fetch timeline");
// Assert
assert_eq!(response.status(), 200);
let body = response.text().await.expect("failed to read response body");
assert!(
body.contains("Roast Added"),
"Expected 'Roast Added' badge in timeline HTML, got: {body}"
);
assert!(
body.contains(roast_name),
"Expected roast name to appear in timeline HTML, got: {body}"
);
assert!(
body.contains("Blueberry"),
"Expected tasting notes to appear in timeline HTML, got: {body}"
);
}