Agent Skillsag-ui-protocol/ag-ui › agui-dotnet-unit-tests

agui-dotnet-unit-tests

GitHub

规范 AG-UI .NET SDK 单元测试编写,涵盖序列化、兼容性测试及项目结构约定。

.github/skills/agui-dotnet-unit-tests/SKILL.md ag-ui-protocol/ag-ui

Trigger Scenarios

为 AG-UI .NET SDK 新增类型或方法编写单元测试 验证事件序列化往返及 Protobuf 编解码 执行向后兼容性测试

Install

npx skills add ag-ui-protocol/ag-ui --skill agui-dotnet-unit-tests -g -y
More Options

Non-standard path

npx skills add https://github.com/ag-ui-protocol/ag-ui/tree/main/.github/skills/agui-dotnet-unit-tests -g -y

Use without installing

npx skills use ag-ui-protocol/ag-ui@agui-dotnet-unit-tests

指定 Agent (Claude Code)

npx skills add ag-ui-protocol/ag-ui --skill agui-dotnet-unit-tests -a claude-code -g -y

安装 repo 全部 skill

npx skills add ag-ui-protocol/ag-ui --all -g -y

预览 repo 内 skill

npx skills add ag-ui-protocol/ag-ui --list

SKILL.md

Frontmatter
{
    "name": "agui-dotnet-unit-tests",
    "description": "Author unit tests for the AG-UI .NET SDK (the *.UnitTests projects), following the SDK's serialization and compatibility conventions. USE FOR: adding unit-test coverage for a new type\/method in AGUI.Abstractions\/Formatting\/Protobuf\/Client\/Server, event serialization round-trips, JsonDocument property-name assertions, backward-compatibility fixtures against TypeScript JSON, protobuf codec round-trips, client builder\/handler tests, server ChatResponseUpdate conversion tests, SSE formatter tests. DO NOT USE FOR: HTTP pipeline \/ WebApplicationFactory end-to-end tests (use the agui-dotnet-integration-tests skill), cross-language TS↔C# server-parity tests (use the cross-language test skill)."
}

AG-UI .NET Unit Tests

