use super::*;
#[tokio::test]
async fn test_add_relationship_success() {
let memory = create_test_memory();
let tools = EpisodeRelationshipTools::new(memory.clone());
let ep1 = create_test_episode(&memory, "Test episode 1").await;
let ep2 = create_test_episode(&memory, "Test episode 2").await;
let input = AddEpisodeRelationshipInput {
from_episode_id: ep1.to_string(),
to_episode_id: ep2.to_string(),
relationship_type: "depends_on".to_string(),
reason: Some("Test reason".to_string()),
priority: Some(5),
created_by: Some("test".to_string()),
};
let result = tools.add_relationship(input).await;
assert!(result.is_ok());
let output = result.unwrap();
assert!(output.success);
assert!(!output.relationship_id.is_empty());
assert_eq!(output.from_episode_id, ep1.to_string());
assert_eq!(output.to_episode_id, ep2.to_string());
assert_eq!(output.relationship_type, "depends_on");
}
#[tokio::test]
async fn test_add_relationship_invalid_uuid() {
let memory = create_test_memory();
let tools = EpisodeRelationshipTools::new(memory);
let input = AddEpisodeRelationshipInput {
from_episode_id: "invalid-uuid".to_string(),
to_episode_id: uuid::Uuid::new_v4().to_string(),
relationship_type: "depends_on".to_string(),
reason: None,
priority: None,
created_by: None,
};
let result = tools.add_relationship(input).await;
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("Invalid from_episode_id")
);
}
#[tokio::test]
async fn test_add_relationship_invalid_type() {
let memory = create_test_memory();
let tools = EpisodeRelationshipTools::new(memory);
let input = AddEpisodeRelationshipInput {
from_episode_id: uuid::Uuid::new_v4().to_string(),
to_episode_id: uuid::Uuid::new_v4().to_string(),
relationship_type: "invalid_type".to_string(),
reason: None,
priority: None,
created_by: None,
};
let result = tools.add_relationship(input).await;
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("Invalid relationship_type")
);
}
#[tokio::test]
async fn test_remove_relationship_success() {
let memory = create_test_memory();
let tools = EpisodeRelationshipTools::new(memory.clone());
let ep1 = create_test_episode(&memory, "Test episode 1").await;
let ep2 = create_test_episode(&memory, "Test episode 2").await;
let add_input = AddEpisodeRelationshipInput {
from_episode_id: ep1.to_string(),
to_episode_id: ep2.to_string(),
relationship_type: "depends_on".to_string(),
reason: None,
priority: None,
created_by: None,
};
let add_result = tools.add_relationship(add_input).await.unwrap();
let rel_id = add_result.relationship_id;
let remove_input = RemoveEpisodeRelationshipInput {
relationship_id: rel_id.clone(),
};
let result = tools.remove_relationship(remove_input).await;
assert!(result.is_ok());
let output = result.unwrap();
assert!(output.success);
assert_eq!(output.relationship_id, rel_id);
}
#[tokio::test]
async fn test_remove_relationship_invalid_uuid() {
let memory = create_test_memory();
let tools = EpisodeRelationshipTools::new(memory);
let input = RemoveEpisodeRelationshipInput {
relationship_id: "invalid-uuid".to_string(),
};
let result = tools.remove_relationship(input).await;
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("Invalid relationship_id")
);
}
#[tokio::test]
async fn test_get_relationships_empty() {
let memory = create_test_memory();
let tools = EpisodeRelationshipTools::new(memory.clone());
let ep = create_test_episode(&memory, "Test episode").await;
let input = GetEpisodeRelationshipsInput {
episode_id: ep.to_string(),
direction: None,
relationship_type: None,
};
let result = tools.get_relationships(input).await;
assert!(result.is_ok());
let output = result.unwrap();
assert!(output.success);
assert!(output.outgoing.is_empty());
assert!(output.incoming.is_empty());
assert_eq!(output.total_count, 0);
}
#[tokio::test]
async fn test_get_relationships_with_relationships() {
let memory = create_test_memory();
let tools = EpisodeRelationshipTools::new(memory.clone());
let ep1 = create_test_episode(&memory, "Test episode 1").await;
let ep2 = create_test_episode(&memory, "Test episode 2").await;
let add_input = AddEpisodeRelationshipInput {
from_episode_id: ep1.to_string(),
to_episode_id: ep2.to_string(),
relationship_type: "depends_on".to_string(),
reason: Some("Test dependency".to_string()),
priority: Some(8),
created_by: None,
};
tools.add_relationship(add_input).await.unwrap();
let input = GetEpisodeRelationshipsInput {
episode_id: ep1.to_string(),
direction: Some("outgoing".to_string()),
relationship_type: None,
};
let result = tools.get_relationships(input).await;
assert!(result.is_ok());
let output = result.unwrap();
assert!(output.success);
assert_eq!(output.outgoing.len(), 1);
assert_eq!(output.outgoing[0].relationship_type, "depends_on");
assert_eq!(
output.outgoing[0].reason,
Some("Test dependency".to_string())
);
assert_eq!(output.outgoing[0].priority, Some(8));
}
#[tokio::test]
async fn test_find_related_episodes() {
let memory = create_test_memory();
let tools = EpisodeRelationshipTools::new(memory.clone());
let ep1 = create_test_episode(&memory, "Test episode 1").await;
let ep2 = create_test_episode(&memory, "Test episode 2").await;
let add_input = AddEpisodeRelationshipInput {
from_episode_id: ep1.to_string(),
to_episode_id: ep2.to_string(),
relationship_type: "depends_on".to_string(),
reason: None,
priority: None,
created_by: None,
};
tools.add_relationship(add_input).await.unwrap();
let input = FindRelatedEpisodesInput {
episode_id: ep1.to_string(),
relationship_type: None,
limit: None,
include_metadata: Some(true),
};
let result = tools.find_related(input).await;
assert!(result.is_ok());
let output = result.unwrap();
assert!(output.success);
assert_eq!(output.count, 1);
assert_eq!(output.related_episodes[0].episode_id, ep2.to_string());
assert_eq!(output.related_episodes[0].direction, "outgoing");
}
#[tokio::test]
async fn test_check_relationship_exists() {
let memory = create_test_memory();
let tools = EpisodeRelationshipTools::new(memory.clone());
let ep1 = create_test_episode(&memory, "Test episode 1").await;
let ep2 = create_test_episode(&memory, "Test episode 2").await;
let add_input = AddEpisodeRelationshipInput {
from_episode_id: ep1.to_string(),
to_episode_id: ep2.to_string(),
relationship_type: "depends_on".to_string(),
reason: None,
priority: None,
created_by: None,
};
tools.add_relationship(add_input).await.unwrap();
let input = CheckRelationshipExistsInput {
from_episode_id: ep1.to_string(),
to_episode_id: ep2.to_string(),
relationship_type: "depends_on".to_string(),
};
let result = tools.check_exists(input).await;
assert!(result.is_ok());
let output = result.unwrap();
assert!(output.success);
}
#[tokio::test]
async fn test_get_dependency_graph() {
let memory = create_test_memory();
let tools = EpisodeRelationshipTools::new(memory.clone());
let ep1 = create_test_episode(&memory, "Test episode 1").await;
let ep2 = create_test_episode(&memory, "Test episode 2").await;
let add_input = AddEpisodeRelationshipInput {
from_episode_id: ep1.to_string(),
to_episode_id: ep2.to_string(),
relationship_type: "depends_on".to_string(),
reason: None,
priority: None,
created_by: None,
};
tools.add_relationship(add_input).await.unwrap();
let input = DependencyGraphInput {
episode_id: ep1.to_string(),
depth: Some(2),
format: Some("json".to_string()),
};
let result = tools.get_dependency_graph(input).await;
assert!(result.is_ok());
let output = result.unwrap();
assert!(output.success);
assert_eq!(output.root, ep1.to_string());
assert!(output.node_count >= 1);
}
#[tokio::test]
async fn test_get_dependency_graph_dot_format() {
let memory = create_test_memory();
let tools = EpisodeRelationshipTools::new(memory.clone());
let ep = create_test_episode(&memory, "Test episode").await;
let input = DependencyGraphInput {
episode_id: ep.to_string(),
depth: Some(2),
format: Some("dot".to_string()),
};
let result = tools.get_dependency_graph(input).await;
assert!(result.is_ok());
let output = result.unwrap();
assert!(output.success);
assert!(output.dot.is_some());
let dot = output.dot.unwrap();
assert!(dot.contains("digraph RelationshipGraph"));
}
#[tokio::test]
async fn test_validate_no_cycles_non_acyclic_type() {
let memory = create_test_memory();
let tools = EpisodeRelationshipTools::new(memory.clone());
let ep1 = create_test_episode(&memory, "Test episode 1").await;
let ep2 = create_test_episode(&memory, "Test episode 2").await;
let input = ValidateNoCyclesInput {
from_episode_id: ep1.to_string(),
to_episode_id: ep2.to_string(),
relationship_type: "related_to".to_string(),
};
let result = tools.validate_no_cycles(input).await;
assert!(result.is_ok());
let output = result.unwrap();
assert!(output.success);
assert!(!output.would_create_cycle);
assert!(output.is_valid);
}
#[tokio::test]
async fn test_validate_no_cycles_acyclic_type() {
let memory = create_test_memory();
let tools = EpisodeRelationshipTools::new(memory.clone());
let ep1 = create_test_episode(&memory, "Test episode 1").await;
let ep2 = create_test_episode(&memory, "Test episode 2").await;
let input = ValidateNoCyclesInput {
from_episode_id: ep1.to_string(),
to_episode_id: ep2.to_string(),
relationship_type: "depends_on".to_string(),
};
let result = tools.validate_no_cycles(input).await;
assert!(result.is_ok());
let output = result.unwrap();
assert!(output.success);
assert!(!output.would_create_cycle);
assert!(output.is_valid);
}
#[tokio::test]
async fn test_get_topological_order_empty() {
let memory = create_test_memory();
let tools = EpisodeRelationshipTools::new(memory);
let input = GetTopologicalOrderInput {
episode_ids: vec![],
};
let result = tools.get_topological_order(input).await;
assert!(result.is_ok());
let output = result.unwrap();
assert!(output.success);
assert!(output.order.is_empty());
assert_eq!(output.count, 0);
assert!(!output.has_cycles);
}
#[tokio::test]
async fn test_get_topological_order_single() {
let memory = create_test_memory();
let tools = EpisodeRelationshipTools::new(memory.clone());
let ep = create_test_episode(&memory, "Test episode").await;
let input = GetTopologicalOrderInput {
episode_ids: vec![ep.to_string()],
};
let result = tools.get_topological_order(input).await;
assert!(result.is_ok());
let output = result.unwrap();
assert!(output.success);
assert_eq!(output.count, 1);
assert_eq!(output.order[0].episode_id, ep.to_string());
assert_eq!(output.order[0].position, 1);
assert!(!output.has_cycles);
}
#[tokio::test]
async fn test_get_topological_order_with_dependencies() {
let memory = create_test_memory();
let tools = EpisodeRelationshipTools::new(memory.clone());
let ep1 = create_test_episode(&memory, "First episode").await;
let ep2 = create_test_episode(&memory, "Second episode").await;
let ep3 = create_test_episode(&memory, "Third episode").await;
tools
.add_relationship(AddEpisodeRelationshipInput {
from_episode_id: ep1.to_string(),
to_episode_id: ep2.to_string(),
relationship_type: "depends_on".to_string(),
reason: None,
priority: None,
created_by: None,
})
.await
.unwrap();
tools
.add_relationship(AddEpisodeRelationshipInput {
from_episode_id: ep2.to_string(),
to_episode_id: ep3.to_string(),
relationship_type: "depends_on".to_string(),
reason: None,
priority: None,
created_by: None,
})
.await
.unwrap();
let input = GetTopologicalOrderInput {
episode_ids: vec![ep1.to_string(), ep2.to_string(), ep3.to_string()],
};
let result = tools.get_topological_order(input).await;
assert!(result.is_ok());
let output = result.unwrap();
assert!(output.success);
assert_eq!(output.count, 3);
assert!(!output.has_cycles);
let pos1 = output
.order
.iter()
.position(|e| e.episode_id == ep1.to_string())
.unwrap();
let pos2 = output
.order
.iter()
.position(|e| e.episode_id == ep2.to_string())
.unwrap();
let pos3 = output
.order
.iter()
.position(|e| e.episode_id == ep3.to_string())
.unwrap();
assert!(pos1 < pos2);
assert!(pos2 < pos3);
}
#[tokio::test]
async fn test_get_topological_order_invalid_uuid() {
let memory = create_test_memory();
let tools = EpisodeRelationshipTools::new(memory);
let input = GetTopologicalOrderInput {
episode_ids: vec!["invalid-uuid".to_string()],
};
let result = tools.get_topological_order(input).await;
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("Invalid episode_id 'invalid-uuid'")
);
}
#[tokio::test]
async fn test_all_relationship_types() {
let memory = create_test_memory();
let tools = EpisodeRelationshipTools::new(memory.clone());
let ep1 = create_test_episode(&memory, "Test episode 1").await;
let ep2 = create_test_episode(&memory, "Test episode 2").await;
let types = vec![
"parent_child",
"depends_on",
"follows",
"related_to",
"blocks",
"duplicates",
"references",
];
for rel_type in types {
let input = AddEpisodeRelationshipInput {
from_episode_id: ep1.to_string(),
to_episode_id: ep2.to_string(),
relationship_type: rel_type.to_string(),
reason: Some(format!("Test {} relationship", rel_type)),
priority: None,
created_by: None,
};
let result = tools.add_relationship(input).await;
assert!(
result.is_ok(),
"Failed to add {} relationship: {:?}",
rel_type,
result.err()
);
let output = result.unwrap();
assert_eq!(output.relationship_type, rel_type);
}
}
#[tokio::test]
async fn test_get_relationships_direction_filter() {
let memory = create_test_memory();
let tools = EpisodeRelationshipTools::new(memory.clone());
let ep1 = create_test_episode(&memory, "Test episode 1").await;
let ep2 = create_test_episode(&memory, "Test episode 2").await;
let ep3 = create_test_episode(&memory, "Test episode 3").await;
tools
.add_relationship(AddEpisodeRelationshipInput {
from_episode_id: ep1.to_string(),
to_episode_id: ep2.to_string(),
relationship_type: "depends_on".to_string(),
reason: None,
priority: None,
created_by: None,
})
.await
.unwrap();
tools
.add_relationship(AddEpisodeRelationshipInput {
from_episode_id: ep3.to_string(),
to_episode_id: ep1.to_string(),
relationship_type: "depends_on".to_string(),
reason: None,
priority: None,
created_by: None,
})
.await
.unwrap();
let outgoing_input = GetEpisodeRelationshipsInput {
episode_id: ep1.to_string(),
direction: Some("outgoing".to_string()),
relationship_type: None,
};
let outgoing_result = tools.get_relationships(outgoing_input).await.unwrap();
assert_eq!(outgoing_result.outgoing.len(), 1);
assert_eq!(outgoing_result.incoming.len(), 0);
assert_eq!(outgoing_result.outgoing[0].to, ep2.to_string());
let incoming_input = GetEpisodeRelationshipsInput {
episode_id: ep1.to_string(),
direction: Some("incoming".to_string()),
relationship_type: None,
};
let incoming_result = tools.get_relationships(incoming_input).await.unwrap();
assert_eq!(incoming_result.outgoing.len(), 0);
assert_eq!(incoming_result.incoming.len(), 1);
assert_eq!(incoming_result.incoming[0].from, ep3.to_string());
}
#[tokio::test]
async fn test_find_related_with_type_filter() {
let memory = create_test_memory();
let tools = EpisodeRelationshipTools::new(memory.clone());
let ep1 = create_test_episode(&memory, "Test episode 1").await;
let ep2 = create_test_episode(&memory, "Test episode 2").await;
let ep3 = create_test_episode(&memory, "Test episode 3").await;
tools
.add_relationship(AddEpisodeRelationshipInput {
from_episode_id: ep1.to_string(),
to_episode_id: ep2.to_string(),
relationship_type: "depends_on".to_string(),
reason: None,
priority: None,
created_by: None,
})
.await
.unwrap();
tools
.add_relationship(AddEpisodeRelationshipInput {
from_episode_id: ep1.to_string(),
to_episode_id: ep3.to_string(),
relationship_type: "related_to".to_string(),
reason: None,
priority: None,
created_by: None,
})
.await
.unwrap();
let input = FindRelatedEpisodesInput {
episode_id: ep1.to_string(),
relationship_type: Some("depends_on".to_string()),
limit: None,
include_metadata: None,
};
let result = tools.find_related(input).await.unwrap();
assert_eq!(result.count, 1);
assert_eq!(result.related_episodes[0].episode_id, ep2.to_string());
assert_eq!(result.related_episodes[0].relationship_type, "depends_on");
}