Skip to content

Commit 07a2c66

Browse files
CQRS Agent Query Migration: Add AgentManagerExtensions with CQRS-backed GetCurrentAgentNameViaQuery (#271)
* Initial plan * CQRS agent query migration: add AgentManagerExtensions with GetCurrentAgentNameViaQuery Co-authored-by: michaelbeale-IL <63321611+michaelbeale-IL@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: michaelbeale-IL <63321611+michaelbeale-IL@users.noreply.github.com>
1 parent 04b7b0e commit 07a2c66

File tree

3 files changed

+141
-0
lines changed

3 files changed

+141
-0
lines changed

src/Libraries/ACATCore.Tests.Configuration/CQRSPatternTests.cs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
using ACAT.Core.Configuration;
1515
using ACAT.Core.Patterns.CQRS;
1616
using ACAT.Core.Patterns.CQRS.Samples;
17+
using ACAT.Core.PanelManagement;
1718
using ACAT.Core.PanelManagement.Common;
19+
using Microsoft.Extensions.DependencyInjection;
1820
using Microsoft.VisualStudio.TestTools.UnitTesting;
1921
using System;
2022

@@ -222,5 +224,69 @@ public void GetActiveAgentNameQueryHandler_ReturnsEmptyStringWhenNoAgent()
222224

223225
Assert.AreEqual(string.Empty, result);
224226
}
227+
228+
// ----------------------------------------------------------------
229+
// AgentManagerExtensions.GetCurrentAgentNameViaQuery – behaviour
230+
// ----------------------------------------------------------------
231+
232+
[TestCleanup]
233+
public void ResetServiceProvider()
234+
{
235+
// Ensure Context.ServiceProvider is reset after every test so
236+
// that DI-enabled tests do not leak state into subsequent tests.
237+
Context.ServiceProvider = null;
238+
}
239+
240+
[TestMethod]
241+
public void GetCurrentAgentNameViaQuery_WithoutServiceProvider_FallsBackToDirectCall()
242+
{
243+
Context.ServiceProvider = null;
244+
var fake = new FakeAgentManager { CurrentAgentName = "FallbackAgent" };
245+
246+
string result = fake.GetCurrentAgentNameViaQuery();
247+
248+
Assert.AreEqual("FallbackAgent", result);
249+
}
250+
251+
[TestMethod]
252+
public void GetCurrentAgentNameViaQuery_WithoutServiceProvider_NullAgentName_ReturnsEmpty()
253+
{
254+
Context.ServiceProvider = null;
255+
var fake = new FakeAgentManager { CurrentAgentName = null };
256+
257+
string result = fake.GetCurrentAgentNameViaQuery();
258+
259+
Assert.AreEqual(string.Empty, result);
260+
}
261+
262+
[TestMethod]
263+
public void GetCurrentAgentNameViaQuery_WithServiceProvider_RoutesViaHandler()
264+
{
265+
var fake = new FakeAgentManager { CurrentAgentName = "DirectAgent" };
266+
var services = new ServiceCollection();
267+
// Register a handler backed by a *different* fake so we can prove
268+
// the call was routed through the DI handler and not the direct path.
269+
var handlerFake = new FakeAgentManager { CurrentAgentName = "HandlerAgent" };
270+
services.AddTransient<IQueryHandler<GetActiveAgentNameQuery, string>>(
271+
_ => new GetActiveAgentNameQueryHandler(handlerFake));
272+
Context.ServiceProvider = services.BuildServiceProvider();
273+
274+
string result = fake.GetCurrentAgentNameViaQuery();
275+
276+
Assert.AreEqual("HandlerAgent", result);
277+
}
278+
279+
[TestMethod]
280+
public void GetCurrentAgentNameViaQuery_WithServiceProvider_NoHandlerRegistered_FallsBackToDirectCall()
281+
{
282+
var fake = new FakeAgentManager { CurrentAgentName = "DirectFallback" };
283+
// Build a DI container that does NOT register the query handler.
284+
var services = new ServiceCollection();
285+
Context.ServiceProvider = services.BuildServiceProvider();
286+
287+
string result = fake.GetCurrentAgentNameViaQuery();
288+
289+
Assert.AreEqual("DirectFallback", result);
290+
}
225291
}
226292
}

