use nika::binding::{
parse_binding_entry, template_resolve, validate_task_id, BindingSpec, ResolvedBindings,
};
use nika::serde_yaml;
use nika::store::{RunContext, TaskResult};
use serde_json::json;
use std::sync::Arc;
use std::time::Duration;
#[test]
fn full_workflow_simple_path() {
let entry = parse_binding_entry("weather.summary").unwrap();
assert_eq!(entry.path, "weather.summary");
assert!(entry.default.is_none());
let mut spec = BindingSpec::default();
spec.insert("forecast".to_string(), entry);
let store = RunContext::new();
store.insert(
Arc::from("weather"),
TaskResult::success(
json!({"summary": "Sunny", "temp": 25}),
Duration::from_secs(1),
),
);
let bindings = ResolvedBindings::from_binding_spec(Some(&spec), &store).unwrap();
assert_eq!(bindings.get("forecast"), Some(&json!("Sunny")));
let template = "Weather: {{with.forecast}}";
let result = template_resolve(template, &bindings, &store).unwrap();
assert_eq!(result, "Weather: Sunny");
}
#[test]
fn full_workflow_with_default() {
let entry = parse_binding_entry(r#"weather.rating ?? 5"#).unwrap();
assert_eq!(entry.path, "weather.rating");
assert_eq!(entry.default, Some(json!(5)));
let mut spec = BindingSpec::default();
spec.insert("rating".to_string(), entry);
let store = RunContext::new();
store.insert(
Arc::from("weather"),
TaskResult::success(json!({"summary": "Sunny"}), Duration::from_secs(1)),
);
let bindings = ResolvedBindings::from_binding_spec(Some(&spec), &store).unwrap();
assert_eq!(bindings.get("rating"), Some(&json!(5)));
let result = template_resolve("Rating: {{with.rating}}/5", &bindings, &store).unwrap();
assert_eq!(result, "Rating: 5/5");
}
#[test]
fn full_workflow_nested_path() {
let entry = parse_binding_entry("flights.cheapest.price").unwrap();
let mut spec = BindingSpec::default();
spec.insert("price".to_string(), entry);
let store = RunContext::new();
store.insert(
Arc::from("flights"),
TaskResult::success(
json!({"cheapest": {"price": 89, "airline": "Ryanair"}}),
Duration::from_secs(1),
),
);
let bindings = ResolvedBindings::from_binding_spec(Some(&spec), &store).unwrap();
assert_eq!(bindings.get("price"), Some(&json!(89)));
let result = template_resolve("Price: ${{with.price}}", &bindings, &store).unwrap();
assert_eq!(result, "Price: $89");
}
#[test]
fn full_workflow_multiple_aliases() {
let mut spec = BindingSpec::default();
spec.insert(
"city".to_string(),
parse_binding_entry("weather.city").unwrap(),
);
spec.insert(
"temp".to_string(),
parse_binding_entry("weather.temp ?? 20").unwrap(),
);
spec.insert(
"price".to_string(),
parse_binding_entry("flights.cheapest.price").unwrap(),
);
let store = RunContext::new();
store.insert(
Arc::from("weather"),
TaskResult::success(json!({"city": "Paris", "temp": 25}), Duration::from_secs(1)),
);
store.insert(
Arc::from("flights"),
TaskResult::success(json!({"cheapest": {"price": 89}}), Duration::from_secs(1)),
);
let bindings = ResolvedBindings::from_binding_spec(Some(&spec), &store).unwrap();
let template = "Travel to {{with.city}}: {{with.temp}}C, ${{with.price}}";
let result = template_resolve(template, &bindings, &store).unwrap();
assert_eq!(result, "Travel to Paris: 25C, $89");
}
#[test]
fn full_workflow_string_default() {
let entry = parse_binding_entry(r#"user.name ?? "Anonymous""#).unwrap();
assert_eq!(entry.default, Some(json!("Anonymous")));
let mut spec = BindingSpec::default();
spec.insert("name".to_string(), entry);
let store = RunContext::new();
let bindings = ResolvedBindings::from_binding_spec(Some(&spec), &store);
assert!(bindings.is_ok());
assert_eq!(bindings.unwrap().get("name"), Some(&json!("Anonymous")));
}
#[test]
fn full_workflow_object_default() {
let entry = parse_binding_entry(r#"settings ?? {"debug": false}"#).unwrap();
assert_eq!(entry.default, Some(json!({"debug": false})));
let mut spec = BindingSpec::default();
spec.insert("config".to_string(), entry);
let store = RunContext::new();
let bindings = ResolvedBindings::from_binding_spec(Some(&spec), &store).unwrap();
assert_eq!(bindings.get("config"), Some(&json!({"debug": false})));
}
#[test]
fn error_task_not_found_no_default() {
let entry = parse_binding_entry("missing.data").unwrap();
let mut spec = BindingSpec::default();
spec.insert("x".to_string(), entry);
let store = RunContext::new();
let result = ResolvedBindings::from_binding_spec(Some(&spec), &store);
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("NIKA-052") || err.contains("not found"));
}
#[test]
fn error_path_not_found_no_default() {
let entry = parse_binding_entry("weather.nonexistent").unwrap();
let mut spec = BindingSpec::default();
spec.insert("x".to_string(), entry);
let store = RunContext::new();
store.insert(
Arc::from("weather"),
TaskResult::success(json!({"summary": "Sunny"}), Duration::from_secs(1)),
);
let result = ResolvedBindings::from_binding_spec(Some(&spec), &store);
assert!(result.is_err());
}
#[test]
fn error_null_value_no_default() {
let entry = parse_binding_entry("weather.temp").unwrap();
let mut spec = BindingSpec::default();
spec.insert("temp".to_string(), entry);
let store = RunContext::new();
store.insert(
Arc::from("weather"),
TaskResult::success(json!({"temp": null}), Duration::from_secs(1)),
);
let result = ResolvedBindings::from_binding_spec(Some(&spec), &store);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("NIKA-072"));
}
#[test]
fn error_template_unknown_alias() {
let bindings = ResolvedBindings::new();
let store = RunContext::new();
let result = template_resolve("Hello {{with.unknown}}", &bindings, &store);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("unknown") || err_msg.contains("not resolved"));
}
#[test]
fn task_id_validation_in_workflow() {
assert!(validate_task_id("weather").is_ok());
assert!(validate_task_id("get_data").is_ok());
assert!(validate_task_id("task123").is_ok());
let err = validate_task_id("fetch-api").unwrap_err();
assert!(err.to_string().contains("NIKA-055"));
let err = validate_task_id("myTask").unwrap_err();
assert!(err.to_string().contains("NIKA-055"));
let err = validate_task_id("weather.api").unwrap_err();
assert!(err.to_string().contains("NIKA-055"));
}
#[test]
fn yaml_to_bindings_full_workflow() {
let yaml = r#"
forecast: weather.summary
temp: weather.temp ?? 20
name: 'user.name ?? "Guest"'
"#;
let spec: BindingSpec = serde_yaml::from_str(yaml).unwrap();
assert_eq!(spec.len(), 3);
let forecast = spec.get("forecast").unwrap();
assert_eq!(forecast.path, "weather.summary");
assert!(forecast.default.is_none());
let temp = spec.get("temp").unwrap();
assert_eq!(temp.path, "weather.temp");
assert_eq!(temp.default, Some(json!(20)));
let name = spec.get("name").unwrap();
assert_eq!(name.path, "user.name");
assert_eq!(name.default, Some(json!("Guest")));
let store = RunContext::new();
store.insert(
Arc::from("weather"),
TaskResult::success(
json!({"summary": "Rainy", "temp": 15}),
Duration::from_secs(1),
),
);
store.insert(
Arc::from("user"),
TaskResult::success(json!({"name": "Alice"}), Duration::from_secs(1)),
);
let bindings = ResolvedBindings::from_binding_spec(Some(&spec), &store).unwrap();
assert_eq!(bindings.get("forecast"), Some(&json!("Rainy")));
assert_eq!(bindings.get("temp"), Some(&json!(15)));
assert_eq!(bindings.get("name"), Some(&json!("Alice")));
}
#[test]
fn edge_case_empty_template() {
let bindings = ResolvedBindings::new();
let store = RunContext::new();
let result = template_resolve("", &bindings, &store).unwrap();
assert_eq!(result, "");
}
#[test]
fn edge_case_no_templates() {
let bindings = ResolvedBindings::new();
let store = RunContext::new();
let result = template_resolve("Hello world!", &bindings, &store).unwrap();
assert_eq!(result, "Hello world!");
}
#[test]
fn edge_case_entire_task_output() {
let entry = parse_binding_entry("weather").unwrap();
assert_eq!(entry.path, "weather");
let mut spec = BindingSpec::default();
spec.insert("data".to_string(), entry);
let store = RunContext::new();
store.insert(
Arc::from("weather"),
TaskResult::success(
json!({"summary": "Sunny", "temp": 25}),
Duration::from_secs(1),
),
);
let bindings = ResolvedBindings::from_binding_spec(Some(&spec), &store).unwrap();
assert_eq!(
bindings.get("data"),
Some(&json!({"summary": "Sunny", "temp": 25}))
);
}
#[test]
fn edge_case_array_index() {
let entry = parse_binding_entry("results.items[0].name").unwrap();
let mut spec = BindingSpec::default();
spec.insert("first".to_string(), entry);
let store = RunContext::new();
store.insert(
Arc::from("results"),
TaskResult::success(
json!({"items": [{"name": "Alpha"}, {"name": "Beta"}]}),
Duration::from_secs(1),
),
);
let bindings = ResolvedBindings::from_binding_spec(Some(&spec), &store).unwrap();
assert_eq!(bindings.get("first"), Some(&json!("Alpha")));
}
#[test]
fn edge_case_default_with_special_chars() {
let entry = parse_binding_entry(r#"x ?? "What?? Really??""#).unwrap();
assert_eq!(entry.default, Some(json!("What?? Really??")));
}