Allow creating a brew when coffee_weight exceeds the bag's remaining amount — remaining is clamped to 0 rather than returning a 409 error. Brews against open bags with 0g remaining also succeed, keeping remaining at 0. Only closed bags are rejected.
845 lines
26 KiB
Rust
845 lines
26 KiB
Rust
use crate::helpers::{
|
|
create_default_bag, create_default_gear, create_default_roast, create_default_roaster,
|
|
spawn_app, spawn_app_with_auth,
|
|
};
|
|
use brewlog::domain::bags::Bag;
|
|
use brewlog::domain::brews::{Brew, BrewWithDetails, NewBrew};
|
|
|
|
#[tokio::test]
|
|
async fn creating_a_brew_returns_201_for_valid_data() {
|
|
// Arrange
|
|
let app = spawn_app_with_auth().await;
|
|
let roaster = create_default_roaster(&app).await;
|
|
let roast = create_default_roast(&app, roaster.id).await;
|
|
let bag = create_default_bag(&app, roast.id).await;
|
|
let grinder = create_default_gear(&app, "grinder", "Comandante", "C40 MK4").await;
|
|
let brewer = create_default_gear(&app, "brewer", "Hario", "V60 02").await;
|
|
let client = reqwest::Client::new();
|
|
|
|
let new_brew = NewBrew {
|
|
bag_id: bag.id,
|
|
coffee_weight: 15.0,
|
|
grinder_id: grinder.id,
|
|
grind_setting: 24.0,
|
|
brewer_id: brewer.id,
|
|
filter_paper_id: None,
|
|
water_volume: 250,
|
|
water_temp: 92.0,
|
|
quick_notes: Vec::new(),
|
|
brew_time: None,
|
|
created_at: None,
|
|
};
|
|
|
|
// Act
|
|
let response = client
|
|
.post(app.api_url("/brews"))
|
|
.bearer_auth(app.auth_token.as_ref().unwrap())
|
|
.json(&new_brew)
|
|
.send()
|
|
.await
|
|
.expect("Failed to execute request");
|
|
|
|
// Assert
|
|
assert_eq!(response.status(), 201);
|
|
|
|
let brew: Brew = response.json().await.expect("Failed to parse response");
|
|
assert_eq!(brew.bag_id, bag.id);
|
|
assert_eq!(brew.coffee_weight, 15.0);
|
|
assert_eq!(brew.grinder_id, grinder.id);
|
|
assert_eq!(brew.grind_setting, 24.0);
|
|
assert_eq!(brew.brewer_id, brewer.id);
|
|
assert_eq!(brew.filter_paper_id, None);
|
|
assert_eq!(brew.water_volume, 250);
|
|
assert_eq!(brew.water_temp, 92.0);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn creating_a_brew_with_filter_paper_returns_201() {
|
|
// Arrange
|
|
let app = spawn_app_with_auth().await;
|
|
let roaster = create_default_roaster(&app).await;
|
|
let roast = create_default_roast(&app, roaster.id).await;
|
|
let bag = create_default_bag(&app, roast.id).await;
|
|
let grinder = create_default_gear(&app, "grinder", "Comandante", "C40 MK4").await;
|
|
let brewer = create_default_gear(&app, "brewer", "Hario", "V60 02").await;
|
|
let filter_paper = create_default_gear(&app, "filter_paper", "Hario", "V60 Tabbed 02").await;
|
|
let client = reqwest::Client::new();
|
|
|
|
let new_brew = NewBrew {
|
|
bag_id: bag.id,
|
|
coffee_weight: 15.0,
|
|
grinder_id: grinder.id,
|
|
grind_setting: 24.0,
|
|
brewer_id: brewer.id,
|
|
filter_paper_id: Some(filter_paper.id),
|
|
water_volume: 250,
|
|
water_temp: 92.0,
|
|
quick_notes: Vec::new(),
|
|
brew_time: None,
|
|
created_at: None,
|
|
};
|
|
|
|
// Act
|
|
let response = client
|
|
.post(app.api_url("/brews"))
|
|
.bearer_auth(app.auth_token.as_ref().unwrap())
|
|
.json(&new_brew)
|
|
.send()
|
|
.await
|
|
.expect("Failed to execute request");
|
|
|
|
// Assert
|
|
assert_eq!(response.status(), 201);
|
|
|
|
let brew: Brew = response.json().await.expect("Failed to parse response");
|
|
assert_eq!(brew.filter_paper_id, Some(filter_paper.id));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn creating_a_brew_deducts_from_bag_remaining() {
|
|
// Arrange
|
|
let app = spawn_app_with_auth().await;
|
|
let roaster = create_default_roaster(&app).await;
|
|
let roast = create_default_roast(&app, roaster.id).await;
|
|
let bag = create_default_bag(&app, roast.id).await; // 250g
|
|
let grinder = create_default_gear(&app, "grinder", "Comandante", "C40 MK4").await;
|
|
let brewer = create_default_gear(&app, "brewer", "Hario", "V60 02").await;
|
|
let client = reqwest::Client::new();
|
|
|
|
let new_brew = NewBrew {
|
|
bag_id: bag.id,
|
|
coffee_weight: 15.0,
|
|
grinder_id: grinder.id,
|
|
grind_setting: 24.0,
|
|
brewer_id: brewer.id,
|
|
filter_paper_id: None,
|
|
water_volume: 250,
|
|
water_temp: 92.0,
|
|
quick_notes: Vec::new(),
|
|
brew_time: None,
|
|
created_at: None,
|
|
};
|
|
|
|
// Act
|
|
client
|
|
.post(app.api_url("/brews"))
|
|
.bearer_auth(app.auth_token.as_ref().unwrap())
|
|
.json(&new_brew)
|
|
.send()
|
|
.await
|
|
.expect("Failed to execute request");
|
|
|
|
// Assert - check bag remaining was deducted
|
|
let bag_response = client
|
|
.get(app.api_url(&format!("/bags/{}", bag.id)))
|
|
.send()
|
|
.await
|
|
.expect("Failed to get bag");
|
|
|
|
let updated_bag: Bag = bag_response.json().await.expect("Failed to parse bag");
|
|
assert_eq!(updated_bag.remaining, 235.0); // 250 - 15 = 235
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn creating_a_brew_with_excess_coffee_clamps_remaining_to_zero() {
|
|
// Arrange
|
|
let app = spawn_app_with_auth().await;
|
|
let roaster = create_default_roaster(&app).await;
|
|
let roast = create_default_roast(&app, roaster.id).await;
|
|
let bag = create_default_bag(&app, roast.id).await; // 250g
|
|
let grinder = create_default_gear(&app, "grinder", "Comandante", "C40 MK4").await;
|
|
let brewer = create_default_gear(&app, "brewer", "Hario", "V60 02").await;
|
|
let client = reqwest::Client::new();
|
|
|
|
let new_brew = NewBrew {
|
|
bag_id: bag.id,
|
|
coffee_weight: 300.0, // More than the 250g in the bag
|
|
grinder_id: grinder.id,
|
|
grind_setting: 24.0,
|
|
brewer_id: brewer.id,
|
|
filter_paper_id: None,
|
|
water_volume: 250,
|
|
water_temp: 92.0,
|
|
quick_notes: Vec::new(),
|
|
brew_time: None,
|
|
created_at: None,
|
|
};
|
|
|
|
// Act
|
|
let response = client
|
|
.post(app.api_url("/brews"))
|
|
.bearer_auth(app.auth_token.as_ref().unwrap())
|
|
.json(&new_brew)
|
|
.send()
|
|
.await
|
|
.expect("Failed to execute request");
|
|
|
|
// Assert
|
|
assert_eq!(response.status(), 201);
|
|
|
|
let brew: Brew = response.json().await.expect("Failed to parse response");
|
|
assert_eq!(brew.coffee_weight, 300.0);
|
|
|
|
let bag_response = client
|
|
.get(app.api_url(&format!("/bags/{}", bag.id)))
|
|
.send()
|
|
.await
|
|
.expect("Failed to get bag");
|
|
|
|
let updated_bag: Bag = bag_response.json().await.expect("Failed to parse bag");
|
|
assert_eq!(updated_bag.remaining, 0.0);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn creating_a_brew_against_empty_open_bag_succeeds() {
|
|
// Arrange
|
|
let app = spawn_app_with_auth().await;
|
|
let roaster = create_default_roaster(&app).await;
|
|
let roast = create_default_roast(&app, roaster.id).await;
|
|
let bag = create_default_bag(&app, roast.id).await;
|
|
let grinder = create_default_gear(&app, "grinder", "Comandante", "C40 MK4").await;
|
|
let brewer = create_default_gear(&app, "brewer", "Hario", "V60 02").await;
|
|
let client = reqwest::Client::new();
|
|
|
|
let update_payload = serde_json::json!({ "remaining": 0.0 });
|
|
client
|
|
.put(app.api_url(&format!("/bags/{}", bag.id)))
|
|
.bearer_auth(app.auth_token.as_ref().unwrap())
|
|
.json(&update_payload)
|
|
.send()
|
|
.await
|
|
.expect("Failed to update bag");
|
|
|
|
let new_brew = NewBrew {
|
|
bag_id: bag.id,
|
|
coffee_weight: 15.0,
|
|
grinder_id: grinder.id,
|
|
grind_setting: 24.0,
|
|
brewer_id: brewer.id,
|
|
filter_paper_id: None,
|
|
water_volume: 250,
|
|
water_temp: 92.0,
|
|
quick_notes: Vec::new(),
|
|
brew_time: None,
|
|
created_at: None,
|
|
};
|
|
|
|
// Act
|
|
let response = client
|
|
.post(app.api_url("/brews"))
|
|
.bearer_auth(app.auth_token.as_ref().unwrap())
|
|
.json(&new_brew)
|
|
.send()
|
|
.await
|
|
.expect("Failed to execute request");
|
|
|
|
// Assert
|
|
assert_eq!(response.status(), 201);
|
|
|
|
let bag_response = client
|
|
.get(app.api_url(&format!("/bags/{}", bag.id)))
|
|
.send()
|
|
.await
|
|
.expect("Failed to get bag");
|
|
|
|
let updated_bag: Bag = bag_response.json().await.expect("Failed to parse bag");
|
|
assert_eq!(updated_bag.remaining, 0.0);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn creating_a_brew_without_auth_returns_401() {
|
|
// Arrange
|
|
let app = spawn_app().await; // No auth
|
|
let client = reqwest::Client::new();
|
|
|
|
let new_brew = serde_json::json!({
|
|
"bag_id": 1,
|
|
"coffee_weight": 15.0,
|
|
"grinder_id": 1,
|
|
"grind_setting": 24.0,
|
|
"brewer_id": 2,
|
|
"water_volume": 250,
|
|
"water_temp": 92.0
|
|
});
|
|
|
|
// Act
|
|
let response = client
|
|
.post(app.api_url("/brews"))
|
|
.json(&new_brew)
|
|
.send()
|
|
.await
|
|
.expect("Failed to execute request");
|
|
|
|
// Assert
|
|
assert_eq!(response.status(), 401);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn listing_brews_returns_200_and_enriched_data() {
|
|
// Arrange
|
|
let app = spawn_app_with_auth().await;
|
|
let roaster = create_default_roaster(&app).await;
|
|
let roast = create_default_roast(&app, roaster.id).await;
|
|
let bag = create_default_bag(&app, roast.id).await;
|
|
let grinder = create_default_gear(&app, "grinder", "Comandante", "C40 MK4").await;
|
|
let brewer = create_default_gear(&app, "brewer", "Hario", "V60 02").await;
|
|
let filter_paper =
|
|
create_default_gear(&app, "filter_paper", "Sibarist", "FAST Specialty 02").await;
|
|
let client = reqwest::Client::new();
|
|
|
|
// Create a brew with filter paper
|
|
let new_brew = NewBrew {
|
|
bag_id: bag.id,
|
|
coffee_weight: 15.0,
|
|
grinder_id: grinder.id,
|
|
grind_setting: 24.0,
|
|
brewer_id: brewer.id,
|
|
filter_paper_id: Some(filter_paper.id),
|
|
water_volume: 250,
|
|
water_temp: 92.0,
|
|
quick_notes: Vec::new(),
|
|
brew_time: None,
|
|
created_at: None,
|
|
};
|
|
|
|
client
|
|
.post(app.api_url("/brews"))
|
|
.bearer_auth(app.auth_token.as_ref().unwrap())
|
|
.json(&new_brew)
|
|
.send()
|
|
.await
|
|
.expect("Failed to create brew");
|
|
|
|
// Act
|
|
let response = client
|
|
.get(app.api_url("/brews"))
|
|
.send()
|
|
.await
|
|
.expect("Failed to execute request");
|
|
|
|
// Assert
|
|
assert_eq!(response.status(), 200);
|
|
|
|
let brews: Vec<BrewWithDetails> = response.json().await.expect("Failed to parse response");
|
|
assert_eq!(brews.len(), 1);
|
|
assert_eq!(brews[0].roast_name, roast.name);
|
|
assert_eq!(brews[0].roaster_name, roaster.name);
|
|
assert_eq!(brews[0].grinder_name, "Comandante C40 MK4");
|
|
assert_eq!(brews[0].brewer_name, "Hario V60 02");
|
|
assert_eq!(
|
|
brews[0].filter_paper_name.as_deref(),
|
|
Some("Sibarist FAST Specialty 02")
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn listing_brews_without_filter_paper_returns_none() {
|
|
// Arrange
|
|
let app = spawn_app_with_auth().await;
|
|
let roaster = create_default_roaster(&app).await;
|
|
let roast = create_default_roast(&app, roaster.id).await;
|
|
let bag = create_default_bag(&app, roast.id).await;
|
|
let grinder = create_default_gear(&app, "grinder", "Comandante", "C40 MK4").await;
|
|
let brewer = create_default_gear(&app, "brewer", "AeroPress", "Original").await;
|
|
let client = reqwest::Client::new();
|
|
|
|
// Create a brew without filter paper
|
|
let new_brew = NewBrew {
|
|
bag_id: bag.id,
|
|
coffee_weight: 17.0,
|
|
grinder_id: grinder.id,
|
|
grind_setting: 20.0,
|
|
brewer_id: brewer.id,
|
|
filter_paper_id: None,
|
|
water_volume: 255,
|
|
water_temp: 88.0,
|
|
quick_notes: Vec::new(),
|
|
brew_time: None,
|
|
created_at: None,
|
|
};
|
|
|
|
client
|
|
.post(app.api_url("/brews"))
|
|
.bearer_auth(app.auth_token.as_ref().unwrap())
|
|
.json(&new_brew)
|
|
.send()
|
|
.await
|
|
.expect("Failed to create brew");
|
|
|
|
// Act
|
|
let response = client
|
|
.get(app.api_url("/brews"))
|
|
.send()
|
|
.await
|
|
.expect("Failed to execute request");
|
|
|
|
// Assert
|
|
assert_eq!(response.status(), 200);
|
|
|
|
let brews: Vec<BrewWithDetails> = response.json().await.expect("Failed to parse response");
|
|
assert_eq!(brews.len(), 1);
|
|
assert_eq!(brews[0].filter_paper_name, None);
|
|
assert_eq!(brews[0].brew.filter_paper_id, None);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn listing_brews_with_bag_filter_returns_filtered_results() {
|
|
// Arrange
|
|
let app = spawn_app_with_auth().await;
|
|
let roaster = create_default_roaster(&app).await;
|
|
let roast = create_default_roast(&app, roaster.id).await;
|
|
let bag1 = create_default_bag(&app, roast.id).await;
|
|
let bag2 = create_default_bag(&app, roast.id).await;
|
|
let grinder = create_default_gear(&app, "grinder", "Comandante", "C40 MK4").await;
|
|
let brewer = create_default_gear(&app, "brewer", "Hario", "V60 02").await;
|
|
let client = reqwest::Client::new();
|
|
|
|
// Create brew for bag1
|
|
let new_brew1 = NewBrew {
|
|
bag_id: bag1.id,
|
|
coffee_weight: 15.0,
|
|
grinder_id: grinder.id,
|
|
grind_setting: 24.0,
|
|
brewer_id: brewer.id,
|
|
filter_paper_id: None,
|
|
water_volume: 250,
|
|
water_temp: 92.0,
|
|
quick_notes: Vec::new(),
|
|
brew_time: None,
|
|
created_at: None,
|
|
};
|
|
|
|
client
|
|
.post(app.api_url("/brews"))
|
|
.bearer_auth(app.auth_token.as_ref().unwrap())
|
|
.json(&new_brew1)
|
|
.send()
|
|
.await
|
|
.expect("Failed to create brew 1");
|
|
|
|
// Create brew for bag2
|
|
let new_brew2 = NewBrew {
|
|
bag_id: bag2.id,
|
|
coffee_weight: 17.0,
|
|
grinder_id: grinder.id,
|
|
grind_setting: 22.0,
|
|
brewer_id: brewer.id,
|
|
filter_paper_id: None,
|
|
water_volume: 255,
|
|
water_temp: 88.0,
|
|
quick_notes: Vec::new(),
|
|
brew_time: None,
|
|
created_at: None,
|
|
};
|
|
|
|
client
|
|
.post(app.api_url("/brews"))
|
|
.bearer_auth(app.auth_token.as_ref().unwrap())
|
|
.json(&new_brew2)
|
|
.send()
|
|
.await
|
|
.expect("Failed to create brew 2");
|
|
|
|
// Act - filter by bag1
|
|
let response = client
|
|
.get(app.api_url(&format!("/brews?bag_id={}", bag1.id)))
|
|
.send()
|
|
.await
|
|
.expect("Failed to execute request");
|
|
|
|
// Assert
|
|
assert_eq!(response.status(), 200);
|
|
|
|
let brews: Vec<BrewWithDetails> = response.json().await.expect("Failed to parse response");
|
|
assert_eq!(brews.len(), 1);
|
|
assert_eq!(brews[0].brew.coffee_weight, 15.0);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn getting_a_brew_returns_200_for_valid_id() {
|
|
// Arrange
|
|
let app = spawn_app_with_auth().await;
|
|
let roaster = create_default_roaster(&app).await;
|
|
let roast = create_default_roast(&app, roaster.id).await;
|
|
let bag = create_default_bag(&app, roast.id).await;
|
|
let grinder = create_default_gear(&app, "grinder", "Comandante", "C40 MK4").await;
|
|
let brewer = create_default_gear(&app, "brewer", "Hario", "V60 02").await;
|
|
let client = reqwest::Client::new();
|
|
|
|
let new_brew = NewBrew {
|
|
bag_id: bag.id,
|
|
coffee_weight: 15.0,
|
|
grinder_id: grinder.id,
|
|
grind_setting: 24.0,
|
|
brewer_id: brewer.id,
|
|
filter_paper_id: None,
|
|
water_volume: 250,
|
|
water_temp: 92.0,
|
|
quick_notes: Vec::new(),
|
|
brew_time: None,
|
|
created_at: None,
|
|
};
|
|
|
|
let create_response = client
|
|
.post(app.api_url("/brews"))
|
|
.bearer_auth(app.auth_token.as_ref().unwrap())
|
|
.json(&new_brew)
|
|
.send()
|
|
.await
|
|
.expect("Failed to create brew");
|
|
|
|
let created_brew: Brew = create_response
|
|
.json()
|
|
.await
|
|
.expect("Failed to parse response");
|
|
|
|
// Act
|
|
let response = client
|
|
.get(app.api_url(&format!("/brews/{}", created_brew.id)))
|
|
.send()
|
|
.await
|
|
.expect("Failed to execute request");
|
|
|
|
// Assert
|
|
assert_eq!(response.status(), 200);
|
|
let brew: BrewWithDetails = response.json().await.expect("Failed to parse response");
|
|
assert_eq!(brew.brew.id, created_brew.id);
|
|
assert_eq!(brew.roast_name, roast.name);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn deleting_a_brew_returns_204() {
|
|
// Arrange
|
|
let app = spawn_app_with_auth().await;
|
|
let roaster = create_default_roaster(&app).await;
|
|
let roast = create_default_roast(&app, roaster.id).await;
|
|
let bag = create_default_bag(&app, roast.id).await;
|
|
let grinder = create_default_gear(&app, "grinder", "Comandante", "C40 MK4").await;
|
|
let brewer = create_default_gear(&app, "brewer", "Hario", "V60 02").await;
|
|
let client = reqwest::Client::new();
|
|
|
|
let new_brew = NewBrew {
|
|
bag_id: bag.id,
|
|
coffee_weight: 15.0,
|
|
grinder_id: grinder.id,
|
|
grind_setting: 24.0,
|
|
brewer_id: brewer.id,
|
|
filter_paper_id: None,
|
|
water_volume: 250,
|
|
water_temp: 92.0,
|
|
quick_notes: Vec::new(),
|
|
brew_time: None,
|
|
created_at: None,
|
|
};
|
|
|
|
let create_response = client
|
|
.post(app.api_url("/brews"))
|
|
.bearer_auth(app.auth_token.as_ref().unwrap())
|
|
.json(&new_brew)
|
|
.send()
|
|
.await
|
|
.expect("Failed to create brew");
|
|
|
|
let created_brew: Brew = create_response
|
|
.json()
|
|
.await
|
|
.expect("Failed to parse response");
|
|
|
|
// Act
|
|
let response = client
|
|
.delete(app.api_url(&format!("/brews/{}", created_brew.id)))
|
|
.bearer_auth(app.auth_token.as_ref().unwrap())
|
|
.send()
|
|
.await
|
|
.expect("Failed to execute request");
|
|
|
|
// Assert
|
|
assert_eq!(response.status(), 204);
|
|
|
|
// Verify deletion
|
|
let get_response = client
|
|
.get(app.api_url(&format!("/brews/{}", created_brew.id)))
|
|
.send()
|
|
.await
|
|
.expect("Failed to execute request");
|
|
|
|
assert_eq!(get_response.status(), 404);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn updating_a_brew_returns_200_and_updates_data() {
|
|
let app = spawn_app_with_auth().await;
|
|
let roaster = create_default_roaster(&app).await;
|
|
let roast = create_default_roast(&app, roaster.id).await;
|
|
let bag = create_default_bag(&app, roast.id).await;
|
|
let grinder = create_default_gear(&app, "grinder", "Comandante", "C40 MK4").await;
|
|
let brewer = create_default_gear(&app, "brewer", "Hario", "V60 02").await;
|
|
let client = reqwest::Client::new();
|
|
|
|
let new_brew = NewBrew {
|
|
bag_id: bag.id,
|
|
coffee_weight: 15.0,
|
|
grinder_id: grinder.id,
|
|
grind_setting: 24.0,
|
|
brewer_id: brewer.id,
|
|
filter_paper_id: None,
|
|
water_volume: 250,
|
|
water_temp: 92.0,
|
|
quick_notes: Vec::new(),
|
|
brew_time: None,
|
|
created_at: None,
|
|
};
|
|
|
|
let create_response = client
|
|
.post(app.api_url("/brews"))
|
|
.bearer_auth(app.auth_token.as_ref().unwrap())
|
|
.json(&new_brew)
|
|
.send()
|
|
.await
|
|
.expect("Failed to create brew");
|
|
|
|
let brew: Brew = create_response
|
|
.json()
|
|
.await
|
|
.expect("Failed to parse response");
|
|
|
|
let update = serde_json::json!({
|
|
"water_temp": 94.0,
|
|
"grind_setting": 22.0,
|
|
});
|
|
|
|
let response = client
|
|
.put(app.api_url(&format!("/brews/{}", brew.id)))
|
|
.bearer_auth(app.auth_token.as_ref().unwrap())
|
|
.json(&update)
|
|
.send()
|
|
.await
|
|
.expect("Failed to update brew");
|
|
|
|
assert_eq!(response.status(), 200);
|
|
let updated: Brew = response.json().await.expect("Failed to parse response");
|
|
assert_eq!(updated.water_temp, 94.0);
|
|
assert_eq!(updated.grind_setting, 22.0);
|
|
assert_eq!(updated.coffee_weight, 15.0); // unchanged
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn updating_a_brew_without_auth_returns_401() {
|
|
let app = spawn_app().await;
|
|
let client = reqwest::Client::new();
|
|
|
|
let update = serde_json::json!({
|
|
"water_temp": 94.0,
|
|
});
|
|
|
|
let response = client
|
|
.put(app.api_url("/brews/1"))
|
|
.json(&update)
|
|
.send()
|
|
.await
|
|
.expect("Failed to execute request");
|
|
|
|
assert_eq!(response.status(), 401);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn updating_a_brew_with_no_changes_returns_400() {
|
|
let app = spawn_app_with_auth().await;
|
|
let roaster = create_default_roaster(&app).await;
|
|
let roast = create_default_roast(&app, roaster.id).await;
|
|
let bag = create_default_bag(&app, roast.id).await;
|
|
let grinder = create_default_gear(&app, "grinder", "Comandante", "C40 MK4").await;
|
|
let brewer = create_default_gear(&app, "brewer", "Hario", "V60 02").await;
|
|
let client = reqwest::Client::new();
|
|
|
|
let new_brew = NewBrew {
|
|
bag_id: bag.id,
|
|
coffee_weight: 15.0,
|
|
grinder_id: grinder.id,
|
|
grind_setting: 24.0,
|
|
brewer_id: brewer.id,
|
|
filter_paper_id: None,
|
|
water_volume: 250,
|
|
water_temp: 92.0,
|
|
quick_notes: Vec::new(),
|
|
brew_time: None,
|
|
created_at: None,
|
|
};
|
|
|
|
let create_response = client
|
|
.post(app.api_url("/brews"))
|
|
.bearer_auth(app.auth_token.as_ref().unwrap())
|
|
.json(&new_brew)
|
|
.send()
|
|
.await
|
|
.expect("Failed to create brew");
|
|
|
|
let brew: Brew = create_response
|
|
.json()
|
|
.await
|
|
.expect("Failed to parse response");
|
|
|
|
let update = serde_json::json!({});
|
|
|
|
let response = client
|
|
.put(app.api_url(&format!("/brews/{}", brew.id)))
|
|
.bearer_auth(app.auth_token.as_ref().unwrap())
|
|
.json(&update)
|
|
.send()
|
|
.await
|
|
.expect("Failed to update brew");
|
|
|
|
assert_eq!(response.status(), 400);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn updating_a_nonexistent_brew_returns_404() {
|
|
let app = spawn_app_with_auth().await;
|
|
let client = reqwest::Client::new();
|
|
|
|
let update = serde_json::json!({
|
|
"water_temp": 94.0,
|
|
});
|
|
|
|
let response = client
|
|
.put(app.api_url("/brews/99999"))
|
|
.bearer_auth(app.auth_token.as_ref().unwrap())
|
|
.json(&update)
|
|
.send()
|
|
.await
|
|
.expect("Failed to execute request");
|
|
|
|
assert_eq!(response.status(), 404);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn creating_brew_with_zero_coffee_weight_returns_400() {
|
|
let app = spawn_app_with_auth().await;
|
|
let roaster = create_default_roaster(&app).await;
|
|
let roast = create_default_roast(&app, roaster.id).await;
|
|
let bag = create_default_bag(&app, roast.id).await;
|
|
let grinder = create_default_gear(&app, "grinder", "Comandante", "C40 MK4").await;
|
|
let brewer = create_default_gear(&app, "brewer", "Hario", "V60 02").await;
|
|
let client = reqwest::Client::new();
|
|
|
|
let response = client
|
|
.post(app.api_url("/brews"))
|
|
.bearer_auth(app.auth_token.as_ref().unwrap())
|
|
.json(&NewBrew {
|
|
bag_id: bag.id,
|
|
coffee_weight: 0.0,
|
|
grinder_id: grinder.id,
|
|
grind_setting: 24.0,
|
|
brewer_id: brewer.id,
|
|
filter_paper_id: None,
|
|
water_volume: 250,
|
|
water_temp: 92.0,
|
|
quick_notes: Vec::new(),
|
|
brew_time: None,
|
|
created_at: None,
|
|
})
|
|
.send()
|
|
.await
|
|
.expect("Failed to execute request");
|
|
|
|
assert_eq!(response.status(), 400);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn creating_brew_with_negative_grind_setting_returns_400() {
|
|
let app = spawn_app_with_auth().await;
|
|
let roaster = create_default_roaster(&app).await;
|
|
let roast = create_default_roast(&app, roaster.id).await;
|
|
let bag = create_default_bag(&app, roast.id).await;
|
|
let grinder = create_default_gear(&app, "grinder", "Comandante", "C40 MK4").await;
|
|
let brewer = create_default_gear(&app, "brewer", "Hario", "V60 02").await;
|
|
let client = reqwest::Client::new();
|
|
|
|
let response = client
|
|
.post(app.api_url("/brews"))
|
|
.bearer_auth(app.auth_token.as_ref().unwrap())
|
|
.json(&NewBrew {
|
|
bag_id: bag.id,
|
|
coffee_weight: 15.0,
|
|
grinder_id: grinder.id,
|
|
grind_setting: -1.0,
|
|
brewer_id: brewer.id,
|
|
filter_paper_id: None,
|
|
water_volume: 250,
|
|
water_temp: 92.0,
|
|
quick_notes: Vec::new(),
|
|
brew_time: None,
|
|
created_at: None,
|
|
})
|
|
.send()
|
|
.await
|
|
.expect("Failed to execute request");
|
|
|
|
assert_eq!(response.status(), 400);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn creating_brew_with_zero_water_volume_returns_400() {
|
|
let app = spawn_app_with_auth().await;
|
|
let roaster = create_default_roaster(&app).await;
|
|
let roast = create_default_roast(&app, roaster.id).await;
|
|
let bag = create_default_bag(&app, roast.id).await;
|
|
let grinder = create_default_gear(&app, "grinder", "Comandante", "C40 MK4").await;
|
|
let brewer = create_default_gear(&app, "brewer", "Hario", "V60 02").await;
|
|
let client = reqwest::Client::new();
|
|
|
|
let response = client
|
|
.post(app.api_url("/brews"))
|
|
.bearer_auth(app.auth_token.as_ref().unwrap())
|
|
.json(&NewBrew {
|
|
bag_id: bag.id,
|
|
coffee_weight: 15.0,
|
|
grinder_id: grinder.id,
|
|
grind_setting: 24.0,
|
|
brewer_id: brewer.id,
|
|
filter_paper_id: None,
|
|
water_volume: 0,
|
|
water_temp: 92.0,
|
|
quick_notes: Vec::new(),
|
|
brew_time: None,
|
|
created_at: None,
|
|
})
|
|
.send()
|
|
.await
|
|
.expect("Failed to execute request");
|
|
|
|
assert_eq!(response.status(), 400);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn creating_brew_with_water_temp_over_100_returns_400() {
|
|
let app = spawn_app_with_auth().await;
|
|
let roaster = create_default_roaster(&app).await;
|
|
let roast = create_default_roast(&app, roaster.id).await;
|
|
let bag = create_default_bag(&app, roast.id).await;
|
|
let grinder = create_default_gear(&app, "grinder", "Comandante", "C40 MK4").await;
|
|
let brewer = create_default_gear(&app, "brewer", "Hario", "V60 02").await;
|
|
let client = reqwest::Client::new();
|
|
|
|
let response = client
|
|
.post(app.api_url("/brews"))
|
|
.bearer_auth(app.auth_token.as_ref().unwrap())
|
|
.json(&NewBrew {
|
|
bag_id: bag.id,
|
|
coffee_weight: 15.0,
|
|
grinder_id: grinder.id,
|
|
grind_setting: 24.0,
|
|
brewer_id: brewer.id,
|
|
filter_paper_id: None,
|
|
water_volume: 250,
|
|
water_temp: 101.0,
|
|
quick_notes: Vec::new(),
|
|
brew_time: None,
|
|
created_at: None,
|
|
})
|
|
.send()
|
|
.await
|
|
.expect("Failed to execute request");
|
|
|
|
assert_eq!(response.status(), 400);
|
|
}
|