src/Libraries/ACATCore/ACAT.Core.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,7 @@
593593
<Compile Include="Patterns\CQRS\Samples\GetConfigurationValueQueryHandler.cs" />
594594
<Compile Include="Patterns\CQRS\Samples\GetActiveAgentNameQuery.cs" />
595595
<Compile Include="Patterns\CQRS\Samples\GetActiveAgentNameQueryHandler.cs" />
596+
<Compile Include="Patterns\CQRS\AgentManagerExtensions.cs" />
596597
</ItemGroup>
597598
<ItemGroup>
598599
<EmbeddedResource Include="ActuatorManagement\UI\ActuatorErrorForm.resx">
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
////////////////////////////////////////////////////////////////////////////
2+
//
3+
// Copyright 2013-2019; 2023 Intel Corporation
4+
// SPDX-License-Identifier: Apache-2.0
5+
//
6+
//
7+
// AgentManagerExtensions.cs
8+
//
9+
// Extension methods for IAgentManager that provide CQRS-based wrappers
10+
// around agent query operations. Each method resolves the registered
11+
// IQueryHandler from the DI container when available and falls back to
12+
// calling the IAgentManager method directly when running outside a
13+
// DI-configured environment (e.g., unit tests or legacy entry points).
14+
//
15+
// Usage – call-site migration:
16+
//
17+
// Before:
18+
// string name = Context.AppAgentMgr.GetCurrentAgentName();
19+
//
20+
// After:
21+
// string name = Context.AppAgentMgr.GetCurrentAgentNameViaQuery();
22+
//
23+
// The extension method body can be simplified to the CQRS-only path once
24+
// every call site has been migrated and legacy fallback is no longer needed.
25+
//
26+
////////////////////////////////////////////////////////////////////////////
27+
28+
using ACAT.Core.AgentManagement;
29+
using ACAT.Core.PanelManagement;
30+
using ACAT.Core.Patterns.CQRS.Samples;
31+
32+
namespace ACAT.Core.Patterns.CQRS
33+
{
34+
/// <summary>
35+
/// Extension methods for <see cref="IAgentManager"/> that route agent
36+
/// queries through the CQRS <see cref="IQueryHandler{TQuery,TResult}"/>
37+
/// infrastructure when available, falling back to direct
38+
/// <see cref="IAgentManager"/> calls when a DI container is not configured.
39+
/// </summary>
40+
public static class AgentManagerExtensions
41+
{
42+
/// <summary>
43+
/// Returns the name of the currently active agent by dispatching a
44+
/// <see cref="GetActiveAgentNameQuery"/> through the registered
45+
/// <see cref="IQueryHandler{TQuery,TResult}"/>.
46+
/// </summary>
47+
/// <remarks>
48+
/// When <see cref="Context.ServiceProvider"/> is configured the call
49+
/// is routed through the DI-registered handler so that the CQRS
50+
/// boundary is respected. When no provider is available the method
51+
/// falls back to <see cref="IAgentManager.GetCurrentAgentName"/> and
52+
/// normalises a <see langword="null"/> return to
53+
/// <see cref="string.Empty"/>.
54+
/// </remarks>
55+
/// <param name="agentManager">The agent manager to query.</param>
56+
/// <returns>
57+
/// The name of the active agent, or <see cref="string.Empty"/> when
58+
/// no agent is currently active.
59+
/// </returns>
60+
public static string GetCurrentAgentNameViaQuery(this IAgentManager agentManager)
61+
{
62+
var handler = Context.ServiceProvider
63+
?.GetService(typeof(IQueryHandler<GetActiveAgentNameQuery, string>))
64+
as IQueryHandler<GetActiveAgentNameQuery, string>;
65+
66+
if (handler != null)
67+
{
68+
return handler.Handle(new GetActiveAgentNameQuery());
69+
}
70+
71+
return agentManager.GetCurrentAgentName() ?? string.Empty;
72+
}
73+
}
74+
}

0 commit comments

Comments
 (0)