mirror of
https://github.com/RaisFast/raisfast.git
synced 2026-09-24 00:02:28 +00:00
776 lines
22 KiB
Rust
776 lines
22 KiB
Rust
use crate::*;
|
|
use serde_json::json;
|
|
|
|
async fn create_simple_workflow(app: &mut axum::Router, token: &str) -> String {
|
|
let id = uuid::Uuid::now_v7().to_string();
|
|
let (status, body) = send(
|
|
app,
|
|
post_json_auth(
|
|
"/api/v1/admin/workflows",
|
|
json!({
|
|
"id": id,
|
|
"name": "Test Workflow",
|
|
"description": "A test workflow",
|
|
"steps": [
|
|
{"id": "s1", "name": "Step 1", "type": "task", "config": {}, "next": "s2"},
|
|
{"id": "s2", "name": "Step 2", "type": "task", "config": {}, "next": ""}
|
|
]
|
|
}),
|
|
token,
|
|
),
|
|
)
|
|
.await;
|
|
assert!(
|
|
status.is_success(),
|
|
"create workflow failed: {status} {body:?}"
|
|
);
|
|
id
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn workflow_crud_lifecycle() {
|
|
let (mut app, _) = test_app().await;
|
|
let token = make_token("u1", 1, raisfast::models::user::UserRole::Admin);
|
|
|
|
let id = create_simple_workflow(&mut app, &token).await;
|
|
|
|
let (status, body) = send(&mut app, get_auth("/api/v1/admin/workflows", &token)).await;
|
|
assert!(status.is_success());
|
|
assert_eq!(body["data"].as_array().unwrap().len(), 1);
|
|
|
|
let (status, body) = send(
|
|
&mut app,
|
|
get_auth(&format!("/api/v1/admin/workflows/{id}"), &token),
|
|
)
|
|
.await;
|
|
assert!(status.is_success());
|
|
assert_eq!(body["data"]["name"], "Test Workflow");
|
|
|
|
let (status, _) = send(
|
|
&mut app,
|
|
delete_auth(&format!("/api/v1/admin/workflows/{id}"), &token),
|
|
)
|
|
.await;
|
|
assert!(status.is_success());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn workflow_start_and_execute_steps() {
|
|
let (mut app, _) = test_app().await;
|
|
let token = make_token("u1", 1, raisfast::models::user::UserRole::Admin);
|
|
|
|
let id = create_simple_workflow(&mut app, &token).await;
|
|
|
|
let (status, body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/{id}/start"),
|
|
json!({"context": {"title": "Hello"}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert!(status.is_success(), "start failed: {status} {body:?}");
|
|
let instance_id = body["data"]["document_id"].as_str().unwrap().to_string();
|
|
assert_eq!(body["data"]["status"], "running");
|
|
assert_eq!(body["data"]["current_step"], "s1");
|
|
|
|
let (status, body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/instances/{instance_id}/execute"),
|
|
json!({"output": {"step1_result": "ok"}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert!(
|
|
status.is_success(),
|
|
"execute step 1 failed: {status} {body:?}"
|
|
);
|
|
assert_eq!(body["data"]["current_step"], "s2");
|
|
|
|
let (status, body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/instances/{instance_id}/execute"),
|
|
json!({"output": {"step2_result": "done"}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert!(
|
|
status.is_success(),
|
|
"execute step 2 failed: {status} {body:?}"
|
|
);
|
|
assert_eq!(body["data"]["status"], "completed");
|
|
assert!(body["data"]["current_step"].is_null());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn workflow_branch_condition() {
|
|
let (mut app, _) = test_app().await;
|
|
let token = make_token("u1", 1, raisfast::models::user::UserRole::Admin);
|
|
|
|
let id = uuid::Uuid::now_v7().to_string();
|
|
let (status, body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
"/api/v1/admin/workflows",
|
|
json!({
|
|
"id": id,
|
|
"name": "Branch Workflow",
|
|
"steps": [
|
|
{"id": "decide", "name": "Decide", "type": "branch", "config": {}, "next": [
|
|
{"condition": {"approved": true}, "step": "publish"},
|
|
{"step": "reject"}
|
|
]},
|
|
{"id": "publish", "name": "Publish", "type": "task", "config": {}, "next": ""},
|
|
{"id": "reject", "name": "Reject", "type": "task", "config": {}, "next": ""}
|
|
]
|
|
}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert!(status.is_success(), "create failed: {status} {body:?}");
|
|
|
|
let (status, body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/{id}/start"),
|
|
json!({"context": {"approved": false}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert!(status.is_success());
|
|
let instance_id = body["data"]["document_id"].as_str().unwrap().to_string();
|
|
|
|
let (status, body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/instances/{instance_id}/execute"),
|
|
json!({"output": {}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert!(status.is_success());
|
|
assert_eq!(body["data"]["current_step"], "reject");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn workflow_cancel_instance() {
|
|
let (mut app, _) = test_app().await;
|
|
let token = make_token("u1", 1, raisfast::models::user::UserRole::Admin);
|
|
|
|
let id = uuid::Uuid::now_v7().to_string();
|
|
let _ = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
"/api/v1/admin/workflows",
|
|
json!({
|
|
"id": id,
|
|
"name": "Cancel Test",
|
|
"steps": [
|
|
{"id": "s1", "name": "Wait", "type": "await", "config": {}, "next": ""}
|
|
]
|
|
}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
|
|
let (status, body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/{id}/start"),
|
|
json!({"context": {}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert!(status.is_success());
|
|
let instance_id = body["data"]["document_id"].as_str().unwrap().to_string();
|
|
|
|
let (status, _) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/instances/{instance_id}/cancel"),
|
|
json!({}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert!(status.is_success());
|
|
|
|
let (status, body) = send(
|
|
&mut app,
|
|
get_auth(
|
|
&format!("/api/v1/admin/workflows/instances/{instance_id}"),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert!(status.is_success());
|
|
assert_eq!(body["data"]["status"], "cancelled");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn workflow_step_logs() {
|
|
let (mut app, _) = test_app().await;
|
|
let token = make_token("u1", 1, raisfast::models::user::UserRole::Admin);
|
|
|
|
let id = uuid::Uuid::now_v7().to_string();
|
|
let _ = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
"/api/v1/admin/workflows",
|
|
json!({
|
|
"id": id,
|
|
"name": "Log Test",
|
|
"steps": [
|
|
{"id": "s1", "name": "Step 1", "type": "task", "config": {}, "next": ""}
|
|
]
|
|
}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
|
|
let (status, body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/{id}/start"),
|
|
json!({"context": {"x": 1}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert!(status.is_success());
|
|
let instance_id = body["data"]["document_id"].as_str().unwrap().to_string();
|
|
|
|
let _ = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/instances/{instance_id}/execute"),
|
|
json!({"output": {"result": "ok"}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
|
|
let (status, body) = send(
|
|
&mut app,
|
|
get_auth(
|
|
&format!("/api/v1/admin/workflows/instances/{instance_id}/logs"),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert!(status.is_success());
|
|
let logs = body["data"].as_array().unwrap();
|
|
assert_eq!(logs.len(), 1);
|
|
assert_eq!(logs[0]["step_id"], "s1");
|
|
assert_eq!(logs[0]["status"], "completed");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn workflow_get_not_found() {
|
|
let (mut app, _) = test_app().await;
|
|
let token = make_token("u1", 1, raisfast::models::user::UserRole::Admin);
|
|
let (status, _) = send(
|
|
&mut app,
|
|
get_auth("/api/v1/admin/workflows/nonexistent", &token),
|
|
)
|
|
.await;
|
|
assert_eq!(status, StatusCode::NOT_FOUND);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn workflow_create_no_steps() {
|
|
let (mut app, _) = test_app().await;
|
|
let token = make_token("u1", 1, raisfast::models::user::UserRole::Admin);
|
|
let id = uuid::Uuid::now_v7().to_string();
|
|
let (status, _) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
"/api/v1/admin/workflows",
|
|
json!({"id": id, "name": "Empty", "steps": []}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert_eq!(status, StatusCode::BAD_REQUEST);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn workflow_start_nonexistent() {
|
|
let (mut app, _) = test_app().await;
|
|
let token = make_token("u1", 1, raisfast::models::user::UserRole::Admin);
|
|
let (status, _) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
"/api/v1/admin/workflows/nonexistent/start",
|
|
json!({"context": {}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert_eq!(status, StatusCode::NOT_FOUND);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn workflow_execute_nonexistent_instance() {
|
|
let (mut app, _) = test_app().await;
|
|
let token = make_token("u1", 1, raisfast::models::user::UserRole::Admin);
|
|
let (status, _) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
"/api/v1/admin/workflows/instances/nonexistent/execute",
|
|
json!({"output": {}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert_eq!(status, StatusCode::NOT_FOUND);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn workflow_cancel_already_completed() {
|
|
let (mut app, _) = test_app().await;
|
|
let token = make_token("u1", 1, raisfast::models::user::UserRole::Admin);
|
|
|
|
let id = uuid::Uuid::now_v7().to_string();
|
|
let _ = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
"/api/v1/admin/workflows",
|
|
json!({
|
|
"id": id,
|
|
"name": "Quick",
|
|
"steps": [
|
|
{"id": "s1", "name": "Done", "type": "task", "config": {}, "next": ""}
|
|
]
|
|
}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
|
|
let (_, body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/{id}/start"),
|
|
json!({"context": {}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
let instance_id = body["data"]["document_id"].as_str().unwrap().to_string();
|
|
|
|
let _ = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/instances/{instance_id}/execute"),
|
|
json!({"output": {}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
|
|
let (status, _) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/instances/{instance_id}/cancel"),
|
|
json!({}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert_eq!(status, StatusCode::BAD_REQUEST);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn workflow_execute_not_running() {
|
|
let (mut app, _) = test_app().await;
|
|
let token = make_token("u1", 1, raisfast::models::user::UserRole::Admin);
|
|
|
|
let id = uuid::Uuid::now_v7().to_string();
|
|
let _ = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
"/api/v1/admin/workflows",
|
|
json!({
|
|
"id": id,
|
|
"name": "Quick",
|
|
"steps": [
|
|
{"id": "s1", "name": "Done", "type": "task", "config": {}, "next": ""}
|
|
]
|
|
}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
|
|
let (_, body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/{id}/start"),
|
|
json!({"context": {}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
let instance_id = body["data"]["document_id"].as_str().unwrap().to_string();
|
|
|
|
let _ = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/instances/{instance_id}/execute"),
|
|
json!({"output": {}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
|
|
let (status, _) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/instances/{instance_id}/execute"),
|
|
json!({"output": {}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert_eq!(status, StatusCode::BAD_REQUEST);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn workflow_list_instances() {
|
|
let (mut app, _) = test_app().await;
|
|
let token = make_token("u1", 1, raisfast::models::user::UserRole::Admin);
|
|
|
|
let id = create_simple_workflow(&mut app, &token).await;
|
|
|
|
let _ = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/{id}/start"),
|
|
json!({"context": {"x": 1}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
|
|
let (status, body) = send(
|
|
&mut app,
|
|
get_auth("/api/v1/admin/workflows/instances", &token),
|
|
)
|
|
.await;
|
|
assert!(
|
|
status.is_success(),
|
|
"list instances failed: {status} {body:?}"
|
|
);
|
|
let total = body["data"]["total"].as_i64().unwrap_or(0);
|
|
assert!(total >= 1, "expected at least 1 instance, got {total}");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn workflow_get_instance_not_found() {
|
|
let (mut app, _) = test_app().await;
|
|
let token = make_token("u1", 1, raisfast::models::user::UserRole::Admin);
|
|
let (status, _) = send(
|
|
&mut app,
|
|
get_auth("/api/v1/admin/workflows/instances/nonexistent", &token),
|
|
)
|
|
.await;
|
|
assert_eq!(status, StatusCode::NOT_FOUND);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn workflow_step_logs_not_found() {
|
|
let (mut app, _) = test_app().await;
|
|
let token = make_token("u1", 1, raisfast::models::user::UserRole::Admin);
|
|
let (status, _) = send(
|
|
&mut app,
|
|
get_auth("/api/v1/admin/workflows/instances/nonexistent/logs", &token),
|
|
)
|
|
.await;
|
|
assert_eq!(status, StatusCode::NOT_FOUND);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn workflow_delete_nonexistent() {
|
|
let (mut app, _) = test_app().await;
|
|
let token = make_token("u1", 1, raisfast::models::user::UserRole::Admin);
|
|
let (status, _) = send(
|
|
&mut app,
|
|
delete_auth("/api/v1/admin/workflows/nonexistent", &token),
|
|
)
|
|
.await;
|
|
assert!(status.is_success() || status == StatusCode::NOT_FOUND);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn workflow_parallel_with_join_next() {
|
|
let (mut app, _) = test_app().await;
|
|
let token = make_token("u1", 1, raisfast::models::user::UserRole::Admin);
|
|
|
|
let id = uuid::Uuid::now_v7().to_string();
|
|
let (status, body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
"/api/v1/admin/workflows",
|
|
json!({
|
|
"id": id,
|
|
"name": "Parallel Join WF",
|
|
"steps": [
|
|
{"id": "s1", "name": "Parallel Start", "type": "parallel", "config": {"join_next": "s4"}, "next": ["s2", "s3"]},
|
|
{"id": "s2", "name": "Branch A", "type": "task", "config": {}, "next": ""},
|
|
{"id": "s3", "name": "Branch B", "type": "task", "config": {}, "next": ""},
|
|
{"id": "s4", "name": "After Join", "type": "task", "config": {}, "next": ""}
|
|
]
|
|
}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert!(status.is_success(), "create failed: {status} {body:?}");
|
|
|
|
let (_, body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/{id}/start"),
|
|
json!({"context": {"x": 1}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
let instance_id = body["data"]["document_id"].as_str().unwrap().to_string();
|
|
assert_eq!(body["data"]["current_step"], "s1");
|
|
|
|
// Execute the parallel step — spawns branches s2 and s3, sets current to s2
|
|
let (status, body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/instances/{instance_id}/execute"),
|
|
json!({"output": {"parallel_started": true}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert!(
|
|
status.is_success(),
|
|
"execute parallel failed: {status} {body:?}"
|
|
);
|
|
assert_eq!(body["data"]["current_step"], "s2");
|
|
|
|
// Complete branch s2 — should advance to s3 (remaining branch)
|
|
let (status, body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/instances/{instance_id}/execute"),
|
|
json!({"output": {"branch_a": "done"}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert!(
|
|
status.is_success(),
|
|
"execute branch A failed: {status} {body:?}"
|
|
);
|
|
assert_eq!(body["data"]["current_step"], "s3");
|
|
|
|
// Complete branch s3 — all branches done, should join to s4
|
|
let (status, body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/instances/{instance_id}/execute"),
|
|
json!({"output": {"branch_b": "done"}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert!(
|
|
status.is_success(),
|
|
"execute branch B failed: {status} {body:?}"
|
|
);
|
|
assert_eq!(body["data"]["current_step"], "s4");
|
|
|
|
// Complete the join step — workflow should finish
|
|
let (status, body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/instances/{instance_id}/execute"),
|
|
json!({"output": {"final": true}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert!(
|
|
status.is_success(),
|
|
"execute final step failed: {status} {body:?}"
|
|
);
|
|
assert_eq!(body["data"]["status"], "completed");
|
|
assert!(body["data"]["current_step"].is_null());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn workflow_parallel_no_join_completes() {
|
|
let (mut app, _) = test_app().await;
|
|
let token = make_token("u1", 1, raisfast::models::user::UserRole::Admin);
|
|
|
|
let id = uuid::Uuid::now_v7().to_string();
|
|
let _ = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
"/api/v1/admin/workflows",
|
|
json!({
|
|
"id": id,
|
|
"name": "Parallel No Join",
|
|
"steps": [
|
|
{"id": "s1", "name": "Parallel", "type": "parallel", "config": {}, "next": ["s2", "s3"]},
|
|
{"id": "s2", "name": "Branch A", "type": "task", "config": {}, "next": ""},
|
|
{"id": "s3", "name": "Branch B", "type": "task", "config": {}, "next": ""}
|
|
]
|
|
}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
|
|
let (_, body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/{id}/start"),
|
|
json!({"context": {}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
let instance_id = body["data"]["document_id"].as_str().unwrap().to_string();
|
|
|
|
// Execute parallel → s2
|
|
let (_, body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/instances/{instance_id}/execute"),
|
|
json!({"output": {}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert_eq!(body["data"]["current_step"], "s2");
|
|
|
|
// Complete s2 → s3
|
|
let (_, body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/instances/{instance_id}/execute"),
|
|
json!({"output": {"a": 1}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert_eq!(body["data"]["current_step"], "s3");
|
|
|
|
// Complete s3 → no join_next, workflow completes
|
|
let (status, body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/instances/{instance_id}/execute"),
|
|
json!({"output": {"b": 2}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert!(status.is_success(), "{status} {body:?}");
|
|
assert_eq!(body["data"]["status"], "completed");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn workflow_parallel_step_logs() {
|
|
let (mut app, _) = test_app().await;
|
|
let token = make_token("u1", 1, raisfast::models::user::UserRole::Admin);
|
|
|
|
let id = uuid::Uuid::now_v7().to_string();
|
|
let _ = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
"/api/v1/admin/workflows",
|
|
json!({
|
|
"id": id,
|
|
"name": "Parallel Logs",
|
|
"steps": [
|
|
{"id": "s1", "name": "Parallel", "type": "parallel", "config": {}, "next": ["s2", "s3"]},
|
|
{"id": "s2", "name": "Branch A", "type": "task", "config": {}, "next": ""},
|
|
{"id": "s3", "name": "Branch B", "type": "task", "config": {}, "next": ""}
|
|
]
|
|
}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
|
|
let (_, body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/{id}/start"),
|
|
json!({"context": {}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
let instance_id = body["data"]["document_id"].as_str().unwrap().to_string();
|
|
|
|
// Execute parallel → spawns s2 + s3
|
|
send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/instances/{instance_id}/execute"),
|
|
json!({"output": {}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
|
|
// Complete s2
|
|
send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/instances/{instance_id}/execute"),
|
|
json!({"output": {}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
|
|
// Complete s3 → done
|
|
send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/workflows/instances/{instance_id}/execute"),
|
|
json!({"output": {}}),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
|
|
// Check logs: s1 (completed) + s2 (completed) + s3 (completed)
|
|
let (status, body) = send(
|
|
&mut app,
|
|
get_auth(
|
|
&format!("/api/v1/admin/workflows/instances/{instance_id}/logs"),
|
|
&token,
|
|
),
|
|
)
|
|
.await;
|
|
assert!(status.is_success());
|
|
let logs = body["data"].as_array().unwrap();
|
|
assert!(logs.len() >= 3, "expected >= 3 logs, got {}", logs.len());
|
|
let completed: Vec<_> = logs.iter().filter(|l| l["status"] == "completed").collect();
|
|
assert_eq!(completed.len(), 3);
|
|
}
|