fix(a2a-client): fake MCP server on the ASP.NET framework, question-first results, safer file names
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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) { } }
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user