Skip to content

Commit c3a6d34

Browse files
authored
Merge pull request #63 from 100monkeys-ai/copilot/add-tests-for-error-extraction
Add tests for error extraction and sanitization, extract constants, rename variable for clarity
2 parents daee095 + 7a3c58d commit c3a6d34

File tree

3 files changed

+99
-7
lines changed

3 files changed

+99
-7
lines changed

cli/src/daemon/client.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,7 +756,62 @@ impl DaemonClient {
756756

757757
#[cfg(test)]
758758
mod tests {
759+
use super::extract_iteration_error_message;
759760
use super::WorkflowListResponse;
761+
use serde_json::json;
762+
763+
#[test]
764+
fn test_error_object_with_message_precedence() {
765+
let event = json!({
766+
"data": {
767+
"error": {
768+
"message": "object message",
769+
"code": "SOME_CODE"
770+
}
771+
},
772+
"error": "top-level error"
773+
});
774+
775+
let msg = extract_iteration_error_message(&event);
776+
assert_eq!(msg, "object message");
777+
}
778+
779+
#[test]
780+
fn test_error_string_in_data_precedence() {
781+
let event = json!({
782+
"data": {
783+
"error": "data error string"
784+
},
785+
"error": "top-level error"
786+
});
787+
788+
let msg = extract_iteration_error_message(&event);
789+
assert_eq!(msg, "data error string");
790+
}
791+
792+
#[test]
793+
fn test_error_string_top_level_precedence() {
794+
let event = json!({
795+
"error": "top-level error"
796+
});
797+
798+
let msg = extract_iteration_error_message(&event);
799+
assert_eq!(msg, "top-level error");
800+
}
801+
802+
#[test]
803+
fn test_error_fallback_unknown() {
804+
let event = json!({
805+
"data": {
806+
"error": {
807+
"not_message": "no message field here"
808+
}
809+
}
810+
});
811+
812+
let msg = extract_iteration_error_message(&event);
813+
assert_eq!(msg, "Unknown error");
814+
}
760815

761816
#[test]
762817
fn parses_wrapped_workflow_list_response() {

orchestrator/core/src/application/tool_invocation_service.rs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ use crate::infrastructure::smcp_gateway_proto::{
3737
};
3838
use crate::infrastructure::workflow_parser::WorkflowParser;
3939

40+
const JUDGE_POLL_INTERVAL_MS: u64 = 500;
41+
4042
pub enum ToolInvocationResult {
4143
Direct(Value),
4244
DispatchRequired(DispatchAction),
@@ -432,8 +434,8 @@ impl ToolInvocationService {
432434
// judge pipeline. When the node config marks a tool with `skip_judge: true`
433435
// (e.g. read-only tools such as fs.read, fs.list), we bypass the judge entirely
434436
// to reduce latency on low-risk operations (NODE_CONFIGURATION_SPEC_V1.md).
435-
let skip_tool_judge = self.tool_router.is_skip_judge(&tool_name).await;
436-
if skip_tool_judge {
437+
let should_skip_judge = self.tool_router.is_skip_judge(&tool_name).await;
438+
if should_skip_judge {
437439
tracing::debug!(
438440
tool_name = %tool_name,
439441
"Inner-loop semantic judge skipped (skip_judge=true in node config for this tool)"
@@ -527,7 +529,7 @@ impl ToolInvocationService {
527529
))
528530
})?;
529531

530-
let poll_interval_ms = 500u64;
532+
let poll_interval_ms = JUDGE_POLL_INTERVAL_MS;
531533
let timeout_ms = timeout_seconds.saturating_mul(1000);
532534
let max_attempts = timeout_ms
533535
.saturating_add(poll_interval_ms.saturating_sub(1))
@@ -1771,4 +1773,35 @@ mod tests {
17711773
.unwrap();
17721774
assert_eq!(exec_mode, "remote_jsonrpc");
17731775
}
1776+
1777+
#[test]
1778+
fn sanitize_segment_handles_empty_and_whitespace() {
1779+
assert_eq!(sanitize_segment(""), "unversioned");
1780+
assert_eq!(sanitize_segment(" "), "unversioned");
1781+
}
1782+
1783+
#[test]
1784+
fn sanitize_segment_blocks_traversal_patterns() {
1785+
assert_eq!(sanitize_segment("."), "unversioned");
1786+
assert_eq!(sanitize_segment(".."), "unversioned");
1787+
assert_eq!(sanitize_segment("..hidden"), "unversioned");
1788+
assert_eq!(sanitize_segment("hidden.."), "unversioned");
1789+
assert_eq!(sanitize_segment("a..b"), "unversioned");
1790+
assert_eq!(sanitize_segment("version..1"), "unversioned");
1791+
}
1792+
1793+
#[test]
1794+
fn sanitize_segment_replaces_special_characters() {
1795+
assert_eq!(sanitize_segment("foo/bar"), "foo_bar");
1796+
assert_eq!(sanitize_segment("foo\\bar"), "foo_bar");
1797+
assert_eq!(sanitize_segment("foo:bar"), "foo_bar");
1798+
assert_eq!(sanitize_segment("foo bar"), "foo_bar");
1799+
assert_eq!(sanitize_segment("name@domain.com"), "name_domain.com");
1800+
}
1801+
1802+
#[test]
1803+
fn sanitize_segment_preserves_safe_mixed_alphanumeric() {
1804+
assert_eq!(sanitize_segment("validName-123"), "validName-123");
1805+
assert_eq!(sanitize_segment("v1.2.3-beta_01"), "v1.2.3-beta_01");
1806+
}
17741807
}

orchestrator/core/src/infrastructure/temporal_event_listener.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,14 @@ use serde::{Deserialize, Serialize};
6666
use std::sync::Arc;
6767
use uuid::Uuid;
6868

69+
const EVENT_TYPE_REFINEMENT_APPLIED: &str = "RefinementApplied";
70+
6971
/// Canonical file name used to store validation feedback artifacts produced by refinement
70-
/// workflows. This is consumed by the `RefinementApplied` event handler, which expects any
71-
/// validation feedback generated during a refinement iteration to be written under this
72-
/// name within the associated artifact set or storage location.
72+
/// workflows.
73+
///
74+
/// This is consumed by the `RefinementApplied` event handler, which expects any validation
75+
/// feedback generated during a refinement iteration to be written under this name within the
76+
/// associated artifact set or storage location.
7377
const VALIDATION_FEEDBACK_FILE_NAME: &str = "validation_feedback";
7478

7579
/// External event payload from Temporal worker
@@ -356,7 +360,7 @@ impl TemporalEventListener {
356360
/// - Missing required fields
357361
pub async fn handle_event(&self, payload: TemporalEventPayload) -> Result<String> {
358362
// Special case for RefinementApplied execution event
359-
if payload.event_type == "RefinementApplied" {
363+
if payload.event_type == EVENT_TYPE_REFINEMENT_APPLIED {
360364
let execution_id = ExecutionId(uuid::Uuid::parse_str(&payload.execution_id)?);
361365
let agent_id_str = payload
362366
.agent_id

0 commit comments

Comments
 (0)