Files

120 lines
5.5 KiB
C#

using System.Net;
using A2A;
using Volcanic.AgenticCore.A2A.Server.Tests.Support;
using Volcanic.AgenticCore.Testing;
namespace Volcanic.AgenticCore.A2A.Server.Tests;
public class ServerEndToEndTests
{
[Fact]
public async Task Auth_UnknownKey_Returns401()
{
await using var host = await A2ATestServer.StartAsync();
var response = await host.Http("nope").PostAsync("/a2a", new StringContent("{}", System.Text.Encoding.UTF8, "application/json"));
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
Assert.Equal(HttpStatusCode.Unauthorized, (await host.Http(null).GetAsync("/a2a/v1/tasks")).StatusCode);
}
[Fact]
public async Task Auth_KeyInTwoWorkspaces_Returns401()
{
await using var host = await A2ATestServer.StartAsync();
host.WriteCallers(host.OtherWorkspace, $$"""[ { "name": "dup", "key": "{{A2ATestServer.Key}}" } ]""");
var response = await host.Http().GetAsync("/a2a/v1/tasks");
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
}
[Fact]
public async Task Card_Public_ServedWithoutKey()
{
await using var host = await A2ATestServer.StartAsync();
var json = await host.Http(null).GetStringAsync("/.well-known/agent-card.json");
Assert.Contains("Volcanic assistant", json);
}
[Fact]
public async Task Send_Completes_ResponseArtifactHasFinalAnswer()
{
await using var host = await A2ATestServer.StartAsync();
host.Faux.Enqueue(FauxTurn.Say("Hello from the assistant"));
var response = await host.Client().SendMessageAsync(Requests.Text("hi"), CancellationToken.None);
var task = response.Task!;
Assert.Equal(TaskState.Completed, task.Status.State);
Assert.Equal("Hello from the assistant", Requests.ResponseText(task));
}
[Fact]
public async Task Prompt_HasFramingHeader_AndSession_UsesWorkspaceProjectContext()
{
await using var host = await A2ATestServer.StartAsync();
File.WriteAllText(Path.Combine(host.Workspace, "PERSONA.md"), "I am the persona of workspace one.");
File.WriteAllText(Path.Combine(host.Workspace, "MEMORY.md"), "Remember: the owner likes tea.");
host.Faux.Enqueue(FauxTurn.Say("ok"));
await host.Client().SendMessageAsync(Requests.Text("hi"), CancellationToken.None);
var request = host.Faux.Requests.Last();
Assert.Contains("I am the persona of workspace one.", request.SystemPrompt);
Assert.Contains("the owner likes tea", request.SystemPrompt);
Assert.Contains("[A2A message from \"alpha\"", request.Messages.Last().Text);
}
[Fact]
public async Task Send_ContinuesContext_SameSession()
{
await using var host = await A2ATestServer.StartAsync();
host.Faux.Enqueue(FauxTurn.Say("first"), FauxTurn.Say("second"));
var first = (await host.Client().SendMessageAsync(Requests.Text("one"), CancellationToken.None)).Task!;
await host.Client().SendMessageAsync(Requests.Text("two", contextId: first.ContextId), CancellationToken.None);
var transcript = string.Join("\n", host.Faux.Requests.Last().Messages.Select(m => m.Text));
Assert.Contains("one", transcript);
Assert.Contains("first", transcript);
}
[Theory]
[InlineData("../x")]
[InlineData("a b")]
[InlineData("ctx.1")]
public async Task Send_BadContextId_InvalidParams(string contextId)
{
await using var host = await A2ATestServer.StartAsync();
var ex = await Assert.ThrowsAsync<A2AException>(() =>
host.Client().SendMessageAsync(Requests.Text("hi", contextId: contextId), CancellationToken.None));
Assert.Equal(A2AErrorCode.InvalidParams, ex.ErrorCode);
Assert.Empty(host.Faux.Requests);
}
[Fact]
public async Task Send_TooLongContextId_InvalidParams()
{
await using var host = await A2ATestServer.StartAsync();
var ex = await Assert.ThrowsAsync<A2AException>(() =>
host.Client().SendMessageAsync(Requests.Text("hi", contextId: new string('a', 200)), CancellationToken.None));
Assert.Equal(A2AErrorCode.InvalidParams, ex.ErrorCode);
Assert.Empty(host.Faux.Requests);
}
[Fact]
public async Task Send_ProviderError_TaskFailed()
{
await using var host = await A2ATestServer.StartAsync();
host.Faux.Enqueue(FauxTurn.Error("model exploded"));
var task = (await host.Client().SendMessageAsync(Requests.Text("hi"), CancellationToken.None)).Task!;
Assert.Equal(TaskState.Failed, task.Status.State);
Assert.Contains("model exploded", task.Status.Message!.Parts[0].Text);
}
[Fact]
public async Task Tasks_OtherCallersTask_NotFound()
{
await using var host = await A2ATestServer.StartAsync();
host.Faux.Enqueue(FauxTurn.Say("ok"));
var task = (await host.Client().SendMessageAsync(Requests.Text("hi"), CancellationToken.None)).Task!;
var otherWorkspace = await Assert.ThrowsAsync<A2AException>(() =>
host.Client(A2ATestServer.OtherKey).GetTaskAsync(new GetTaskRequest { Id = task.Id }, CancellationToken.None));
Assert.Equal(A2AErrorCode.TaskNotFound, otherWorkspace.ErrorCode);
var sameWorkspaceOtherCaller = await Assert.ThrowsAsync<A2AException>(() =>
host.Client("key-gamma").GetTaskAsync(new GetTaskRequest { Id = task.Id }, CancellationToken.None));
Assert.Equal(A2AErrorCode.TaskNotFound, sameWorkspaceOtherCaller.ErrorCode);
}
}