feat: implement household workflows binary files and write receipts
This commit is contained in:
135
tests/workflows.rs
Normal file
135
tests/workflows.rs
Normal file
@@ -0,0 +1,135 @@
|
||||
use axum::{Router, http::StatusCode, response::IntoResponse};
|
||||
use hermes_grocy_mcp::{
|
||||
catalog,
|
||||
client::{ApiBase, ApiClient},
|
||||
model::{AccessMode, PrivateProfile, TransportPolicy, Verification, WriteStatus},
|
||||
tools,
|
||||
};
|
||||
use serde_json::json;
|
||||
use std::sync::{
|
||||
Arc,
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
};
|
||||
fn profile(url: &str) -> PrivateProfile {
|
||||
PrivateProfile {
|
||||
api_base: ApiBase::parse(url, TransportPolicy::LoopbackDevelopment).unwrap(),
|
||||
api_key: secrecy::SecretString::from("synthetic-key"),
|
||||
access: AccessMode::Full,
|
||||
timezone: None,
|
||||
defaults: json!(null),
|
||||
ca_path: None,
|
||||
transport: TransportPolicy::LoopbackDevelopment,
|
||||
unsafe_storage: false,
|
||||
}
|
||||
}
|
||||
#[tokio::test]
|
||||
async fn workflow_results_and_files() {
|
||||
let writes = Arc::new(AtomicUsize::new(0));
|
||||
let accepted = writes.clone();
|
||||
let uploaded = Arc::new(std::sync::Mutex::new(Vec::new()));
|
||||
let captured = uploaded.clone();
|
||||
let app = Router::new().fallback(move |req: axum::extract::Request| {
|
||||
let accepted = accepted.clone();
|
||||
let captured = captured.clone();
|
||||
async move {
|
||||
let path = req.uri().path().to_string();
|
||||
let method = req.method().to_string();
|
||||
if path.starts_with("/api/files/") {
|
||||
if method == "PUT" {
|
||||
*captured.lock().unwrap() =
|
||||
axum::body::to_bytes(req.into_body(), 8 * 1024 * 1024)
|
||||
.await
|
||||
.unwrap()
|
||||
.to_vec();
|
||||
return StatusCode::NO_CONTENT.into_response();
|
||||
}
|
||||
return (
|
||||
[("content-type", "application/pdf")],
|
||||
b"%PDF-synthetic".to_vec(),
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
if path == "/api/calendar/ical" {
|
||||
return (
|
||||
[("content-type", "text/calendar")],
|
||||
"BEGIN:VCALENDAR\r\nEND:VCALENDAR",
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
if method == "POST" {
|
||||
accepted.fetch_add(1, Ordering::SeqCst);
|
||||
return (
|
||||
[("content-type", "application/json")],
|
||||
"[{\"transaction_id\":\"synthetic-transaction\"}]",
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
if path == "/api/stock/products/7" {
|
||||
if accepted.load(Ordering::SeqCst) > 0 {
|
||||
return StatusCode::SERVICE_UNAVAILABLE.into_response();
|
||||
}
|
||||
return (
|
||||
[("content-type", "application/json")],
|
||||
"{\"product\":{\"id\":7,\"qu_id_stock\":1}}",
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
if path == "/api/stock/volatile" {
|
||||
return axum::Json(json!({"due_products":[{"id":7}],"overdue_products":[]}))
|
||||
.into_response();
|
||||
}
|
||||
axum::Json(json!([])).into_response()
|
||||
}
|
||||
});
|
||||
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
|
||||
let port = listener.local_addr().unwrap().port();
|
||||
let server = tokio::spawn(async move { axum::serve(listener, app).await.unwrap() });
|
||||
let client = ApiClient::new(profile(&format!("http://127.0.0.1:{port}"))).unwrap();
|
||||
let call = |name, args| catalog::validate(name, args, AccessMode::Full).unwrap();
|
||||
let volatile = tools::execute(&client, call("stock_volatile", json!({})))
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(volatile.data["due_products"]["items"][0]["id"], 7);
|
||||
let written = tools::execute(
|
||||
&client,
|
||||
call("stock_add", json!({"product_id":7,"amount":1})),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let report = written.write.unwrap();
|
||||
assert_eq!(report.status, WriteStatus::Completed);
|
||||
assert_eq!(report.verification, Verification::Incomplete);
|
||||
assert_eq!(report.receipt[0]["transaction_id"], "synthetic-transaction");
|
||||
assert_eq!(writes.load(Ordering::SeqCst), 1);
|
||||
let upload=tools::execute(&client,call("file_upload",json!({"group":"userfiles","filename":"sample.pdf","content_base64":"JVBERi1zeW50aGV0aWM="}))).await.unwrap();
|
||||
assert_eq!(*uploaded.lock().unwrap(), b"%PDF-synthetic");
|
||||
assert_eq!(upload.write.unwrap().status, WriteStatus::Completed);
|
||||
let file = tools::execute(
|
||||
&client,
|
||||
call(
|
||||
"file_get",
|
||||
json!({"group":"userfiles","filename":"sample.pdf"}),
|
||||
),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(file.attachment.unwrap().bytes, b"%PDF-synthetic");
|
||||
assert!(
|
||||
catalog::validate(
|
||||
"file_get",
|
||||
json!({"group":"userfiles","filename":"../unsafe"}),
|
||||
AccessMode::Full
|
||||
)
|
||||
.is_err()
|
||||
);
|
||||
let ical = tools::execute(&client, call("calendar_export", json!({})))
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(
|
||||
ical.data["content"]
|
||||
.as_str()
|
||||
.unwrap()
|
||||
.starts_with("BEGIN:VCALENDAR")
|
||||
);
|
||||
server.abort();
|
||||
}
|
||||
Reference in New Issue
Block a user