fix(a2a-client): fake MCP server on the ASP.NET framework, question-first results, safer file names

This commit is contained in:
EugeneTes
2026-10-08 12:46:32 +00:00
parent ef906b34dd
commit 24747858eb
8 changed files with 95 additions and 25 deletions

View File

@@ -6,6 +6,7 @@
<Description>Test helper: a stdio MCP server whose behaviour FAKE_MCP_MODE selects.</Description>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="ModelContextProtocol.Core" Version="2.2.0" />
</ItemGroup>
</Project>

View File

@@ -45,7 +45,7 @@ public sealed class A2AResultsTests : IDisposable
ArtifactId = "f1", Name = "plan.pdf", Parts = [Part.FromRaw(Encoding.UTF8.GetBytes("pdf!"), "application/pdf", "../../plan.pdf")],
});
var files = A2AResults.SaveFiles(_ws, "travel-bot", task, 1024);
var file = Assert.Single(files);
var file = Assert.Single(files.Saved);
Assert.Equal("inbox/a2a/travel-bot/plan.pdf", file);
Assert.Equal("pdf!", File.ReadAllText(Path.Combine(_ws, file)));
}
@@ -71,5 +71,35 @@ public sealed class A2AResultsTests : IDisposable
Assert.Contains("Files: inbox/a2a/travel-bot/x.pdf", text);
}
[Fact]
public void Describe_InputRequired_QuestionComesBeforeStaleResponseArtifact()
{
var task = Task_(TaskState.InputRequired, "Which date?",
new Artifact { ArtifactId = "response", Parts = [Part.FromText("old answer")] });
var text = A2AResults.Describe("travel-bot", task, []);
Assert.True(text.IndexOf("Which date?", StringComparison.Ordinal) < text.IndexOf("old answer", StringComparison.Ordinal));
}
[Fact]
public void SaveFiles_DotDotName_BecomesFile_AndControlCharsStripped()
{
var task = Task_(TaskState.Completed, null,
new Artifact { ArtifactId = "a", Parts = [Part.FromRaw(Encoding.UTF8.GetBytes("x"), "text/plain", "..")] },
new Artifact { ArtifactId = "b", Parts = [Part.FromRaw(Encoding.UTF8.GetBytes("y"), "text/plain", "a\u0001b.txt")] });
var files = A2AResults.SaveFiles(_ws, "bot", task, 1024).Saved;
Assert.Equal(["inbox/a2a/bot/file", "inbox/a2a/bot/ab.txt"], files);
}
[Fact]
public void SaveFiles_ReportsOversizedParts_AndDescribeListsThem()
{
var task = Task_(TaskState.Completed, null,
new Artifact { ArtifactId = "a", Parts = [Part.FromRaw(new byte[100], "text/plain", "big.bin")] });
var result = A2AResults.SaveFiles(_ws, "bot", task, 10);
Assert.Empty(result.Saved);
Assert.Equal(["big.bin"], result.Skipped.Names);
Assert.Contains("Files skipped (over 10 bytes): big.bin", A2AResults.Describe("bot", task, result.Saved, result.Skipped));
}
public void Dispose() { try { Directory.Delete(_ws, true); } catch (IOException) { } }
}

View File

@@ -46,6 +46,13 @@ public sealed class AgentsFileTests : IDisposable
Assert.DoesNotContain(r.Problems, p => p.Contains("secret"));
}
[Fact]
public void Read_NameWithTrailingNewline_Rejected()
{
Write("""[ { "name": "a\n", "url": "https://a.example/" } ]""");
Assert.Empty(AgentsFile.Read(_ws).Agents);
}
[Fact]
public void Agents_FileEdited_AppliesToNextCall()
{

View File

@@ -22,6 +22,16 @@ public sealed class PendingFileTests : IDisposable
Assert.Equal("b", Assert.Single(PendingFile.Read(_ws)).WatchId);
}
[Fact]
public void Read_NullElements_Filtered()
{
Directory.CreateDirectory(Path.GetDirectoryName(PendingFile.PathFor(_ws))!);
File.WriteAllText(PendingFile.PathFor(_ws), "[null]");
Assert.Empty(PendingFile.Read(_ws));
PendingFile.Upsert(_ws, Record("a"));
Assert.Single(PendingFile.Read(_ws));
}
[Fact]
public void Read_MissingOrMalformed_Empty()
{

View File

@@ -29,12 +29,4 @@
<ProjectReference Include="../../extensions/Volcanic.AgenticCore.Extensions.Scheduler/Volcanic.AgenticCore.Extensions.Scheduler.csproj" />
<ProjectReference Include="../Volcanic.AgenticCore.Extensions.Tests.FakeMcpServer/Volcanic.AgenticCore.Extensions.Tests.FakeMcpServer.csproj" />
</ItemGroup>
<!-- The ASP.NET shared framework (FrameworkReference above) makes the SDK skip copying Microsoft.Extensions.* assemblies,
but the fake MCP server runs on Microsoft.NETCore.App only and needs its own copies next to its dll. -->
<Target Name="CopyFakeMcpServerFrameworkAssemblies" AfterTargets="Build">
<ItemGroup>
<FakeMcpExtraDll Include="$(MSBuildProjectDirectory)/../Volcanic.AgenticCore.Extensions.Tests.FakeMcpServer/bin/$(Configuration)/$(TargetFramework)/Microsoft.Extensions.*.dll" />
</ItemGroup>
<Copy SourceFiles="@(FakeMcpExtraDll)" DestinationFolder="$(OutDir)" SkipUnchangedFiles="true" />
</Target>
</Project>