mod common;
use common::{
ServerProcess, create_test_job, create_test_result, create_test_workflow, run_cli_with_json,
start_server,
};
use rstest::rstest;
use serde_json::json;
use torc::client::apis;
use torc::client::workflow_manager::WorkflowManager;
use torc::config::TorcConfig;
use torc::models;
use torc::models::JobStatus;
#[rstest]
fn test_results_list_command_json(start_server: &ServerProcess) {
let config = &start_server.config;
let workflow = create_test_workflow(config, "test_results_list_workflow");
let workflow_id = workflow.id.unwrap();
let job1 = create_test_job(config, workflow_id, "job1");
let job2 = create_test_job(config, workflow_id, "job2");
let _result1 = create_test_result(config, workflow_id, job1.id.unwrap());
let _result2 = create_test_result(config, workflow_id, job2.id.unwrap());
let args = [
"results",
"list",
&workflow_id.to_string(),
"--limit",
"10",
"--all-runs",
];
let json_output =
run_cli_with_json(&args, start_server, None).expect("Failed to run results list command");
assert!(
json_output.is_object(),
"Results list should return an object"
);
assert!(
json_output.get("results").is_some(),
"Response should have 'results' field"
);
let results_array = json_output.get("results").unwrap().as_array().unwrap();
assert!(results_array.len() >= 2, "Should have at least 2 results");
for result in results_array {
assert!(result.get("id").is_some());
assert!(result.get("job_id").is_some());
assert!(result.get("workflow_id").is_some());
assert!(result.get("run_id").is_some());
assert!(result.get("return_code").is_some());
assert!(result.get("exec_time_minutes").is_some());
assert!(result.get("completion_time").is_some());
assert!(result.get("status").is_some());
}
}
#[rstest]
fn test_results_list_with_job_filter(start_server: &ServerProcess) {
let config = &start_server.config;
let workflow = create_test_workflow(config, "test_job_filter_workflow");
let workflow_id = workflow.id.unwrap();
let job1 = create_test_job(config, workflow_id, "filtered_job");
let job2 = create_test_job(config, workflow_id, "other_job");
let _result1 = create_test_result(config, workflow_id, job1.id.unwrap());
let _result2 = create_test_result(config, workflow_id, job2.id.unwrap());
let args = [
"results",
"list",
&workflow_id.to_string(),
"--job-id",
&job1.id.unwrap().to_string(),
"--all-runs",
];
let json_output = run_cli_with_json(&args, start_server, None)
.expect("Failed to run results list with job filter");
let results_array = json_output.get("results").unwrap().as_array().unwrap();
assert!(!results_array.is_empty());
for result in results_array {
assert_eq!(result.get("job_id").unwrap(), &json!(job1.id.unwrap()));
}
}
#[rstest]
fn test_results_get_command_json(start_server: &ServerProcess) {
let config = &start_server.config;
let workflow = create_test_workflow(config, "test_results_get_workflow");
let workflow_id = workflow.id.unwrap();
let job = create_test_job(config, workflow_id, "test_get_job");
let result = create_test_result(config, workflow_id, job.id.unwrap());
let result_id = result.id.unwrap();
let status_done = JobStatus::Completed.to_string();
let args = ["results", "get", &result_id.to_string()];
let json_output =
run_cli_with_json(&args, start_server, None).expect("Failed to run results get command");
assert_eq!(json_output.get("id").unwrap(), &json!(result_id));
assert_eq!(json_output.get("job_id").unwrap(), &json!(job.id.unwrap()));
assert_eq!(json_output.get("workflow_id").unwrap(), &json!(workflow_id));
assert_eq!(json_output.get("run_id").unwrap(), &json!(1));
assert_eq!(json_output.get("return_code").unwrap(), &json!(0));
assert_eq!(json_output.get("exec_time_minutes").unwrap(), &json!(5.5));
assert_eq!(json_output.get("status").unwrap(), &json!(status_done));
}
#[rstest]
fn test_results_delete_command_json(start_server: &ServerProcess) {
let config = &start_server.config;
let workflow = create_test_workflow(config, "test_results_remove_workflow");
let workflow_id = workflow.id.unwrap();
let job = create_test_job(config, workflow_id, "test_remove_job");
let result = create_test_result(config, workflow_id, job.id.unwrap());
let result_id = result.id.unwrap();
let args = ["results", "delete", &result_id.to_string()];
let json_output =
run_cli_with_json(&args, start_server, None).expect("Failed to run results delete command");
assert_eq!(json_output.get("id").unwrap(), &json!(result_id));
assert_eq!(json_output.get("job_id").unwrap(), &json!(job.id.unwrap()));
assert_eq!(json_output.get("workflow_id").unwrap(), &json!(workflow_id));
let get_result = apis::results_api::get_result(config, result_id);
assert!(get_result.is_err(), "Result should be deleted");
}
#[rstest]
fn test_results_list_pagination(start_server: &ServerProcess) {
let config = &start_server.config;
let workflow = create_test_workflow(config, "test_pagination_workflow");
let workflow_id = workflow.id.unwrap();
for i in 0..5 {
let job = create_test_job(config, workflow_id, &format!("pagination_job_{}", i));
let _result = create_test_result(config, workflow_id, job.id.unwrap());
}
let args = [
"results",
"list",
&workflow_id.to_string(),
"--limit",
"3",
"--all-runs",
];
let json_output =
run_cli_with_json(&args, start_server, None).expect("Failed to run paginated results list");
let results_array = json_output.get("results").unwrap().as_array().unwrap();
assert!(results_array.len() >= 3, "Should respect limit parameter");
let args_with_offset = [
"results",
"list",
&workflow_id.to_string(),
"--limit",
"2",
"--offset",
"2",
"--all-runs",
];
let json_output_offset = run_cli_with_json(&args_with_offset, start_server, None)
.expect("Failed to run results list with offset");
let results_with_offset = json_output_offset
.get("results")
.unwrap()
.as_array()
.unwrap();
assert!(
!results_with_offset.is_empty(),
"Should have results with offset"
);
}
#[rstest]
fn test_results_list_sorting(start_server: &ServerProcess) {
let config = &start_server.config;
let workflow = create_test_workflow(config, "test_sorting_workflow");
let workflow_id = workflow.id.unwrap();
for i in 0..3 {
let job = create_test_job(config, workflow_id, &format!("sort_job_{}", i));
let result = models::ResultModel::new(
job.id.unwrap(),
workflow_id,
1,
1, 1, i, 5.0,
"2024-01-01T12:00:00.000Z".to_string(),
models::JobStatus::Completed,
);
let _created =
apis::results_api::create_result(config, result).expect("Failed to create result");
}
let args = [
"results",
"list",
&workflow_id.to_string(),
"--sort-by",
"return_code",
"--reverse-sort",
"--all-runs",
];
let json_output =
run_cli_with_json(&args, start_server, None).expect("Failed to run sorted results list");
let results_array = json_output.get("results").unwrap().as_array().unwrap();
assert!(results_array.len() >= 3);
if results_array.len() >= 2 {
let first_return_code = results_array[0]
.get("return_code")
.unwrap()
.as_i64()
.unwrap();
let second_return_code = results_array[1]
.get("return_code")
.unwrap()
.as_i64()
.unwrap();
assert!(
first_return_code >= second_return_code,
"Results should be sorted in reverse order"
);
}
}
#[rstest]
fn test_results_list_with_return_code_filter(start_server: &ServerProcess) {
let config = &start_server.config;
let workflow = create_test_workflow(config, "test_return_code_filter_workflow");
let workflow_id = workflow.id.unwrap();
let job1 = create_test_job(config, workflow_id, "success_job");
let job2 = create_test_job(config, workflow_id, "failed_job");
let success_result = models::ResultModel::new(
job1.id.unwrap(),
workflow_id,
1,
1, 1, 0, 2.5,
"2024-01-01T10:00:00.000Z".to_string(),
models::JobStatus::Completed,
);
let _result1 = apis::results_api::create_result(config, success_result)
.expect("Failed to create success result");
let failed_result = models::ResultModel::new(
job2.id.unwrap(),
workflow_id,
1,
1, 1, 1, 3.5,
"2024-01-01T11:00:00.000Z".to_string(),
models::JobStatus::Terminated,
);
let _result2 = apis::results_api::create_result(config, failed_result)
.expect("Failed to create failed result");
let args = [
"results",
"list",
&workflow_id.to_string(),
"--return-code",
"0",
];
let json_output = run_cli_with_json(&args, start_server, None)
.expect("Failed to run results list command with return_code filter");
assert!(
json_output.is_object(),
"Results list should return an object"
);
assert!(
json_output.get("results").is_some(),
"Response should have 'results' field"
);
}
#[rstest]
fn test_results_list_with_status_filter(start_server: &ServerProcess) {
let config = &start_server.config;
let workflow = create_test_workflow(config, "test_status_filter_workflow");
let workflow_id = workflow.id.unwrap();
let job1 = create_test_job(config, workflow_id, "done_job");
let job2 = create_test_job(config, workflow_id, "terminated_job");
let done_result = models::ResultModel::new(
job1.id.unwrap(),
workflow_id,
1,
1, 1, 0,
2.5,
"2024-01-01T10:00:00.000Z".to_string(),
models::JobStatus::Completed,
);
let _result1 = apis::results_api::create_result(config, done_result)
.expect("Failed to create done result");
let terminated_result = models::ResultModel::new(
job2.id.unwrap(),
workflow_id,
1,
1, 1, 1,
3.5,
"2024-01-01T11:00:00.000Z".to_string(),
models::JobStatus::Terminated,
);
let _result2 = apis::results_api::create_result(config, terminated_result)
.expect("Failed to create terminated result");
let args = [
"results",
"list",
&workflow_id.to_string(),
"--status",
"completed",
];
let json_output = run_cli_with_json(&args, start_server, None)
.expect("Failed to run results list command with status filter");
assert!(
json_output.is_object(),
"Results list should return an object"
);
assert!(
json_output.get("results").is_some(),
"Response should have 'results' field"
);
}
#[rstest]
fn test_results_list_all_runs_default_behavior(start_server: &ServerProcess) {
let config = &start_server.config;
let workflow = create_test_workflow(config, "test_all_runs_default_workflow");
let workflow_id = workflow.id.unwrap();
let job1 = create_test_job(config, workflow_id, "job1_all_runs");
let job2 = create_test_job(config, workflow_id, "job2_all_runs");
let job1_id = job1.id.unwrap();
let job2_id = job2.id.unwrap();
let torc_config = TorcConfig::load().unwrap_or_default();
let workflow_manager = WorkflowManager::new(config.clone(), torc_config, workflow.clone());
workflow_manager
.initialize(true)
.expect("Failed to initialize workflow for run 1");
let status = apis::workflows_api::get_workflow_status(config, workflow_id)
.expect("Failed to get workflow status");
let run_id_1 = status.run_id;
apis::jobs_api::complete_job(
config,
job1_id,
models::JobStatus::Failed,
run_id_1,
models::ResultModel::new(
job1_id,
workflow_id,
run_id_1,
1, 1, 1, 2.5,
"2024-01-01T10:00:00.000Z".to_string(),
models::JobStatus::Failed,
),
)
.expect("Failed to complete job1 for run 1");
apis::jobs_api::complete_job(
config,
job2_id,
models::JobStatus::Failed,
run_id_1,
models::ResultModel::new(
job2_id,
workflow_id,
run_id_1,
1, 1, 1, 3.5,
"2024-01-01T11:00:00.000Z".to_string(),
models::JobStatus::Failed,
),
)
.expect("Failed to complete job2 for run 1");
apis::workflows_api::reset_job_status(config, workflow_id, Some(true))
.expect("Failed to reset job status");
workflow_manager
.reinitialize(false, false)
.expect("Failed to reinitialize workflow for run 2");
let status = apis::workflows_api::get_workflow_status(config, workflow_id)
.expect("Failed to get workflow status");
let run_id_2 = status.run_id;
apis::jobs_api::complete_job(
config,
job1_id,
models::JobStatus::Completed,
run_id_2,
models::ResultModel::new(
job1_id,
workflow_id,
run_id_2,
1, 1, 0,
4.5,
"2024-01-02T10:00:00.000Z".to_string(),
models::JobStatus::Completed,
),
)
.expect("Failed to complete job1 for run 2");
apis::jobs_api::complete_job(
config,
job2_id,
models::JobStatus::Terminated,
run_id_2,
models::ResultModel::new(
job2_id,
workflow_id,
run_id_2,
1, 1, 1, 5.5,
"2024-01-02T11:00:00.000Z".to_string(),
models::JobStatus::Terminated,
),
)
.expect("Failed to complete job2 for run 2");
let args = ["results", "list", &workflow_id.to_string()];
let json_output = run_cli_with_json(&args, start_server, None)
.expect("Failed to run results list command with default behavior");
assert!(
json_output.is_object(),
"Results list should return an object"
);
assert!(
json_output.get("results").is_some(),
"Response should have 'results' field"
);
let results_array = json_output.get("results").unwrap().as_array().unwrap();
assert_eq!(
results_array.len(),
2,
"Default behavior should only show current results (2 from run 2)"
);
for result in results_array {
assert_eq!(
result.get("run_id").unwrap(),
&json!(run_id_2),
"Default should only show results from latest run (run 2)"
);
}
let job_ids: Vec<i64> = results_array
.iter()
.map(|r| r.get("job_id").unwrap().as_i64().unwrap())
.collect();
assert!(job_ids.contains(&job1_id), "Should have result for job1");
assert!(job_ids.contains(&job2_id), "Should have result for job2");
}
#[rstest]
fn test_results_list_all_runs_true(start_server: &ServerProcess) {
let config = &start_server.config;
let workflow = create_test_workflow(config, "test_all_runs_true_workflow");
let workflow_id = workflow.id.unwrap();
let job1 = create_test_job(config, workflow_id, "job1_all_runs_true");
let job2 = create_test_job(config, workflow_id, "job2_all_runs_true");
let job1_id = job1.id.unwrap();
let job2_id = job2.id.unwrap();
apis::workflows_api::initialize_jobs(config, workflow_id, None, None)
.expect("Failed to initialize jobs for run 1");
let result1_run1 = models::ResultModel::new(
job1_id,
workflow_id,
1, 1, 1, 0,
2.5,
"2024-01-01T10:00:00.000Z".to_string(),
models::JobStatus::Completed,
);
apis::results_api::create_result(config, result1_run1)
.expect("Failed to create result1 for run 1");
let result2_run1 = models::ResultModel::new(
job2_id,
workflow_id,
1, 1, 1, 0,
3.5,
"2024-01-01T11:00:00.000Z".to_string(),
models::JobStatus::Completed,
);
apis::results_api::create_result(config, result2_run1)
.expect("Failed to create result2 for run 1");
apis::workflows_api::initialize_jobs(config, workflow_id, None, None)
.expect("Failed to reinitialize jobs for run 2");
let result1_run2 = models::ResultModel::new(
job1_id,
workflow_id,
2, 1, 1, 0,
4.5,
"2024-01-02T10:00:00.000Z".to_string(),
models::JobStatus::Completed,
);
apis::results_api::create_result(config, result1_run2)
.expect("Failed to create result1 for run 2");
let result2_run2 = models::ResultModel::new(
job2_id,
workflow_id,
2, 1, 1, 1,
5.5,
"2024-01-02T11:00:00.000Z".to_string(),
models::JobStatus::Terminated,
);
apis::results_api::create_result(config, result2_run2)
.expect("Failed to create result2 for run 2");
apis::workflows_api::initialize_jobs(config, workflow_id, None, None)
.expect("Failed to reinitialize jobs for run 3");
let result1_run3 = models::ResultModel::new(
job1_id,
workflow_id,
3, 1, 1, 0,
6.5,
"2024-01-03T10:00:00.000Z".to_string(),
models::JobStatus::Completed,
);
apis::results_api::create_result(config, result1_run3)
.expect("Failed to create result1 for run 3");
let args = ["results", "list", &workflow_id.to_string(), "--all-runs"];
let json_output = run_cli_with_json(&args, start_server, None)
.expect("Failed to run results list command with --all-runs");
assert!(
json_output.is_object(),
"Results list should return an object"
);
assert!(
json_output.get("results").is_some(),
"Response should have 'results' field"
);
let results_array = json_output.get("results").unwrap().as_array().unwrap();
assert_eq!(
results_array.len(),
5,
"With --all-runs should show all historical results (5 total)"
);
let mut run1_count = 0;
let mut run2_count = 0;
let mut run3_count = 0;
for result in results_array {
match result.get("run_id").unwrap().as_i64().unwrap() {
1 => run1_count += 1,
2 => run2_count += 1,
3 => run3_count += 1,
_ => panic!("Unexpected run_id in results"),
}
}
assert_eq!(run1_count, 2, "Should have 2 results from run 1");
assert_eq!(run2_count, 2, "Should have 2 results from run 2");
assert_eq!(run3_count, 1, "Should have 1 result from run 3");
}
#[rstest]
fn test_results_list_all_runs_with_filters(start_server: &ServerProcess) {
let config = &start_server.config;
let workflow = create_test_workflow(config, "test_all_runs_filters_workflow");
let workflow_id = workflow.id.unwrap();
let job1 = create_test_job(config, workflow_id, "job1_filters");
let job2 = create_test_job(config, workflow_id, "job2_filters");
let job1_id = job1.id.unwrap();
let job2_id = job2.id.unwrap();
apis::workflows_api::initialize_jobs(config, workflow_id, None, None)
.expect("Failed to initialize jobs for run 1");
let result1_run1 = models::ResultModel::new(
job1_id,
workflow_id,
1,
1, 1, 0,
2.5,
"2024-01-01T10:00:00.000Z".to_string(),
models::JobStatus::Completed,
);
apis::results_api::create_result(config, result1_run1)
.expect("Failed to create result1 for run 1");
let result2_run1 = models::ResultModel::new(
job2_id,
workflow_id,
1,
1, 1, 1,
3.5,
"2024-01-01T11:00:00.000Z".to_string(),
models::JobStatus::Terminated,
);
apis::results_api::create_result(config, result2_run1)
.expect("Failed to create result2 for run 1");
apis::workflows_api::initialize_jobs(config, workflow_id, None, None)
.expect("Failed to reinitialize jobs for run 2");
let result1_run2 = models::ResultModel::new(
job1_id,
workflow_id,
2,
1, 1, 0,
4.5,
"2024-01-02T10:00:00.000Z".to_string(),
models::JobStatus::Completed,
);
apis::results_api::create_result(config, result1_run2)
.expect("Failed to create result1 for run 2");
let result2_run2 = models::ResultModel::new(
job2_id,
workflow_id,
2,
1, 1, 0,
5.5,
"2024-01-02T11:00:00.000Z".to_string(),
models::JobStatus::Completed,
);
apis::results_api::create_result(config, result2_run2)
.expect("Failed to create result2 for run 2");
let args = [
"results",
"list",
&workflow_id.to_string(),
"--all-runs",
"--job-id",
&job1_id.to_string(),
];
let json_output = run_cli_with_json(&args, start_server, None)
.expect("Failed to run results list with --all-runs and job filter");
let results_array = json_output.get("results").unwrap().as_array().unwrap();
assert_eq!(
results_array.len(),
2,
"Should show 2 results for job1 across both runs"
);
for result in results_array {
assert_eq!(
result.get("job_id").unwrap(),
&json!(job1_id),
"All results should be for job1"
);
}
let args = [
"results",
"list",
&workflow_id.to_string(),
"--all-runs",
"--status",
"terminated",
];
let json_output = run_cli_with_json(&args, start_server, None)
.expect("Failed to run results list with --all-runs and status filter");
let results_array = json_output.get("results").unwrap().as_array().unwrap();
assert_eq!(
results_array.len(),
1,
"Should show 1 terminated result across all runs"
);
let args = [
"results",
"list",
&workflow_id.to_string(),
"--status",
"terminated",
];
let json_output = run_cli_with_json(&args, start_server, None)
.expect("Failed to run results list with status filter (no --all-runs)");
let results_array = json_output.get("results").unwrap().as_array().unwrap();
assert_eq!(
results_array.len(),
0,
"Default behavior should show 0 terminated results from current run (run 2)"
);
}
#[rstest]
fn test_results_workflow_result_table_cleanup_on_reinitialize(start_server: &ServerProcess) {
let config = &start_server.config;
let workflow = create_test_workflow(config, "test_workflow_result_cleanup");
let workflow_id = workflow.id.unwrap();
let job1 = create_test_job(config, workflow_id, "job1_cleanup");
let job2 = create_test_job(config, workflow_id, "job2_cleanup");
let job1_id = job1.id.unwrap();
let job2_id = job2.id.unwrap();
let torc_config = TorcConfig::load().unwrap_or_default();
let workflow_manager = WorkflowManager::new(config.clone(), torc_config, workflow.clone());
workflow_manager
.initialize(true)
.expect("Failed to initialize workflow for run 1");
let status = apis::workflows_api::get_workflow_status(config, workflow_id)
.expect("Failed to get workflow status");
let run_id = status.run_id;
apis::jobs_api::complete_job(
config,
job1_id,
models::JobStatus::Completed,
run_id,
models::ResultModel::new(
job1_id,
workflow_id,
run_id,
1, 1, 0,
2.5,
"2024-01-01T10:00:00.000Z".to_string(),
models::JobStatus::Completed,
),
)
.expect("Failed to complete job1 for run 1");
apis::jobs_api::complete_job(
config,
job2_id,
models::JobStatus::Completed,
run_id,
models::ResultModel::new(
job2_id,
workflow_id,
run_id,
1, 1, 0,
3.5,
"2024-01-01T11:00:00.000Z".to_string(),
models::JobStatus::Completed,
),
)
.expect("Failed to complete job2 for run 1");
let args = ["results", "list", &workflow_id.to_string()];
let json_output = run_cli_with_json(&args, start_server, None)
.expect("Failed to list results before reinitialize");
assert_eq!(
json_output
.get("results")
.unwrap()
.as_array()
.unwrap()
.len(),
2,
"Should have 2 current results before reinitialize"
);
apis::workflows_api::reset_job_status(config, workflow_id, Some(false))
.expect("Failed to reset job status");
workflow_manager
.reinitialize(false, false)
.expect("Failed to reinitialize workflow");
let json_output = run_cli_with_json(&args, start_server, None)
.expect("Failed to list results after reinitialize");
assert_eq!(
json_output
.get("results")
.unwrap()
.as_array()
.unwrap()
.len(),
0,
"Default list should show 0 results after reinitialize (workflow_result cleaned up)"
);
let args_all_runs = ["results", "list", &workflow_id.to_string(), "--all-runs"];
let json_output = run_cli_with_json(&args_all_runs, start_server, None)
.expect("Failed to list all results after reinitialize");
assert_eq!(
json_output
.get("results")
.unwrap()
.as_array()
.unwrap()
.len(),
2,
"With --all-runs should still show 2 historical results after reinitialize"
);
}