Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions src/graph/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,4 +347,52 @@ topic main:
// At least one edge should exist (the Routes edge from start_agent → main)
assert!(graph.edge_count() > 0, "Expected at least one edge in the graph");
}

#[test]
fn test_find_usages_of_transition_target_topic() {
// topic_a has a TransitionsTo edge → topic_b.
// find_usages(topic_b_idx) must include topic_a as a source of that edge,
// because any node whose outgoing edge terminates at topic_b "uses" topic_b.
let graph = parse_and_build(two_topic_source());
let topic_a_idx = graph.get_topic("topic_a").expect("topic_a not found");
let topic_b_idx = graph.get_topic("topic_b").expect("topic_b not found");

let usages = graph.find_usages(topic_b_idx);
assert!(
usages.nodes.contains(&topic_a_idx),
"Expected topic_a to appear as a user of topic_b via TransitionsTo edge"
);
}

#[test]
fn test_find_usages_of_topic_routed_from_start_agent() {
// start_agent routes to topic_a via a Routes edge.
// find_usages(topic_a_idx) must include the start_agent node as a source,
// because start_agent has an outgoing Routes edge terminating at topic_a.
let graph = parse_and_build(two_topic_source());
let topic_a_idx = graph.get_topic("topic_a").expect("topic_a not found");
let start_agent_idx = graph.get_start_agent().expect("start_agent not found");

let usages = graph.find_usages(topic_a_idx);
assert!(
usages.nodes.contains(&start_agent_idx),
"Expected start_agent to appear as a user of topic_a via Routes edge"
);
}

#[test]
fn test_find_dependencies_of_start_agent_returns_routed_topic() {
// start_agent routes to topic_a via a Routes edge.
// find_dependencies(start_agent_idx) must include topic_a as a dependency,
// because start_agent has an outgoing Routes edge pointing at topic_a.
let graph = parse_and_build(two_topic_source());
let topic_a_idx = graph.get_topic("topic_a").expect("topic_a not found");
let start_agent_idx = graph.get_start_agent().expect("start_agent not found");

let deps = graph.find_dependencies(start_agent_idx);
assert!(
deps.nodes.contains(&topic_a_idx),
"Expected topic_a to appear as a dependency of start_agent via Routes edge"
);
}
}
124 changes: 124 additions & 0 deletions tests/test_serializer_roundtrip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,3 +216,127 @@ topic main:
assert!(topic.before_reasoning.is_some(), "before_reasoning lost after roundtrip");
assert!(topic.after_reasoning.is_some(), "after_reasoning lost after roundtrip");
}

#[test]
fn test_roundtrip_start_agent_basic() {
// Covers the `start_agent` block — the serializer writes it but no roundtrip
// test existed. Verifies that the name and reasoning instructions survive a
// parse → serialize → parse cycle.
let original = r#"config:
agent_name: "TestAgent"

start_agent topic_selector:
description: "Routes customers to the appropriate topic"
reasoning:
instructions: "Analyze the customer request and select the most relevant topic"
"#;

let ast = parse(original).expect("Failed to parse original");
let serialized = serialize(&ast);

assert!(
serialized.contains("start_agent topic_selector:"),
"start_agent header missing from serialized output"
);
assert!(
serialized.contains("Routes customers"),
"start_agent description missing from serialized output"
);

let reparsed = parse(&serialized).expect("Failed to reparse serialized");
assert!(reparsed.start_agent.is_some(), "start_agent block lost after roundtrip");
let sa = &reparsed.start_agent.as_ref().unwrap().node;
assert_eq!(sa.name.node, "topic_selector", "start_agent name changed after roundtrip");
assert!(sa.reasoning.is_some(), "reasoning block lost from start_agent after roundtrip");
}

#[test]
fn test_roundtrip_start_agent_with_reasoning_actions() {
// Verifies that multiple reasoning actions inside `start_agent` survive a
// parse → serialize → parse cycle without being dropped or duplicated.
let original = r#"config:
agent_name: "TestAgent"

start_agent topic_selector:
description: "Routes customers"
reasoning:
instructions: "Select the best topic"
actions:
go_help: @utils.transition to @topic.help
description: "Route to help"
go_orders: @utils.transition to @topic.orders
description: "Route to orders"

topic help:
description: "Help"

topic orders:
description: "Orders"
"#;

let ast = parse(original).expect("Failed to parse original");
let serialized = serialize(&ast);
let reparsed = parse(&serialized).expect("Failed to reparse serialized");

let sa = reparsed
.start_agent
.as_ref()
.expect("start_agent lost after roundtrip");
let reasoning = sa.node.reasoning.as_ref().expect("reasoning lost from start_agent");
let actions = reasoning
.node
.actions
.as_ref()
.expect("reasoning actions lost from start_agent");
assert_eq!(
actions.node.len(),
2,
"Expected 2 reasoning actions in start_agent after roundtrip, got {}",
actions.node.len()
);
}

#[test]
fn test_roundtrip_system_block_with_messages() {
// Covers the top-level `system:` block with a `messages:` sub-block.
// The serializer writes messages but no roundtrip test existed. Verifies
// that the welcome and error messages survive a parse → serialize → parse cycle.
let original = r#"config:
agent_name: "TestAgent"

system:
messages:
welcome: "Welcome to our service!"
error: "Something went wrong."
instructions: "You are a helpful AI assistant."

topic main:
description: "Main topic"
"#;

let ast = parse(original).expect("Failed to parse original");
let serialized = serialize(&ast);

assert!(serialized.contains("system:"), "system block missing from serialized output");
assert!(serialized.contains("messages:"), "messages sub-block missing");
assert!(
serialized.contains("Welcome to our service!"),
"welcome message missing from serialized output"
);

let reparsed = parse(&serialized).expect("Failed to reparse serialized");
let sys = reparsed
.system
.as_ref()
.expect("system block lost after roundtrip");
let msgs = sys
.node
.messages
.as_ref()
.expect("messages sub-block lost after roundtrip");
let welcome = msgs.node.welcome.as_ref().expect("welcome message lost after roundtrip");
assert_eq!(
welcome.node, "Welcome to our service!",
"welcome message content changed after roundtrip"
);
}