Files
attune/crates/common/tests/execution_repository_tests.rs
2026-03-04 22:42:23 -06:00

1107 lines
30 KiB
Rust

//! Integration tests for Execution repository
//!
//! These tests verify CRUD operations, queries, and constraints
//! for the Execution repository.
mod helpers;
use attune_common::{
models::enums::ExecutionStatus,
repositories::{
execution::{CreateExecutionInput, ExecutionRepository, UpdateExecutionInput},
Create, Delete, FindById, List, Update,
},
};
use helpers::*;
use serde_json::json;
// ============================================================================
// CREATE Tests
// ============================================================================
#[tokio::test]
async fn test_create_execution_basic() {
let pool = create_test_pool().await.unwrap();
let pack = PackFixture::new_unique("exec_pack")
.create(&pool)
.await
.unwrap();
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "test_action")
.create(&pool)
.await
.unwrap();
let input = CreateExecutionInput {
action: Some(action.id),
action_ref: action.r#ref.clone(),
config: Some(json!({"param1": "value1"})),
env_vars: None,
parent: None,
enforcement: None,
executor: None,
status: ExecutionStatus::Requested,
result: None,
workflow_task: None,
};
let execution = ExecutionRepository::create(&pool, input).await.unwrap();
assert_eq!(execution.action, Some(action.id));
assert_eq!(execution.action_ref, action.r#ref);
assert_eq!(execution.config, Some(json!({"param1": "value1"})));
assert_eq!(execution.parent, None);
assert_eq!(execution.enforcement, None);
assert_eq!(execution.executor, None);
assert_eq!(execution.status, ExecutionStatus::Requested);
assert_eq!(execution.result, None);
assert!(execution.created.timestamp() > 0);
assert!(execution.updated.timestamp() > 0);
}
#[tokio::test]
async fn test_create_execution_without_action() {
let pool = create_test_pool().await.unwrap();
let action_ref = format!("core.{}", unique_execution_ref("deleted_action"));
let input = CreateExecutionInput {
action: None,
action_ref: action_ref.clone(),
config: None,
env_vars: None,
parent: None,
enforcement: None,
executor: None,
status: ExecutionStatus::Requested,
result: None,
workflow_task: None,
};
let execution = ExecutionRepository::create(&pool, input).await.unwrap();
assert_eq!(execution.action, None);
assert_eq!(execution.action_ref, action_ref);
}
#[tokio::test]
async fn test_create_execution_with_all_fields() {
let pool = create_test_pool().await.unwrap();
let pack = PackFixture::new_unique("full_pack")
.create(&pool)
.await
.unwrap();
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
.create(&pool)
.await
.unwrap();
let input = CreateExecutionInput {
action: Some(action.id),
action_ref: action.r#ref.clone(),
config: Some(json!({"timeout": 300, "retry": true})),
env_vars: None,
parent: None,
enforcement: None,
executor: None, // Don't reference non-existent identity
status: ExecutionStatus::Scheduled,
result: Some(json!({"status": "ok"})),
workflow_task: None,
};
let execution = ExecutionRepository::create(&pool, input).await.unwrap();
assert_eq!(execution.executor, None);
assert_eq!(execution.status, ExecutionStatus::Scheduled);
assert_eq!(execution.result, Some(json!({"status": "ok"})));
}
#[tokio::test]
async fn test_create_execution_with_parent() {
let pool = create_test_pool().await.unwrap();
let pack = PackFixture::new_unique("parent_pack")
.create(&pool)
.await
.unwrap();
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
.create(&pool)
.await
.unwrap();
// Create parent execution
let parent_input = CreateExecutionInput {
action: Some(action.id),
action_ref: action.r#ref.clone(),
config: None,
env_vars: None,
parent: None,
enforcement: None,
executor: None,
status: ExecutionStatus::Running,
result: None,
workflow_task: None,
};
let parent = ExecutionRepository::create(&pool, parent_input)
.await
.unwrap();
// Create child execution
let child_input = CreateExecutionInput {
action: Some(action.id),
action_ref: action.r#ref.clone(),
config: None,
env_vars: None,
parent: Some(parent.id),
enforcement: None,
executor: None,
status: ExecutionStatus::Requested,
result: None,
workflow_task: None,
};
let child = ExecutionRepository::create(&pool, child_input)
.await
.unwrap();
assert_eq!(child.parent, Some(parent.id));
}
// ============================================================================
// READ Tests
// ============================================================================
#[tokio::test]
async fn test_find_execution_by_id() {
let pool = create_test_pool().await.unwrap();
let pack = PackFixture::new_unique("find_pack")
.create(&pool)
.await
.unwrap();
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
.create(&pool)
.await
.unwrap();
let input = CreateExecutionInput {
action: Some(action.id),
action_ref: action.r#ref.clone(),
config: None,
env_vars: None,
parent: None,
enforcement: None,
executor: None,
status: ExecutionStatus::Requested,
result: None,
workflow_task: None,
};
let created = ExecutionRepository::create(&pool, input).await.unwrap();
let found = ExecutionRepository::find_by_id(&pool, created.id)
.await
.unwrap()
.expect("Execution should exist");
assert_eq!(found.id, created.id);
assert_eq!(found.action_ref, created.action_ref);
assert_eq!(found.status, created.status);
}
#[tokio::test]
async fn test_find_execution_by_id_not_found() {
let pool = create_test_pool().await.unwrap();
let result = ExecutionRepository::find_by_id(&pool, 999999)
.await
.unwrap();
assert!(result.is_none());
}
#[tokio::test]
async fn test_list_executions() {
let pool = create_test_pool().await.unwrap();
let pack = PackFixture::new_unique("list_pack")
.create(&pool)
.await
.unwrap();
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
.create(&pool)
.await
.unwrap();
// Create multiple executions
for i in 1..=3 {
let input = CreateExecutionInput {
action: Some(action.id),
action_ref: format!("{}_{}", action.r#ref, i),
config: None,
env_vars: None,
parent: None,
enforcement: None,
executor: None,
status: ExecutionStatus::Requested,
result: None,
workflow_task: None,
};
ExecutionRepository::create(&pool, input).await.unwrap();
}
let executions = ExecutionRepository::list(&pool).await.unwrap();
// Should have at least our 3 executions (may have more from parallel tests)
let our_executions: Vec<_> = executions
.iter()
.filter(|e| e.action_ref.starts_with(&action.r#ref))
.collect();
assert_eq!(our_executions.len(), 3);
}
#[tokio::test]
async fn test_list_executions_ordered_by_created_desc() {
let pool = create_test_pool().await.unwrap();
let pack = PackFixture::new_unique("order_pack")
.create(&pool)
.await
.unwrap();
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
.create(&pool)
.await
.unwrap();
let mut created_ids = vec![];
// Create executions in sequence
for i in 1..=3 {
let input = CreateExecutionInput {
action: Some(action.id),
action_ref: format!("{}_{}", action.r#ref, i),
config: None,
env_vars: None,
parent: None,
enforcement: None,
executor: None,
status: ExecutionStatus::Requested,
result: None,
workflow_task: None,
};
let exec = ExecutionRepository::create(&pool, input).await.unwrap();
created_ids.push(exec.id);
// Small delay to ensure different timestamps
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
}
let executions = ExecutionRepository::list(&pool).await.unwrap();
let our_executions: Vec<_> = executions
.iter()
.filter(|e| e.action_ref.starts_with(&action.r#ref))
.collect();
// Should be in reverse order (newest first)
assert_eq!(our_executions[0].id, created_ids[2]);
assert_eq!(our_executions[1].id, created_ids[1]);
assert_eq!(our_executions[2].id, created_ids[0]);
}
// ============================================================================
// UPDATE Tests
// ============================================================================
#[tokio::test]
async fn test_update_execution_status() {
let pool = create_test_pool().await.unwrap();
let pack = PackFixture::new_unique("update_pack")
.create(&pool)
.await
.unwrap();
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
.create(&pool)
.await
.unwrap();
let input = CreateExecutionInput {
action: Some(action.id),
action_ref: action.r#ref.clone(),
config: None,
env_vars: None,
parent: None,
enforcement: None,
executor: None,
status: ExecutionStatus::Requested,
result: None,
workflow_task: None,
};
let created = ExecutionRepository::create(&pool, input).await.unwrap();
let update = UpdateExecutionInput {
status: Some(ExecutionStatus::Running),
result: None,
executor: None,
..Default::default()
};
let updated = ExecutionRepository::update(&pool, created.id, update)
.await
.unwrap();
assert_eq!(updated.status, ExecutionStatus::Running);
assert!(updated.updated > created.updated);
}
#[tokio::test]
async fn test_update_execution_result() {
let pool = create_test_pool().await.unwrap();
let pack = PackFixture::new_unique("result_pack")
.create(&pool)
.await
.unwrap();
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
.create(&pool)
.await
.unwrap();
let input = CreateExecutionInput {
action: Some(action.id),
action_ref: action.r#ref.clone(),
config: None,
env_vars: None,
parent: None,
enforcement: None,
executor: None,
status: ExecutionStatus::Running,
result: None,
workflow_task: None,
};
let created = ExecutionRepository::create(&pool, input).await.unwrap();
let result_data = json!({"output": "success", "data": {"count": 42}});
let update = UpdateExecutionInput {
status: Some(ExecutionStatus::Completed),
result: Some(result_data.clone()),
executor: None,
..Default::default()
};
let updated = ExecutionRepository::update(&pool, created.id, update)
.await
.unwrap();
assert_eq!(updated.status, ExecutionStatus::Completed);
assert_eq!(updated.result, Some(result_data));
}
#[tokio::test]
async fn test_update_execution_executor() {
let pool = create_test_pool().await.unwrap();
let pack = PackFixture::new_unique("executor_pack")
.create(&pool)
.await
.unwrap();
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
.create(&pool)
.await
.unwrap();
let input = CreateExecutionInput {
action: Some(action.id),
action_ref: action.r#ref.clone(),
config: None,
env_vars: None,
parent: None,
enforcement: None,
executor: None,
status: ExecutionStatus::Requested,
result: None,
workflow_task: None,
};
let created = ExecutionRepository::create(&pool, input).await.unwrap();
let update = UpdateExecutionInput {
status: Some(ExecutionStatus::Scheduled),
result: None,
executor: None,
..Default::default()
};
let updated = ExecutionRepository::update(&pool, created.id, update)
.await
.unwrap();
assert_eq!(updated.status, ExecutionStatus::Scheduled);
}
#[tokio::test]
async fn test_update_execution_status_transitions() {
let pool = create_test_pool().await.unwrap();
let pack = PackFixture::new_unique("status_pack")
.create(&pool)
.await
.unwrap();
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
.create(&pool)
.await
.unwrap();
let input = CreateExecutionInput {
action: Some(action.id),
action_ref: action.r#ref.clone(),
config: None,
env_vars: None,
parent: None,
enforcement: None,
executor: None,
status: ExecutionStatus::Requested,
result: None,
workflow_task: None,
};
let exec = ExecutionRepository::create(&pool, input).await.unwrap();
// Transition: Requested -> Scheduling
let exec = ExecutionRepository::update(
&pool,
exec.id,
UpdateExecutionInput {
status: Some(ExecutionStatus::Scheduling),
result: None,
executor: None,
..Default::default()
},
)
.await
.unwrap();
assert_eq!(exec.status, ExecutionStatus::Scheduling);
// Transition: Scheduling -> Scheduled
let exec = ExecutionRepository::update(
&pool,
exec.id,
UpdateExecutionInput {
status: Some(ExecutionStatus::Scheduled),
result: None,
executor: None,
..Default::default()
},
)
.await
.unwrap();
assert_eq!(exec.status, ExecutionStatus::Scheduled);
// Transition: Scheduled -> Running
let exec = ExecutionRepository::update(
&pool,
exec.id,
UpdateExecutionInput {
status: Some(ExecutionStatus::Running),
result: None,
executor: None,
..Default::default()
},
)
.await
.unwrap();
assert_eq!(exec.status, ExecutionStatus::Running);
// Transition: Running -> Completed
let exec = ExecutionRepository::update(
&pool,
exec.id,
UpdateExecutionInput {
status: Some(ExecutionStatus::Completed),
result: Some(json!({"success": true})),
executor: None,
..Default::default()
},
)
.await
.unwrap();
assert_eq!(exec.status, ExecutionStatus::Completed);
}
#[tokio::test]
async fn test_update_execution_failed_status() {
let pool = create_test_pool().await.unwrap();
let pack = PackFixture::new_unique("failed_pack")
.create(&pool)
.await
.unwrap();
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
.create(&pool)
.await
.unwrap();
let input = CreateExecutionInput {
action: Some(action.id),
action_ref: action.r#ref.clone(),
config: None,
env_vars: None,
parent: None,
enforcement: None,
executor: None,
status: ExecutionStatus::Running,
result: None,
workflow_task: None,
};
let created = ExecutionRepository::create(&pool, input).await.unwrap();
let update = UpdateExecutionInput {
status: Some(ExecutionStatus::Failed),
result: Some(json!({"error": "Connection timeout"})),
executor: None,
..Default::default()
};
let updated = ExecutionRepository::update(&pool, created.id, update)
.await
.unwrap();
assert_eq!(updated.status, ExecutionStatus::Failed);
assert!(updated.result.is_some());
}
#[tokio::test]
async fn test_update_execution_no_changes() {
let pool = create_test_pool().await.unwrap();
let pack = PackFixture::new_unique("nochange_pack")
.create(&pool)
.await
.unwrap();
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
.create(&pool)
.await
.unwrap();
let input = CreateExecutionInput {
action: Some(action.id),
action_ref: action.r#ref.clone(),
config: None,
env_vars: None,
parent: None,
enforcement: None,
executor: None,
status: ExecutionStatus::Requested,
result: None,
workflow_task: None,
};
let created = ExecutionRepository::create(&pool, input).await.unwrap();
let update = UpdateExecutionInput::default();
let updated = ExecutionRepository::update(&pool, created.id, update)
.await
.unwrap();
assert_eq!(updated.status, created.status);
assert_eq!(updated.result, created.result);
}
// ============================================================================
// DELETE Tests
// ============================================================================
#[tokio::test]
async fn test_delete_execution() {
let pool = create_test_pool().await.unwrap();
let pack = PackFixture::new_unique("delete_pack")
.create(&pool)
.await
.unwrap();
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
.create(&pool)
.await
.unwrap();
let input = CreateExecutionInput {
action: Some(action.id),
action_ref: action.r#ref.clone(),
config: None,
env_vars: None,
parent: None,
enforcement: None,
executor: None,
status: ExecutionStatus::Completed,
result: None,
workflow_task: None,
};
let created = ExecutionRepository::create(&pool, input).await.unwrap();
let deleted = ExecutionRepository::delete(&pool, created.id)
.await
.unwrap();
assert!(deleted);
let found = ExecutionRepository::find_by_id(&pool, created.id)
.await
.unwrap();
assert!(found.is_none());
}
#[tokio::test]
async fn test_delete_execution_not_found() {
let pool = create_test_pool().await.unwrap();
let deleted = ExecutionRepository::delete(&pool, 999999).await.unwrap();
assert!(!deleted);
}
// ============================================================================
// SPECIALIZED QUERY Tests
// ============================================================================
#[tokio::test]
async fn test_find_executions_by_status() {
let pool = create_test_pool().await.unwrap();
let pack = PackFixture::new_unique("status_filter_pack")
.create(&pool)
.await
.unwrap();
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
.create(&pool)
.await
.unwrap();
// Create executions with different statuses
for (i, status) in [
ExecutionStatus::Requested,
ExecutionStatus::Running,
ExecutionStatus::Running,
ExecutionStatus::Completed,
]
.iter()
.enumerate()
{
let input = CreateExecutionInput {
action: Some(action.id),
action_ref: format!("{}_{}", action.r#ref, i),
config: None,
env_vars: None,
parent: None,
enforcement: None,
executor: None,
status: *status,
result: None,
workflow_task: None,
};
ExecutionRepository::create(&pool, input).await.unwrap();
}
let running = ExecutionRepository::find_by_status(&pool, ExecutionStatus::Running)
.await
.unwrap();
let our_running: Vec<_> = running
.iter()
.filter(|e| e.action_ref.starts_with(&action.r#ref))
.collect();
assert_eq!(our_running.len(), 2);
assert!(our_running
.iter()
.all(|e| e.status == ExecutionStatus::Running));
}
#[tokio::test]
async fn test_find_executions_by_enforcement() {
let pool = create_test_pool().await.unwrap();
let pack = PackFixture::new_unique("enforcement_pack")
.create(&pool)
.await
.unwrap();
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
.create(&pool)
.await
.unwrap();
// Create first execution with enforcement placeholder
let exec1_input = CreateExecutionInput {
action: Some(action.id),
action_ref: format!("{}_1", action.r#ref),
config: None,
env_vars: None,
parent: None,
enforcement: None,
executor: None,
status: ExecutionStatus::Requested,
result: None,
workflow_task: None,
};
let _exec1 = ExecutionRepository::create(&pool, exec1_input)
.await
.unwrap();
// Create executions with enforcement reference
for i in 2..=3 {
let input = CreateExecutionInput {
action: Some(action.id),
action_ref: format!("{}_{}", action.r#ref, i),
config: None,
env_vars: None,
parent: None,
enforcement: if i == 2 { None } else { None }, // Can't reference non-existent enforcement
executor: None,
status: ExecutionStatus::Requested,
result: None,
workflow_task: None,
};
ExecutionRepository::create(&pool, input).await.unwrap();
}
// Test find_by_enforcement with non-existent ID returns empty
let by_enforcement = ExecutionRepository::find_by_enforcement(&pool, 999999)
.await
.unwrap();
assert_eq!(by_enforcement.len(), 0);
}
// ============================================================================
// PARENT-CHILD RELATIONSHIP Tests
// ============================================================================
#[tokio::test]
async fn test_parent_child_execution_hierarchy() {
let pool = create_test_pool().await.unwrap();
let pack = PackFixture::new_unique("hierarchy_pack")
.create(&pool)
.await
.unwrap();
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
.create(&pool)
.await
.unwrap();
// Create parent
let parent_input = CreateExecutionInput {
action: Some(action.id),
action_ref: format!("{}.parent", action.r#ref),
config: None,
env_vars: None,
parent: None,
enforcement: None,
executor: None,
status: ExecutionStatus::Running,
result: None,
workflow_task: None,
};
let parent = ExecutionRepository::create(&pool, parent_input)
.await
.unwrap();
// Create children
let mut children = vec![];
for i in 1..=3 {
let child_input = CreateExecutionInput {
action: Some(action.id),
action_ref: format!("{}.child_{}", action.r#ref, i),
config: None,
env_vars: None,
parent: Some(parent.id),
enforcement: None,
executor: None,
status: ExecutionStatus::Requested,
result: None,
workflow_task: None,
};
let child = ExecutionRepository::create(&pool, child_input)
.await
.unwrap();
children.push(child);
}
// Verify all children have correct parent
for child in children {
assert_eq!(child.parent, Some(parent.id));
}
// Verify parent has no parent
assert_eq!(parent.parent, None);
}
#[tokio::test]
async fn test_nested_execution_hierarchy() {
let pool = create_test_pool().await.unwrap();
let pack = PackFixture::new_unique("nested_pack")
.create(&pool)
.await
.unwrap();
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
.create(&pool)
.await
.unwrap();
// Create grandparent
let grandparent_input = CreateExecutionInput {
action: Some(action.id),
action_ref: format!("{}.grandparent", action.r#ref),
config: None,
env_vars: None,
parent: None,
enforcement: None,
executor: None,
status: ExecutionStatus::Running,
result: None,
workflow_task: None,
};
let grandparent = ExecutionRepository::create(&pool, grandparent_input)
.await
.unwrap();
// Create parent
let parent_input = CreateExecutionInput {
action: Some(action.id),
action_ref: format!("{}.parent", action.r#ref),
config: None,
env_vars: None,
parent: Some(grandparent.id),
enforcement: None,
executor: None,
status: ExecutionStatus::Running,
result: None,
workflow_task: None,
};
let parent = ExecutionRepository::create(&pool, parent_input)
.await
.unwrap();
// Create child
let child_input = CreateExecutionInput {
action: Some(action.id),
action_ref: format!("{}.child", action.r#ref),
config: None,
env_vars: None,
parent: Some(parent.id),
enforcement: None,
executor: None,
status: ExecutionStatus::Requested,
result: None,
workflow_task: None,
};
let child = ExecutionRepository::create(&pool, child_input)
.await
.unwrap();
// Verify hierarchy
assert_eq!(grandparent.parent, None);
assert_eq!(parent.parent, Some(grandparent.id));
assert_eq!(child.parent, Some(parent.id));
}
// ============================================================================
// TIMESTAMP Tests
// ============================================================================
#[tokio::test]
async fn test_execution_timestamps() {
let pool = create_test_pool().await.unwrap();
let pack = PackFixture::new_unique("timestamp_pack")
.create(&pool)
.await
.unwrap();
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
.create(&pool)
.await
.unwrap();
let input = CreateExecutionInput {
action: Some(action.id),
action_ref: action.r#ref.clone(),
config: None,
env_vars: None,
parent: None,
enforcement: None,
executor: None,
status: ExecutionStatus::Requested,
result: None,
workflow_task: None,
};
let created = ExecutionRepository::create(&pool, input).await.unwrap();
assert!(created.created.timestamp() > 0);
assert!(created.updated.timestamp() > 0);
assert_eq!(created.created, created.updated);
// Sleep briefly to ensure timestamp difference
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
let update = UpdateExecutionInput {
status: Some(ExecutionStatus::Running),
result: None,
executor: None,
..Default::default()
};
let updated = ExecutionRepository::update(&pool, created.id, update)
.await
.unwrap();
assert_eq!(updated.created, created.created); // created unchanged
assert!(updated.updated > created.updated); // updated changed
}
// ============================================================================
// JSON FIELD Tests
// ============================================================================
#[tokio::test]
async fn test_execution_config_json() {
let pool = create_test_pool().await.unwrap();
let pack = PackFixture::new_unique("config_pack")
.create(&pool)
.await
.unwrap();
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
.create(&pool)
.await
.unwrap();
let complex_config = json!({
"parameters": {
"timeout": 300,
"retry_count": 3,
"retry_delay": 1000
},
"environment": {
"NODE_ENV": "production"
},
"metadata": {
"triggered_by": "webhook",
"source": "github"
}
});
let input = CreateExecutionInput {
action: Some(action.id),
action_ref: action.r#ref.clone(),
config: Some(complex_config.clone()),
env_vars: None,
parent: None,
enforcement: None,
executor: None,
status: ExecutionStatus::Requested,
result: None,
workflow_task: None,
};
let execution = ExecutionRepository::create(&pool, input).await.unwrap();
assert_eq!(execution.config, Some(complex_config));
}
#[tokio::test]
async fn test_execution_result_json() {
let pool = create_test_pool().await.unwrap();
let pack = PackFixture::new_unique("result_json_pack")
.create(&pool)
.await
.unwrap();
let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "action")
.create(&pool)
.await
.unwrap();
let input = CreateExecutionInput {
action: Some(action.id),
action_ref: action.r#ref.clone(),
config: None,
env_vars: None,
parent: None,
enforcement: None,
executor: None,
status: ExecutionStatus::Running,
result: None,
workflow_task: None,
};
let created = ExecutionRepository::create(&pool, input).await.unwrap();
let complex_result = json!({
"output": {
"stdout": "Process completed successfully",
"stderr": ""
},
"metrics": {
"duration_ms": 1234,
"memory_mb": 128,
"cpu_percent": 45.2
},
"artifacts": [
{"name": "report.pdf", "size": 1024000},
{"name": "data.json", "size": 512}
]
});
let update = UpdateExecutionInput {
status: Some(ExecutionStatus::Completed),
result: Some(complex_result.clone()),
executor: None,
..Default::default()
};
let updated = ExecutionRepository::update(&pool, created.id, update)
.await
.unwrap();
assert_eq!(updated.result, Some(complex_result));
}