mod common;
use common::run_kelora_with_input;
#[test]
fn test_discover_json_profiles_nested_input_fields() {
let input = r#"{"level":"info","user":{"name":"alice","roles":["admin","ops"]},"bytes":1536}
{"level":"error","user":{"name":"bob","roles":["ops"]},"bytes":2048,"extra":{"nested":{"x":1}}}"#;
let (stdout, stderr, exit_code) =
run_kelora_with_input(&["-f", "json", "--discover=json"], input);
assert_eq!(exit_code, 0, "discover should succeed: {}", stderr);
assert!(
stderr.is_empty(),
"discover json mode should not emit stderr on success: {}",
stderr
);
let doc: serde_json::Value =
serde_json::from_str(&stdout).expect("discover output should be valid json");
assert_eq!(doc["total_events"], 2);
assert_eq!(doc["flatten_depth_limit"], 3);
assert_eq!(doc["flatten_depth_capped"], false);
assert_eq!(doc["truncated"], false);
let fields = doc["fields"].as_array().expect("fields should be an array");
let names: Vec<&str> = fields
.iter()
.map(|field| {
field["name"]
.as_str()
.expect("field name should be a string")
})
.collect();
assert!(
names.contains(&"level"),
"should include top-level fields: {:?}",
names
);
assert!(
names.contains(&"user.name"),
"should flatten nested maps: {:?}",
names
);
assert!(
names.contains(&"user.roles[]"),
"should flatten array elements: {:?}",
names
);
assert!(
names.contains(&"extra.nested.x"),
"should flatten nested fields up to depth limit: {:?}",
names
);
let roles = fields
.iter()
.find(|field| field["name"] == "user.roles[]")
.expect("user.roles[] field should exist");
assert_eq!(
roles["seen"], 3,
"array entries should be counted individually"
);
assert_eq!(roles["cardinality"]["count"], 2);
let bytes = fields
.iter()
.find(|field| field["name"] == "bytes")
.expect("bytes field should exist");
assert_eq!(bytes["samples"][0], 1536);
}
#[test]
fn test_discover_final_profiles_post_filter_post_exec_fields() {
let input = r#"{"level":"info","keep":true,"bytes":1536}
{"level":"error","keep":false,"bytes":2048}"#;
let (stdout, stderr, exit_code) = run_kelora_with_input(
&[
"-f",
"json",
"--filter",
"e.keep",
"--exec",
r#"e.pretty = human_bytes(e.bytes); e = e.keep(["level","pretty"])"#,
"--discover-final=json",
],
input,
);
assert_eq!(exit_code, 0, "discover final should succeed: {}", stderr);
assert!(
stderr.is_empty(),
"discover final should not emit stderr on success: {}",
stderr
);
let doc: serde_json::Value =
serde_json::from_str(&stdout).expect("discover output should be valid json");
assert_eq!(
doc["total_events"], 1,
"filter should run before output discovery"
);
let fields = doc["fields"].as_array().expect("fields should be an array");
let names: Vec<&str> = fields
.iter()
.map(|field| {
field["name"]
.as_str()
.expect("field name should be a string")
})
.collect();
assert_eq!(names.len(), 2, "only projected output fields should remain");
assert!(
names.contains(&"level"),
"output discovery should keep level"
);
assert!(
names.contains(&"pretty"),
"output discovery should see exec output"
);
assert!(
!names.contains(&"bytes") && !names.contains(&"keep"),
"output discovery should not report filtered/projection-only input fields: {:?}",
names
);
let pretty = fields
.iter()
.find(|field| field["name"] == "pretty")
.expect("pretty field should exist");
assert_eq!(pretty["samples"][0], "1.5 KiB");
}
#[test]
fn test_discover_rejects_parallel_mode_at_cli_validation() {
let input = r#"{"level":"info"}"#;
let (stdout, stderr, exit_code) =
run_kelora_with_input(&["-f", "json", "--parallel", "--discover"], input);
assert_eq!(
exit_code, 2,
"parallel discover should be a CLI usage error"
);
assert!(
stdout.is_empty(),
"validation errors should not emit stdout"
);
assert!(
stderr.contains("--discover and --discover-final are not supported with --parallel"),
"expected clear validation message, got: {}",
stderr
);
}
#[test]
fn test_discover_and_discover_final_conflict() {
let (stdout, stderr, exit_code) = run_kelora_with_input(
&["-f", "json", "--discover", "--discover-final"],
r#"{"x":1}"#,
);
assert_eq!(
exit_code, 2,
"conflicting discover flags should be usage errors"
);
assert!(
stdout.is_empty(),
"validation errors should not emit stdout"
);
assert!(
stderr.contains("--discover") && stderr.contains("--discover-final"),
"expected conflict message to mention both flags, got: {}",
stderr
);
}