fix(safety): add stop_array to DESTRUCTIVE_ACTIONS, add error propagation test

stop_array can cause data loss for running containers/VMs that depend on
array shares — requires confirm=True like other destructive mutations.

- Add stop_array to DESTRUCTIVE_ACTIONS and desc_map in array.py
- Update safety audit KNOWN_DESTRUCTIVE[array] to include stop_array
- Add stop_array negative/positive tests (test_array.py, safety tests)
- Add test_snapshot_wraps_bare_exception to test_live.py (bare Exception
  from subscribe_once is wrapped by tool_error_handler into ToolError)

748 tests passing
This commit is contained in:
Jacob Magar
2026-03-15 20:02:33 -04:00
parent 252ec520d1
commit 94850333e8
4 changed files with 29 additions and 4 deletions

View File

@@ -47,7 +47,7 @@ KNOWN_DESTRUCTIVE: dict[str, dict[str, set[str] | str]] = {
"module": "unraid_mcp.tools.array",
"register_fn": "register_array_tool",
"tool_name": "unraid_array",
"actions": {"remove_disk", "clear_disk_stats"},
"actions": {"remove_disk", "clear_disk_stats", "stop_array"},
"runtime_set": ARRAY_DESTRUCTIVE,
},
"vm": {
@@ -198,6 +198,7 @@ _DESTRUCTIVE_TEST_CASES: list[tuple[str, str, dict]] = [
# Array
("array", "remove_disk", {"disk_id": "abc123:local"}),
("array", "clear_disk_stats", {"disk_id": "abc123:local"}),
("array", "stop_array", {}),
# VM
("vm", "force_stop", {"vm_id": "test-vm-uuid"}),
("vm", "reset", {"vm_id": "test-vm-uuid"}),
@@ -468,6 +469,12 @@ class TestConfirmAllowsExecution:
result = await tool_fn(action="clear_disk_stats", disk_id="abc:local", confirm=True)
assert result["success"] is True
async def test_array_stop_array_with_confirm(self, _mock_array_graphql: AsyncMock) -> None:
_mock_array_graphql.return_value = {"array": {"setState": {"state": "STOPPED"}}}
tool_fn = make_tool_fn("unraid_mcp.tools.array", "register_array_tool", "unraid_array")
result = await tool_fn(action="stop_array", confirm=True)
assert result["success"] is True
async def test_plugins_remove_with_confirm(self, _mock_plugins_graphql: AsyncMock) -> None:
_mock_plugins_graphql.return_value = {"removePlugin": True}
tool_fn = make_tool_fn(