|
1 | | -use asap_planner::{ElasticController, ElasticRuntimeOptions, PlannerOutput, StreamingEngine}; |
| 1 | +use asap_planner::{ |
| 2 | + ControllerError, ElasticController, ElasticRuntimeOptions, PlannerOutput, StreamingEngine, |
| 3 | +}; |
2 | 4 | use asap_types::{QueryLanguage, SchemaConfig}; |
3 | 5 | use std::io::Write; |
4 | 6 | use std::path::Path; |
@@ -41,6 +43,23 @@ fn elastic_output(index: &str, time_field: &str, query: &str, t_repeat_ms: u64) |
41 | 43 | elastic_output_with_interval(index, time_field, query, t_repeat_ms, 15_000) |
42 | 44 | } |
43 | 45 |
|
| 46 | +fn try_elastic_with_interval( |
| 47 | + index: &str, |
| 48 | + time_field: &str, |
| 49 | + query: &str, |
| 50 | + t_repeat_ms: u64, |
| 51 | + data_ingestion_interval_ms: u64, |
| 52 | +) -> Result<PlannerOutput, ControllerError> { |
| 53 | + let yaml = elastic_yaml(index, time_field, query, t_repeat_ms); |
| 54 | + let mut file = NamedTempFile::new().unwrap(); |
| 55 | + file.write_all(yaml.as_bytes()).unwrap(); |
| 56 | + let opts = ElasticRuntimeOptions { |
| 57 | + streaming_engine: StreamingEngine::Arroyo, |
| 58 | + data_ingestion_interval_ms, |
| 59 | + }; |
| 60 | + ElasticController::from_file(Path::new(file.path()), opts)?.generate() |
| 61 | +} |
| 62 | + |
44 | 63 | fn elastic_output_with_interval( |
45 | 64 | index: &str, |
46 | 65 | time_field: &str, |
@@ -493,3 +512,78 @@ fn sub_second_repetition_delay_ms() { |
493 | 512 | assert_eq!(out.streaming_aggregation_count(), 2); |
494 | 513 | assert!(out.all_tumbling_window_sizes_eq(500)); |
495 | 514 | } |
| 515 | + |
| 516 | +// ── time-range validation ───────────────────────────────────────────────────── |
| 517 | + |
| 518 | +fn time_range_query(duration: &str) -> String { |
| 519 | + format!( |
| 520 | + r#"{{ |
| 521 | + "aggs": {{ "sum_cpu": {{ "sum": {{ "field": "cpu_usage" }} }} }}, |
| 522 | + "query": {{ "bool": {{ "filter": [{{ "range": {{ "@timestamp": {{ "gte": "now-{duration}", "lte": "now" }} }} }}] }} }} |
| 523 | +}}"# |
| 524 | + ) |
| 525 | +} |
| 526 | + |
| 527 | +const QUERY_NO_TIME_RANGE: &str = r#"{ |
| 528 | + "aggs": { "sum_cpu": { "sum": { "field": "cpu_usage" } } } |
| 529 | +}"#; |
| 530 | + |
| 531 | +#[test] |
| 532 | +fn no_time_range_predicate_is_rejected() { |
| 533 | + let result = try_elastic_with_interval( |
| 534 | + "metrics", |
| 535 | + "\"@timestamp\"", |
| 536 | + QUERY_NO_TIME_RANGE, |
| 537 | + 300_000, |
| 538 | + 15_000, |
| 539 | + ); |
| 540 | + assert!(matches!( |
| 541 | + result, |
| 542 | + Err(ControllerError::UnsupportedElasticDSLQuery(_)) |
| 543 | + )); |
| 544 | +} |
| 545 | + |
| 546 | +#[test] |
| 547 | +fn time_range_shorter_than_ingestion_interval_is_rejected() { |
| 548 | + // data_ingestion_interval_ms = 15_000ms (15s), query range = 5s → duration < interval |
| 549 | + let result = try_elastic_with_interval( |
| 550 | + "metrics", |
| 551 | + "\"@timestamp\"", |
| 552 | + &time_range_query("5s"), |
| 553 | + 300_000, |
| 554 | + 15_000, |
| 555 | + ); |
| 556 | + assert!(matches!( |
| 557 | + result, |
| 558 | + Err(ControllerError::UnsupportedElasticDSLQuery(_)) |
| 559 | + )); |
| 560 | +} |
| 561 | + |
| 562 | +#[test] |
| 563 | +fn time_range_equal_to_ingestion_interval_uses_interval_as_window_size() { |
| 564 | + // data_ingestion_interval_ms = 15_000ms (15s), query range = 15s → Spatial: window = interval |
| 565 | + let out = try_elastic_with_interval( |
| 566 | + "metrics", |
| 567 | + "\"@timestamp\"", |
| 568 | + &time_range_query("15s"), |
| 569 | + 300_000, |
| 570 | + 15_000, |
| 571 | + ) |
| 572 | + .unwrap(); |
| 573 | + assert!(out.all_tumbling_window_sizes_eq(15_000)); |
| 574 | +} |
| 575 | + |
| 576 | +#[test] |
| 577 | +fn time_range_longer_than_ingestion_interval_uses_t_repeat_as_window_size() { |
| 578 | + // data_ingestion_interval_ms = 15_000ms (15s), query range = 5m > 15s → Temporal: window = t_repeat_ms |
| 579 | + let t_repeat_ms = 300_000; |
| 580 | + let out = try_elastic_with_interval( |
| 581 | + "metrics", |
| 582 | + "\"@timestamp\"", |
| 583 | + &time_range_query("5m"), |
| 584 | + t_repeat_ms, |
| 585 | + 15_000, |
| 586 | + ) |
| 587 | + .unwrap(); |
| 588 | + assert!(out.all_tumbling_window_sizes_eq(t_repeat_ms)); |
| 589 | +} |
0 commit comments