170 lines
5.9 KiB
Rust
170 lines
5.9 KiB
Rust
//! Unit tests for `filter_from_args`. Verifies the clap enums are faithfully translated into
|
|
//! `TaskListFilter` without dropping any fields.
|
|
|
|
use chrono::{TimeZone, Utc};
|
|
|
|
use warp_cli::json_filter::JsonOutput;
|
|
use warp_cli::task::{
|
|
ArtifactTypeArg, ExecutionLocationArg, ListTasksArgs, RunSortByArg, RunSortOrderArg,
|
|
RunSourceArg, RunStateArg,
|
|
};
|
|
|
|
use super::*;
|
|
use crate::server::server_api::ai::{ArtifactType, ExecutionLocation, RunSortBy, RunSortOrder};
|
|
|
|
/// A `ListTasksArgs` whose fields are all at their defaults.
|
|
fn empty_args() -> ListTasksArgs {
|
|
ListTasksArgs {
|
|
limit: 10,
|
|
state: vec![],
|
|
source: None,
|
|
execution_location: None,
|
|
creator: None,
|
|
environment: None,
|
|
skill: None,
|
|
schedule: None,
|
|
ancestor_run: None,
|
|
name: None,
|
|
model: None,
|
|
artifact_type: None,
|
|
created_after: None,
|
|
created_before: None,
|
|
updated_after: None,
|
|
query: None,
|
|
sort_by: None,
|
|
sort_order: None,
|
|
cursor: None,
|
|
json_output: JsonOutput::default(),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn empty_args_yields_default_filter() {
|
|
let filter = filter_from_args(&empty_args());
|
|
assert!(filter.creator_uid.is_none());
|
|
assert!(filter.updated_after.is_none());
|
|
assert!(filter.created_after.is_none());
|
|
assert!(filter.created_before.is_none());
|
|
assert!(filter.states.is_none());
|
|
assert!(filter.source.is_none());
|
|
assert!(filter.execution_location.is_none());
|
|
assert!(filter.environment_id.is_none());
|
|
assert!(filter.skill_spec.is_none());
|
|
assert!(filter.schedule_id.is_none());
|
|
assert!(filter.ancestor_run_id.is_none());
|
|
assert!(filter.config_name.is_none());
|
|
assert!(filter.model_id.is_none());
|
|
assert!(filter.artifact_type.is_none());
|
|
assert!(filter.search_query.is_none());
|
|
assert!(filter.sort_by.is_none());
|
|
assert!(filter.sort_order.is_none());
|
|
assert!(filter.cursor.is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn state_flags_map_to_filter() {
|
|
let args = ListTasksArgs {
|
|
state: vec![
|
|
RunStateArg::Failed,
|
|
RunStateArg::Error,
|
|
RunStateArg::Cancelled,
|
|
],
|
|
..empty_args()
|
|
};
|
|
let filter = filter_from_args(&args);
|
|
assert_eq!(
|
|
filter.states.as_deref(),
|
|
Some(
|
|
[
|
|
AmbientAgentTaskState::Failed,
|
|
AmbientAgentTaskState::Error,
|
|
AmbientAgentTaskState::Cancelled,
|
|
]
|
|
.as_slice()
|
|
)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn source_cli_maps_to_cli() {
|
|
let args = ListTasksArgs {
|
|
source: Some(RunSourceArg::Cli),
|
|
..empty_args()
|
|
};
|
|
let filter = filter_from_args(&args);
|
|
assert_eq!(filter.source, Some(AgentSource::Cli));
|
|
// Sanity-check the wire value: `--source CLI` must send `source=CLI`.
|
|
assert_eq!(filter.source.as_ref().map(AgentSource::as_str), Some("CLI"));
|
|
}
|
|
|
|
#[test]
|
|
fn source_interactive_maps_to_local() {
|
|
// The public API uses `LOCAL` as the source value for local interactive
|
|
// tasks. The CLI exposes this as `--source INTERACTIVE` for readability,
|
|
// but the request sent to the server must use `LOCAL`.
|
|
let args = ListTasksArgs {
|
|
source: Some(RunSourceArg::Interactive),
|
|
..empty_args()
|
|
};
|
|
let filter = filter_from_args(&args);
|
|
assert_eq!(filter.source, Some(AgentSource::Interactive));
|
|
assert_eq!(
|
|
filter.source.as_ref().map(AgentSource::as_str),
|
|
Some("LOCAL")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn every_field_maps_through() {
|
|
let created_after = Utc.with_ymd_and_hms(2026, 4, 1, 0, 0, 0).unwrap();
|
|
let created_before = Utc.with_ymd_and_hms(2026, 4, 2, 0, 0, 0).unwrap();
|
|
let updated_after = Utc.with_ymd_and_hms(2026, 4, 3, 12, 30, 0).unwrap();
|
|
|
|
let args = ListTasksArgs {
|
|
limit: 20,
|
|
state: vec![RunStateArg::InProgress],
|
|
source: Some(RunSourceArg::Api),
|
|
execution_location: Some(ExecutionLocationArg::Remote),
|
|
creator: Some("user-uid".to_string()),
|
|
environment: Some("env-123".to_string()),
|
|
skill: Some("owner/repo:SKILL.md".to_string()),
|
|
schedule: Some("sched-1".to_string()),
|
|
ancestor_run: Some("run-parent".to_string()),
|
|
name: Some("nightly".to_string()),
|
|
model: Some("claude-4-5".to_string()),
|
|
artifact_type: Some(ArtifactTypeArg::PullRequest),
|
|
created_after: Some(created_after),
|
|
created_before: Some(created_before),
|
|
updated_after: Some(updated_after),
|
|
query: Some("oz run".to_string()),
|
|
sort_by: Some(RunSortByArg::CreatedAt),
|
|
sort_order: Some(RunSortOrderArg::Asc),
|
|
cursor: Some("abcd==".to_string()),
|
|
json_output: JsonOutput::default(),
|
|
};
|
|
|
|
let filter = filter_from_args(&args);
|
|
|
|
assert_eq!(filter.creator_uid.as_deref(), Some("user-uid"));
|
|
assert_eq!(filter.updated_after, Some(updated_after));
|
|
assert_eq!(filter.created_after, Some(created_after));
|
|
assert_eq!(filter.created_before, Some(created_before));
|
|
assert_eq!(
|
|
filter.states.as_deref(),
|
|
Some([AmbientAgentTaskState::InProgress].as_slice())
|
|
);
|
|
assert_eq!(filter.source, Some(AgentSource::AgentWebhook));
|
|
assert_eq!(filter.execution_location, Some(ExecutionLocation::Remote));
|
|
assert_eq!(filter.environment_id.as_deref(), Some("env-123"));
|
|
assert_eq!(filter.skill_spec.as_deref(), Some("owner/repo:SKILL.md"));
|
|
assert_eq!(filter.schedule_id.as_deref(), Some("sched-1"));
|
|
assert_eq!(filter.ancestor_run_id.as_deref(), Some("run-parent"));
|
|
assert_eq!(filter.config_name.as_deref(), Some("nightly"));
|
|
assert_eq!(filter.model_id.as_deref(), Some("claude-4-5"));
|
|
assert_eq!(filter.artifact_type, Some(ArtifactType::PullRequest));
|
|
assert_eq!(filter.search_query.as_deref(), Some("oz run"));
|
|
assert_eq!(filter.sort_by, Some(RunSortBy::CreatedAt));
|
|
assert_eq!(filter.sort_order, Some(RunSortOrder::Asc));
|
|
assert_eq!(filter.cursor.as_deref(), Some("abcd=="));
|
|
}
|