Skip to content

Commit fb97e93

Browse files
authored
.NET: Add dedicated Foundry.Hosting UnitTest project (#5592)
* Foundry.Hosting.UnitTests: extract project from Foundry.UnitTests Move all Hosting/* tests, three toolbox TestData JSONs, and the FakeAuthenticationTokenProvider/HttpHandlerAssert/TestDataUtil helpers (trimmed to toolbox getters) into a new Microsoft.Agents.AI.Foundry.Hosting.UnitTests project. Add it to the slnx and grant the new assembly InternalsVisibleTo from Microsoft.Agents.AI.Foundry and Microsoft.Agents.AI.Foundry.Hosting. * Foundry.Hosting.UnitTests: align namespaces to assembly name Rename namespaces from Microsoft.Agents.AI.Foundry.UnitTests(.Hosting) to Microsoft.Agents.AI.Foundry.Hosting.UnitTests across all moved tests, the duplicated helpers, and the trimmed TestDataUtil. Also fixes the prior namespace inconsistency in FoundryToolboxTests. * Foundry.Hosting.UnitTests: split WorkflowIntegrationTests by SUT Replace the WorkflowIntegrationTests file (an IT-named file inside a UT project) with two SUT-focused files plus a shared test-doubles file: - AgentFrameworkResponseHandlerWorkflowTests.cs - the 5 handler-driven tests that exercise AgentFrameworkResponseHandler with a real workflow agent. - OutputConverterWorkflowTests.cs - the 5 OutputConverter tests driven by hand-crafted update sequences mirroring real workflow patterns. - WorkflowTestAgents.cs - StreamingTextAgent and ThrowingStreamingAgent extracted as internal types used by both files. * Foundry.UnitTests: trim Hosting-related conditionals and dead testdata Now that Hosting tests live in their own project: - drop the Compile Remove guard for the Hosting subfolder, - drop the .NETCoreApp-only PackageReferences (Azure.AI.AgentServer.Responses, Microsoft.AspNetCore.TestHost, OpenTelemetry, OpenTelemetry.Exporter.InMemory), - drop the conditional ProjectReference to Microsoft.Agents.AI.Foundry.Hosting, - delete the three Toolbox JSON files and the matching Toolbox getters in TestDataUtil. * Foundry.Hosting.UnitTests: drop redundant 'using Microsoft.Agents.AI.Foundry.Hosting' The new project namespace is Microsoft.Agents.AI.Foundry.Hosting.UnitTests, which already brings the parent Microsoft.Agents.AI.Foundry.Hosting namespace into scope. The explicit using statement is therefore redundant (IDE0005). Caught by 'dotnet format --verify-no-changes' running on Linux against the .NET 10 SDK. * Foundry.Hosting: drop InternalsVisibleTo to Foundry.UnitTests The non-hosting Foundry.UnitTests project no longer holds any Hosting tests after the split, so it doesn't need access to internal types in Microsoft.Agents.AI.Foundry.Hosting. Only Microsoft.Agents.AI.Foundry.Hosting.UnitTests needs it. * Foundry.Hosting: rename DelegatingResponsesClient to UserAgentResponsesClient Address westey-m's review feedback on PR #5453: `Delegating*` is conventionally reserved for inheritable base classes (mirroring `DelegatingHandler`) where consumers override one or two members. This polyfill is sealed and only injects the User-Agent supplement, so the new name reflects its actual purpose. Renamed via `git mv` to preserve history: * `src/Microsoft.Agents.AI.Foundry.Hosting/DelegatingResponsesClient.cs` to `UserAgentResponsesClient.cs` * `tests/Microsoft.Agents.AI.Foundry.Hosting.UnitTests/DelegatingResponsesClientTests.cs` to `UserAgentResponsesClientTests.cs` Class, constructor, and all references updated across: * `src/.../UserAgentResponsesClient.cs` (class + constructor + internal log message) * `src/.../ServiceCollectionExtensions.cs` (cref + type check + instantiation) * `src/.../HostedAgentUserAgentPolicy.cs` (cref) * `tests/Foundry.UnitTests/RequestOptionsExtensionsTests.cs` (comment) * `tests/Foundry.Hosting.UnitTests/UserAgentResponsesClientTests.cs` (class + cref + instantiations)
1 parent 317ef44 commit fb97e93

31 files changed

Lines changed: 681 additions & 591 deletions

dotnet/agent-framework-dotnet.slnx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,7 @@
592592
<Project Path="tests/Microsoft.Agents.AI.DevUI.UnitTests/Microsoft.Agents.AI.DevUI.UnitTests.csproj" />
593593
<Project Path="tests/Microsoft.Agents.AI.DurableTask.UnitTests/Microsoft.Agents.AI.DurableTask.UnitTests.csproj" />
594594
<Project Path="tests/Microsoft.Agents.AI.Foundry.UnitTests/Microsoft.Agents.AI.Foundry.UnitTests.csproj" />
595+
<Project Path="tests/Microsoft.Agents.AI.Foundry.Hosting.UnitTests/Microsoft.Agents.AI.Foundry.Hosting.UnitTests.csproj" />
595596
<Project Path="tests/Microsoft.Agents.AI.GitHub.Copilot.UnitTests/Microsoft.Agents.AI.GitHub.Copilot.UnitTests.csproj" />
596597
<Project Path="tests/Microsoft.Agents.AI.Hosting.A2A.UnitTests/Microsoft.Agents.AI.Hosting.A2A.UnitTests.csproj" />
597598
<Project Path="tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests.csproj" />

dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/HostedAgentUserAgentPolicy.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace Microsoft.Agents.AI.Foundry.Hosting;
1919
/// </para>
2020
/// <para>
2121
/// This policy is added at request time (per-call <see cref="PipelinePosition"/>)
22-
/// by <see cref="DelegatingResponsesClient"/> when invoking the wrapped
22+
/// by <see cref="UserAgentResponsesClient"/> when invoking the wrapped
2323
/// <see cref="OpenAI.Responses.ResponsesClient"/>. It is only registered when an agent is
2424
/// resolved by the Foundry hosting layer.
2525
/// </para>

dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/Microsoft.Agents.AI.Foundry.Hosting.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
</ItemGroup>
4545

4646
<ItemGroup>
47-
<InternalsVisibleTo Include="Microsoft.Agents.AI.Foundry.UnitTests" />
47+
<InternalsVisibleTo Include="Microsoft.Agents.AI.Foundry.Hosting.UnitTests" />
4848
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
4949
</ItemGroup>
5050

dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/ServiceCollectionExtensions.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ internal static AIAgent ApplyOpenTelemetry(AIAgent agent)
210210

211211
/// <summary>
212212
/// Attempts to wrap the agent's underlying <see cref="ResponsesClient"/>
213-
/// with a <see cref="DelegatingResponsesClient"/> so every outgoing Responses-API request
213+
/// with a <see cref="UserAgentResponsesClient"/> so every outgoing Responses-API request
214214
/// carries the hosted-agent <c>User-Agent</c> segment.
215215
/// </summary>
216216
/// <remarks>
@@ -219,7 +219,7 @@ internal static AIAgent ApplyOpenTelemetry(AIAgent agent)
219219
/// <list type="bullet">
220220
/// <item><description><paramref name="agent"/> exposes no <see cref="IChatClient"/>;</description></item>
221221
/// <item><description>the chat client is not backed by MEAI's internal <c>OpenAIResponsesChatClient</c> (e.g., a non-OpenAI provider or a custom impl);</description></item>
222-
/// <item><description>the inner <see cref="ResponsesClient"/> is already a <see cref="DelegatingResponsesClient"/>.</description></item>
222+
/// <item><description>the inner <see cref="ResponsesClient"/> is already a <see cref="UserAgentResponsesClient"/>.</description></item>
223223
/// </list>
224224
/// </para>
225225
/// <para>
@@ -261,12 +261,12 @@ internal static AIAgent TryApplyUserAgent(AIAgent agent)
261261
}
262262

263263
var current = field.GetValue(meaiInstance) as ResponsesClient;
264-
if (current is null or DelegatingResponsesClient)
264+
if (current is null or UserAgentResponsesClient)
265265
{
266266
return agent;
267267
}
268268

269-
field.SetValue(meaiInstance, new DelegatingResponsesClient(current));
269+
field.SetValue(meaiInstance, new UserAgentResponsesClient(current));
270270
return agent;
271271
}
272272

dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/DelegatingResponsesClient.cs renamed to dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/UserAgentResponsesClient.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ namespace Microsoft.Agents.AI.Foundry.Hosting;
3333
/// never expected to run; the throwing transport surfaces any unexpected escape route loudly.
3434
/// </para>
3535
/// </remarks>
36-
internal sealed class DelegatingResponsesClient : ResponsesClient
36+
internal sealed class UserAgentResponsesClient : ResponsesClient
3737
{
3838
private readonly ResponsesClient _inner;
3939

40-
public DelegatingResponsesClient(ResponsesClient inner)
40+
public UserAgentResponsesClient(ResponsesClient inner)
4141
: base(BuildDummyPipeline(), new OpenAIClientOptions { Endpoint = inner?.Endpoint })
4242
{
4343
this._inner = inner ?? throw new ArgumentNullException(nameof(inner));
@@ -104,7 +104,7 @@ private static ClientPipeline BuildDummyPipeline()
104104
private sealed class ThrowingTransport : PipelineTransport
105105
{
106106
private const string Message =
107-
"DelegatingResponsesClient transport invoked bypassed the override-and-delegate design. This exception should be unreachable and should never be thrown following the correct usage of DelegatingResponsesClient.";
107+
"UserAgentResponsesClient transport invoked bypassed the override-and-delegate design. This exception should be unreachable and should never be thrown following the correct usage of UserAgentResponsesClient.";
108108

109109
protected override PipelineMessage CreateMessageCore() => throw new InvalidOperationException(Message);
110110
protected override void ProcessCore(PipelineMessage message) => throw new InvalidOperationException(Message);

dotnet/src/Microsoft.Agents.AI.Foundry/Microsoft.Agents.AI.Foundry.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353

5454
<ItemGroup>
5555
<InternalsVisibleTo Include="Microsoft.Agents.AI.Foundry.UnitTests" />
56+
<InternalsVisibleTo Include="Microsoft.Agents.AI.Foundry.Hosting.UnitTests" />
5657
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
5758
</ItemGroup>
5859

dotnet/tests/Microsoft.Agents.AI.Foundry.UnitTests/Hosting/AgentFrameworkResponseHandlerTelemetryTests.cs renamed to dotnet/tests/Microsoft.Agents.AI.Foundry.Hosting.UnitTests/AgentFrameworkResponseHandlerTelemetryTests.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
using System.Threading.Tasks;
1111
using Azure.AI.AgentServer.Responses;
1212
using Azure.AI.AgentServer.Responses.Models;
13-
using Microsoft.Agents.AI.Foundry.Hosting;
1413
using Microsoft.Extensions.AI;
1514
using Microsoft.Extensions.DependencyInjection;
1615
using Microsoft.Extensions.Logging.Abstractions;
@@ -19,7 +18,7 @@
1918
using OpenTelemetry.Trace;
2019
using MeaiTextContent = Microsoft.Extensions.AI.TextContent;
2120

22-
namespace Microsoft.Agents.AI.Foundry.UnitTests.Hosting;
21+
namespace Microsoft.Agents.AI.Foundry.Hosting.UnitTests;
2322

2423
/// <summary>
2524
/// Tests that verify OTel spans are actually emitted and captured through the

dotnet/tests/Microsoft.Agents.AI.Foundry.UnitTests/Hosting/AgentFrameworkResponseHandlerTests.cs renamed to dotnet/tests/Microsoft.Agents.AI.Foundry.Hosting.UnitTests/AgentFrameworkResponseHandlerTests.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,14 @@
99
using System.Threading.Tasks;
1010
using Azure.AI.AgentServer.Responses;
1111
using Azure.AI.AgentServer.Responses.Models;
12-
using Microsoft.Agents.AI.Foundry.Hosting;
1312
using Microsoft.Extensions.AI;
1413
using Microsoft.Extensions.DependencyInjection;
1514
using Microsoft.Extensions.Logging;
1615
using Microsoft.Extensions.Logging.Abstractions;
1716
using Moq;
1817
using MeaiTextContent = Microsoft.Extensions.AI.TextContent;
1918

20-
namespace Microsoft.Agents.AI.Foundry.UnitTests.Hosting;
19+
namespace Microsoft.Agents.AI.Foundry.Hosting.UnitTests;
2120

2221
public class AgentFrameworkResponseHandlerTests
2322
{
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Azure.AI.AgentServer.Responses;
9+
using Azure.AI.AgentServer.Responses.Models;
10+
using Microsoft.Agents.AI.Workflows;
11+
using Microsoft.Extensions.DependencyInjection;
12+
using Microsoft.Extensions.Logging;
13+
using Microsoft.Extensions.Logging.Abstractions;
14+
using Moq;
15+
16+
namespace Microsoft.Agents.AI.Foundry.Hosting.UnitTests;
17+
18+
/// <summary>
19+
/// Unit tests for <see cref="AgentFrameworkResponseHandler"/> that verify behavior
20+
/// when the registered agent is a workflow-backed <see cref="AIAgent"/>. These exercise
21+
/// real workflow builders and the in-process execution environment to drive the handler
22+
/// through realistic streaming event patterns.
23+
/// </summary>
24+
public class AgentFrameworkResponseHandlerWorkflowTests
25+
{
26+
[Fact]
27+
public async Task SequentialWorkflow_SingleAgent_ProducesTextOutputAsync()
28+
{
29+
// Arrange: single-agent sequential workflow
30+
var echoAgent = new StreamingTextAgent("echo", "Hello from the workflow!");
31+
var workflow = AgentWorkflowBuilder.BuildSequential("test-sequential", echoAgent);
32+
var workflowAgent = workflow.AsAIAgent(
33+
id: "workflow-agent",
34+
name: "Test Workflow",
35+
executionEnvironment: InProcessExecution.OffThread,
36+
includeExceptionDetails: true);
37+
38+
var (handler, request, context) = CreateHandlerWithAgent(workflowAgent, "Hello");
39+
40+
// Act
41+
var events = await CollectEventsAsync(handler, request, context);
42+
43+
// Assert: should have lifecycle events + at least one text output + terminal
44+
Assert.IsType<ResponseCreatedEvent>(events[0]);
45+
Assert.IsType<ResponseInProgressEvent>(events[1]);
46+
Assert.True(events.Count >= 4, $"Expected at least 4 events, got {events.Count}");
47+
48+
var lastEvent = events[^1];
49+
Assert.True(
50+
lastEvent is ResponseCompletedEvent || lastEvent is ResponseFailedEvent,
51+
$"Expected terminal event, got {lastEvent.GetType().Name}");
52+
}
53+
54+
[Fact]
55+
public async Task SequentialWorkflow_TwoAgents_ProducesOutputFromBothAsync()
56+
{
57+
// Arrange: two agents in sequence
58+
var agent1 = new StreamingTextAgent("agent1", "First agent says hello");
59+
var agent2 = new StreamingTextAgent("agent2", "Second agent says goodbye");
60+
var workflow = AgentWorkflowBuilder.BuildSequential("test-sequential-2", agent1, agent2);
61+
var workflowAgent = workflow.AsAIAgent(
62+
id: "seq-workflow",
63+
name: "Sequential Workflow",
64+
executionEnvironment: InProcessExecution.OffThread,
65+
includeExceptionDetails: true);
66+
67+
var (handler, request, context) = CreateHandlerWithAgent(workflowAgent, "Process this");
68+
69+
// Act
70+
var events = await CollectEventsAsync(handler, request, context);
71+
72+
// Assert: should have workflow action events for executor lifecycle
73+
var lastEvent = events[^1];
74+
Assert.True(
75+
lastEvent is ResponseCompletedEvent || lastEvent is ResponseFailedEvent,
76+
$"Expected terminal event, got {lastEvent.GetType().Name}");
77+
78+
// Should have output item events (either text messages or workflow actions)
79+
Assert.True(events.OfType<ResponseOutputItemAddedEvent>().Any(),
80+
"Expected at least one output item from the workflow");
81+
}
82+
83+
[Fact]
84+
public async Task Workflow_AgentThrowsException_ProducesErrorOutputAsync()
85+
{
86+
// Arrange: workflow with an agent that throws
87+
var throwingAgent = new ThrowingStreamingAgent("thrower", new InvalidOperationException("Agent crashed"));
88+
var workflow = AgentWorkflowBuilder.BuildSequential("test-error", throwingAgent);
89+
var workflowAgent = workflow.AsAIAgent(
90+
id: "error-workflow",
91+
name: "Error Workflow",
92+
executionEnvironment: InProcessExecution.OffThread,
93+
includeExceptionDetails: true);
94+
95+
var (handler, request, context) = CreateHandlerWithAgent(workflowAgent, "Trigger error");
96+
97+
// Act
98+
var events = await CollectEventsAsync(handler, request, context);
99+
100+
// Assert: should have lifecycle events + error/failure indicator
101+
Assert.IsType<ResponseCreatedEvent>(events[0]);
102+
Assert.IsType<ResponseInProgressEvent>(events[1]);
103+
104+
var lastEvent = events[^1];
105+
// Workflow errors surface as either Failed or Completed (depending on error handling)
106+
Assert.True(
107+
lastEvent is ResponseCompletedEvent || lastEvent is ResponseFailedEvent,
108+
$"Expected terminal event, got {lastEvent.GetType().Name}");
109+
}
110+
111+
[Fact]
112+
public async Task Workflow_ExecutorEvents_ProduceWorkflowActionItemsAsync()
113+
{
114+
// Arrange
115+
var agent = new StreamingTextAgent("test-agent", "Result");
116+
var workflow = AgentWorkflowBuilder.BuildSequential("test-actions", agent);
117+
var workflowAgent = workflow.AsAIAgent(
118+
id: "actions-workflow",
119+
name: "Actions Workflow",
120+
executionEnvironment: InProcessExecution.OffThread);
121+
122+
var (handler, request, context) = CreateHandlerWithAgent(workflowAgent, "Hello");
123+
124+
// Act
125+
var events = await CollectEventsAsync(handler, request, context);
126+
127+
// Assert: workflow should produce OutputItemAdded events for executor lifecycle
128+
var addedEvents = events.OfType<ResponseOutputItemAddedEvent>().ToList();
129+
Assert.True(addedEvents.Count >= 1,
130+
$"Expected at least 1 output item added event, got {addedEvents.Count}");
131+
}
132+
133+
[Fact]
134+
public async Task WorkflowAgent_RegisteredWithKey_ResolvesCorrectlyAsync()
135+
{
136+
// Arrange: workflow agent registered with a keyed service name
137+
var agent = new StreamingTextAgent("inner", "Keyed workflow response");
138+
var workflow = AgentWorkflowBuilder.BuildSequential("keyed-wf", agent);
139+
var workflowAgent = workflow.AsAIAgent(
140+
id: "keyed-workflow",
141+
name: "Keyed Workflow",
142+
executionEnvironment: InProcessExecution.OffThread);
143+
144+
var services = new ServiceCollection();
145+
services.AddSingleton<AgentSessionStore>(new InMemoryAgentSessionStore());
146+
services.AddKeyedSingleton("my-workflow", workflowAgent);
147+
var sp = services.BuildServiceProvider();
148+
149+
var handler = new AgentFrameworkResponseHandler(sp, NullLogger<AgentFrameworkResponseHandler>.Instance);
150+
var request = new CreateResponse { Model = "test", AgentReference = new AgentReference("my-workflow") };
151+
request.Input = CreateUserInput("Test keyed workflow");
152+
var mockContext = CreateMockContext();
153+
154+
// Act
155+
var events = await CollectEventsAsync(handler, request, mockContext.Object);
156+
157+
// Assert
158+
Assert.IsType<ResponseCreatedEvent>(events[0]);
159+
Assert.True(events.Count >= 3, $"Expected at least 3 events, got {events.Count}");
160+
}
161+
162+
private static (AgentFrameworkResponseHandler handler, CreateResponse request, ResponseContext context)
163+
CreateHandlerWithAgent(AIAgent agent, string userMessage)
164+
{
165+
var services = new ServiceCollection();
166+
services.AddSingleton<AgentSessionStore>(new InMemoryAgentSessionStore());
167+
services.AddSingleton(agent);
168+
services.AddSingleton<ILogger<AgentFrameworkResponseHandler>>(NullLogger<AgentFrameworkResponseHandler>.Instance);
169+
var sp = services.BuildServiceProvider();
170+
171+
var handler = new AgentFrameworkResponseHandler(sp, NullLogger<AgentFrameworkResponseHandler>.Instance);
172+
var request = new CreateResponse { Model = "test" };
173+
request.Input = CreateUserInput(userMessage);
174+
var mockContext = CreateMockContext();
175+
176+
return (handler, request, mockContext.Object);
177+
}
178+
179+
private static BinaryData CreateUserInput(string text)
180+
{
181+
return BinaryData.FromObjectAsJson(new[]
182+
{
183+
new { type = "message", id = "msg_in_1", status = "completed", role = "user",
184+
content = new[] { new { type = "input_text", text } }
185+
}
186+
});
187+
}
188+
189+
private static Mock<ResponseContext> CreateMockContext()
190+
{
191+
var mock = new Mock<ResponseContext>("resp_" + new string('0', 46)) { CallBase = true };
192+
mock.Setup(x => x.GetHistoryAsync(It.IsAny<CancellationToken>()))
193+
.ReturnsAsync(Array.Empty<OutputItem>());
194+
mock.Setup(x => x.GetInputItemsAsync(It.IsAny<bool>(), It.IsAny<CancellationToken>()))
195+
.ReturnsAsync(Array.Empty<Item>());
196+
return mock;
197+
}
198+
199+
private static async Task<List<ResponseStreamEvent>> CollectEventsAsync(
200+
AgentFrameworkResponseHandler handler,
201+
CreateResponse request,
202+
ResponseContext context)
203+
{
204+
var events = new List<ResponseStreamEvent>();
205+
await foreach (var evt in handler.CreateAsync(request, context, CancellationToken.None))
206+
{
207+
events.Add(evt);
208+
}
209+
210+
return events;
211+
}
212+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
3+
using System;
4+
using System.ClientModel;
5+
using System.ClientModel.Primitives;
6+
using System.Collections.Generic;
7+
using System.Threading;
8+
using System.Threading.Tasks;
9+
10+
namespace Microsoft.Agents.AI.Foundry.Hosting.UnitTests;
11+
12+
internal sealed class FakeAuthenticationTokenProvider : AuthenticationTokenProvider
13+
{
14+
public override GetTokenOptions? CreateTokenOptions(IReadOnlyDictionary<string, object> properties)
15+
{
16+
return new GetTokenOptions(new Dictionary<string, object>());
17+
}
18+
19+
public override AuthenticationToken GetToken(GetTokenOptions options, CancellationToken cancellationToken)
20+
{
21+
return new AuthenticationToken("token-value", "token-type", DateTimeOffset.UtcNow.AddHours(1));
22+
}
23+
24+
public override ValueTask<AuthenticationToken> GetTokenAsync(GetTokenOptions options, CancellationToken cancellationToken)
25+
{
26+
return new ValueTask<AuthenticationToken>(this.GetToken(options, cancellationToken));
27+
}
28+
}

0 commit comments

Comments
 (0)