From 7d56c4d560b60892b50a4a98a52d21b662f77ea1 Mon Sep 17 00:00:00 2001 From: Copilot Date: Wed, 29 Jul 2026 11:29:34 +0200 Subject: [PATCH 1/7] Upgrade Squad sample Copilot dependencies Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Directory.Packages.props | 14 +++++------- ...Toolkit.Aspire.Hosting.Squad.ApiApp.csproj | 22 +++++++++++++------ 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 74e3b4e47..09aabaec6 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -77,13 +77,10 @@ - - + + @@ -106,7 +103,8 @@ - + + diff --git a/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.ApiApp/CommunityToolkit.Aspire.Hosting.Squad.ApiApp.csproj b/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.ApiApp/CommunityToolkit.Aspire.Hosting.Squad.ApiApp.csproj index fca71350b..50319bb87 100644 --- a/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.ApiApp/CommunityToolkit.Aspire.Hosting.Squad.ApiApp.csproj +++ b/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.ApiApp/CommunityToolkit.Aspire.Hosting.Squad.ApiApp.csproj @@ -1,16 +1,24 @@ + + + <_MicrosoftAgentsAICopilotSdkVersion>1.0.8 + + - + - + + From faff44224863a6bb1283fce8374d4d7fb9756604 Mon Sep 17 00:00:00 2001 From: Copilot Date: Wed, 29 Jul 2026 11:46:16 +0200 Subject: [PATCH 2/7] Select Squad tests for example dependency bumps Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/testing/select-affected-tests.cs | 130 ++++++++++++++++++++++----- 1 file changed, 107 insertions(+), 23 deletions(-) diff --git a/eng/testing/select-affected-tests.cs b/eng/testing/select-affected-tests.cs index 9d0f41964..22b7dee87 100644 --- a/eng/testing/select-affected-tests.cs +++ b/eng/testing/select-affected-tests.cs @@ -54,7 +54,8 @@ public static async Task MainAsync(string[] args) var diffFiles = await ChangedFilesAsync(repoRoot, options.BaseSha, options.HeadSha); var (projectRefs, packageRefs, projectPaths) = ProjectData(repoRoot); var nodeToTests = BuildNodeToTests(allTests, projectRefs); - var packageToTests = BuildPackageToTests(packageRefs, nodeToTests); + var exampleToTests = BuildExampleToTests(projectPaths, projectRefs, nodeToTests); + var packageToTests = BuildPackageToTests(packageRefs, projectRefs, nodeToTests, exampleToTests); var packageToProjects = BuildPackageToProjects(packageRefs); var testInfraPackages = LoadTestInfraPackages(repoRoot); var typeScriptAppHostToTests = BuildTypeScriptAppHostToTests(repoRoot, projectPaths); @@ -144,13 +145,18 @@ public static async Task MainAsync(string[] args) var project = NearestProject(filePath, projectPaths); if (project is not null) { - if (nodeToTests.TryGetValue(project, out var impacted)) + var impacted = CollectImpactedTests(project, projectRefs, nodeToTests); + if (impacted.Count == 0 && + TryGetExampleName(project, out var projectExampleName) && + exampleToTests.TryGetValue(projectExampleName, out var exampleImpacted)) + { + impacted = exampleImpacted; + } + + if (impacted.Count > 0) { selected.UnionWith(impacted); - if (impacted.Count > 0) - { - reasons.Add($"Selected {impacted.Count} tests because {filePath} belongs to {project}."); - } + reasons.Add($"Selected {impacted.Count} tests because {filePath} belongs to {project}."); } continue; @@ -172,7 +178,7 @@ public static async Task MainAsync(string[] args) continue; } - if (TryGetExampleTests(filePath, projectPaths, nodeToTests, out var exampleName, out var impactedExampleTests)) + if (TryGetExampleTests(filePath, exampleToTests, out var exampleName, out var impactedExampleTests)) { selected.UnionWith(impactedExampleTests); reasons.Add($"Selected {impactedExampleTests.Count} tests because {filePath} belongs to example {exampleName}."); @@ -419,13 +425,23 @@ private static Dictionary> BuildNodeToTests( private static Dictionary> BuildPackageToTests( Dictionary> packageRefs, - Dictionary> nodeToTests) + Dictionary> projectRefs, + Dictionary> nodeToTests, + Dictionary> exampleToTests) { var packageToTests = new Dictionary>(StringComparer.Ordinal); foreach (var (project, packages) in packageRefs) { - if (!nodeToTests.TryGetValue(project, out var impacted)) + var impacted = CollectImpactedTests(project, projectRefs, nodeToTests); + if (impacted.Count == 0 && + TryGetExampleName(project, out var exampleName) && + exampleToTests.TryGetValue(exampleName, out var exampleImpacted)) + { + impacted = exampleImpacted; + } + + if (impacted.Count == 0) { continue; } @@ -445,6 +461,68 @@ private static Dictionary> BuildPackageToTests( return packageToTests; } + private static Dictionary> BuildExampleToTests( + List projectPaths, + Dictionary> projectRefs, + Dictionary> nodeToTests) + { + var exampleToTests = new Dictionary>(StringComparer.Ordinal); + + foreach (var projectPath in projectPaths.Where(static path => path.StartsWith("examples/", StringComparison.Ordinal))) + { + if (!TryGetExampleName(projectPath, out var exampleName)) + { + continue; + } + + if (!exampleToTests.TryGetValue(exampleName, out var tests)) + { + tests = new HashSet(StringComparer.Ordinal); + exampleToTests[exampleName] = tests; + } + + tests.UnionWith(CollectImpactedTests(projectPath, projectRefs, nodeToTests)); + } + + return exampleToTests; + } + + private static HashSet CollectImpactedTests( + string project, + Dictionary> projectRefs, + Dictionary> nodeToTests) + { + var impacted = new HashSet(StringComparer.Ordinal); + var queue = new Stack(); + var seen = new HashSet(StringComparer.Ordinal); + + queue.Push(project); + + while (queue.Count > 0) + { + var current = queue.Pop(); + if (!seen.Add(current)) + { + continue; + } + + if (nodeToTests.TryGetValue(current, out var currentTests)) + { + impacted.UnionWith(currentTests); + } + + if (projectRefs.TryGetValue(current, out var refs)) + { + foreach (var reference in refs) + { + queue.Push(reference); + } + } + } + + return impacted; + } + private static Dictionary> BuildPackageToProjects(Dictionary> packageRefs) { var packageToProjects = new Dictionary>(StringComparer.Ordinal); @@ -580,38 +658,44 @@ private static bool TryGetTypeScriptAppHostTests( private static bool TryGetExampleTests( string filePath, - List projectPaths, - Dictionary> nodeToTests, + Dictionary> exampleToTests, out string exampleName, out HashSet tests) { tests = []; exampleName = string.Empty; - if (!filePath.StartsWith("examples/", StringComparison.Ordinal)) + if (!TryGetExampleName(filePath, out exampleName)) { return false; } - var segments = filePath.Split('/', StringSplitOptions.RemoveEmptyEntries); - if (segments.Length < 2) + if (!exampleToTests.TryGetValue(exampleName, out var impacted)) { return false; } - exampleName = segments[1]; - var examplePrefix = $"examples/{exampleName}/"; - var exampleProjects = projectPaths.Where(projectPath => projectPath.StartsWith(examplePrefix, StringComparison.Ordinal)); + tests = impacted; + return impacted.Count > 0; + } - foreach (var projectPath in exampleProjects) + private static bool TryGetExampleName(string path, out string exampleName) + { + exampleName = string.Empty; + + if (!path.StartsWith("examples/", StringComparison.Ordinal)) { - if (nodeToTests.TryGetValue(projectPath, out var impacted)) - { - tests.UnionWith(impacted); - } + return false; + } + + var segments = path.Split('/', StringSplitOptions.RemoveEmptyEntries); + if (segments.Length < 2) + { + return false; } - return tests.Count > 0; + exampleName = segments[1]; + return true; } private static Dictionary ParseStringConstants(string contents) From 967ed3ef2c9972aeac05dd6a51c50e98f3ce396e Mon Sep 17 00:00:00 2001 From: Copilot Date: Wed, 29 Jul 2026 11:57:11 +0200 Subject: [PATCH 3/7] Revert "Select Squad tests for example dependency bumps" This reverts commit faff44224863a6bb1283fce8374d4d7fb9756604. --- eng/testing/select-affected-tests.cs | 130 +++++---------------------- 1 file changed, 23 insertions(+), 107 deletions(-) diff --git a/eng/testing/select-affected-tests.cs b/eng/testing/select-affected-tests.cs index 22b7dee87..9d0f41964 100644 --- a/eng/testing/select-affected-tests.cs +++ b/eng/testing/select-affected-tests.cs @@ -54,8 +54,7 @@ public static async Task MainAsync(string[] args) var diffFiles = await ChangedFilesAsync(repoRoot, options.BaseSha, options.HeadSha); var (projectRefs, packageRefs, projectPaths) = ProjectData(repoRoot); var nodeToTests = BuildNodeToTests(allTests, projectRefs); - var exampleToTests = BuildExampleToTests(projectPaths, projectRefs, nodeToTests); - var packageToTests = BuildPackageToTests(packageRefs, projectRefs, nodeToTests, exampleToTests); + var packageToTests = BuildPackageToTests(packageRefs, nodeToTests); var packageToProjects = BuildPackageToProjects(packageRefs); var testInfraPackages = LoadTestInfraPackages(repoRoot); var typeScriptAppHostToTests = BuildTypeScriptAppHostToTests(repoRoot, projectPaths); @@ -145,18 +144,13 @@ public static async Task MainAsync(string[] args) var project = NearestProject(filePath, projectPaths); if (project is not null) { - var impacted = CollectImpactedTests(project, projectRefs, nodeToTests); - if (impacted.Count == 0 && - TryGetExampleName(project, out var projectExampleName) && - exampleToTests.TryGetValue(projectExampleName, out var exampleImpacted)) - { - impacted = exampleImpacted; - } - - if (impacted.Count > 0) + if (nodeToTests.TryGetValue(project, out var impacted)) { selected.UnionWith(impacted); - reasons.Add($"Selected {impacted.Count} tests because {filePath} belongs to {project}."); + if (impacted.Count > 0) + { + reasons.Add($"Selected {impacted.Count} tests because {filePath} belongs to {project}."); + } } continue; @@ -178,7 +172,7 @@ public static async Task MainAsync(string[] args) continue; } - if (TryGetExampleTests(filePath, exampleToTests, out var exampleName, out var impactedExampleTests)) + if (TryGetExampleTests(filePath, projectPaths, nodeToTests, out var exampleName, out var impactedExampleTests)) { selected.UnionWith(impactedExampleTests); reasons.Add($"Selected {impactedExampleTests.Count} tests because {filePath} belongs to example {exampleName}."); @@ -425,23 +419,13 @@ private static Dictionary> BuildNodeToTests( private static Dictionary> BuildPackageToTests( Dictionary> packageRefs, - Dictionary> projectRefs, - Dictionary> nodeToTests, - Dictionary> exampleToTests) + Dictionary> nodeToTests) { var packageToTests = new Dictionary>(StringComparer.Ordinal); foreach (var (project, packages) in packageRefs) { - var impacted = CollectImpactedTests(project, projectRefs, nodeToTests); - if (impacted.Count == 0 && - TryGetExampleName(project, out var exampleName) && - exampleToTests.TryGetValue(exampleName, out var exampleImpacted)) - { - impacted = exampleImpacted; - } - - if (impacted.Count == 0) + if (!nodeToTests.TryGetValue(project, out var impacted)) { continue; } @@ -461,68 +445,6 @@ private static Dictionary> BuildPackageToTests( return packageToTests; } - private static Dictionary> BuildExampleToTests( - List projectPaths, - Dictionary> projectRefs, - Dictionary> nodeToTests) - { - var exampleToTests = new Dictionary>(StringComparer.Ordinal); - - foreach (var projectPath in projectPaths.Where(static path => path.StartsWith("examples/", StringComparison.Ordinal))) - { - if (!TryGetExampleName(projectPath, out var exampleName)) - { - continue; - } - - if (!exampleToTests.TryGetValue(exampleName, out var tests)) - { - tests = new HashSet(StringComparer.Ordinal); - exampleToTests[exampleName] = tests; - } - - tests.UnionWith(CollectImpactedTests(projectPath, projectRefs, nodeToTests)); - } - - return exampleToTests; - } - - private static HashSet CollectImpactedTests( - string project, - Dictionary> projectRefs, - Dictionary> nodeToTests) - { - var impacted = new HashSet(StringComparer.Ordinal); - var queue = new Stack(); - var seen = new HashSet(StringComparer.Ordinal); - - queue.Push(project); - - while (queue.Count > 0) - { - var current = queue.Pop(); - if (!seen.Add(current)) - { - continue; - } - - if (nodeToTests.TryGetValue(current, out var currentTests)) - { - impacted.UnionWith(currentTests); - } - - if (projectRefs.TryGetValue(current, out var refs)) - { - foreach (var reference in refs) - { - queue.Push(reference); - } - } - } - - return impacted; - } - private static Dictionary> BuildPackageToProjects(Dictionary> packageRefs) { var packageToProjects = new Dictionary>(StringComparer.Ordinal); @@ -658,44 +580,38 @@ private static bool TryGetTypeScriptAppHostTests( private static bool TryGetExampleTests( string filePath, - Dictionary> exampleToTests, + List projectPaths, + Dictionary> nodeToTests, out string exampleName, out HashSet tests) { tests = []; exampleName = string.Empty; - if (!TryGetExampleName(filePath, out exampleName)) + if (!filePath.StartsWith("examples/", StringComparison.Ordinal)) { return false; } - if (!exampleToTests.TryGetValue(exampleName, out var impacted)) + var segments = filePath.Split('/', StringSplitOptions.RemoveEmptyEntries); + if (segments.Length < 2) { return false; } - tests = impacted; - return impacted.Count > 0; - } - - private static bool TryGetExampleName(string path, out string exampleName) - { - exampleName = string.Empty; - - if (!path.StartsWith("examples/", StringComparison.Ordinal)) - { - return false; - } + exampleName = segments[1]; + var examplePrefix = $"examples/{exampleName}/"; + var exampleProjects = projectPaths.Where(projectPath => projectPath.StartsWith(examplePrefix, StringComparison.Ordinal)); - var segments = path.Split('/', StringSplitOptions.RemoveEmptyEntries); - if (segments.Length < 2) + foreach (var projectPath in exampleProjects) { - return false; + if (nodeToTests.TryGetValue(projectPath, out var impacted)) + { + tests.UnionWith(impacted); + } } - exampleName = segments[1]; - return true; + return tests.Count > 0; } private static Dictionary ParseStringConstants(string contents) From 9c895cfba817e7a493af7664c8e6fd9071196790 Mon Sep 17 00:00:00 2001 From: Copilot Date: Wed, 29 Jul 2026 12:12:03 +0200 Subject: [PATCH 4/7] Add Squad AppHost smoke test Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../AppHostTests.cs | 42 +++++++++++++++++++ ...yToolkit.Aspire.Hosting.Squad.Tests.csproj | 1 + 2 files changed, 43 insertions(+) create mode 100644 tests/CommunityToolkit.Aspire.Hosting.Squad.Tests/AppHostTests.cs diff --git a/tests/CommunityToolkit.Aspire.Hosting.Squad.Tests/AppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Squad.Tests/AppHostTests.cs new file mode 100644 index 000000000..557722ee0 --- /dev/null +++ b/tests/CommunityToolkit.Aspire.Hosting.Squad.Tests/AppHostTests.cs @@ -0,0 +1,42 @@ +using CommunityToolkit.Aspire.Testing; + +namespace CommunityToolkit.Aspire.Hosting.Squad.Tests; + +public class AppHostTests(AspireIntegrationTestFixture fixture) + : IClassFixture> +{ + [Fact] + public async Task SquadSampleAppHost_StartsAndServesMetadata() + { + const string resourceName = "squad-api"; + var httpClient = fixture.CreateHttpClient(resourceName); + using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(2)); + + HttpResponseMessage? response = null; + while (!cts.IsCancellationRequested) + { + try + { + response = await httpClient.GetAsync("/", cts.Token); + if (response.IsSuccessStatusCode) + { + break; + } + } + catch when (!cts.IsCancellationRequested) + { + } + + await Task.Delay(TimeSpan.FromSeconds(2), cts.Token); + } + + Assert.NotNull(response); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + var body = await response.Content.ReadAsStringAsync(); + Assert.Contains("research-squad", body, StringComparison.Ordinal); + Assert.Contains("dev-squad", body, StringComparison.Ordinal); + Assert.Contains("/ask", body, StringComparison.Ordinal); + } +} diff --git a/tests/CommunityToolkit.Aspire.Hosting.Squad.Tests/CommunityToolkit.Aspire.Hosting.Squad.Tests.csproj b/tests/CommunityToolkit.Aspire.Hosting.Squad.Tests/CommunityToolkit.Aspire.Hosting.Squad.Tests.csproj index dc02b8d2a..cc0828641 100644 --- a/tests/CommunityToolkit.Aspire.Hosting.Squad.Tests/CommunityToolkit.Aspire.Hosting.Squad.Tests.csproj +++ b/tests/CommunityToolkit.Aspire.Hosting.Squad.Tests/CommunityToolkit.Aspire.Hosting.Squad.Tests.csproj @@ -1,6 +1,7 @@ + From 48e06e415883fd200297658ba655f7ca5fc24947 Mon Sep 17 00:00:00 2001 From: Copilot Date: Wed, 29 Jul 2026 12:25:14 +0200 Subject: [PATCH 5/7] Fix Squad AppHost smoke test on Windows Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../CommunityToolkit.Aspire.Hosting.Squad.Tests/AppHostTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/CommunityToolkit.Aspire.Hosting.Squad.Tests/AppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Squad.Tests/AppHostTests.cs index 557722ee0..b37b10a54 100644 --- a/tests/CommunityToolkit.Aspire.Hosting.Squad.Tests/AppHostTests.cs +++ b/tests/CommunityToolkit.Aspire.Hosting.Squad.Tests/AppHostTests.cs @@ -9,7 +9,7 @@ public class AppHostTests(AspireIntegrationTestFixture Date: Wed, 29 Jul 2026 14:04:05 +0200 Subject: [PATCH 6/7] Remove redundant Copilot SDK override Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../CommunityToolkit.Aspire.Hosting.Squad.ApiApp.csproj | 8 -------- 1 file changed, 8 deletions(-) diff --git a/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.ApiApp/CommunityToolkit.Aspire.Hosting.Squad.ApiApp.csproj b/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.ApiApp/CommunityToolkit.Aspire.Hosting.Squad.ApiApp.csproj index 50319bb87..8bdea1760 100644 --- a/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.ApiApp/CommunityToolkit.Aspire.Hosting.Squad.ApiApp.csproj +++ b/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.ApiApp/CommunityToolkit.Aspire.Hosting.Squad.ApiApp.csproj @@ -1,13 +1,5 @@ - - - <_MicrosoftAgentsAICopilotSdkVersion>1.0.8 - - From 1bf1ceef5d31ad43f4da4d9235c797df65a3b665 Mon Sep 17 00:00:00 2001 From: Copilot Date: Thu, 30 Jul 2026 18:02:45 +0200 Subject: [PATCH 7/7] Polish Squad sample upgrade Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...Toolkit.Aspire.Hosting.Squad.ApiApp.csproj | 13 +++--- .../Program.cs | 32 +++++++++++--- .../AppHostTests.cs | 43 ++++++++++++++----- 3 files changed, 66 insertions(+), 22 deletions(-) diff --git a/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.ApiApp/CommunityToolkit.Aspire.Hosting.Squad.ApiApp.csproj b/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.ApiApp/CommunityToolkit.Aspire.Hosting.Squad.ApiApp.csproj index 8bdea1760..2b2d94e59 100644 --- a/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.ApiApp/CommunityToolkit.Aspire.Hosting.Squad.ApiApp.csproj +++ b/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.ApiApp/CommunityToolkit.Aspire.Hosting.Squad.ApiApp.csproj @@ -3,13 +3,16 @@ - + + diff --git a/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.ApiApp/Program.cs b/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.ApiApp/Program.cs index c783f0404..7dba6b703 100644 --- a/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.ApiApp/Program.cs +++ b/examples/squad/CommunityToolkit.Aspire.Hosting.Squad.ApiApp/Program.cs @@ -10,7 +10,7 @@ // or CliArgs boilerplate required. // // The single POST /ask endpoint takes a ?squad=research|dev query parameter and a -// JSON body with a list of prompts. The coordinator decides per-prompt whether to +// JSON body with either a single `prompt` or a list of `prompts`. The coordinator decides per-prompt whether to // answer directly (Direct Mode in squad.agent.md), do a single agent spawn // (Lightweight), or fan out via the task tool (Full). The "Sample prompts" // section returned from GET / shows what each mode looks like — paste them @@ -131,11 +131,12 @@ // Index: shows the single /ask endpoint plus a copy-paste menu of sample // prompts that exercise each coordinator mode (Direct, Lightweight, Full). -// Same body shape as /ask accepts — just `{"prompts": [...]}`. +// Same body shape as /ask accepts — either `{"prompts": [...]}` or, for a +// quick single turn, `{"prompt": "..."}`. app.MapGet("/", () => Results.Ok(new { squads = squadKeysByShortName, - endpoint = "POST /ask?squad=research|dev — body: { \"prompts\": [...] }. " + + endpoint = "POST /ask?squad=research|dev — body: { \"prompts\": [...] } or { \"prompt\": \"...\" }. " + "Each prompt runs sequentially on a single AgentSession (multi-turn memory). " + "The coordinator picks Direct / Lightweight / Full mode per prompt; only Full mode produces squad.subagent spans.", sample_prompts = new @@ -201,17 +202,23 @@ var agent = ResolveSquad(sp, q.Squad, out var error); if (agent is null) return Results.BadRequest(new { error }); + var prompts = req.GetPrompts(); + if (prompts.Length == 0) + { + return Results.BadRequest(new { error = "Provide either 'prompt' or a non-empty 'prompts' array." }); + } + using var activity = ApiAppDiagnostics.ActivitySource .StartActivity($"squad.ask {q.Squad}", ActivityKind.Server); activity?.SetTag("squad.name", q.Squad); - activity?.SetTag("squad.prompt.count", req.Prompts.Length); + activity?.SetTag("squad.prompt.count", prompts.Length); - logger.LogInformation("/ask squad={Squad} prompts={Count}", q.Squad, req.Prompts.Length); + logger.LogInformation("/ask squad={Squad} prompts={Count}", q.Squad, prompts.Length); var turns = new List(); var session = await agent.CreateSessionAsync(ct); - foreach (var prompt in req.Prompts) + foreach (var prompt in prompts) { var response = await agent.RunAsync(prompt, session, cancellationToken: ct); turns.Add(new TurnResult(prompt, response.Text)); @@ -239,7 +246,18 @@ internal sealed record SquadQuery(string Squad = "research"); -internal sealed record AskRequest(string[] Prompts); +internal sealed record AskRequest(string[]? Prompts, string? Prompt = null) +{ + public string[] GetPrompts() + { + if (Prompts is { Length: > 0 }) + { + return Prompts; + } + + return string.IsNullOrWhiteSpace(Prompt) ? [] : [Prompt]; + } +} internal sealed record TurnResult(string Prompt, string? Response); diff --git a/tests/CommunityToolkit.Aspire.Hosting.Squad.Tests/AppHostTests.cs b/tests/CommunityToolkit.Aspire.Hosting.Squad.Tests/AppHostTests.cs index b37b10a54..654392850 100644 --- a/tests/CommunityToolkit.Aspire.Hosting.Squad.Tests/AppHostTests.cs +++ b/tests/CommunityToolkit.Aspire.Hosting.Squad.Tests/AppHostTests.cs @@ -1,3 +1,4 @@ +using Aspire.Hosting.ApplicationModel; using CommunityToolkit.Aspire.Testing; namespace CommunityToolkit.Aspire.Hosting.Squad.Tests; @@ -9,8 +10,22 @@ public class AppHostTests(AspireIntegrationTestFixture(); + var apiApp = model.Resources + .OfType() + .Single(resource => resource.Name == resourceName); + + using HttpClient httpClient = new() + { + BaseAddress = new Uri(apiApp.GetEndpoint("http").Url) + }; + + using var cts = CancellationTokenSource.CreateLinkedTokenSource(TestContext.Current.CancellationToken); + cts.CancelAfter(TimeSpan.FromMinutes(2)); HttpResponseMessage? response = null; while (!cts.IsCancellationRequested) @@ -22,21 +37,29 @@ public async Task SquadSampleAppHost_StartsAndServesMetadata() { break; } + + response.Dispose(); + response = null; } - catch when (!cts.IsCancellationRequested) + catch (HttpRequestException) when (!cts.IsCancellationRequested) + { + } + catch (TaskCanceledException) when (!cts.IsCancellationRequested) { } - await Task.Delay(TimeSpan.FromSeconds(2), cts.Token); + await Task.Delay(TimeSpan.FromSeconds(1), cts.Token); } Assert.NotNull(response); + using (response) + { + Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - var body = await response.Content.ReadAsStringAsync(); - Assert.Contains("research-squad", body, StringComparison.Ordinal); - Assert.Contains("dev-squad", body, StringComparison.Ordinal); - Assert.Contains("/ask", body, StringComparison.Ordinal); + var body = await response.Content.ReadAsStringAsync(); + Assert.Contains("research-squad", body, StringComparison.Ordinal); + Assert.Contains("dev-squad", body, StringComparison.Ordinal); + Assert.Contains("/ask", body, StringComparison.Ordinal); + } } }