Conventions for the tests/*.UnitTests projects. Mined from sdks/dotnet/AGENTS.md and existing tests. An agent gets these wrong by default: asserting on deserialized objects (misses naming bugs), comparing full JSON strings (fragile), using reflection, or skipping the FixtureLoader compatibility pattern.

All commands run from sdks/dotnet/.

Unit-test projects

Project Focus
tests/AGUI.Abstractions.UnitTests/ Event serialization round-trips; backward compat vs TypeScript fixtures (Compatibility/)
tests/AGUI.Protobuf.UnitTests/ Protobuf codec round-trips; JsonElement↔protobuf Value conversion
tests/AGUI.Client.UnitTests/ Client builders, content-negotiation handler, transport, protocol rules
tests/AGUI.Server.UnitTests/ ChatResponseUpdate → AG-UI event conversion
tests/AGUI.Formatting.UnitTests/ SSE event-stream formatter (read/write, media type)

Run one project: dotnet test tests/AGUI.Abstractions.UnitTests/

Conventions

  • Test files live directly under the project root (not mirrored into Events/ subfolders). Compatibility tests go in the Compatibility/ subfolder.
  • Test class name: {TypeUnderTest}Test (RunStartedEventTest, ToolCallBuilderTest). Compatibility class: {Category}CompatibilityTest (RunEventsCompatibilityTest).
  • public sealed class, one class per file, file name matches type.
  • [Fact] for single cases, [Theory] + [InlineData] for parameterized cases.
  • Serialize via the source-generated context: AGUIJsonSerializerContext.Default.{Type} — never JsonSerializer.Serialize<object>(...) or hand-rolled options.

Pattern 1 — Event serialization round-trip (Abstractions)

Serialize via the source-gen context, parse with JsonDocument, assert each camelCase property name AND the type discriminator. This is what catches [JsonPropertyName] bugs.

[Fact]
public void Serialization_RoundTrips()
{
    var evt = new RunStartedEvent { ThreadId = "t1", RunId = "r1", Timestamp = 1234567890 };

    var json = JsonSerializer.Serialize(evt, AGUIJsonSerializerContext.Default.RunStartedEvent);
    using var doc = JsonDocument.Parse(json);

    Assert.Equal("RUN_STARTED", doc.RootElement.GetProperty("type").GetString());
    Assert.Equal("t1", doc.RootElement.GetProperty("threadId").GetString());
    Assert.Equal("r1", doc.RootElement.GetProperty("runId").GetString());
    Assert.Equal(1234567890, doc.RootElement.GetProperty("timestamp").GetInt64());
}

Pattern 2 — Backward-compatibility fixtures (Abstractions/Compatibility)

Compatibility/Fixtures/*.json are produced by the TypeScript reference implementation. Deserialize them into .NET types and assert the values — this catches wire-format drift. Load arrays with FixtureLoader; write one test method per event shape.

public sealed class RunEventsCompatibilityTest
{
    private readonly JsonElement[] _fixtures = FixtureLoader.LoadFixture("run-events.json");

    [Fact]
    public void RunStartedEvent_DeserializesFromTypeScriptPayload()
    {
        var evt = FixtureLoader.DeserializeAsBaseEvent(_fixtures[0]);

        var typed = Assert.IsType<RunStartedEvent>(evt);
        Assert.Equal("thread-1234", typed.ThreadId);
        Assert.Equal("run-5678", typed.RunId);
    }
}

Adding a fixture: place the TS-produced JSON array under Compatibility/Fixtures/ (embedded resource), then index into the loaded array per shape.

Pattern 3 — Protobuf codec round-trip (Protobuf)

Encode→decode and assert typed values. Compare JsonElement payloads with JsonTestHelpers.AssertEqual (deep-equals), not string compare. ProtoValueConverter tests verify JsonElementValue for each ValueKind.

var result = RoundTrip(new RunStartedEvent { ThreadId = "thread-1", RunId = "run-1" });
Assert.Equal("thread-1", result.ThreadId);
JsonTestHelpers.AssertEqual(expectedElement, result.RawEvent!.Value);

Pattern 4 — Client builders / handler (Client)

Drive the builder/handler through its real API and assert the produced MEAI types. ToolCallBuilder/TextMessageBuilder: feed events, flush, assert FunctionCallContent, ConversationId, ResponseId. AGUIEventStreamHandler: assert content-negotiation Accept header ordering via a test inner handler. Standard xunit assertions.

Pattern 5 — Server conversion (Server)

Convert a ChatResponseUpdate stream to AG-UI events and assert the sequence with Assert.Collection, type-checking each event and its fields (e.g. run lifecycle wraps content with RunStartedEvent/RunFinishedEvent).

Pattern 6 — SSE formatter (Formatting)

Assert MediaType, CanRead(contentType) (use [Theory]/[InlineData] for case and null/empty handling), and that WriteAsync produces the data: {json} shape per event.

❌ Critical anti-patterns

  1. Never assert only on the deserialized object for serialization tests. Round-trip through your own type hides [JsonPropertyName] mistakes. Parse the JSON with JsonDocument and assert the literal camelCase property names + type discriminator.
  2. Never compare full JSON strings — fragile against property ordering/whitespace. Parse and check individual properties (or JsonTestHelpers.AssertEqual for payloads).
  3. Never use reflection to enumerate types or verify membership. Write explicit per-type / per-shape tests.
  4. Never use ad-hoc serializer options. Serialize through AGUIJsonSerializerContext.Default.{Type} so tests exercise the real AOT context.
  5. Never collapse compatibility shapes into one loop-only assertion. Keep one test method per event shape so a drift failure names the exact event.

Version History

  • 1a78c27 Current 2026-07-24 20:27

Same Skill Collection

skills/ag-ui-a2ui-integration/SKILL.md
.github/skills/agui-cross-sdk-parity/SKILL.md
.github/skills/agui-dojo/SKILL.md
.github/skills/agui-dotnet-agents-sync/SKILL.md
.github/skills/agui-dotnet-code-review/SKILL.md
.github/skills/agui-dotnet-cross-language-tests/SKILL.md
.github/skills/agui-dotnet-feature-workflow/SKILL.md
.github/skills/agui-dotnet-integration-tests/SKILL.md
.github/skills/agui-dotnet-sample-step/SKILL.md
.github/skills/agui-dotnet-sdk-docs/SKILL.md
.github/skills/agui-dotnet-transport/SKILL.md
.github/skills/agui-dotnet-wire-types/SKILL.md
.github/skills/agui-playwright-validate/SKILL.md

Metadata

Files
0
Version
3bba136
Hash
bcef4be6
Indexed
2026-07-24 20:27

Главная - Вики-сайт
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-08-20 12:26
浙ICP备14020137号-1 $Гость$