Skip to content

test(execute): fill unit test gaps in Stage 2 execution logic#11

Merged
jamesadevine merged 2 commits intomainfrom
copilot/add-unit-tests-for-execute-rs
Mar 6, 2026
Merged

test(execute): fill unit test gaps in Stage 2 execution logic#11
jamesadevine merged 2 commits intomainfrom
copilot/add-unit-tests-for-execute-rs

Conversation

Copy link
Contributor

Copilot AI commented Mar 6, 2026

execute.rs had zero unit tests covering several distinct code paths in execute_safe_output and execute_safe_outputs, leaving silent failures and untested dispatch branches.

Added tests

  • Empty NDJSON file → Ok(vec![]) — the file-exists-but-empty early-return path was not exercised
  • missing-data passthrough → success: true — the only one of the three informational passthroughs (noop, missing-tool, missing-data) without a test
  • Malformed create-work-item payload → Err — entry missing required title/description fields; existing test only validated missing ADO context, not deserialization failure
  • Unknown tool error contains tool name — tightened assertion on the bail! path to verify the offending tool name appears in the error message, not just the generic prefix
#[tokio::test]
async fn test_execute_safe_output_malformed_work_item_returns_err() {
    let entry = serde_json::json!({"name": "create-work-item"});
    let result = execute_safe_output(&entry, &ExecutionContext::default()).await;
    assert!(result.is_err());
}

#[tokio::test]
async fn test_execute_unknown_tool_error_contains_tool_name() {
    let entry = serde_json::json!({"name": "evil-backdoor"});
    let result = execute_safe_output(&entry, &ExecutionContext::default()).await;
    assert!(result.unwrap_err().to_string().contains("evil-backdoor"));
}
Original prompt

This section details on the original issue you should resolve

<issue_title>🧪 Test gap analysis — 0 unit tests for Stage 2 execution logic in execute.rs</issue_title>
<issue_description>## Test Gap Analysis

Test suite snapshot: 205 unit tests, 21 integration tests (7 compiler + 5 mcp_firewall + 9 proxy), 226 total — all passing.

Context: 25 tests were added since the prior audit cycle (new coverage for compile/standalone.rs::generate_firewall_config, compile/common.rs, and tools/result.rs). The one significant gap that remains untouched is execute.rs.


Priority Gap

Module Function/Path Why It Matters Suggested Test
execute.rs execute_safe_output — missing name field Safe output dispatch fails silently on malformed NDJSON Test entry with no name field returns Err
execute.rs execute_safe_output — unknown tool type bail! path is entirely untested Test entry with name: "evil-tool" returns error containing tool name
execute.rs execute_safe_outputnoop / missing-tool / missing-data passthrough These branches return success with no side effects — should be trivially testable Test each returns ExecutionResult with success: true
execute.rs execute_safe_outputs — empty NDJSON file Returns Ok(vec![]) early — no test exercises this path Write an empty NDJSON to a temp dir and assert empty result
execute.rs execute_safe_outputs — no file present Returns Ok(vec![]) early — no test exercises this path Point at a non-existent dir and assert empty result
execute.rs execute_safe_output — malformed create-work-item JSON serde_json::from_value failure path returns Err — untested Provide a JSON entry with name: "create-work-item" but missing required fields

Suggested Test Cases

1. No safe outputs file → empty result

#[tokio::test]
async fn test_execute_safe_outputs_no_file_returns_empty() {
    let dir = tempfile::tempdir().unwrap();
    let ctx = ExecutionContext::default(); // or minimal stub
    let results = execute_safe_outputs(dir.path(), &ctx).await.unwrap();
    assert!(results.is_empty());
}

2. Empty NDJSON file → empty result

#[tokio::test]
async fn test_execute_safe_outputs_empty_file_returns_empty() {
    let dir = tempfile::tempdir().unwrap();
    std::fs::write(dir.path().join("safe-outputs.ndjson"), "").unwrap();
    let ctx = ExecutionContext::default();
    let results = execute_safe_outputs(dir.path(), &ctx).await.unwrap();
    assert!(results.is_empty());
}

3. Missing name field → Err

#[tokio::test]
async fn test_execute_safe_output_missing_name_returns_err() {
    let entry = serde_json::json!({"title": "oops"});
    let ctx = ExecutionContext::default();
    let result = execute_safe_output(&entry, &ctx).await;
    assert!(result.is_err());
    assert!(result.unwrap_err().to_string().contains("name"));
}

4. Unknown tool type → Err with tool name in message

#[tokio::test]
async fn test_execute_safe_output_unknown_tool_returns_err() {
    let entry = serde_json::json!({"name": "evil-backdoor"});
    let ctx = ExecutionContext::default();
    let result = execute_safe_output(&entry, &ctx).await;
    assert!(result.is_err());
    assert!(result.unwrap_err().to_string().contains("evil-backdoor"));
}

5. Informational tool passthroughs → success

#[tokio::test]
async fn test_execute_safe_output_noop_succeeds() {
    let entry = serde_json::json!({"name": "noop", "context": "nothing to do"});
    let ctx = ExecutionContext::default();
    let (_name, result) = execute_safe_output(&entry, &ctx).await.unwrap();
    assert!(result.success);
}

#[tokio::test]
async fn test_execute_safe_output_missing_tool_succeeds() {
    let entry = serde_json::json!({"name": "missing-tool"});
    let ctx = ExecutionContext::default();
    let (_name, result) = execute_safe_output(&entry, &ctx).await.unwrap();
    assert!(result.success);
}

#[tokio::test]
async fn test_execute_safe_output_missing_data_succeeds() {
    let entry = serde_json::json!({"name": "missing-data"});
    let ctx = ExecutionContext::default();
    let (_name, result) = execute_safe_output(&entry, &ctx).await.unwrap();
    assert!(result.success);
}

6. Malformed create-work-item payload → Err

#[tokio::test]
async fn test_execute_safe_output_malformed_work_item_returns_err() {
    // Missing required fields title/description
    let entry = serde_json::json!({"name": "create-work-item"});
    let ctx = ExecutionContext::default();
    let result = execute_safe_output(&entry, &ctx).await;
    assert!(result.is_err());
}

Coverage Summary

Module Public Fns Unit Tests Notes
execute.rs 2 0 ⚠️ No unit tests at all
`compile/stan...

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Copilot AI changed the title [WIP] Add unit tests for Stage 2 execution logic in execute.rs test(execute): fill unit test gaps in Stage 2 execution logic Mar 6, 2026
@jamesadevine jamesadevine marked this pull request as ready for review March 6, 2026 19:20
@jamesadevine jamesadevine merged commit ccf9c34 into main Mar 6, 2026
6 checks passed
@jamesadevine jamesadevine deleted the copilot/add-unit-tests-for-execute-rs branch March 6, 2026 19:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🧪 Test gap analysis — 0 unit tests for Stage 2 execution logic in execute.rs

2 participants