use crank_core::{ ToolDescription, ToolQualityFinding, ToolQualityReport, ToolQualitySchemaNode, ToolQualitySeverity, analyze_tool_identity_quality, analyze_tool_schema_quality, }; #[test] fn serializes_tool_quality_finding_contract() { let finding = ToolQualityFinding { severity: ToolQualitySeverity::Warning, code: "tool_description_too_short".to_owned(), message: "Описание инструмента слишком короткое.".to_owned(), suggested_action: Some("Добавьте назначение и условия применения инструмента.".to_owned()), field_path: Some("tool_description.description".to_owned()), }; let value = serde_json::to_value(finding).unwrap(); assert_eq!(value["severity"], "warning"); assert_eq!(value["code"], "tool_description_too_short"); assert_eq!(value["field_path"], "tool_description.description"); } #[test] fn report_is_blocking_when_error_finding_exists() { let report = ToolQualityReport::new(vec![ToolQualityFinding { severity: ToolQualitySeverity::Error, code: "tool_name_missing".to_owned(), message: "Имя инструмента обязательно.".to_owned(), suggested_action: None, field_path: Some("name".to_owned()), }]); assert!(report.blocking); assert_eq!(report.findings.len(), 1); } #[test] fn reports_missing_tool_name_as_blocking_error() { let report = analyze_tool_identity_quality( " ", &ToolDescription { title: "Create lead".to_owned(), description: "Создает новый лид в CRM, когда пользователь просит добавить потенциального клиента.".to_owned(), tags: Vec::new(), examples: Vec::new(), }, ); assert!(report.blocking); assert!(has_finding( &report, "tool_name_missing", ToolQualitySeverity::Error )); } #[test] fn warns_about_generic_tool_name() { let report = analyze_tool_identity_quality( "call_api", &ToolDescription { title: "Call API".to_owned(), description: "Получает данные клиента по идентификатору, когда пользователю нужна карточка клиента.".to_owned(), tags: Vec::new(), examples: Vec::new(), }, ); assert!(!report.blocking); assert!(has_finding( &report, "tool_name_too_generic", ToolQualitySeverity::Warning )); } #[test] fn reports_unsafe_tool_name_format_as_blocking_error() { let report = analyze_tool_identity_quality( "Create Lead!", &ToolDescription { title: "Create lead".to_owned(), description: "Создает новый лид в CRM, когда пользователь просит добавить потенциального клиента.".to_owned(), tags: Vec::new(), examples: Vec::new(), }, ); assert!(report.blocking); assert!(has_finding( &report, "tool_name_invalid_format", ToolQualitySeverity::Error )); } #[test] fn warns_about_short_description() { let report = analyze_tool_identity_quality( "create_lead", &ToolDescription { title: "Create lead".to_owned(), description: "Создает лид.".to_owned(), tags: Vec::new(), examples: Vec::new(), }, ); assert!(!report.blocking); assert!(has_finding( &report, "tool_description_too_short", ToolQualitySeverity::Warning )); } #[test] fn warns_about_object_schema_without_fields() { let schema = ToolQualitySchemaNode::object(None, vec![]); let report = analyze_tool_schema_quality("input_schema", &schema); assert!(has_finding( &report, "schema_object_without_fields", ToolQualitySeverity::Warning )); } #[test] fn warns_about_parameter_without_description() { let schema = ToolQualitySchemaNode::object( Some("Входные параметры"), vec![("customer_id", ToolQualitySchemaNode::string(None))], ); let report = analyze_tool_schema_quality("input_schema", &schema); assert!(has_finding( &report, "schema_field_missing_description", ToolQualitySeverity::Warning )); } #[test] fn warns_about_multi_action_parameter_names() { let schema = ToolQualitySchemaNode::object( Some("Входные параметры"), vec![( "action", ToolQualitySchemaNode::string(Some("Что нужно сделать")), )], ); let report = analyze_tool_schema_quality("input_schema", &schema); assert!(has_finding( &report, "schema_multi_action_parameter", ToolQualitySeverity::Warning )); } #[test] fn recommends_enum_for_mode_like_string_parameter() { let schema = ToolQualitySchemaNode::object( Some("Входные параметры"), vec![( "mode", ToolQualitySchemaNode::string(Some("Режим поиска клиента")), )], ); let report = analyze_tool_schema_quality("input_schema", &schema); assert!(has_finding( &report, "schema_enum_recommended", ToolQualitySeverity::Info )); } fn has_finding(report: &ToolQualityReport, code: &str, severity: ToolQualitySeverity) -> bool { report .findings .iter() .any(|finding| finding.code == code && finding.severity == severity) }