Files
VolcanicAgenticCore/tests/Volcanic.AgenticCore.A2A.Server.Tests/PromptTests.cs
2026-10-07 14:31:35 +00:00

75 lines
3.2 KiB
C#

using System.Text;
using System.Text.Json;
using A2A;
using Volcanic.AgenticCore.A2A.Server.Agent;
using Volcanic.AgenticCore.A2A.Server.Callers;
namespace Volcanic.AgenticCore.A2A.Server.Tests;
public sealed class PromptTests : IDisposable
{
private readonly string _ws = Path.Combine(Path.GetTempPath(), "a2a-prompt", Guid.NewGuid().ToString("N"));
private A2ACaller Caller => new("travel-bot", "isolated", _ws, "p", "1");
private static Message Msg(params Part[] parts) => new() { Role = Role.User, MessageId = "m", Parts = [.. parts] };
[Fact]
public async Task Prompt_HasFramingHeader_AndText()
{
var text = await A2APrompt.BuildAsync(Caller, Msg(Part.FromText("Book a table")), true, 1024, new HttpClient(), TimeProvider.System, default);
Assert.StartsWith("[A2A message from \"travel-bot\", an external agent, not your owner.", text);
Assert.Contains("Book a table", text);
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task Header_MentionsNotifyOwner_OnlyWhenTheToolExists(bool canNotifyOwner)
{
var text = await A2APrompt.BuildAsync(Caller, Msg(Part.FromText("hi")), canNotifyOwner, 1024, new HttpClient(), TimeProvider.System, default);
var header = text.Split('\n')[0];
Assert.Equal(canNotifyOwner, header.Contains("notify_owner"));
Assert.Contains("request_input to ask the caller a question.]", header);
}
[Fact]
public async Task Files_InboundPart_SavedToInbox()
{
var part = Part.FromRaw(Encoding.UTF8.GetBytes("hello"), "text/plain", "../../plan.txt");
var text = await A2APrompt.BuildAsync(Caller, Msg(Part.FromText("see file"), part), true, 1024, new HttpClient(), TimeProvider.System, default);
var line = text.Split('\n').Single(l => l.StartsWith("Files: "));
var relative = line["Files: ".Length..];
Assert.StartsWith("inbox/a2a-travel-bot-", relative);
Assert.EndsWith("-plan.txt", relative);
Assert.Equal("hello", File.ReadAllText(Path.Combine(_ws, relative)));
}
[Fact]
public async Task Files_BadUrl_Throws()
{
var part = Part.FromUrl("not a url", "text/plain", null);
await Assert.ThrowsAsync<A2APromptException>(() =>
A2APrompt.BuildAsync(Caller, Msg(part), true, 1024, new HttpClient(), TimeProvider.System, default));
}
[Fact]
public async Task DataPart_BecomesFencedJson()
{
using var doc = JsonDocument.Parse("""{"a":1}""");
var text = await A2APrompt.BuildAsync(Caller, Msg(Part.FromData(doc.RootElement.Clone())), true, 1024, new HttpClient(), TimeProvider.System, default);
Assert.Contains("```json\n{\"a\":1}\n```", text);
}
[Fact]
public void Hops_ReadsNumberOrString_DefaultZero()
{
Assert.Equal(0, Hops.Read(null, null));
var meta = new Dictionary<string, JsonElement> { [Hops.Key] = JsonSerializer.SerializeToElement(2) };
Assert.Equal(2, Hops.Read(meta, null));
var asString = new Dictionary<string, JsonElement> { [Hops.Key] = JsonSerializer.SerializeToElement("3") };
Assert.Equal(3, Hops.Read(null, asString));
}
public void Dispose() { try { Directory.Delete(_ws, true); } catch (IOException) { } }
}