diff options
| author | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2025-10-08 11:58:49 +0200 |
|---|---|---|
| committer | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2025-10-08 11:58:49 +0200 |
| commit | 9204e472e4f714c84237bca5ebe740080a589917 (patch) | |
| tree | d17f31240b24fcb2c47eec21edf6a9f512d80123 /crates/rocie-server/tests | |
| parent | feat(crates/rocie-server/unit-property): Init (diff) | |
| download | server-9204e472e4f714c84237bca5ebe740080a589917.zip | |
test(crates/rocie-server/testenv/init): Automatically choose the port and wait for server start
This avoids issues regarding a race condition between server start and our start of requests and removes the requirement for specifying free ports in the test files.
Diffstat (limited to 'crates/rocie-server/tests')
| -rw-r--r-- | crates/rocie-server/tests/_testenv/init.rs | 57 | ||||
| -rw-r--r-- | crates/rocie-server/tests/_testenv/mod.rs | 2 | ||||
| -rw-r--r-- | crates/rocie-server/tests/products/barcode.rs | 163 | ||||
| -rw-r--r-- | crates/rocie-server/tests/products/mod.rs | 44 | ||||
| -rw-r--r-- | crates/rocie-server/tests/products/register.rs | 27 | ||||
| -rw-r--r-- | crates/rocie-server/tests/tests.rs | 1 | ||||
| -rw-r--r-- | crates/rocie-server/tests/units/mod.rs | 1 | ||||
| -rw-r--r-- | crates/rocie-server/tests/units/register.rs | 50 |
8 files changed, 288 insertions, 57 deletions
diff --git a/crates/rocie-server/tests/_testenv/init.rs b/crates/rocie-server/tests/_testenv/init.rs index 5309fea..9cb0b91 100644 --- a/crates/rocie-server/tests/_testenv/init.rs +++ b/crates/rocie-server/tests/_testenv/init.rs @@ -12,17 +12,29 @@ use std::{ env, ffi::OsStr, fmt::Write, - fs, io, mem, + fs, + io::{self, BufRead, BufReader}, + mem, path::{Path, PathBuf}, process::{self, Stdio}, - thread::sleep, - time::Duration, }; use rocie_client::apis::configuration::Configuration; use crate::{_testenv::Paths, testenv::TestEnv}; +macro_rules! function_name { + () => {{ + fn f() {} + fn type_name_of<T>(_: T) -> &'static str { + std::any::type_name::<T>() + } + let name = type_name_of(f); + name.strip_suffix("::{{closure}}::f").unwrap() + }}; +} +pub(crate) use function_name; + fn target_dir() -> PathBuf { // Tests exe is in target/debug/deps, the *rocie-server* exe is in target/debug env::current_exe() @@ -36,8 +48,8 @@ fn target_dir() -> PathBuf { .to_path_buf() } -fn test_dir(name: &'static str, port: u32) -> PathBuf { - target_dir().join("tests").join(name).join(port.to_string()) +fn test_dir(name: &'static str) -> PathBuf { + target_dir().join("tests").join(name) } fn prepare_files_and_dirs(test_dir: &Path) -> io::Result<Paths> { @@ -83,25 +95,24 @@ fn rocie_base_path(port: &str) -> String { format!("http://127.0.0.1:{port}") } -fn rocie_server_args<'a>(paths: &'a Paths, port: &'a str) -> [&'a OsStr; 5] { +fn rocie_server_args(paths: &Paths) -> [&OsStr; 4] { [ OsStr::new("serve"), OsStr::new("--db-path"), paths.db.as_os_str(), - OsStr::new("--port"), - OsStr::new(port), + OsStr::new("--print-port"), ] } impl TestEnv { - pub(crate) fn new(name: &'static str, port: u32) -> TestEnv { - let test_dir = test_dir(name, port); + pub(crate) fn new(name: &'static str) -> TestEnv { + let test_dir = test_dir(name); let paths = prepare_files_and_dirs(&test_dir) .inspect_err(|err| panic!("Error during test dir preparation: {err}")) .unwrap(); - let server_process = { + let (server_process, port) = { let server_exe = find_server_exe(); let mut cmd = process::Command::new(&server_exe); @@ -110,15 +121,22 @@ impl TestEnv { cmd.stdout(Stdio::piped()); cmd.stderr(Stdio::piped()); - cmd.args(rocie_server_args(&paths, port.to_string().as_str())); + cmd.args(rocie_server_args(&paths)); + + let mut child = cmd.spawn().expect("server spawn"); + + let port: u16 = { + let mut stdout = BufReader::new(child.stdout.as_mut().expect("Was captured")); - let child = cmd.spawn().expect("server spawn"); + let mut port = String::new(); + assert_ne!(stdout.read_line(&mut port).expect("Works"), 0); - // Give the server time to start. - // TODO(@bpeetz): Use a better synchronization primitive <2025-09-11> - sleep(Duration::from_millis(240)); + port.trim_end() + .parse() + .expect("The server should reply with a u16 port number") + }; - child + (child, port) }; let config = { @@ -201,10 +219,7 @@ impl Drop for TestEnv { eprintln!( "{}", - format_exit( - rocie_server_args(&self.paths, &self.port).as_slice(), - &output - ) + format_exit(rocie_server_args(&self.paths).as_slice(), &output) ); } } diff --git a/crates/rocie-server/tests/_testenv/mod.rs b/crates/rocie-server/tests/_testenv/mod.rs index a37925e..56cea71 100644 --- a/crates/rocie-server/tests/_testenv/mod.rs +++ b/crates/rocie-server/tests/_testenv/mod.rs @@ -4,7 +4,7 @@ use std::{path::PathBuf, process}; use rocie_client::apis::configuration::Configuration; -mod init; +pub(crate) mod init; pub(crate) mod log; /// Environment for the integration tests. diff --git a/crates/rocie-server/tests/products/barcode.rs b/crates/rocie-server/tests/products/barcode.rs index 480dcb9..8e1bf42 100644 --- a/crates/rocie-server/tests/products/barcode.rs +++ b/crates/rocie-server/tests/products/barcode.rs @@ -2,24 +2,28 @@ use rocie_client::{ apis::{ api_get_inventory_api::amount_by_id, api_set_barcode_api::{buy_barcode, consume_barcode}, + api_set_product_api::{associate_barcode, register_product}, + api_set_unit_api::register_unit, + api_set_unit_property_api::register_unit_property, }, - models::{BarcodeId, UnitAmount}, + models::{Barcode, BarcodeId, ProductStub, UnitAmount, UnitPropertyStub, UnitStub}, }; use crate::{ + _testenv::init::function_name, products::create_associated_barcode, testenv::{TestEnv, log::request}, }; #[tokio::test] async fn test_barcode_buy() { - let env = TestEnv::new(module_path!(), 8087); + let env = TestEnv::new(function_name!()); let barcode_id = 23; let unit_value = 1; let (unit_id, product_id) = - create_associated_barcode(&env, "Liter", "Milk", unit_value, barcode_id).await; + create_associated_barcode(&env, "Liter", "Milk", "Volume", unit_value, barcode_id).await; request!(env, buy_barcode(BarcodeId { value: barcode_id })); @@ -32,13 +36,20 @@ async fn test_barcode_buy() { #[tokio::test] async fn test_barcode_consume() { - let env = TestEnv::new(module_path!(), 8083); + let env = TestEnv::new(function_name!()); let barcode_id = BarcodeId { value: 23 }; let unit_value = 1; - let (unit_id, product_id) = - create_associated_barcode(&env, "Liter", "Milk", unit_value, barcode_id.value).await; + let (unit_id, product_id) = create_associated_barcode( + &env, + "Liter", + "Milk", + "Volume", + unit_value, + barcode_id.value, + ) + .await; request!(env, buy_barcode(barcode_id)); @@ -62,13 +73,20 @@ async fn test_barcode_consume() { #[tokio::test] async fn test_barcode_consume_error() { - let env = TestEnv::new(module_path!(), 8084); + let env = TestEnv::new(function_name!()); let barcode_id = BarcodeId { value: 23 }; let unit_value = 1; - let (unit_id, product_id) = - create_associated_barcode(&env, "Liter", "Milk", unit_value, barcode_id.value).await; + let (unit_id, product_id) = create_associated_barcode( + &env, + "Liter", + "Milk", + "Volume", + unit_value, + barcode_id.value, + ) + .await; request!(env, buy_barcode(barcode_id)); @@ -94,13 +112,20 @@ async fn test_barcode_consume_error() { #[tokio::test] async fn test_barcode_consume_error_other() { - let env = TestEnv::new(module_path!(), 8085); + let env = TestEnv::new(function_name!()); let barcode_id = BarcodeId { value: 23 }; let unit_value = 1; - let (unit_id, product_id) = - create_associated_barcode(&env, "Liter", "Milk", unit_value, barcode_id.value).await; + let (unit_id, product_id) = create_associated_barcode( + &env, + "Liter", + "Milk", + "Volume", + unit_value, + barcode_id.value, + ) + .await; request!(env, buy_barcode(barcode_id)); @@ -137,13 +162,20 @@ async fn test_barcode_consume_error_other() { #[tokio::test] async fn test_barcode_multiple_buy_and_consume() { - let env = TestEnv::new(module_path!(), 8086); + let env = TestEnv::new(function_name!()); let barcode_id = BarcodeId { value: 23 }; let unit_value = 1; - let (unit_id, product_id) = - create_associated_barcode(&env, "Liter", "Milk", unit_value, barcode_id.value).await; + let (unit_id, product_id) = create_associated_barcode( + &env, + "Liter", + "Milk", + "Volume", + unit_value, + barcode_id.value, + ) + .await; request!(env, buy_barcode(barcode_id)); @@ -183,3 +215,104 @@ async fn test_barcode_multiple_buy_and_consume() { assert_eq!(product_amount.amount.unit, unit_id); assert_eq!(product_amount.amount.value, 0); } + +#[tokio::test] +async fn test_barcode_fill_up() { + let env = TestEnv::new(function_name!()); + + let milk_id = BarcodeId { value: 23 }; + let bread_id = BarcodeId { value: 24 }; + let nuts_id = BarcodeId { value: 25 }; + + let _ = create_associated_barcode(&env, "Liter", "Milk", "Volume", 2, milk_id.value).await; + let _ = create_associated_barcode(&env, "Kilogram", "Bread", "Mass", 2, bread_id.value).await; + let _ = create_associated_barcode(&env, "Piece", "Nut", "Quantity", 2, nuts_id.value).await; + + request!(env, buy_barcode(milk_id)); + request!(env, buy_barcode(bread_id)); + request!(env, buy_barcode(nuts_id)); +} + +#[tokio::test] +async fn test_barcode_associate_false_unit() { + let env = TestEnv::new(function_name!()); + + let unit_property_mass = request!( + env, + register_unit_property(UnitPropertyStub { + description: None, + name: "Mass".to_owned() + }) + ); + let unit_property_volume = request!( + env, + register_unit_property(UnitPropertyStub { + description: None, + name: "Volume".to_owned() + }) + ); + + let product = request!( + env, + register_product(ProductStub { + description: None, + name: "Milk".to_owned(), + parent: None, + unit_property: unit_property_mass, + }) + ); + + let unit_mass = request!( + env, + register_unit(UnitStub { + description: None, + full_name_plural: "Grams".to_owned(), + full_name_singular: "Gram".to_owned(), + short_name: "g".to_owned(), + unit_property: unit_property_mass + }) + ); + let unit_volume = request!( + env, + register_unit(UnitStub { + description: None, + full_name_plural: "Liters".to_owned(), + full_name_singular: "Liter".to_owned(), + short_name: "L".to_owned(), + unit_property: unit_property_volume + }) + ); + + let barcode_id_1 = BarcodeId { value: 23 }; + let barcode_id_2 = BarcodeId { value: 24 }; + let value = 1; + + request!( + env, + associate_barcode( + product, + Barcode { + amount: UnitAmount { + unit: unit_mass, + value, + }, + id: barcode_id_1, + }, + ) + ); + + request!(@expect_error + "Should not work, as we registered the base product with unit_property_mass" + env, + associate_barcode( + product, + Barcode { + amount: UnitAmount { + unit: unit_volume, + value, + }, + id: barcode_id_2, + }, + ) + ); +} diff --git a/crates/rocie-server/tests/products/mod.rs b/crates/rocie-server/tests/products/mod.rs index 2ae52fa..b39a07c 100644 --- a/crates/rocie-server/tests/products/mod.rs +++ b/crates/rocie-server/tests/products/mod.rs @@ -1,10 +1,13 @@ use rocie_client::{ apis::{ - api_get_product_api::product_by_id, api_set_product_api::{associate_barcode, register_product}, api_set_unit_api::register_unit, + api_set_unit_property_api::register_unit_property, + }, + models::{ + Barcode, BarcodeId, ProductId, ProductStub, UnitAmount, UnitId, UnitPropertyId, + UnitPropertyStub, UnitStub, }, - models::{Barcode, BarcodeId, Product, ProductId, ProductStub, UnitAmount, UnitId, UnitStub}, }; use crate::testenv::{TestEnv, log::request}; @@ -12,17 +15,24 @@ use crate::testenv::{TestEnv, log::request}; mod barcode; mod register; -async fn create_product(env: &TestEnv, name: &str) -> ProductId { - request!( +async fn create_product( + env: &TestEnv, + unit_property: UnitPropertyId, + name: &str, +) -> (ProductId, UnitPropertyId) { + let product_id = request!( env, register_product(ProductStub { description: Some(None), name: name.to_owned(), parent: None, - },) - ) + unit_property + }) + ); + + (product_id, unit_property) } -async fn create_unit(env: &TestEnv, name: &str) -> UnitId { +async fn create_unit(env: &TestEnv, name: &str, unit_property: UnitPropertyId) -> UnitId { request!( env, register_unit(UnitStub { @@ -30,7 +40,8 @@ async fn create_unit(env: &TestEnv, name: &str) -> UnitId { full_name_plural: name.to_owned(), full_name_singular: name.to_owned(), short_name: name.to_owned(), - },) + unit_property + }) ) } @@ -38,11 +49,20 @@ async fn create_associated_barcode( env: &TestEnv, unit_name: &str, product_name: &str, + unit_property_name: &str, barcode_value: u32, barcode_id: u32, ) -> (UnitId, ProductId) { - let unit_id = create_unit(env, unit_name).await; - let product_id = create_product(env, product_name).await; + let unit_property = request!( + env, + register_unit_property(UnitPropertyStub { + description: None, + name: unit_property_name.to_owned() + }) + ); + + let (product_id, unit_property) = create_product(env, unit_property, product_name).await; + let unit_id = create_unit(env, unit_name, unit_property).await; let barcode_id = BarcodeId { value: barcode_id }; request!( @@ -61,7 +81,3 @@ async fn create_associated_barcode( (unit_id, product_id) } - -async fn get_product(env: &TestEnv, id: ProductId) -> Product { - product_by_id(&env.config, id).await.unwrap() -} diff --git a/crates/rocie-server/tests/products/register.rs b/crates/rocie-server/tests/products/register.rs index 15abec4..4284bd1 100644 --- a/crates/rocie-server/tests/products/register.rs +++ b/crates/rocie-server/tests/products/register.rs @@ -1,13 +1,27 @@ use rocie_client::{ - apis::{api_get_product_api::product_by_id, api_set_product_api::register_product}, - models::ProductStub, + apis::{ + api_get_product_api::product_by_id, api_set_product_api::register_product, + api_set_unit_property_api::register_unit_property, + }, + models::{ProductStub, UnitPropertyStub}, }; -use crate::testenv::{TestEnv, log::request}; +use crate::{ + _testenv::init::function_name, + testenv::{TestEnv, log::request}, +}; #[tokio::test] async fn test_product_register_roundtrip() { - let env = TestEnv::new(module_path!(), 8081); + let env = TestEnv::new(function_name!()); + + let unit_property = request!( + env, + register_unit_property(UnitPropertyStub { + description: Some(Some("The total mass of a product".to_owned())), + name: "Mass".to_owned() + }) + ); let id = request!( env, @@ -15,7 +29,8 @@ async fn test_product_register_roundtrip() { description: Some(Some("A soy based alternative to milk".to_owned())), name: "Soy drink".to_owned(), parent: None, - },) + unit_property, + }) ); let product = request!(env, product_by_id(id)); @@ -25,7 +40,7 @@ async fn test_product_register_roundtrip() { product.description, Some(Some("A soy based alternative to milk".to_owned())) ); - assert_eq!(product.id, id,); + assert_eq!(product.id, id); // assert_eq!( // product.parent, // None, diff --git a/crates/rocie-server/tests/tests.rs b/crates/rocie-server/tests/tests.rs index 34395a6..cf34156 100644 --- a/crates/rocie-server/tests/tests.rs +++ b/crates/rocie-server/tests/tests.rs @@ -4,3 +4,4 @@ mod _testenv; pub(crate) use _testenv as testenv; mod products; +mod units; diff --git a/crates/rocie-server/tests/units/mod.rs b/crates/rocie-server/tests/units/mod.rs new file mode 100644 index 0000000..5518167 --- /dev/null +++ b/crates/rocie-server/tests/units/mod.rs @@ -0,0 +1 @@ +mod register; diff --git a/crates/rocie-server/tests/units/register.rs b/crates/rocie-server/tests/units/register.rs new file mode 100644 index 0000000..5367b55 --- /dev/null +++ b/crates/rocie-server/tests/units/register.rs @@ -0,0 +1,50 @@ +use rocie_client::{ + apis::{ + api_get_unit_api::unit_by_id, api_set_unit_api::register_unit, + api_set_unit_property_api::register_unit_property, + }, + models::{UnitPropertyStub, UnitStub}, +}; + +use crate::{ + _testenv::init::function_name, + testenv::{TestEnv, log::request}, +}; + +#[tokio::test] +async fn test_unit_register_roundtrip() { + let env = TestEnv::new(function_name!()); + + let unit_property = request!( + env, + register_unit_property(UnitPropertyStub { + description: Some(Some("The total mass of a product".to_owned())), + name: "Mass".to_owned() + }) + ); + + let id = request!( + env, + register_unit(UnitStub { + description: Some(Some("Fancy new unit".to_owned())), + full_name_plural: "Grams".to_owned(), + full_name_singular: "Gram".to_owned(), + short_name: "g".to_owned(), + unit_property, + }) + ); + + let unit = request!(env, unit_by_id(id)); + + assert_eq!(&unit.short_name, "g"); + assert_eq!(&unit.full_name_plural, "Grams"); + assert_eq!(&unit.full_name_singular, "Gram"); + assert_eq!(unit.description, Some(Some("Fancy new unit".to_owned()))); + assert_eq!(unit.id, id); +} + +#[tokio::test] +async fn test_unit_conversions_register_error() { + // TODO test if a conversion between two units from different universes actually returns an + // error. +} |
