summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/Agent.Core.Tests/InternalToolTests.cs1
-rw-r--r--tests/Agent.Core.Tests/PromptAndRuntimeTests.cs49
2 files changed, 50 insertions, 0 deletions
diff --git a/tests/Agent.Core.Tests/InternalToolTests.cs b/tests/Agent.Core.Tests/InternalToolTests.cs
index 1e241ce..e452cab 100644
--- a/tests/Agent.Core.Tests/InternalToolTests.cs
+++ b/tests/Agent.Core.Tests/InternalToolTests.cs
@@ -36,6 +36,7 @@ public sealed class InternalToolTests
Assert.False(result.Success);
Assert.False(result.ExternalEffect);
+ Assert.False(result.RetryAllowed);
Assert.Empty(backend.Calls);
}
diff --git a/tests/Agent.Core.Tests/PromptAndRuntimeTests.cs b/tests/Agent.Core.Tests/PromptAndRuntimeTests.cs
index 23ad2d2..c3c634d 100644
--- a/tests/Agent.Core.Tests/PromptAndRuntimeTests.cs
+++ b/tests/Agent.Core.Tests/PromptAndRuntimeTests.cs
@@ -144,6 +144,29 @@ public sealed class PromptAndRuntimeTests
Assert.Contains("iteration limit", failed.Result, StringComparison.OrdinalIgnoreCase);
}
+ [Fact]
+ public async Task Runtime_does_not_retry_a_sandbox_policy_denial()
+ {
+ TaskRegistry registry = new();
+ var backend = new UnauthorizedBackend();
+ var dispatcher = new InternalToolDispatcher(
+ new DenyByDefaultPolicy(new HashSet<string>(["file_search"], StringComparer.Ordinal)), backend);
+ var ollama = new FileSearchOllamaClient();
+ var runtime = new AgentRuntime(registry, ollama, new AgentRuntimeSettings("", "en", [], "sandbox"), dispatcher);
+ TaskSnapshot task = registry.Enqueue("research regional curry", null, null);
+ registry.Transition(task.TaskId, AgentTaskState.Planning);
+ registry.Transition(task.TaskId, AgentTaskState.Running);
+
+ await runtime.ExecuteAsync(task.TaskId, TestContext.Current.CancellationToken);
+
+ Assert.True(registry.TryGet(task.TaskId, null, out TaskSnapshot? failed));
+ Assert.Equal(AgentTaskState.Failed, failed!.State);
+ Assert.Contains("denied by sandbox policy", failed.Result, StringComparison.OrdinalIgnoreCase);
+ Assert.Contains("not approved for Read", failed.Result, StringComparison.OrdinalIgnoreCase);
+ Assert.Equal(1, ollama.CallCount);
+ Assert.Equal(1, backend.CallCount);
+ }
+
private sealed class RecordingOllamaClient : IOllamaClient
{
public List<IReadOnlyList<OllamaMessage>> Calls { get; } = [];
@@ -217,4 +240,30 @@ public sealed class PromptAndRuntimeTests
return Task.FromResult(new OllamaResult("", 1, 1, [new OllamaToolCall(new OllamaFunctionCall("process_run", arguments.RootElement.Clone()))]));
}
}
+
+ private sealed class UnauthorizedBackend : IInternalToolBackend
+ {
+ public int CallCount { get; private set; }
+
+ public Task<ToolExecutionResult> ExecuteAsync(Guid taskId, string toolName, JsonElement arguments, CancellationToken cancellationToken)
+ {
+ CallCount++;
+ throw new UnauthorizedAccessException("'C:\\Users' is not approved for Read.");
+ }
+ }
+
+ private sealed class FileSearchOllamaClient : IOllamaClient
+ {
+ public int CallCount { get; private set; }
+
+ public Task<OllamaResult> ChatAsync(IReadOnlyList<OllamaMessage> messages, double? temperatureOverride, CancellationToken cancellationToken) =>
+ Task.FromResult(new OllamaResult("unexpected", 1, 1));
+
+ public Task<OllamaResult> ChatWithToolsAsync(IReadOnlyList<OllamaMessage> messages, double? temperatureOverride, IReadOnlyList<InternalToolDefinition> tools, CancellationToken cancellationToken)
+ {
+ CallCount++;
+ using JsonDocument arguments = JsonDocument.Parse("""{"root":"C:\\Users","mode":"name","query":"*.txt"}""");
+ return Task.FromResult(new OllamaResult("", 1, 1, [new OllamaToolCall(new OllamaFunctionCall("file_search", arguments.RootElement.Clone()))]));
+ }
+ }
}