From 9b8c7902117c675cb58f514736b043265954a0b2 Mon Sep 17 00:00:00 2001 From: "pi.admin" Date: Tue, 26 May 2026 23:02:57 -0700 Subject: [PATCH 1/9] Fix custom processing --- .github/package_version.env | 4 ++-- .../Declarative/IOptionDescriptorAttribute.cs | 5 +++++ .../Extensions/ITerminalBuilderExtensions.cs | 22 +++++++++---------- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/.github/package_version.env b/.github/package_version.env index 23e708f9..47437ef4 100644 --- a/.github/package_version.env +++ b/.github/package_version.env @@ -1,4 +1,4 @@ PI_MAJOR_VERSION=7 -PI_MINOR_VERSION=0 -PI_PATCH_VERSION=0 +PI_MINOR_VERSION=1 +PI_PATCH_VERSION=1 PI_DEPENDENCIES_SUFFIX=-* diff --git a/src/OneImlx.Terminal.Shared/Declarative/IOptionDescriptorAttribute.cs b/src/OneImlx.Terminal.Shared/Declarative/IOptionDescriptorAttribute.cs index e6fb33e5..7e66f4de 100644 --- a/src/OneImlx.Terminal.Shared/Declarative/IOptionDescriptorAttribute.cs +++ b/src/OneImlx.Terminal.Shared/Declarative/IOptionDescriptorAttribute.cs @@ -9,6 +9,11 @@ namespace OneImlx.Terminal.Shared.Declarative /// public interface IOptionDescriptorAttribute { + /// + /// The option alias. + /// + public string? Alias { get; } + /// /// The option data type. /// diff --git a/src/OneImlx.Terminal/Extensions/ITerminalBuilderExtensions.cs b/src/OneImlx.Terminal/Extensions/ITerminalBuilderExtensions.cs index c0292929..9d7fe89c 100644 --- a/src/OneImlx.Terminal/Extensions/ITerminalBuilderExtensions.cs +++ b/src/OneImlx.Terminal/Extensions/ITerminalBuilderExtensions.cs @@ -486,18 +486,18 @@ private static void ProcessOwners(object[] attrs, ICommandBuilder commandBuilder private static void ProcessArgumentDescriptors(object[] attrs, ICommandBuilder commandBuilder) { - List argAttrs = GetDeclarativeInterfaces(attrs); - List argVdls = GetDeclarativeInterfaces(attrs); + List argAttrs = GetDeclarativeInterfaces(attrs); + List argVdls = GetDeclarativeInterfaces(attrs); for (int i = 0; i < argAttrs.Count; i++) { - ArgumentDescriptorAttribute argAttr = argAttrs[i]; + IArgumentDescriptorAttribute argAttr = argAttrs[i]; IArgumentBuilder argBuilder = commandBuilder.DefineArgument(argAttr.Order, argAttr.Id, argAttr.DataType, argAttr.Description, argAttr.Flags); for (int j = 0; j < argVdls.Count; j++) { - ArgumentValidationAttribute argVdl = argVdls[j]; + IArgumentValidationAttribute argVdl = argVdls[j]; if (argVdl.ArgumentId.Equals(argAttr.Id)) { @@ -511,18 +511,18 @@ private static void ProcessArgumentDescriptors(object[] attrs, ICommandBuilder c private static void ProcessOptionDescriptors(object[] attrs, ICommandBuilder commandBuilder) { - List optAttrs = GetDeclarativeInterfaces(attrs); - List optVdls = GetDeclarativeInterfaces(attrs); + List optAttrs = GetDeclarativeInterfaces(attrs); + List optVdls = GetDeclarativeInterfaces(attrs); for (int i = 0; i < optAttrs.Count; i++) { - OptionDescriptorAttribute optAttr = optAttrs[i]; + IOptionDescriptorAttribute optAttr = optAttrs[i]; IOptionBuilder optBuilder = commandBuilder.DefineOption(optAttr.Id, optAttr.DataType, optAttr.Description, optAttr.Flags, optAttr.Alias); for (int j = 0; j < optVdls.Count; j++) { - OptionValidationAttribute optVdl = optVdls[j]; + IOptionValidationAttribute optVdl = optVdls[j]; if (optVdl.OptionId.Equals(optAttr.Id)) { @@ -536,18 +536,18 @@ private static void ProcessOptionDescriptors(object[] attrs, ICommandBuilder com private static void ProcessCustomProperties(object[] attrs, ICommandBuilder commandBuilder) { - List cmdPropAttrs = GetDeclarativeInterfaces(attrs); + List cmdPropAttrs = GetDeclarativeInterfaces(attrs); for (int i = 0; i < cmdPropAttrs.Count; i++) { - CommandCustomPropertyAttribute attr = cmdPropAttrs[i]; + ICommandCustomPropertyAttribute attr = cmdPropAttrs[i]; commandBuilder.CustomProperty(attr.Key, attr.Value); } } private static void ProcessTags(object[] attrs, ICommandBuilder commandBuilder) { - CommandTagsAttribute? tagsAttr = GetDeclarativeInterface(attrs); + ICommandTagsAttribute? tagsAttr = GetDeclarativeInterface(attrs); if (tagsAttr != null) { From 79c86932007738439e9a78f98e0734b94a66e40b Mon Sep 17 00:00:00 2001 From: "pi.admin" Date: Fri, 29 May 2026 18:32:23 -0700 Subject: [PATCH 2/9] enhance Icontext --- .../TestClient/Runners/SendApiHttpRunner.cs | 5 +- apps/s2s/TestClient/Runners/SendGrpcRunner.cs | 3 +- apps/s2s/TestClient/Runners/SendHttpRunner.cs | 3 +- apps/s2s/TestClient/Runners/SendTcpRunner.cs | 2 +- apps/s2s/TestClient/Runners/SendUdpRunner.cs | 2 +- .../ICommandContext.cs | 10 - .../TerminalIdentifiers.cs | 10 + .../Commands/CommandContext.cs | 25 +- .../Commands/CommandRouter.cs | 2 +- .../Commands/Handlers/CommandHandler.cs | 3 +- .../Commands/Parsers/CommandParser.cs | 7 +- .../Extensions/ICommandContextExtensions.cs | 148 ++++++-- .../TerminalIdentifiersTests.cs | 4 +- .../Commands/CommandContextFactoryTests.cs | 5 +- .../Commands/CommandContextTests.cs | 31 ++ .../ICommandContextExtensionsTests.cs | 358 ++++++++++++------ .../Mocks/MockCommandRouter.cs | 7 +- .../Mocks/MockSocketCommandRouter.cs | 3 +- .../Runtime/TerminalProcessorTests.cs | 93 ++--- 19 files changed, 487 insertions(+), 234 deletions(-) create mode 100644 test/OneImlx.Terminal.Tests/Commands/CommandContextTests.cs diff --git a/apps/s2s/TestClient/Runners/SendApiHttpRunner.cs b/apps/s2s/TestClient/Runners/SendApiHttpRunner.cs index d32c3e1a..bcc3b4ea 100644 --- a/apps/s2s/TestClient/Runners/SendApiHttpRunner.cs +++ b/apps/s2s/TestClient/Runners/SendApiHttpRunner.cs @@ -12,6 +12,7 @@ using OneImlx.Terminal.Runtime; using OneImlx.Terminal.Shared; using OneImlx.Terminal.Shared.Declarative; +using OneImlx.Terminal.Extensions; namespace OneImlx.Terminal.Apps.TestClient.Runners { @@ -44,7 +45,7 @@ public override async Task RunCommandAsync(ICommandContext var clientTasks = new Task[maxClients]; for (int idx = 0; idx < clientTasks.Length; idx++) { - clientTasks[idx] = StartHttpClientAsync(serverAddress, idx, context.RouterContext.TerminalCancellationToken); + clientTasks[idx] = StartHttpClientAsync(serverAddress, idx, context.GetRouterContext().TerminalCancellationToken); } await Task.WhenAll(clientTasks); @@ -55,7 +56,7 @@ public override async Task RunCommandAsync(ICommandContext stopwatch.Stop(); await terminalConsole.WriteLineColorAsync(ConsoleColor.Green, $"{_commandCount} Terminal HTTP, {_requestCount} ASP.NET HTTP, {maxClients} Clients, {stopwatch.Elapsed.TotalMilliseconds} Milliseconds"); } - } + } private async Task SendTerminalHttpCommandsAsync(HttpClient client, int clientIndex, CancellationToken cToken) { diff --git a/apps/s2s/TestClient/Runners/SendGrpcRunner.cs b/apps/s2s/TestClient/Runners/SendGrpcRunner.cs index af8ad769..0bbaa624 100644 --- a/apps/s2s/TestClient/Runners/SendGrpcRunner.cs +++ b/apps/s2s/TestClient/Runners/SendGrpcRunner.cs @@ -15,6 +15,7 @@ using OneImlx.Terminal.Shared; using OneImlx.Terminal.Client.Grpc; using OneImlx.Terminal.Shared.Declarative; +using OneImlx.Terminal.Extensions; namespace OneImlx.Terminal.Apps.TestClient.Runners { @@ -45,7 +46,7 @@ public override async Task RunCommandAsync(ICommandContext var clientTasks = new Task[maxClients]; for (int idx = 0; idx < clientTasks.Length; idx++) { - clientTasks[idx] = StartClientAsync(serverAddress, idx, context.RouterContext.TerminalCancellationToken); + clientTasks[idx] = StartClientAsync(serverAddress, idx, context.GetRouterContext().TerminalCancellationToken); } await Task.WhenAll(clientTasks); diff --git a/apps/s2s/TestClient/Runners/SendHttpRunner.cs b/apps/s2s/TestClient/Runners/SendHttpRunner.cs index b3f6abd2..3d99db8e 100644 --- a/apps/s2s/TestClient/Runners/SendHttpRunner.cs +++ b/apps/s2s/TestClient/Runners/SendHttpRunner.cs @@ -9,6 +9,7 @@ using OneImlx.Terminal.Client.Extensions; using OneImlx.Terminal.Commands; using OneImlx.Terminal.Commands.Runners; +using OneImlx.Terminal.Extensions; using OneImlx.Terminal.Runtime; using OneImlx.Terminal.Shared; using OneImlx.Terminal.Shared.Declarative; @@ -43,7 +44,7 @@ public override async Task RunCommandAsync(ICommandContext var clientTasks = new Task[maxClients]; for (int idx = 0; idx < clientTasks.Length; idx++) { - clientTasks[idx] = StartHttpClientAsync(serverAddress, idx, context.RouterContext.TerminalCancellationToken); + clientTasks[idx] = StartHttpClientAsync(serverAddress, idx, context.GetRouterContext().TerminalCancellationToken); } await Task.WhenAll(clientTasks); diff --git a/apps/s2s/TestClient/Runners/SendTcpRunner.cs b/apps/s2s/TestClient/Runners/SendTcpRunner.cs index a48387b4..3f58d15d 100644 --- a/apps/s2s/TestClient/Runners/SendTcpRunner.cs +++ b/apps/s2s/TestClient/Runners/SendTcpRunner.cs @@ -55,7 +55,7 @@ public override async Task RunCommandAsync(ICommandContext var clientTasks = new Task[maxClients]; for (int idx = 0; idx < clientTasks.Length; idx++) { - clientTasks[idx] = StartClientAsync(server, port, idx, context.RouterContext.TerminalCancellationToken); + clientTasks[idx] = StartClientAsync(server, port, idx, context.GetRouterContext().TerminalCancellationToken); } await Task.WhenAll(clientTasks); diff --git a/apps/s2s/TestClient/Runners/SendUdpRunner.cs b/apps/s2s/TestClient/Runners/SendUdpRunner.cs index 63dfa190..b35df940 100644 --- a/apps/s2s/TestClient/Runners/SendUdpRunner.cs +++ b/apps/s2s/TestClient/Runners/SendUdpRunner.cs @@ -58,7 +58,7 @@ public override async Task RunCommandAsync(ICommandContext var clientTasks = new Task[maxClients]; for (int idx = 0; idx < clientTasks.Length; idx++) { - clientTasks[idx] = StartClientAsync(server, port, idx, context.RouterContext.TerminalCancellationToken); + clientTasks[idx] = StartClientAsync(server, port, idx, context.GetRouterContext().TerminalCancellationToken); } await Task.WhenAll(clientTasks); diff --git a/src/OneImlx.Terminal.Shared/ICommandContext.cs b/src/OneImlx.Terminal.Shared/ICommandContext.cs index ab77248f..9d0cdee4 100644 --- a/src/OneImlx.Terminal.Shared/ICommandContext.cs +++ b/src/OneImlx.Terminal.Shared/ICommandContext.cs @@ -15,15 +15,5 @@ public interface ICommandContext /// The additional router properties. /// public Dictionary Properties { get; } - - /// - /// The terminal request. - /// - public CommandRequest Request { get; } - - /// - /// The terminal router context. - /// - public TerminalRouterContext RouterContext { get; } } } \ No newline at end of file diff --git a/src/OneImlx.Terminal.Shared/TerminalIdentifiers.cs b/src/OneImlx.Terminal.Shared/TerminalIdentifiers.cs index 6d90cc2c..d9b6c9ef 100644 --- a/src/OneImlx.Terminal.Shared/TerminalIdentifiers.cs +++ b/src/OneImlx.Terminal.Shared/TerminalIdentifiers.cs @@ -106,5 +106,15 @@ public sealed class TerminalIdentifiers /// The current running command result. /// public const string CommandResult = "oneimlx_command_result"; + + /// + /// The current running router context. + /// + public const string RouterContext = "oneimlx_router_context"; + + /// + /// The current running command request. + /// + public const string CommandRequest = "oneimlx_command_request"; } } \ No newline at end of file diff --git a/src/OneImlx.Terminal/Commands/CommandContext.cs b/src/OneImlx.Terminal/Commands/CommandContext.cs index 5130f597..89dc82d6 100644 --- a/src/OneImlx.Terminal/Commands/CommandContext.cs +++ b/src/OneImlx.Terminal/Commands/CommandContext.cs @@ -2,6 +2,7 @@ // For license, terms, and data policies, go to: // https://terms.perpetualintelligence.com/articles/intro.html +using OneImlx.Terminal.Extensions; using OneImlx.Terminal.Shared; using System; using System.Collections.Generic; @@ -21,16 +22,6 @@ public sealed class CommandContext : ICommandContext /// public Dictionary Properties { get; } - /// - /// The terminal request. - /// - public CommandRequest Request { get; } - - /// - /// The terminal router context. - /// - public TerminalRouterContext RouterContext { get; } - /// /// Initialize a new instance of . /// @@ -42,9 +33,19 @@ internal CommandContext( TerminalRouterContext context, Dictionary properties) { + if (request is null) + { + throw new ArgumentNullException(nameof(request)); + } + + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + Properties = properties ?? throw new ArgumentNullException(nameof(properties)); - Request = request ?? throw new ArgumentNullException(nameof(request)); - RouterContext = context ?? throw new ArgumentNullException(nameof(context)); + this.SetCommandRequest(request); + this.SetRouterContext(context); } } } \ No newline at end of file diff --git a/src/OneImlx.Terminal/Commands/CommandRouter.cs b/src/OneImlx.Terminal/Commands/CommandRouter.cs index 4917de1a..177334e0 100644 --- a/src/OneImlx.Terminal/Commands/CommandRouter.cs +++ b/src/OneImlx.Terminal/Commands/CommandRouter.cs @@ -47,7 +47,7 @@ public async Task RouteCommandAsync(ICommandContext context) { ParsedCommand? parsedCommand = null; CommandResult? commandResult = null!; - CommandRequest request = context.Request; + CommandRequest request = context.GetCommandRequest(); string requestId = request.Id; try diff --git a/src/OneImlx.Terminal/Commands/Handlers/CommandHandler.cs b/src/OneImlx.Terminal/Commands/Handlers/CommandHandler.cs index e24a7ebc..23224f0d 100644 --- a/src/OneImlx.Terminal/Commands/Handlers/CommandHandler.cs +++ b/src/OneImlx.Terminal/Commands/Handlers/CommandHandler.cs @@ -36,7 +36,8 @@ public CommandHandler(ICommandResolver commandResolver, IOptions public async Task HandleCommandAsync(ICommandContext context) { - logger.LogDebug("Handle request. request={0}", context.Request.Id); + CommandRequest commandRequest = context.GetCommandRequest(); + logger.LogDebug("Handle request. request={0}", commandRequest.Id); // Check and run the command (CommandCheckerResult checkerResult, CommandRunnerResult runnerResult) = await CheckAndRunCommandInnerAsync(context).ConfigureAwait(false); diff --git a/src/OneImlx.Terminal/Commands/Parsers/CommandParser.cs b/src/OneImlx.Terminal/Commands/Parsers/CommandParser.cs index 140a9a2c..8e344906 100644 --- a/src/OneImlx.Terminal/Commands/Parsers/CommandParser.cs +++ b/src/OneImlx.Terminal/Commands/Parsers/CommandParser.cs @@ -41,9 +41,10 @@ public CommandParser(ITerminalRequestParser terminalRequestParser, ITerminalText /// public async Task ParseCommandAsync(ICommandContext context) { - logger.LogDebug("Parse request. request={0} raw={1}", context.Request.Id, context.Request.Raw); - TerminalParsedRequest parsedOutput = await terminalRequestParser.ParseRequestAsync(context.Request).ConfigureAwait(false); - context.SetParsedCommand(await MapParsedRequestAsync(context.Request, parsedOutput).ConfigureAwait(false)); + CommandRequest commandRequest = context.GetCommandRequest(); + logger.LogDebug("Parse request. request={0} raw={1}", commandRequest.Id, commandRequest.Raw); + TerminalParsedRequest parsedOutput = await terminalRequestParser.ParseRequestAsync(commandRequest).ConfigureAwait(false); + context.SetParsedCommand(await MapParsedRequestAsync(commandRequest, parsedOutput).ConfigureAwait(false)); } private async Task<(List parsedCommands, List parsedArguments)> MapCommandAndArguments(TerminalParsedRequest parsedOutput) diff --git a/src/OneImlx.Terminal/Extensions/ICommandContextExtensions.cs b/src/OneImlx.Terminal/Extensions/ICommandContextExtensions.cs index 6700eff1..feb7c77d 100644 --- a/src/OneImlx.Terminal/Extensions/ICommandContextExtensions.cs +++ b/src/OneImlx.Terminal/Extensions/ICommandContextExtensions.cs @@ -14,11 +14,11 @@ namespace OneImlx.Terminal.Extensions public static class ICommandContextExtensions { /// - /// Gets the current parsed command from the context. + /// Gets the parsed command from the context. /// /// The command context. - /// The available parsed command. - /// Thrown when the parsed command is not available. + /// The parsed command. + /// Thrown when the parsed command is not available in the context. public static ParsedCommand GetParsedCommand(this ICommandContext commandContext) { commandContext.Properties.TryGetValue(TerminalIdentifiers.ParsedCommand, out object? parsedCommand); @@ -30,11 +30,50 @@ public static ParsedCommand GetParsedCommand(this ICommandContext commandContext } /// - /// Gets the current executing command from the context. + /// Gets the command request from the context. /// /// The command context. - /// The available command. - /// Thrown when the parsed command is not available. + /// The command request. + /// Thrown when the command request is not available in the context. + public static CommandRequest GetCommandRequest(this ICommandContext commandContext) + { + commandContext.Properties.TryGetValue(TerminalIdentifiers.CommandRequest, out object? result); + if (result is not CommandRequest commandRequest) + { + throw new TerminalException(TerminalErrors.ServerError, "The command request is missing in the context."); + } + return commandRequest; + } + + /// + /// Attempts to get the command request from the context. + /// + /// The command context. + /// The command request if found; otherwise, . + /// if the command request was found; otherwise, . + public static bool TryGetCommandRequest(this ICommandContext commandContext, out CommandRequest? commandRequest) + { + bool found = commandContext.Properties.TryGetValue(TerminalIdentifiers.CommandRequest, out object? result); + commandRequest = result as CommandRequest; + return found; + } + + /// + /// Sets the command request in the context. + /// + /// The command context. + /// The command request to set. + public static void SetCommandRequest(this ICommandContext commandContext, CommandRequest commandRequest) + { + commandContext.Properties[TerminalIdentifiers.CommandRequest] = commandRequest; + } + + /// + /// Gets the executing command from the context. + /// + /// The command context. + /// The executing command. + /// Thrown when the parsed command is not available in the context. public static Command GetCommand(this ICommandContext commandContext) { ParsedCommand parsedCommand = commandContext.GetParsedCommand(); @@ -42,11 +81,11 @@ public static Command GetCommand(this ICommandContext commandContext) } /// - /// Gets the current command result from the context. + /// Gets the command result from the context. /// /// The command context. - /// The available command result. - /// Thrown when the command result is not available. + /// The command result. + /// Thrown when the command result is not available in the context. public static CommandResult GetCommandResult(this ICommandContext commandContext) { commandContext.Properties.TryGetValue(TerminalIdentifiers.CommandResult, out object? result); @@ -58,46 +97,85 @@ public static CommandResult GetCommandResult(this ICommandContext commandContext } /// - /// Determines whether the command context contains a command result. + /// Attempts to get the command result from the context. /// - /// The command context to inspect for a command result. Cannot be null. - /// The command result if available; otherwise, null. - /// True if a command result is present; otherwise, false. + /// The command context. + /// The command result if found; otherwise, . + /// if the command result was found; otherwise, . public static bool TryGetCommandResult(this ICommandContext commandContext, out CommandResult? commandResult) { bool found = commandContext.Properties.TryGetValue(TerminalIdentifiers.CommandResult, out object? result); - commandResult = (CommandResult)result; + commandResult = result as CommandResult; return found; } /// - /// Determines whether the command context contains a parsed command. + /// Gets the router context from the context. /// - /// The command context to inspect for a parsed command. Cannot be null. - /// The parsed command if available; otherwise, null. - /// True if a parsed command is present; otherwise, false. + /// The command context. + /// The router context. + /// Thrown when the router context is not available in the context. + public static TerminalRouterContext GetRouterContext(this ICommandContext commandContext) + { + bool found = commandContext.Properties.TryGetValue(TerminalIdentifiers.RouterContext, out object? result); + if (result is not TerminalRouterContext routerContext) + { + throw new TerminalException(TerminalErrors.ServerError, "The router context is missing in the context."); + } + return routerContext; + } + + /// + /// Attempts to get the from the context. + /// + /// The command context. + /// The router context if found; otherwise, . + /// if the router context was found; otherwise, . + public static bool TryGetRouterContext(this ICommandContext commandContext, out TerminalRouterContext? routerContext) + { + bool found = commandContext.Properties.TryGetValue(TerminalIdentifiers.RouterContext, out object? result); + routerContext = result as TerminalRouterContext; + return found; + } + + /// + /// Sets the router context in the context. + /// + /// The command context. + /// The router context to set. + public static void SetRouterContext(this ICommandContext commandContext, TerminalRouterContext routerContext) + { + commandContext.Properties[TerminalIdentifiers.RouterContext] = routerContext; + } + + /// + /// Attempts to get the parsed command from the context. + /// + /// The command context. + /// The parsed command if found; otherwise, . + /// if the parsed command was found; otherwise, . public static bool TryGetParsedCommand(this ICommandContext commandContext, out ParsedCommand? parsedCommand) { bool found = commandContext.Properties.TryGetValue(TerminalIdentifiers.ParsedCommand, out object? parsedObject); - parsedCommand = (ParsedCommand)parsedObject; + parsedCommand = parsedObject as ParsedCommand; return found; } /// - /// Sets the current parsed command in the context. + /// Sets the parsed command in the context. /// /// The command context. - /// The parsed command. + /// The parsed command to set. public static void SetParsedCommand(this ICommandContext commandContext, ParsedCommand parsedCommand) { commandContext.Properties[TerminalIdentifiers.ParsedCommand] = parsedCommand; } /// - /// Sets the current command result in the context. + /// Sets the command result in the context. /// /// The command context. - /// The command result. + /// The command result to set. public static void SetCommandResult(this ICommandContext commandContext, CommandResult commandResult) { commandContext.Properties[TerminalIdentifiers.CommandResult] = commandResult; @@ -106,6 +184,10 @@ public static void SetCommandResult(this ICommandContext commandContext, Command /// /// Gets the required argument value by argument id. /// + /// The command context. + /// The argument identifier. + /// The argument value. + /// Thrown when the argument is not found or the value cannot be cast to . public static TValue GetRequiredArgumentValue(this ICommandContext commandContext, string argId) { Command command = GetCommand(commandContext); @@ -115,6 +197,10 @@ public static TValue GetRequiredArgumentValue(this ICommandContext comma /// /// Gets the required argument value by argument index. /// + /// The command context. + /// The zero-based argument index. + /// The argument value. + /// Thrown when the argument is not found or the value cannot be cast to . public static TValue GetRequiredArgumentValue(this ICommandContext commandContext, int argIndex) { Command command = GetCommand(commandContext); @@ -122,8 +208,12 @@ public static TValue GetRequiredArgumentValue(this ICommandContext comma } /// - /// Tries to get the argument value by argument id. + /// Attempts to get the argument value by argument id. /// + /// The command context. + /// The argument identifier. + /// The argument value if found; otherwise, the default value of . + /// if the argument was found; otherwise, . public static bool TryGetArgumentValue(this ICommandContext commandContext, string argId, out TValue? value) { Command command = GetCommand(commandContext); @@ -133,6 +223,10 @@ public static bool TryGetArgumentValue(this ICommandContext commandConte /// /// Gets the required option value by option id or alias. /// + /// The command context. + /// The option identifier or alias. + /// The option value. + /// Thrown when the option is not found or the value cannot be cast to . public static TValue GetRequiredOptionValue(this ICommandContext commandContext, string idOrAlias) { Command command = GetCommand(commandContext); @@ -140,8 +234,12 @@ public static TValue GetRequiredOptionValue(this ICommandContext command } /// - /// Tries to get the option value by option id or alias. + /// Attempts to get the option value by option id or alias. /// + /// The command context. + /// The option identifier or alias. + /// The option value if found; otherwise, the default value of . + /// if the option was found; otherwise, . public static bool TryGetOptionValue(this ICommandContext commandContext, string idOrAlias, out TValue? value) { Command command = GetCommand(commandContext); diff --git a/test/OneImlx.Terminal.Shared.Tests/TerminalIdentifiersTests.cs b/test/OneImlx.Terminal.Shared.Tests/TerminalIdentifiersTests.cs index 3c488e46..8f0067cb 100644 --- a/test/OneImlx.Terminal.Shared.Tests/TerminalIdentifiersTests.cs +++ b/test/OneImlx.Terminal.Shared.Tests/TerminalIdentifiersTests.cs @@ -13,7 +13,7 @@ public class TerminalIdentifiersTests [Fact] public void TerminalIdentifiers_Defines_Identifiers() { - typeof(TerminalIdentifiers).Should().HaveConstantCount(19); + typeof(TerminalIdentifiers).Should().HaveConstantCount(21); TerminalIdentifiers.OfflineLicenseMode.Should().Be("offline"); TerminalIdentifiers.StandardDeployment.Should().Be("standard"); @@ -34,6 +34,8 @@ public void TerminalIdentifiers_Defines_Identifiers() TerminalIdentifiers.ExplicitDefinition.Should().Be("explicit"); TerminalIdentifiers.ParsedCommand.Should().Be("oneimlx_parsed_command"); TerminalIdentifiers.CommandResult.Should().Be("oneimlx_command_result"); + TerminalIdentifiers.CommandRequest.Should().Be("oneimlx_command_request"); + TerminalIdentifiers.RouterContext.Should().Be("oneimlx_router_context"); } } } \ No newline at end of file diff --git a/test/OneImlx.Terminal.Tests/Commands/CommandContextFactoryTests.cs b/test/OneImlx.Terminal.Tests/Commands/CommandContextFactoryTests.cs index fa70f97e..62adaca3 100644 --- a/test/OneImlx.Terminal.Tests/Commands/CommandContextFactoryTests.cs +++ b/test/OneImlx.Terminal.Tests/Commands/CommandContextFactoryTests.cs @@ -3,6 +3,7 @@ // https://terms.perpetualintelligence.com/articles/intro.html using FluentAssertions; +using OneImlx.Terminal.Extensions; using OneImlx.Terminal.Mocks; using OneImlx.Terminal.Shared; using System.Collections.Generic; @@ -23,9 +24,9 @@ public void Create_SetsRequestContextAndProperties() var result = factory.Create(request, routerContext, properties); - result.Request.Should().BeSameAs(request); - result.RouterContext.Should().BeSameAs(routerContext); result.Properties.Should().BeSameAs(properties); + result.GetCommandRequest().Should().BeSameAs(request); + result.GetRouterContext().Should().BeSameAs(routerContext); } } } \ No newline at end of file diff --git a/test/OneImlx.Terminal.Tests/Commands/CommandContextTests.cs b/test/OneImlx.Terminal.Tests/Commands/CommandContextTests.cs new file mode 100644 index 00000000..a76d2395 --- /dev/null +++ b/test/OneImlx.Terminal.Tests/Commands/CommandContextTests.cs @@ -0,0 +1,31 @@ +// Copyright © 2019-2026 Perpetual Intelligence L.L.C. All rights reserved. +// For license, terms, and data policies, go to: +// https://terms.perpetualintelligence.com/articles/intro.html + +using System.Collections.Generic; +using System.Threading; +using FluentAssertions; +using OneImlx.Terminal.Extensions; +using OneImlx.Terminal.Mocks; +using OneImlx.Terminal.Shared; +using Xunit; + +namespace OneImlx.Terminal.Commands +{ + public class CommandContextTests + { + [Fact] + public void Constructor_Sets_Request_RouterContext_And_Properties() + { + var request = new CommandRequest("req-1", "test command"); + var routerContext = new MockRoutingContext(TerminalStartMode.Console, CancellationToken.None); + var properties = new Dictionary { ["key"] = "value" }; + + ICommandContext context = new CommandContextFactory().Create(request, routerContext, properties); + + context.Properties.Should().BeSameAs(properties); + context.GetCommandRequest().Should().BeSameAs(request); + context.GetRouterContext().Should().BeSameAs(routerContext); + } + } +} \ No newline at end of file diff --git a/test/OneImlx.Terminal.Tests/Extensions/ICommandContextExtensionsTests.cs b/test/OneImlx.Terminal.Tests/Extensions/ICommandContextExtensionsTests.cs index 79c9bb7a..c30f62f8 100644 --- a/test/OneImlx.Terminal.Tests/Extensions/ICommandContextExtensionsTests.cs +++ b/test/OneImlx.Terminal.Tests/Extensions/ICommandContextExtensionsTests.cs @@ -7,9 +7,12 @@ using OneImlx.Terminal.Commands; using OneImlx.Terminal.Commands.Parsers; using OneImlx.Terminal.Extensions; +using OneImlx.Terminal.Mocks; using OneImlx.Terminal.Shared; using OneImlx.Test.FluentAssertions; +using System; using System.Collections.Generic; +using System.Threading; using Xunit; namespace OneImlx.Terminal.Tests.Extensions @@ -18,219 +21,322 @@ public class ICommandContextExtensionsTests { private readonly Command command; private readonly ParsedCommand parsedCommand; - private readonly ITerminalTextHandler textHandler; public ICommandContextExtensionsTests() { - textHandler = new Mock().Object; - var optionDescriptor = new OptionDescriptor("opt1", "System.Int32", "Option 1", 0, null); - var options = new Options(textHandler, new[] { new Option(optionDescriptor, 123) }); - var argumentDescriptor = new ArgumentDescriptor(0, "arg1", "System.Int32", "Argument 1", 0); - var arguments = new Arguments(textHandler, new[] { new Argument(argumentDescriptor, 42) }); - command = new Command(new CommandDescriptor("id1", "name1", "desc1", CommandTypes.Leaf), arguments, options); + ITerminalTextHandler textHandler = new Mock().Object; + + OptionDescriptor optionDescriptor = new("opt1", "System.Int32", "Option 1", 0, null); + Option option = new(optionDescriptor, 123); + Options options = new(textHandler, new[] { option }); + + ArgumentDescriptor argumentDescriptor = new(0, "arg1", "System.Int32", "Argument 1", 0); + Argument argument = new(argumentDescriptor, 42); + Arguments arguments = new(textHandler, new[] { argument }); + + CommandDescriptor commandDescriptor = new("id1", "name1", "desc1", CommandTypes.Leaf); + command = new Command(commandDescriptor, arguments, options); parsedCommand = new ParsedCommand(command, null); } + private static ICommandContext Context(Dictionary? properties = null) + { + Mock mock = new(); + Dictionary props = properties ?? new Dictionary(); + mock.Setup(x => x.Properties).Returns(props); + return mock.Object; + } + [Fact] - public void GetParsedCommand_ReturnsParsedCommand_WhenAvailable() + public void GetParsedCommand_Returns_WhenAvailable() { - var mockContext = new Mock(); - var properties = new Dictionary { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; - mockContext.Setup(x => x.Properties).Returns(properties); - mockContext.Object.GetParsedCommand().Should().Be(parsedCommand); + Dictionary properties = new() { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; + ICommandContext context = Context(properties); + ParsedCommand result = context.GetParsedCommand(); + result.Should().Be(parsedCommand); } [Fact] - public void GetParsedCommand_ThrowsTerminalException_WhenNotAvailable() + public void GetParsedCommand_Throws_WhenMissing() { - var mockContext = new Mock(); - mockContext.Setup(x => x.Properties).Returns([]); - var act = () => mockContext.Object.GetParsedCommand(); - act.Should().Throw().WithErrorCode(TerminalErrors.ServerError).WithErrorDescription("The parsed command is missing in the context."); + ICommandContext context = Context(); + Action act = () => context.GetParsedCommand(); + act.Should().Throw().WithErrorCode(TerminalErrors.ServerError) + .WithErrorDescription("The parsed command is missing in the context."); } [Fact] - public void GetParsedCommand_ThrowsTerminalException_WhenWrongType() + public void GetParsedCommand_Throws_WhenWrongType() { - var mockContext = new Mock(); - var properties = new Dictionary { { TerminalIdentifiers.ParsedCommand, "wrong type" } }; - mockContext.Setup(x => x.Properties).Returns(properties); - var act = () => mockContext.Object.GetParsedCommand(); - act.Should().Throw().WithErrorCode(TerminalErrors.ServerError).WithErrorDescription("The parsed command is missing in the context."); + Dictionary properties = new() { { TerminalIdentifiers.ParsedCommand, "wrong" } }; + ICommandContext context = Context(properties); + Action act = () => context.GetParsedCommand(); + act.Should().Throw().WithErrorCode(TerminalErrors.ServerError); } [Fact] - public void GetCommand_ReturnsCommand_WhenParsedCommandAvailable() + public void TryGetParsedCommand_ReturnsTrue_WhenAvailable() { - var mockContext = new Mock(); - var properties = new Dictionary { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; - mockContext.Setup(x => x.Properties).Returns(properties); - mockContext.Object.GetCommand().Should().Be(command); + Dictionary properties = new() { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; + ICommandContext context = Context(properties); + bool found = context.TryGetParsedCommand(out ParsedCommand? result); + found.Should().BeTrue(); + result.Should().Be(parsedCommand); } [Fact] - public void GetCommand_ThrowsTerminalException_WhenParsedCommandNotAvailable() + public void TryGetParsedCommand_ReturnsFalse_WhenMissing() { - var mockContext = new Mock(); - mockContext.Setup(x => x.Properties).Returns([]); - var act = () => mockContext.Object.GetCommand(); - act.Should().Throw().WithErrorCode(TerminalErrors.ServerError).WithErrorDescription("The parsed command is missing in the context."); + ICommandContext context = Context(); + bool found = context.TryGetParsedCommand(out ParsedCommand? result); + found.Should().BeFalse(); + result.Should().BeNull(); } [Fact] - public void GetCommandResult_ReturnsCommandResult_WhenAvailable() + public void SetParsedCommand_SetsValue() { - var mockContext = new Mock(); - var commandResult = new CommandResult(); - var properties = new Dictionary { { TerminalIdentifiers.CommandResult, commandResult } }; - mockContext.Setup(x => x.Properties).Returns(properties); - mockContext.Object.GetCommandResult().Should().Be(commandResult); + ICommandContext context = Context(new Dictionary()); + context.SetParsedCommand(parsedCommand); + context.Properties[TerminalIdentifiers.ParsedCommand].Should().Be(parsedCommand); } [Fact] - public void GetCommandResult_ThrowsTerminalException_WhenNotAvailable() + public void GetCommand_Returns_WhenAvailable() { - var mockContext = new Mock(); - mockContext.Setup(x => x.Properties).Returns([]); - var act = () => mockContext.Object.GetCommandResult(); - act.Should().Throw().WithErrorCode(TerminalErrors.ServerError).WithErrorDescription("The command result is missing in the context."); + Dictionary properties = new() { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; + ICommandContext context = Context(properties); + Command result = context.GetCommand(); + result.Should().Be(command); } [Fact] - public void GetCommandResult_ThrowsTerminalException_WhenWrongType() + public void GetCommand_Throws_WhenParsedCommandMissing() { - var mockContext = new Mock(); - var properties = new Dictionary { { TerminalIdentifiers.CommandResult, "wrong type" } }; - mockContext.Setup(x => x.Properties).Returns(properties); - var act = () => mockContext.Object.GetCommandResult(); - act.Should().Throw().WithErrorCode(TerminalErrors.ServerError).WithErrorDescription("The command result is missing in the context."); + ICommandContext context = Context(); + Action act = () => context.GetCommand(); + act.Should().Throw().WithErrorCode(TerminalErrors.ServerError); } [Fact] - public void TryGetCommandResult_ReturnsTrue_WhenCommandResultAvailable() + public void GetCommandRequest_Returns_WhenAvailable() { - var mockContext = new Mock(); - var commandResult = new CommandResult(); - var properties = new Dictionary { { TerminalIdentifiers.CommandResult, commandResult } }; - mockContext.Setup(x => x.Properties).Returns(properties); - mockContext.Object.TryGetCommandResult(out var outResult).Should().BeTrue(); - outResult.Should().Be(commandResult); + CommandRequest request = new("req-1", "cmd"); + Dictionary properties = new() { { TerminalIdentifiers.CommandRequest, request } }; + ICommandContext context = Context(properties); + CommandRequest result = context.GetCommandRequest(); + result.Should().BeSameAs(request); } [Fact] - public void TryGetCommandResult_ReturnsFalse_WhenCommandResultNotAvailable() + public void GetCommandRequest_Throws_WhenMissing() { - var mockContext = new Mock(); - mockContext.Setup(x => x.Properties).Returns([]); - mockContext.Object.TryGetCommandResult(out var outResult).Should().BeFalse(); + ICommandContext context = Context(); + Action act = () => context.GetCommandRequest(); + act.Should().Throw().WithErrorCode(TerminalErrors.ServerError) + .WithErrorDescription("The command request is missing in the context."); } [Fact] - public void TryGetParsedCommand_ReturnsTrue_WhenParsedCommandAvailable() + public void TryGetCommandRequest_ReturnsTrue_WhenAvailable() { - var mockContext = new Mock(); - var properties = new Dictionary { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; - mockContext.Setup(x => x.Properties).Returns(properties); - mockContext.Object.TryGetParsedCommand(out var outResult).Should().BeTrue(); - outResult.Should().Be(parsedCommand); + CommandRequest request = new("req-1", "cmd"); + Dictionary properties = new() { { TerminalIdentifiers.CommandRequest, request } }; + ICommandContext context = Context(properties); + bool found = context.TryGetCommandRequest(out CommandRequest? result); + found.Should().BeTrue(); + result.Should().BeSameAs(request); } [Fact] - public void TryGetParsedCommand_ReturnsFalse_WhenParsedCommandNotAvailable() + public void TryGetCommandRequest_ReturnsFalse_WhenMissing() { - var mockContext = new Mock(); - mockContext.Setup(x => x.Properties).Returns([]); - mockContext.Object.TryGetParsedCommand(out var outResult).Should().BeFalse(); + ICommandContext context = Context(); + bool found = context.TryGetCommandRequest(out CommandRequest? result); + found.Should().BeFalse(); + result.Should().BeNull(); } [Fact] - public void SetParsedCommand_AddsParsedCommandToProperties() + public void SetCommandRequest_SetsValue() { - var mockContext = new Mock(); - var properties = new Dictionary(); - mockContext.Setup(x => x.Properties).Returns(properties); - mockContext.Object.SetParsedCommand(parsedCommand); - properties.Should().ContainKey(TerminalIdentifiers.ParsedCommand); - properties[TerminalIdentifiers.ParsedCommand].Should().Be(parsedCommand); + CommandRequest request = new("req-1", "cmd"); + ICommandContext context = Context(new Dictionary()); + context.SetCommandRequest(request); + context.Properties[TerminalIdentifiers.CommandRequest].Should().BeSameAs(request); } [Fact] - public void SetParsedCommand_OverwritesExistingParsedCommand() + public void GetRouterContext_Returns_WhenAvailable() { - var mockContext = new Mock(); - var properties = new Dictionary { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; - mockContext.Setup(x => x.Properties).Returns(properties); - var newParsedCommand = new ParsedCommand(command, null); - mockContext.Object.SetParsedCommand(newParsedCommand); - properties[TerminalIdentifiers.ParsedCommand].Should().Be(newParsedCommand); + MockRoutingContext routerContext = new(TerminalStartMode.Console, CancellationToken.None); + Dictionary properties = new() { { TerminalIdentifiers.RouterContext, routerContext } }; + ICommandContext context = Context(properties); + TerminalRouterContext result = context.GetRouterContext(); + result.Should().BeSameAs(routerContext); } [Fact] - public void SetCommandResult_AddsCommandResultToProperties() + public void GetRouterContext_Throws_WhenMissing() { - var mockContext = new Mock(); - var properties = new Dictionary(); - mockContext.Setup(x => x.Properties).Returns(properties); - var commandResult = new CommandResult(); - mockContext.Object.SetCommandResult(commandResult); - properties.Should().ContainKey(TerminalIdentifiers.CommandResult); - properties[TerminalIdentifiers.CommandResult].Should().Be(commandResult); + ICommandContext context = Context(); + Action act = () => context.GetRouterContext(); + act.Should().Throw().WithErrorCode(TerminalErrors.ServerError).WithErrorDescription("The router context is missing in the context."); } [Fact] - public void SetCommandResult_OverwritesExistingCommandResult() + public void TryGetRouterContext_ReturnsTrue_WhenAvailable() { - var mockContext = new Mock(); - var commandResult = new CommandResult(); - var properties = new Dictionary { { TerminalIdentifiers.CommandResult, commandResult } }; - mockContext.Setup(x => x.Properties).Returns(properties); - var newCommandResult = new CommandResult(); - mockContext.Object.SetCommandResult(newCommandResult); - properties[TerminalIdentifiers.CommandResult].Should().Be(newCommandResult); + MockRoutingContext routerContext = new(TerminalStartMode.Console, CancellationToken.None); + Dictionary properties = new() { { TerminalIdentifiers.RouterContext, routerContext } }; + ICommandContext context = Context(properties); + bool found = context.TryGetRouterContext(out TerminalRouterContext? result); + found.Should().BeTrue(); + result.Should().BeSameAs(routerContext); } [Fact] - public void GetRequiredOptionValue_ReturnsOptionValue_WhenAvailable() + public void TryGetRouterContext_ReturnsFalse_WhenMissing() { - var mockContext = new Mock(); - var properties = new Dictionary { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; - mockContext.Setup(x => x.Properties).Returns(properties); - int value = mockContext.Object.GetRequiredOptionValue("opt1"); + ICommandContext context = Context(); + bool found = context.TryGetRouterContext(out TerminalRouterContext? result); + found.Should().BeFalse(); + result.Should().BeNull(); + } + + [Fact] + public void SetRouterContext_SetsValue() + { + MockRoutingContext routerContext = new(TerminalStartMode.Console, CancellationToken.None); + ICommandContext context = Context(new Dictionary()); + context.SetRouterContext(routerContext); + context.Properties[TerminalIdentifiers.RouterContext].Should().BeSameAs(routerContext); + } + + [Fact] + public void GetCommandResult_Returns_WhenAvailable() + { + CommandResult commandResult = new(); + Dictionary properties = new() { { TerminalIdentifiers.CommandResult, commandResult } }; + ICommandContext context = Context(properties); + CommandResult result = context.GetCommandResult(); + result.Should().Be(commandResult); + } + + [Fact] + public void GetCommandResult_Throws_WhenMissing() + { + ICommandContext context = Context(); + Action act = () => context.GetCommandResult(); + act.Should().Throw().WithErrorCode(TerminalErrors.ServerError).WithErrorDescription("The command result is missing in the context."); + } + + [Fact] + public void TryGetCommandResult_ReturnsTrue_WhenAvailable() + { + CommandResult commandResult = new(); + Dictionary properties = new() { { TerminalIdentifiers.CommandResult, commandResult } }; + ICommandContext context = Context(properties); + bool found = context.TryGetCommandResult(out CommandResult? result); + found.Should().BeTrue(); + result.Should().Be(commandResult); + } + + [Fact] + public void TryGetCommandResult_ReturnsFalse_WhenMissing() + { + ICommandContext context = Context(); + bool found = context.TryGetCommandResult(out CommandResult? result); + found.Should().BeFalse(); + result.Should().BeNull(); + } + + [Fact] + public void SetCommandResult_SetsValue() + { + CommandResult commandResult = new(); + ICommandContext context = Context(new Dictionary()); + context.SetCommandResult(commandResult); + context.Properties[TerminalIdentifiers.CommandResult].Should().Be(commandResult); + } + + [Fact] + public void GetRequiredOptionValue_Returns_WhenAvailable() + { + Dictionary properties = new() { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; + ICommandContext context = Context(properties); + int value = context.GetRequiredOptionValue("opt1"); value.Should().Be(123); } [Fact] - public void GetRequiredOptionValue_ThrowsTerminalException_WhenOptionsNull() + public void GetRequiredOptionValue_Throws_WhenOptionsNull() { - var textHandler = new Mock().Object; - var cmd = new Command(new CommandDescriptor("id2", "name2", "desc2", CommandTypes.Leaf), new Arguments(textHandler, new List()), null); - var mockContext = new Mock(); - var parsedCmd = new ParsedCommand(cmd, null); - var properties = new Dictionary { { TerminalIdentifiers.ParsedCommand, parsedCmd } }; - mockContext.Setup(x => x.Properties).Returns(properties); - var act = () => mockContext.Object.GetRequiredOptionValue("opt1"); + ITerminalTextHandler textHandler = new Mock().Object; + CommandDescriptor commandDescriptor = new("id2", "name2", "desc2", CommandTypes.Leaf); + Arguments arguments = new(textHandler, new List()); + Command cmd = new(commandDescriptor, arguments, null); + ParsedCommand parsedCmd = new(cmd, null); + Dictionary properties = new() { { TerminalIdentifiers.ParsedCommand, parsedCmd } }; + ICommandContext context = Context(properties); + Action act = () => context.GetRequiredOptionValue("opt1"); act.Should().Throw().WithErrorCode(TerminalErrors.UnsupportedOption); } [Fact] - public void TryGetOptionValue_ReturnsFalse_WhenOptionNotAvailable() + public void TryGetOptionValue_ReturnsTrue_WhenAvailable() + { + Dictionary properties = new() { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; + ICommandContext context = Context(properties); + bool found = context.TryGetOptionValue("opt1", out int value); + found.Should().BeTrue(); + value.Should().Be(123); + } + + [Fact] + public void TryGetOptionValue_ReturnsFalse_WhenNotAvailable() { - var mockContext = new Mock(); - var properties = new Dictionary { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; - mockContext.Setup(x => x.Properties).Returns(properties); - bool found = mockContext.Object.TryGetOptionValue("notfound", out var value); + Dictionary properties = new() { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; + ICommandContext context = Context(properties); + bool found = context.TryGetOptionValue("notfound", out int value); found.Should().BeFalse(); } [Fact] - public void TryGetOptionValue_ReturnsTrue_WhenOptionAvailable() + public void GetRequiredArgumentValue_ById_Returns_WhenAvailable() + { + Dictionary properties = new() { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; + ICommandContext context = Context(properties); + int value = context.GetRequiredArgumentValue("arg1"); + value.Should().Be(42); + } + + [Fact] + public void GetRequiredArgumentValue_ByIndex_Returns_WhenAvailable() + { + Dictionary properties = new() { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; + ICommandContext context = Context(properties); + int value = context.GetRequiredArgumentValue(0); + value.Should().Be(42); + } + + [Fact] + public void TryGetArgumentValue_ReturnsTrue_WhenAvailable() { - var mockContext = new Mock(); - var properties = new Dictionary { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; - mockContext.Setup(x => x.Properties).Returns(properties); - bool found = mockContext.Object.TryGetOptionValue("opt1", out var value); + Dictionary properties = new() { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; + ICommandContext context = Context(properties); + bool found = context.TryGetArgumentValue("arg1", out int value); found.Should().BeTrue(); - value.Should().Be(123); + value.Should().Be(42); + } + + [Fact] + public void TryGetArgumentValue_ReturnsFalse_WhenNotAvailable() + { + Dictionary properties = new() { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; + ICommandContext context = Context(properties); + bool found = context.TryGetArgumentValue("notfound", out int value); + found.Should().BeFalse(); } } -} \ No newline at end of file +} diff --git a/test/OneImlx.Terminal.Tests/Mocks/MockCommandRouter.cs b/test/OneImlx.Terminal.Tests/Mocks/MockCommandRouter.cs index 10256f11..e4d96ac5 100644 --- a/test/OneImlx.Terminal.Tests/Mocks/MockCommandRouter.cs +++ b/test/OneImlx.Terminal.Tests/Mocks/MockCommandRouter.cs @@ -28,7 +28,7 @@ public MockCommandRouter(int? routeDelay = null, CancellationTokenSource? cancel public List MultipleRawString { get; set; } - public ICommandContext PassedContext { get; private set; } + public ICommandContext? PassedContext { get; private set; } public string? RawCommandString { get; set; } @@ -51,8 +51,9 @@ public async Task RouteCommandAsync(ICommandContext context) { // Your critical section code here RouteCalled = true; - RawCommandString = context.Request.Raw; - MultipleRawString.Add(context.Request.Raw); + CommandRequest request = context.GetCommandRequest(); + RawCommandString = request.Raw; + MultipleRawString.Add(request.Raw); RouteCounter += 1; if (routeDelay != null) diff --git a/test/OneImlx.Terminal.Tests/Mocks/MockSocketCommandRouter.cs b/test/OneImlx.Terminal.Tests/Mocks/MockSocketCommandRouter.cs index 65140f8b..76c7193c 100644 --- a/test/OneImlx.Terminal.Tests/Mocks/MockSocketCommandRouter.cs +++ b/test/OneImlx.Terminal.Tests/Mocks/MockSocketCommandRouter.cs @@ -37,8 +37,9 @@ public MockSocketCommandRouter(int? routeDelay = null, CancellationTokenSource? public async Task RouteCommandAsync(ICommandContext context) { // Stats + CommandRequest commandRequest = context.GetCommandRequest(); RouteCalled = true; - RawCommandStrings.Add(context.Request.Raw); + RawCommandStrings.Add(commandRequest.Raw); RouteCounter += 1; // Add delay diff --git a/test/OneImlx.Terminal.Tests/Runtime/TerminalProcessorTests.cs b/test/OneImlx.Terminal.Tests/Runtime/TerminalProcessorTests.cs index e58e770b..94890a60 100644 --- a/test/OneImlx.Terminal.Tests/Runtime/TerminalProcessorTests.cs +++ b/test/OneImlx.Terminal.Tests/Runtime/TerminalProcessorTests.cs @@ -2,9 +2,18 @@ // For license, terms, and data policies, go to: // https://terms.perpetualintelligence.com/articles/intro.html -using FluentAssertions; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using FluentAssertions; using Moq; using OneImlx.Shared.Infrastructure; using OneImlx.Terminal.Commands; @@ -14,15 +23,6 @@ using OneImlx.Terminal.Extensions; using OneImlx.Terminal.Shared; using OneImlx.Test.FluentAssertions; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; -using System.Text; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; using Xunit; namespace OneImlx.Terminal.Runtime @@ -82,7 +82,7 @@ public async Task AddAsync_Batch_Commands_HandlesConcurrentCalls() // Mock the setup for the command router List routedCommands = []; _mockCommandRouter.Setup(r => r.RouteCommandAsync(It.IsAny())) - .Callback(c => routedCommands.Add(c.Request.Raw)); + .Callback(c => routedCommands.Add(c.GetCommandRequest().Raw)); _terminalProcessor.StartProcessing(_mockTerminalRouterContext.Object, background: true); int idx = 0; @@ -124,7 +124,7 @@ public async Task AddAsync_Does_Add_When_BatchDelimiter_Missing_In_Non_BatchMode // Setup that the mock command router was invoked List routedCommands = []; _mockCommandRouter.Setup(r => r.RouteCommandAsync(It.IsAny())) - .Callback(c => routedCommands.Add(c.Request.Raw)); + .Callback(c => routedCommands.Add(c.GetCommandRequest().Raw)); // Act await _terminalProcessor.AddAsync(TerminalInputOutput.Single("id1", "command1", "sender", "endpoint")); @@ -141,7 +141,7 @@ public async Task AddAsync_Handles_ConcurrentCalls() // Mock the setup for the command router List routedCommands = []; _mockCommandRouter.Setup(r => r.RouteCommandAsync(It.IsAny())) - .Callback(c => routedCommands.Add(c.Request.Raw)); + .Callback(c => routedCommands.Add(c.GetCommandRequest().Raw)); _terminalProcessor.StartProcessing(_mockTerminalRouterContext.Object, background: true); int idx = 1; @@ -166,7 +166,7 @@ public async Task AddAsync_Processes_BatchCommand_In_Order() // Setup that the mock command router was invoked List routedCommands = []; _mockCommandRouter.Setup(r => r.RouteCommandAsync(It.IsAny())) - .Callback(c => routedCommands.Add(c.Request.Raw)); + .Callback(c => routedCommands.Add(c.GetCommandRequest().Raw)); OrderedDictionary commands1 = []; for (int i = 0; i < 1000; i++) @@ -288,7 +288,7 @@ public async Task AddAsync_Processes_Large_Batch() // Setup that the mock command router was invoked Dictionary routedCommands = []; _mockCommandRouter.Setup(r => r.RouteCommandAsync(It.IsAny())) - .Callback(c => routedCommands.Add(c.Request.Id, c.Request.Raw)); + .Callback(c => routedCommands.Add(c.GetCommandRequest().Id, c.GetCommandRequest().Raw)); // Send batch of 100000 commands by using TerminalServices Dictionary allCommands = []; @@ -360,7 +360,7 @@ public async Task ExecuteAsync_Routes_Batched_Commands_And_Processes_In_Order() .Callback(c => { routeContext = c; - switch (c.Request.Raw) + switch (c.GetCommandRequest().Raw) { case "command1": c.SetCommandResult(routerResult1); @@ -388,12 +388,14 @@ public async Task ExecuteAsync_Routes_Batched_Commands_And_Processes_In_Order() // Assert route context and response routeContext.Should().NotBeNull(); - routeContext.Properties.Should().HaveCount(3); + routeContext.Properties.Should().HaveCount(5); routeContext.Properties["sender_endpoint"].Should().Be("sender_endpoint_1"); routeContext.Properties["sender_id"].Should().Be("sender_1"); - routeContext.Properties[TerminalIdentifiers.CommandResult].Should().BeOfType(); + routeContext.Properties[TerminalIdentifiers.CommandResult].Should().BeAssignableTo(); + routeContext.Properties[TerminalIdentifiers.CommandRequest].Should().BeAssignableTo(); + routeContext.Properties[TerminalIdentifiers.RouterContext].Should().BeAssignableTo(); - routeContext.RouterContext.Should().BeSameAs(_mockTerminalRouterContext.Object); + routeContext.GetRouterContext().Should().BeSameAs(_mockTerminalRouterContext.Object); batch.Requests.Should().HaveCount(3); @@ -428,7 +430,7 @@ public async Task ExecuteAsync_Routes_Batched_Commands_With_Null_Value_Result() .Callback(c => { routeContext = c; - switch (c.Request.Raw) + switch (c.GetCommandRequest().Raw) { case "command1": c.SetCommandResult(routerResult1); @@ -464,11 +466,16 @@ public async Task ExecuteAsync_Routes_Batched_Commands_With_Null_Value_Result() // Assert route context and response routeContext.Should().NotBeNull(); - routeContext.Properties.Should().HaveCount(3); + routeContext.Properties.Should().HaveCount(5); routeContext.Properties["sender_endpoint"].Should().Be("sender_endpoint_1"); routeContext.Properties["sender_id"].Should().Be("sender_1"); - routeContext.Properties[TerminalIdentifiers.CommandResult].Should().BeOfType(); - routeContext.RouterContext.Should().BeSameAs(_mockTerminalRouterContext.Object); + routeContext.Properties[TerminalIdentifiers.CommandResult].Should().BeAssignableTo(); + routeContext.Properties[TerminalIdentifiers.CommandRequest].Should().BeAssignableTo(); + routeContext.Properties[TerminalIdentifiers.RouterContext].Should().BeAssignableTo(); + + routeContext.GetRouterContext().Should().BeSameAs(_mockTerminalRouterContext.Object); + routeContext.GetCommandRequest().Raw.Should().Be("command5"); + routeContext.GetCommandResult().Should().BeAssignableTo(); // Assert requests and results terminalIO.Requests[0].Raw.Should().Be("command1"); @@ -512,14 +519,16 @@ public async Task ExecuteAsync_Routes_Command_And_Returns_Result() // Make sure context is correctly populated routeContext.Should().NotBeNull(); - routeContext.Properties.Should().HaveCount(3); + routeContext.Properties.Should().HaveCount(5); routeContext.Properties["sender_endpoint"].Should().Be("sender_endpoint_1"); routeContext.Properties["sender_id"].Should().Be("sender_1"); - routeContext.Properties[TerminalIdentifiers.CommandResult].Should().BeOfType(); + routeContext.Properties[TerminalIdentifiers.CommandResult].Should().BeAssignableTo(); + routeContext.Properties[TerminalIdentifiers.CommandRequest].Should().BeAssignableTo(); + routeContext.Properties[TerminalIdentifiers.RouterContext].Should().BeAssignableTo(); - routeContext.Request.Id.Should().NotBeNullOrWhiteSpace(); - routeContext.Request.Raw.Should().Be("command1"); - routeContext.RouterContext.Should().BeSameAs(_mockTerminalRouterContext.Object); + routeContext.GetCommandRequest().Id.Should().NotBeNullOrWhiteSpace(); + routeContext.GetCommandRequest().Raw.Should().Be("command1"); + routeContext.GetRouterContext().Should().BeSameAs(_mockTerminalRouterContext.Object); // Make sure response is correct terminalIO.SenderId.Should().Be("sender_1"); @@ -570,14 +579,13 @@ public async Task StartProcessing_Populates_Router_Context() await Task.Delay(500, TestContext.Current.CancellationToken); routeContext.Should().NotBeNull(); - routeContext.Properties.Should().HaveCount(2); + routeContext.Properties.Should().HaveCount(4); routeContext.Properties["sender_endpoint"].Should().Be("sender_endpoint_1"); routeContext.Properties["sender_id"].Should().Be("sender_1"); - routeContext.Request.Id.Should().NotBeNullOrWhiteSpace(); - routeContext.Request.Raw.Should().Be("command1"); - - routeContext.RouterContext.Should().BeSameAs(_mockTerminalRouterContext.Object); + routeContext.GetCommandRequest().Id.Should().NotBeNullOrWhiteSpace(); + routeContext.GetCommandRequest().Raw.Should().Be("command1"); + routeContext.GetRouterContext().Should().BeSameAs(_mockTerminalRouterContext.Object); // Add without sender endpoint and sender id routeContext = null; @@ -585,14 +593,13 @@ public async Task StartProcessing_Populates_Router_Context() await Task.Delay(500, TestContext.Current.CancellationToken); routeContext.Should().NotBeNull(); - routeContext!.Properties.Should().HaveCount(2); + routeContext!.Properties.Should().HaveCount(4); routeContext.Properties!["sender_id"].Should().Be("$unknown$"); routeContext.Properties!["sender_endpoint"].Should().Be("$unknown$"); - routeContext.Request.Id.Should().NotBeNullOrWhiteSpace(); - routeContext.Request.Raw.Should().Be("command2"); - - routeContext.RouterContext.Should().BeSameAs(_mockTerminalRouterContext.Object); + routeContext.GetCommandRequest().Id.Should().NotBeNullOrWhiteSpace(); + routeContext.GetCommandRequest().Raw.Should().Be("command2"); + routeContext.GetRouterContext().Should().BeSameAs(_mockTerminalRouterContext.Object); await _terminalProcessor.StopProcessingAsync(2000); } @@ -635,7 +642,7 @@ public async Task StopProcessingAsync_Any_Timeout_And_Completed_Processing_Sets_ _terminalProcessor.IsProcessing.Should().BeTrue(); _terminalTokenSource.Cancel(); - await Task.Delay(500); + await Task.Delay(500, TestContext.Current.CancellationToken); var timedOut = await _terminalProcessor.StopProcessingAsync(timeout); timedOut.Should().BeFalse(); @@ -695,7 +702,7 @@ public async Task Stream_Does_No_Processes_Partial_Batch() _mockCommandRouter.Setup(x => x.RouteCommandAsync(It.IsAny())) .Callback(ctx => { - processedCommands.Add(ctx.Request.Raw); + processedCommands.Add(ctx.GetCommandRequest().Raw); }); // Start the processor @@ -738,7 +745,7 @@ public async Task Stream_Does_Not_Process_Non_Delimited_Input() _mockCommandRouter.Setup(x => x.RouteCommandAsync(It.IsAny())) .Callback(ctx => { - processedCommand = ctx.Request.Raw; + processedCommand = ctx.GetCommandRequest().Raw; }); // Start the processor @@ -771,7 +778,7 @@ public async Task Stream_Processes_Chunks_In_Order() _mockCommandRouter.Setup(x => x.RouteCommandAsync(It.IsAny())) .Callback(ctx => { - processedCommands.Enqueue(ctx.Request.Raw); + processedCommands.Enqueue(ctx.GetCommandRequest().Raw); }); // Create a large batch of commands to simulate streaming @@ -818,7 +825,7 @@ public async Task StreamRequestAsync_Processes_Delimited_Input() _mockCommandRouter.Setup(x => x.RouteCommandAsync(It.IsAny())) .Callback(ctx => { - processedCommand = ctx.Request.Raw; + processedCommand = ctx.GetCommandRequest().Raw; }); // Start the processor From 64d8f51d81bf4c4b46e0122e8e19abe476367738 Mon Sep 17 00:00:00 2001 From: "pi.admin" Date: Fri, 5 Jun 2026 15:59:40 -0700 Subject: [PATCH 3/9] Support custom context --- apps/auth/TestMsal/Runners/AuthRunner.cs | 4 +- apps/auth/TestMsal/Runners/AuthUserRunner.cs | 4 +- apps/auth/TestMsal/Runners/TestAuthRunner.cs | 9 +-- apps/cli/TestConsole/Custom/CustomContext.cs | 10 ++++ apps/cli/TestConsole/Runners/ClearRunner.cs | 4 +- apps/cli/TestConsole/Runners/Cmd7Runner.cs | 4 +- apps/cli/TestConsole/Runners/Cmd8Runner.cs | 4 +- apps/cli/TestConsole/Runners/Cmd9Runner.cs | 4 +- apps/cli/TestConsole/Runners/ExitRunner.cs | 4 +- apps/cli/TestConsole/Runners/Grp1Runner.cs | 4 +- apps/cli/TestConsole/Runners/Grp2Runner.cs | 4 +- apps/cli/TestConsole/Runners/Grp3Runner.cs | 4 +- apps/cli/TestConsole/Runners/HelpRunner.cs | 4 +- apps/cli/TestConsole/Runners/RunRunner.cs | 4 +- apps/cli/TestConsole/Runners/TestRunner.cs | 4 +- apps/s2s/TestApiServer/Runners/Cmd1Runner.cs | 4 +- apps/s2s/TestApiServer/Runners/Cmd2Runner.cs | 4 +- apps/s2s/TestApiServer/Runners/Grp1Runner.cs | 4 +- apps/s2s/TestApiServer/Runners/Grp2Runner.cs | 4 +- .../Runners/TestApiServerRunner.cs | 4 +- apps/s2s/TestClient/Runners/ClsRunner.cs | 4 +- apps/s2s/TestClient/Runners/ExitRunner.cs | 4 +- apps/s2s/TestClient/Runners/HelpRunner.cs | 4 +- apps/s2s/TestClient/Runners/RunRunner.cs | 4 +- .../TestClient/Runners/SendApiHttpRunner.cs | 4 +- apps/s2s/TestClient/Runners/SendGrpcRunner.cs | 4 +- apps/s2s/TestClient/Runners/SendHttpRunner.cs | 4 +- apps/s2s/TestClient/Runners/SendRunner.cs | 4 +- apps/s2s/TestClient/Runners/SendTcpRunner.cs | 4 +- apps/s2s/TestClient/Runners/SendUdpRunner.cs | 4 +- .../TestClient/Runners/TestClientRunner.cs | 4 +- apps/s2s/TestServer/Runners/Cmd1Runner.cs | 4 +- apps/s2s/TestServer/Runners/Cmd2Runner.cs | 4 +- apps/s2s/TestServer/Runners/Grp1Runner.cs | 4 +- apps/s2s/TestServer/Runners/Grp2Runner.cs | 4 +- .../TestServer/Runners/TestServerRunner.cs | 4 +- .../Commands/Checkers/CommandChecker.cs | 4 +- .../Commands/RunMethodDescriptor.cs | 6 +- .../Commands/Runners/CommandRunner.cs | 15 +++-- .../Commands/Runners/ICommandRunner.cs | 8 ++- .../Commands/Runners/LicenseInfoRunner.cs | 4 +- .../Extensions/ITerminalBuilderExtensions.cs | 2 +- .../Mocks/MockCommandRunnerGenericsInner.cs | 4 +- .../Handlers/Mocks/MockCommandRunnerInner.cs | 2 +- .../Mocks/MockErrorCommandRunnerInner.cs | 2 +- .../Commands/MockRunnerWithBaseResult.cs | 4 +- .../Commands/MockRunnerWithDerivedResult.cs | 2 +- .../Runners/Mocks/MockDefaultCommandRunner.cs | 17 +++--- .../Mocks/MockCommandRunner.cs | 17 +++--- .../Mocks/MockCommands.cs | 60 +++++++++---------- 50 files changed, 155 insertions(+), 147 deletions(-) create mode 100644 apps/cli/TestConsole/Custom/CustomContext.cs diff --git a/apps/auth/TestMsal/Runners/AuthRunner.cs b/apps/auth/TestMsal/Runners/AuthRunner.cs index a1e7e942..7bd5028e 100644 --- a/apps/auth/TestMsal/Runners/AuthRunner.cs +++ b/apps/auth/TestMsal/Runners/AuthRunner.cs @@ -13,7 +13,7 @@ namespace OneImlx.Terminal.Apps.TestAuth.Runners /// [CommandOwners("test")] [CommandDescriptor("auth", "Auth group", "Test auth group description.", CommandTypes.IsolatedGroup)] - public class AuthRunner : CommandRunner, IDeclarativeRunner + public class AuthRunner : CommandRunner, IDeclarativeRunner { private readonly ITerminalConsole _terminalConsole; private readonly ILogger _logger; @@ -34,7 +34,7 @@ public AuthRunner(ITerminalConsole terminalConsole, ILogger logger) /// /// Command runner context. /// Command runner result. - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { await _terminalConsole.WriteLineAsync("Auth group command called."); diff --git a/apps/auth/TestMsal/Runners/AuthUserRunner.cs b/apps/auth/TestMsal/Runners/AuthUserRunner.cs index 08397a73..b318db1e 100644 --- a/apps/auth/TestMsal/Runners/AuthUserRunner.cs +++ b/apps/auth/TestMsal/Runners/AuthUserRunner.cs @@ -16,7 +16,7 @@ namespace OneImlx.Terminal.Apps.TestAuth.Runners /// [CommandOwners("auth")] [CommandDescriptor("user", "Get user", "Fetches user information from Microsoft Graph API.", CommandTypes.IsolatedGroup)] - public class AuthUserRunner : CommandRunner, IDeclarativeRunner + public class AuthUserRunner : CommandRunner, IDeclarativeRunner { /// /// Initializes a new instance. @@ -36,7 +36,7 @@ public AuthUserRunner(ITerminalConsole terminalConsole, IHttpClientFactory httpC /// /// Command runner context. /// Command runner result. - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { // Get the HTTP client from the factory with the name "demo-http" since this name is configured to use the diff --git a/apps/auth/TestMsal/Runners/TestAuthRunner.cs b/apps/auth/TestMsal/Runners/TestAuthRunner.cs index 68415d27..71c148fa 100644 --- a/apps/auth/TestMsal/Runners/TestAuthRunner.cs +++ b/apps/auth/TestMsal/Runners/TestAuthRunner.cs @@ -1,7 +1,4 @@ -// Copyright © 2019-2026 Perpetual Intelligence L.L.C. All rights reserved. -// For license, terms, and data policies, go to: -// https://terms.perpetualintelligence.com/articles/intro.html -using System; +using System; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using OneImlx.Terminal.Commands; @@ -20,7 +17,7 @@ namespace OneImlx.Terminal.Apps.TestAuth.Runners [CommandDescriptor("test", "Test App", "Test application description.", CommandTypes.Root)] [OptionDescriptor("version", nameof(String), "Test version description", BehaviorFlags.None, "v")] [CommandChecker(typeof(CommandChecker))] - public class TestRunner : CommandRunner, IDeclarativeRunner + public class TestRunner : CommandRunner, IDeclarativeRunner { /// /// Initializes a new instance. @@ -38,7 +35,7 @@ public TestRunner(ITerminalConsole terminalConsole, ILogger logger) /// /// Command runner context. /// Command runner result. - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { await _terminalConsole.WriteLineAsync("Test root command called."); diff --git a/apps/cli/TestConsole/Custom/CustomContext.cs b/apps/cli/TestConsole/Custom/CustomContext.cs new file mode 100644 index 00000000..a9e7afd2 --- /dev/null +++ b/apps/cli/TestConsole/Custom/CustomContext.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using OneImlx.Terminal.Shared; + +namespace OneImlx.Terminal.Apps.Test.Custom +{ + public class CustomContext(Dictionary properties) : ICommandContext + { + public Dictionary Properties { get; } = properties; + } +} \ No newline at end of file diff --git a/apps/cli/TestConsole/Runners/ClearRunner.cs b/apps/cli/TestConsole/Runners/ClearRunner.cs index 73d9e1e9..a4abe182 100644 --- a/apps/cli/TestConsole/Runners/ClearRunner.cs +++ b/apps/cli/TestConsole/Runners/ClearRunner.cs @@ -11,7 +11,7 @@ namespace OneImlx.Terminal.Apps.Test.Runners /// Clears the console. /// [CommandDescriptor("cls", "Clear Console", "Clears the console.", CommandTypes.Native)] - public class ClearRunner : CommandRunner, IDeclarativeRunner + public class ClearRunner : CommandRunner , IDeclarativeRunner { /// /// Initializes a new instance of the class. @@ -23,7 +23,7 @@ public ClearRunner(ITerminalConsole terminalConsole) } /// - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { await terminalConsole.ClearAsync(); return await CommandRunnerResult.EmptyAsync(); diff --git a/apps/cli/TestConsole/Runners/Cmd7Runner.cs b/apps/cli/TestConsole/Runners/Cmd7Runner.cs index 7e0bf811..8154c4ee 100644 --- a/apps/cli/TestConsole/Runners/Cmd7Runner.cs +++ b/apps/cli/TestConsole/Runners/Cmd7Runner.cs @@ -13,7 +13,7 @@ namespace OneImlx.Terminal.Apps.Test.Runners /// [CommandOwners("grp3")] [CommandDescriptor("cmd7", "Command 7", "Command 7 under grp3.", CommandTypes.Leaf)] - public class Cmd7Runner : CommandRunner, IDeclarativeRunner + public class Cmd7Runner : CommandRunner , IDeclarativeRunner { private readonly ITerminalConsole terminalConsole; private readonly ILogger logger; @@ -24,7 +24,7 @@ public Cmd7Runner(ITerminalConsole terminalConsole, ILogger logger) this.logger = logger; } - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { logger.LogInformation("Executing grp3 cmd7"); await terminalConsole.WriteLineAsync("Executing: grp3 cmd7"); diff --git a/apps/cli/TestConsole/Runners/Cmd8Runner.cs b/apps/cli/TestConsole/Runners/Cmd8Runner.cs index 07541f04..ffaf967e 100644 --- a/apps/cli/TestConsole/Runners/Cmd8Runner.cs +++ b/apps/cli/TestConsole/Runners/Cmd8Runner.cs @@ -13,7 +13,7 @@ namespace OneImlx.Terminal.Apps.Test.Runners /// [CommandOwners("grp3")] [CommandDescriptor("cmd8", "Command 8", "Command 8 under grp3.", CommandTypes.Leaf)] - public class Cmd8Runner : CommandRunner, IDeclarativeRunner + public class Cmd8Runner : CommandRunner , IDeclarativeRunner { private readonly ITerminalConsole terminalConsole; private readonly ILogger logger; @@ -24,7 +24,7 @@ public Cmd8Runner(ITerminalConsole terminalConsole, ILogger logger) this.logger = logger; } - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { logger.LogInformation("Executing grp3 cmd8"); await terminalConsole.WriteLineAsync("Executing: grp3 cmd8"); diff --git a/apps/cli/TestConsole/Runners/Cmd9Runner.cs b/apps/cli/TestConsole/Runners/Cmd9Runner.cs index c0b4f8b0..adacc7ab 100644 --- a/apps/cli/TestConsole/Runners/Cmd9Runner.cs +++ b/apps/cli/TestConsole/Runners/Cmd9Runner.cs @@ -16,7 +16,7 @@ namespace OneImlx.Terminal.Apps.Test.Runners [CommandOwners("grp3")] [CommandDescriptor("cmd9", "Command 9", "Command 9 under grp3 with custom checker.", CommandTypes.Leaf)] [CommandChecker(typeof(Cmd3CommandChecker))] - public class Cmd9Runner : CommandRunner, IDeclarativeRunner + public class Cmd9Runner : CommandRunner , IDeclarativeRunner { private readonly ITerminalConsole terminalConsole; private readonly ILogger logger; @@ -27,7 +27,7 @@ public Cmd9Runner(ITerminalConsole terminalConsole, ILogger logger) this.logger = logger; } - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { logger.LogInformation("Executing grp3 cmd9"); await terminalConsole.WriteLineAsync("Executing: grp3 cmd9"); diff --git a/apps/cli/TestConsole/Runners/ExitRunner.cs b/apps/cli/TestConsole/Runners/ExitRunner.cs index 9ecf8dd5..563db6a7 100644 --- a/apps/cli/TestConsole/Runners/ExitRunner.cs +++ b/apps/cli/TestConsole/Runners/ExitRunner.cs @@ -12,7 +12,7 @@ namespace OneImlx.Terminal.Apps.Test.Runners /// Runs native OS commands. /// [CommandDescriptor("exit", "Exit", "Exits the client terminal application.", CommandTypes.Native)] - public class ExitRunner : CommandRunner, IDeclarativeRunner + public class ExitRunner : CommandRunner , IDeclarativeRunner { /// /// Initializes a new instance of the class. @@ -24,7 +24,7 @@ public ExitRunner(ITerminalConsole terminalConsole, IHostApplicationLifetime app } /// - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { string answer = await terminalConsole.ReadAnswerAsync("Are you sure you want to exit ?", "y", "Y"); if (answer == "y" || answer == "Y") diff --git a/apps/cli/TestConsole/Runners/Grp1Runner.cs b/apps/cli/TestConsole/Runners/Grp1Runner.cs index b110a465..92400a32 100644 --- a/apps/cli/TestConsole/Runners/Grp1Runner.cs +++ b/apps/cli/TestConsole/Runners/Grp1Runner.cs @@ -23,7 +23,7 @@ namespace OneImlx.Terminal.Apps.Test.Runners [CommandDescriptor("grp1", "Group 1", "Group 1 as a composite group", CommandTypes.CompositeGroup)] [CommandChecker(typeof(CommandChecker))] [CommandTags("group", "composite")] - public class Grp1Runner : CommandRunner, IDeclarativeRunner + public class Grp1Runner : CommandRunner , IDeclarativeRunner { private readonly ITerminalConsole terminalConsole; private readonly ILogger logger; @@ -34,7 +34,7 @@ public Grp1Runner(ITerminalConsole terminalConsole, ILogger logger) this.logger = logger; } - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { logger.LogInformation("Executing grp1 base command"); await terminalConsole.WriteLineAsync("Group 1 (CompositeGroup)"); diff --git a/apps/cli/TestConsole/Runners/Grp2Runner.cs b/apps/cli/TestConsole/Runners/Grp2Runner.cs index 496c90c5..dab3c71b 100644 --- a/apps/cli/TestConsole/Runners/Grp2Runner.cs +++ b/apps/cli/TestConsole/Runners/Grp2Runner.cs @@ -19,7 +19,7 @@ namespace OneImlx.Terminal.Apps.Test.Runners [CommandDescriptor("grp2", "Group 2", "Group 2 CompositeGroup under grp1 with cmd4, cmd5, cmd6.", CommandTypes.CompositeGroup)] [CommandChecker(typeof(CommandChecker))] [CommandTags("group", "composite", "nested")] - public class Grp2Runner : CommandRunner, IDeclarativeRunner + public class Grp2Runner : CommandRunner , IDeclarativeRunner { private readonly ITerminalConsole terminalConsole; private readonly ILogger logger; @@ -30,7 +30,7 @@ public Grp2Runner(ITerminalConsole terminalConsole, ILogger logger) this.logger = logger; } - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { logger.LogInformation("Executing grp2 base command"); await terminalConsole.WriteLineAsync("Group 2 (CompositeGroup under grp1)"); diff --git a/apps/cli/TestConsole/Runners/Grp3Runner.cs b/apps/cli/TestConsole/Runners/Grp3Runner.cs index 08dae23c..5e9e29d0 100644 --- a/apps/cli/TestConsole/Runners/Grp3Runner.cs +++ b/apps/cli/TestConsole/Runners/Grp3Runner.cs @@ -16,7 +16,7 @@ namespace OneImlx.Terminal.Apps.Test.Runners [CommandDescriptor("grp3", "Group 3", "Group 3 IsolatedGroup (independent) with cmd7, cmd8, cmd9.", CommandTypes.IsolatedGroup)] [CommandChecker(typeof(CommandChecker))] [CommandTags("group", "isolated", "independent")] - public class Grp3Runner : CommandRunner, IDeclarativeRunner + public class Grp3Runner : CommandRunner , IDeclarativeRunner { private readonly ITerminalConsole terminalConsole; private readonly ILogger logger; @@ -27,7 +27,7 @@ public Grp3Runner(ITerminalConsole terminalConsole, ILogger logger) this.logger = logger; } - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { logger.LogInformation("Executing grp3 command"); await terminalConsole.WriteLineAsync("Group 3 (IsolatedGroup - independent)"); diff --git a/apps/cli/TestConsole/Runners/HelpRunner.cs b/apps/cli/TestConsole/Runners/HelpRunner.cs index 11543cf1..3898177c 100644 --- a/apps/cli/TestConsole/Runners/HelpRunner.cs +++ b/apps/cli/TestConsole/Runners/HelpRunner.cs @@ -12,7 +12,7 @@ namespace OneImlx.Terminal.Apps.Test.Runners /// Runs native OS commands. /// [CommandDescriptor("help", "Help Command", "Displays all supported commands.", CommandTypes.Native)] - public class HelpRunner : CommandRunner, IDeclarativeRunner + public class HelpRunner : CommandRunner , IDeclarativeRunner { /// /// Initializes a new instance of the class. @@ -24,7 +24,7 @@ public HelpRunner(ITerminalConsole console, ITerminalCommandStore commandStore) } /// - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { var commands = await commandStore.AllAsync(); diff --git a/apps/cli/TestConsole/Runners/RunRunner.cs b/apps/cli/TestConsole/Runners/RunRunner.cs index 47b81813..a3fc2d53 100644 --- a/apps/cli/TestConsole/Runners/RunRunner.cs +++ b/apps/cli/TestConsole/Runners/RunRunner.cs @@ -18,7 +18,7 @@ namespace OneImlx.Terminal.Apps.Test.Runners /// [CommandDescriptor("run", "Run Command", "Runs a native OS command.", CommandTypes.Native)] [ArgumentDescriptor(0, "cmd", nameof(String), "The full native command to execute, e.g., 'ls -all'", BehaviorFlags.Required)] - public class RunRunner : CommandRunner, IDeclarativeRunner + public class RunRunner : CommandRunner , IDeclarativeRunner { /// /// Initializes a new instance of the class. @@ -29,7 +29,7 @@ public RunRunner(ITerminalConsole terminalConsole) } /// - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { var command = context.GetParsedCommand().Command; var osCommand = command.GetRequiredArgumentValue("cmd"); diff --git a/apps/cli/TestConsole/Runners/TestRunner.cs b/apps/cli/TestConsole/Runners/TestRunner.cs index 7420e45c..6aaaff21 100644 --- a/apps/cli/TestConsole/Runners/TestRunner.cs +++ b/apps/cli/TestConsole/Runners/TestRunner.cs @@ -20,7 +20,7 @@ namespace OneImlx.Terminal.Apps.Test.Runners [CommandDescriptor("test", "Test App", "Test application description.", CommandTypes.Root)] [OptionDescriptor("version", nameof(String), "Test version description", BehaviorFlags.None, "v")] [CommandChecker(typeof(CommandChecker))] - public class TestRunner : CommandRunner, IDeclarativeRunner + public class TestRunner : CommandRunner , IDeclarativeRunner { private readonly ITerminalConsole terminalConsole; private readonly ILogger logger; @@ -31,7 +31,7 @@ public TestRunner(ITerminalConsole terminalConsole, ILogger logger) this.logger = logger; } - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { await terminalConsole.WriteLineAsync("Test root command called."); diff --git a/apps/s2s/TestApiServer/Runners/Cmd1Runner.cs b/apps/s2s/TestApiServer/Runners/Cmd1Runner.cs index 45588dc3..96e1d975 100644 --- a/apps/s2s/TestApiServer/Runners/Cmd1Runner.cs +++ b/apps/s2s/TestApiServer/Runners/Cmd1Runner.cs @@ -13,7 +13,7 @@ namespace OneImlx.Terminal.Apps.TestApiServer.Runners [CommandOwners("grp1")] [CommandDescriptor("cmd1", "Command 1", "Command1 description.", CommandTypes.Leaf)] [CommandChecker(typeof(CommandChecker))] - public class Cmd1Runner : CommandRunner, IDeclarativeRunner + public class Cmd1Runner : CommandRunner, IDeclarativeRunner { public Cmd1Runner(ITerminalConsole terminalConsole, ILogger logger) { @@ -21,7 +21,7 @@ public Cmd1Runner(ITerminalConsole terminalConsole, ILogger logger) this.logger = logger; } - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { await terminalConsole.WriteLineAsync("Command1 of Group1 called."); return new CommandRunnerResult("Response from cmd1"); diff --git a/apps/s2s/TestApiServer/Runners/Cmd2Runner.cs b/apps/s2s/TestApiServer/Runners/Cmd2Runner.cs index f387ea52..ddb86f83 100644 --- a/apps/s2s/TestApiServer/Runners/Cmd2Runner.cs +++ b/apps/s2s/TestApiServer/Runners/Cmd2Runner.cs @@ -13,7 +13,7 @@ namespace OneImlx.Terminal.Apps.TestApiServer.Runners [CommandOwners("grp2")] [CommandDescriptor("cmd2", "Command 2", "Command2 description.", CommandTypes.Leaf)] [CommandChecker(typeof(CommandChecker))] - public class Cmd2Runner : CommandRunner, IDeclarativeRunner + public class Cmd2Runner : CommandRunner, IDeclarativeRunner { private readonly ITerminalConsole terminalConsole; private readonly ILogger logger; @@ -24,7 +24,7 @@ public Cmd2Runner(ITerminalConsole terminalConsole, ILogger logger) this.logger = logger; } - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { await terminalConsole.WriteLineAsync("Command2 of Group2 called."); return new CommandRunnerResult("Response from cmd2"); diff --git a/apps/s2s/TestApiServer/Runners/Grp1Runner.cs b/apps/s2s/TestApiServer/Runners/Grp1Runner.cs index ce5c2dba..6dde3ea0 100644 --- a/apps/s2s/TestApiServer/Runners/Grp1Runner.cs +++ b/apps/s2s/TestApiServer/Runners/Grp1Runner.cs @@ -13,7 +13,7 @@ namespace OneImlx.Terminal.Apps.TestApiServer.Runners [CommandOwners("ts")] [CommandDescriptor("grp1", "Group 1", "Group1 description.", CommandTypes.IsolatedGroup)] [CommandChecker(typeof(CommandChecker))] - public class Grp1Runner : CommandRunner, IDeclarativeRunner + public class Grp1Runner : CommandRunner, IDeclarativeRunner { private readonly ITerminalConsole terminalConsole; private readonly ILogger logger; @@ -24,7 +24,7 @@ public Grp1Runner(ITerminalConsole terminalConsole, ILogger logger) this.logger = logger; } - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { await terminalConsole.WriteLineAsync("Group1 command called."); return new CommandRunnerResult("Response from grp1"); diff --git a/apps/s2s/TestApiServer/Runners/Grp2Runner.cs b/apps/s2s/TestApiServer/Runners/Grp2Runner.cs index 2d1b5b6d..0fa2b83f 100644 --- a/apps/s2s/TestApiServer/Runners/Grp2Runner.cs +++ b/apps/s2s/TestApiServer/Runners/Grp2Runner.cs @@ -13,7 +13,7 @@ namespace OneImlx.Terminal.Apps.TestApiServer.Runners [CommandOwners("grp1")] [CommandDescriptor("grp2", "Group 2", "Group2 description.", CommandTypes.IsolatedGroup)] [CommandChecker(typeof(CommandChecker))] - public class Grp2Runner : CommandRunner, IDeclarativeRunner + public class Grp2Runner : CommandRunner, IDeclarativeRunner { private readonly ITerminalConsole terminalConsole; private readonly ILogger logger; @@ -24,7 +24,7 @@ public Grp2Runner(ITerminalConsole terminalConsole, ILogger logger) this.logger = logger; } - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { await terminalConsole.WriteLineAsync("Group2 command called."); return new CommandRunnerResult("Response from grp2"); diff --git a/apps/s2s/TestApiServer/Runners/TestApiServerRunner.cs b/apps/s2s/TestApiServer/Runners/TestApiServerRunner.cs index 3fe8205f..08c42a29 100644 --- a/apps/s2s/TestApiServer/Runners/TestApiServerRunner.cs +++ b/apps/s2s/TestApiServer/Runners/TestApiServerRunner.cs @@ -15,7 +15,7 @@ namespace OneImlx.Terminal.Apps.TestApiServer.Runners /// [CommandDescriptor("ts", "Test Server", "Test server description.", CommandTypes.Root)] [OptionDescriptor("version", nameof(String), "Test server version description", BehaviorFlags.None, "v")] - public class TestApiServerRunner : CommandRunner, IDeclarativeRunner + public class TestApiServerRunner : CommandRunner, IDeclarativeRunner { private readonly ITerminalConsole terminalConsole; private readonly ILogger logger; @@ -26,7 +26,7 @@ public TestApiServerRunner(ITerminalConsole terminalConsole, ILogger RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { await terminalConsole.WriteLineAsync("Test API server root command called."); diff --git a/apps/s2s/TestClient/Runners/ClsRunner.cs b/apps/s2s/TestClient/Runners/ClsRunner.cs index 2154e317..db83c2d1 100644 --- a/apps/s2s/TestClient/Runners/ClsRunner.cs +++ b/apps/s2s/TestClient/Runners/ClsRunner.cs @@ -11,7 +11,7 @@ namespace OneImlx.Terminal.Apps.TestClient.Runners /// Clears the console. /// [CommandDescriptor("cls", "Clear Console", "Clears the console.", CommandTypes.Native)] - public class ClsRunner : CommandRunner, IDeclarativeRunner + public class ClsRunner : CommandRunner, IDeclarativeRunner { /// /// Initializes a new instance of the class. @@ -23,7 +23,7 @@ public ClsRunner(ITerminalConsole terminalConsole) } /// - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { await terminalConsole.ClearAsync(); return await CommandRunnerResult.EmptyAsync(); diff --git a/apps/s2s/TestClient/Runners/ExitRunner.cs b/apps/s2s/TestClient/Runners/ExitRunner.cs index e28533fe..6c32ab80 100644 --- a/apps/s2s/TestClient/Runners/ExitRunner.cs +++ b/apps/s2s/TestClient/Runners/ExitRunner.cs @@ -12,7 +12,7 @@ namespace OneImlx.Terminal.Apps.TestClient.Runners /// Runs native OS commands. /// [CommandDescriptor("exit", "Exit", "Exits the client terminal application.", CommandTypes.Native)] - public class ExitRunner : CommandRunner, IDeclarativeRunner + public class ExitRunner : CommandRunner, IDeclarativeRunner { /// /// Initializes a new instance of the class. @@ -24,7 +24,7 @@ public ExitRunner(ITerminalConsole terminalConsole, IHostApplicationLifetime app } /// - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { string answer = await terminalConsole.ReadAnswerAsync("Are you sure you want to exit ?", "y", "Y"); if (answer == "y" || answer == "Y") diff --git a/apps/s2s/TestClient/Runners/HelpRunner.cs b/apps/s2s/TestClient/Runners/HelpRunner.cs index 98ce6b18..a2f099cd 100644 --- a/apps/s2s/TestClient/Runners/HelpRunner.cs +++ b/apps/s2s/TestClient/Runners/HelpRunner.cs @@ -12,7 +12,7 @@ namespace OneImlx.Terminal.Apps.TestClient.Runners /// Runs native OS commands. /// [CommandDescriptor("help", "Help Command", "Displays all supported commands.", CommandTypes.Native)] - public class HelpRunner : CommandRunner, IDeclarativeRunner + public class HelpRunner : CommandRunner, IDeclarativeRunner { /// /// Initializes a new instance of the class. @@ -24,7 +24,7 @@ public HelpRunner(ITerminalConsole console, ITerminalCommandStore commandStore) } /// - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { var commands = await commandStore.AllAsync(); diff --git a/apps/s2s/TestClient/Runners/RunRunner.cs b/apps/s2s/TestClient/Runners/RunRunner.cs index 9067a741..06771747 100644 --- a/apps/s2s/TestClient/Runners/RunRunner.cs +++ b/apps/s2s/TestClient/Runners/RunRunner.cs @@ -18,7 +18,7 @@ namespace OneImlx.Terminal.Apps.TestClient.Runners /// [CommandDescriptor("run", "Run Command", "Runs a native OS command.", CommandTypes.Native)] [ArgumentDescriptor(0, "os", nameof(String), "The full native command to execute, e.g., 'ls -all'", BehaviorFlags.Required)] - public class RunRunner : CommandRunner, IDeclarativeRunner + public class RunRunner : CommandRunner, IDeclarativeRunner { /// /// Initializes a new instance of the class. @@ -29,7 +29,7 @@ public RunRunner(ITerminalConsole terminalConsole) } /// - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { var command = context.GetParsedCommand().Command; var osCommand = command.GetRequiredArgumentValue("os"); diff --git a/apps/s2s/TestClient/Runners/SendApiHttpRunner.cs b/apps/s2s/TestClient/Runners/SendApiHttpRunner.cs index bcc3b4ea..340b06df 100644 --- a/apps/s2s/TestClient/Runners/SendApiHttpRunner.cs +++ b/apps/s2s/TestClient/Runners/SendApiHttpRunner.cs @@ -18,7 +18,7 @@ namespace OneImlx.Terminal.Apps.TestClient.Runners { [CommandOwners("send")] [CommandDescriptor("apihttp", "HTTP test", "Send HTTP commands to both Terminal router and ASP.NET API server.", CommandTypes.Leaf)] - public class SendApiHttpRunner : CommandRunner, IDeclarativeRunner + public class SendApiHttpRunner : CommandRunner, IDeclarativeRunner { public SendApiHttpRunner(IConfiguration configuration, ITerminalConsole terminalConsole, IHttpClientFactory httpClientFactory) { @@ -27,7 +27,7 @@ public SendApiHttpRunner(IConfiguration configuration, ITerminalConsole terminal this.httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory)); } - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { string ip = configuration["testclient:testserver:ip"] ?? throw new InvalidOperationException("Server IP address is missing."); string port = configuration.GetValue("testclient:testserver:port") ?? throw new InvalidOperationException("Server port is missing."); diff --git a/apps/s2s/TestClient/Runners/SendGrpcRunner.cs b/apps/s2s/TestClient/Runners/SendGrpcRunner.cs index 0bbaa624..eb10d0b1 100644 --- a/apps/s2s/TestClient/Runners/SendGrpcRunner.cs +++ b/apps/s2s/TestClient/Runners/SendGrpcRunner.cs @@ -21,7 +21,7 @@ namespace OneImlx.Terminal.Apps.TestClient.Runners { [CommandOwners("send")] [CommandDescriptor("grpc", "gRPC test", "Send gRPC commands to the terminal server.", CommandTypes.Leaf)] - public class SendGrpcRunner : CommandRunner, IDeclarativeRunner + public class SendGrpcRunner : CommandRunner, IDeclarativeRunner { public SendGrpcRunner(IConfiguration configuration, ITerminalConsole terminalConsole) { @@ -29,7 +29,7 @@ public SendGrpcRunner(IConfiguration configuration, ITerminalConsole terminalCon this.terminalConsole = terminalConsole ?? throw new ArgumentNullException(nameof(terminalConsole)); } - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { string ip = configuration["testclient:testserver:ip"] ?? throw new InvalidOperationException("Server IP address is missing."); string port = configuration.GetValue("testclient:testserver:port") ?? throw new InvalidOperationException("Server port is missing."); diff --git a/apps/s2s/TestClient/Runners/SendHttpRunner.cs b/apps/s2s/TestClient/Runners/SendHttpRunner.cs index 3d99db8e..d9c812c8 100644 --- a/apps/s2s/TestClient/Runners/SendHttpRunner.cs +++ b/apps/s2s/TestClient/Runners/SendHttpRunner.cs @@ -18,7 +18,7 @@ namespace OneImlx.Terminal.Apps.TestClient.Runners { [CommandOwners("send")] [CommandDescriptor("http", "HTTP test", "Send HTTP commands to the terminal server.", CommandTypes.Leaf)] - public class SendHttpRunner : CommandRunner, IDeclarativeRunner + public class SendHttpRunner : CommandRunner, IDeclarativeRunner { public SendHttpRunner(IConfiguration configuration, ITerminalConsole terminalConsole, IHttpClientFactory httpClientFactory) { @@ -27,7 +27,7 @@ public SendHttpRunner(IConfiguration configuration, ITerminalConsole terminalCon this.httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory)); } - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { string ip = configuration["testclient:testserver:ip"] ?? throw new InvalidOperationException("Server IP address is missing."); string port = configuration.GetValue("testclient:testserver:port") ?? throw new InvalidOperationException("Server port is missing."); diff --git a/apps/s2s/TestClient/Runners/SendRunner.cs b/apps/s2s/TestClient/Runners/SendRunner.cs index 171f6ef4..e560a171 100644 --- a/apps/s2s/TestClient/Runners/SendRunner.cs +++ b/apps/s2s/TestClient/Runners/SendRunner.cs @@ -9,14 +9,14 @@ namespace OneImlx.Terminal.Apps.TestClient.Runners { [CommandOwners("tc")] [CommandDescriptor("send", "Send", "Send group.", CommandTypes.IsolatedGroup)] - public class SendRunner : CommandRunner, IDeclarativeRunner + public class SendRunner : CommandRunner, IDeclarativeRunner { public SendRunner(ITerminalConsole terminalConsole) { this.terminalConsole = terminalConsole; } - public override Task RunCommandAsync(ICommandContext context) + public override Task RunCommandAsync(CommandContext context) { terminalConsole.WriteLineAsync("Sends messages to the test server."); return Task.FromResult(new CommandRunnerResult()); diff --git a/apps/s2s/TestClient/Runners/SendTcpRunner.cs b/apps/s2s/TestClient/Runners/SendTcpRunner.cs index 3f58d15d..bb6a4f02 100644 --- a/apps/s2s/TestClient/Runners/SendTcpRunner.cs +++ b/apps/s2s/TestClient/Runners/SendTcpRunner.cs @@ -22,7 +22,7 @@ namespace OneImlx.Terminal.Apps.TestClient.Runners { [CommandOwners("send")] [CommandDescriptor("tcp", "TCP test", "Send TCP commands to the terminal server.", CommandTypes.Leaf)] - public class SendTcpRunner : CommandRunner, IDeclarativeRunner + public class SendTcpRunner : CommandRunner, IDeclarativeRunner { public SendTcpRunner(IOptions terminalOptions, ITerminalTextHandler terminalTextHandler, @@ -39,7 +39,7 @@ public SendTcpRunner(IOptions terminalOptions, this.terminalExceptionHandler = terminalExceptionHandler; } - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { string server = configuration["testclient:testserver:ip"] ?? throw new InvalidOperationException("Server IP address is missing."); int port = configuration.GetValue("testclient:testserver:port"); diff --git a/apps/s2s/TestClient/Runners/SendUdpRunner.cs b/apps/s2s/TestClient/Runners/SendUdpRunner.cs index b35df940..45a195a2 100644 --- a/apps/s2s/TestClient/Runners/SendUdpRunner.cs +++ b/apps/s2s/TestClient/Runners/SendUdpRunner.cs @@ -22,7 +22,7 @@ namespace OneImlx.Terminal.Apps.TestClient.Runners { [CommandOwners("send")] [CommandDescriptor("udp", "UDP test", "Send UDP commands to the terminal server.", CommandTypes.Leaf)] - public class SendUdpRunner : CommandRunner, IDeclarativeRunner + public class SendUdpRunner : CommandRunner, IDeclarativeRunner { public SendUdpRunner( IOptions terminalOptions, @@ -40,7 +40,7 @@ public SendUdpRunner( this.terminalExceptionHandler = terminalExceptionHandler; } - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { string server = configuration.GetValue("testclient:testserver:ip") ?? throw new InvalidOperationException("Server IP address is missing."); diff --git a/apps/s2s/TestClient/Runners/TestClientRunner.cs b/apps/s2s/TestClient/Runners/TestClientRunner.cs index ce0dd074..a23710a5 100644 --- a/apps/s2s/TestClient/Runners/TestClientRunner.cs +++ b/apps/s2s/TestClient/Runners/TestClientRunner.cs @@ -9,7 +9,7 @@ namespace OneImlx.Terminal.Apps.TestClient.Runners { [CommandDescriptor("tc", "Test root", "Sample test client for testing the server.", CommandTypes.Root)] - public class TestClientRunner : CommandRunner, IDeclarativeRunner + public class TestClientRunner : CommandRunner, IDeclarativeRunner { public TestClientRunner(ITerminalConsole terminalConsole, ILogger logger) { @@ -17,7 +17,7 @@ public TestClientRunner(ITerminalConsole terminalConsole, ILogger RunCommandAsync(ICommandContext context) + public override Task RunCommandAsync(CommandContext context) { terminalConsole.WriteLineAsync("Test client"); return Task.FromResult(new CommandRunnerResult()); diff --git a/apps/s2s/TestServer/Runners/Cmd1Runner.cs b/apps/s2s/TestServer/Runners/Cmd1Runner.cs index 4943a4da..a7fcb920 100644 --- a/apps/s2s/TestServer/Runners/Cmd1Runner.cs +++ b/apps/s2s/TestServer/Runners/Cmd1Runner.cs @@ -15,7 +15,7 @@ namespace OneImlx.Terminal.Apps.TestServer.Runners [CommandOwners("grp1")] [CommandDescriptor("cmd1", "Command 1", "Command1 description.", CommandTypes.Leaf)] [CommandChecker(typeof(CommandChecker))] - public class Cmd1Runner : CommandRunner, IDeclarativeRunner + public class Cmd1Runner : CommandRunner , IDeclarativeRunner { public Cmd1Runner(ITerminalConsole terminalConsole, ILogger logger) { @@ -23,7 +23,7 @@ public Cmd1Runner(ITerminalConsole terminalConsole, ILogger logger) this.logger = logger; } - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { await terminalConsole.WriteLineAsync("Command1 of Group1 called."); return new CommandRunnerResult("Response from cmd1"); diff --git a/apps/s2s/TestServer/Runners/Cmd2Runner.cs b/apps/s2s/TestServer/Runners/Cmd2Runner.cs index de8d767a..5cf82560 100644 --- a/apps/s2s/TestServer/Runners/Cmd2Runner.cs +++ b/apps/s2s/TestServer/Runners/Cmd2Runner.cs @@ -15,7 +15,7 @@ namespace OneImlx.Terminal.Apps.TestServer.Runners [CommandOwners("grp2")] [CommandDescriptor("cmd2", "Command 2", "Command2 description.", CommandTypes.Leaf)] [CommandChecker(typeof(CommandChecker))] - public class Cmd2Runner : CommandRunner, IDeclarativeRunner + public class Cmd2Runner : CommandRunner , IDeclarativeRunner { private readonly ITerminalConsole terminalConsole; private readonly ILogger logger; @@ -26,7 +26,7 @@ public Cmd2Runner(ITerminalConsole terminalConsole, ILogger logger) this.logger = logger; } - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { await terminalConsole.WriteLineAsync("Command2 of Group2 called."); return new CommandRunnerResult("Response from cmd2"); diff --git a/apps/s2s/TestServer/Runners/Grp1Runner.cs b/apps/s2s/TestServer/Runners/Grp1Runner.cs index 37a7457c..c1c20542 100644 --- a/apps/s2s/TestServer/Runners/Grp1Runner.cs +++ b/apps/s2s/TestServer/Runners/Grp1Runner.cs @@ -15,7 +15,7 @@ namespace OneImlx.Terminal.Apps.TestServer.Runners [CommandOwners("ts")] [CommandDescriptor("grp1", "Group 1", "Group1 description.", CommandTypes.IsolatedGroup)] [CommandChecker(typeof(CommandChecker))] - public class Grp1Runner : CommandRunner, IDeclarativeRunner + public class Grp1Runner : CommandRunner , IDeclarativeRunner { private readonly ITerminalConsole terminalConsole; private readonly ILogger logger; @@ -26,7 +26,7 @@ public Grp1Runner(ITerminalConsole terminalConsole, ILogger logger) this.logger = logger; } - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { await terminalConsole.WriteLineAsync("Group1 command called."); return new CommandRunnerResult("Response from grp1"); diff --git a/apps/s2s/TestServer/Runners/Grp2Runner.cs b/apps/s2s/TestServer/Runners/Grp2Runner.cs index 3d7fb3e2..14d0b745 100644 --- a/apps/s2s/TestServer/Runners/Grp2Runner.cs +++ b/apps/s2s/TestServer/Runners/Grp2Runner.cs @@ -15,7 +15,7 @@ namespace OneImlx.Terminal.Apps.TestServer.Runners [CommandOwners("grp1")] [CommandDescriptor("grp2", "Group 2", "Group2 description.", CommandTypes.IsolatedGroup)] [CommandChecker(typeof(CommandChecker))] - public class Grp2Runner : CommandRunner, IDeclarativeRunner + public class Grp2Runner : CommandRunner , IDeclarativeRunner { private readonly ITerminalConsole terminalConsole; private readonly ILogger logger; @@ -26,7 +26,7 @@ public Grp2Runner(ITerminalConsole terminalConsole, ILogger logger) this.logger = logger; } - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { await terminalConsole.WriteLineAsync("Group2 command called."); return new CommandRunnerResult("Response from grp2"); diff --git a/apps/s2s/TestServer/Runners/TestServerRunner.cs b/apps/s2s/TestServer/Runners/TestServerRunner.cs index 47e68a5b..1228d5ca 100644 --- a/apps/s2s/TestServer/Runners/TestServerRunner.cs +++ b/apps/s2s/TestServer/Runners/TestServerRunner.cs @@ -18,7 +18,7 @@ namespace OneImlx.Terminal.Apps.TestServer.Runners /// [CommandDescriptor("ts", "Test Server", "Test server description.", CommandTypes.Root)] [OptionDescriptor("version", nameof(String), "Test server version description", BehaviorFlags.None, "v")] - public class TestServerRunner : CommandRunner, IDeclarativeRunner + public class TestServerRunner : CommandRunner , IDeclarativeRunner { private readonly ITerminalConsole terminalConsole; private readonly ILogger logger; @@ -29,7 +29,7 @@ public TestServerRunner(ITerminalConsole terminalConsole, ILogger RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { await terminalConsole.WriteLineAsync("Test server root command called."); diff --git a/src/OneImlx.Terminal/Commands/Checkers/CommandChecker.cs b/src/OneImlx.Terminal/Commands/Checkers/CommandChecker.cs index a7a434b0..444e43e6 100644 --- a/src/OneImlx.Terminal/Commands/Checkers/CommandChecker.cs +++ b/src/OneImlx.Terminal/Commands/Checkers/CommandChecker.cs @@ -15,7 +15,7 @@ namespace OneImlx.Terminal.Commands.Checkers /// /// The default command checker. /// - public sealed class CommandChecker : ICommandChecker + public class CommandChecker : ICommandChecker { /// /// Initialize a new instance. @@ -33,7 +33,7 @@ public CommandChecker(IOptionChecker optionChecker, IArgumentChecker argumentChe } /// - public async Task CheckCommandAsync(ICommandContext context) + public virtual async Task CheckCommandAsync(ICommandContext context) { Command command = context.GetCommand(); logger.LogDebug("Check command. command={0}", command.Id); diff --git a/src/OneImlx.Terminal/Commands/RunMethodDescriptor.cs b/src/OneImlx.Terminal/Commands/RunMethodDescriptor.cs index d40de8da..0423401c 100644 --- a/src/OneImlx.Terminal/Commands/RunMethodDescriptor.cs +++ b/src/OneImlx.Terminal/Commands/RunMethodDescriptor.cs @@ -13,7 +13,7 @@ namespace OneImlx.Terminal.Commands { /// - /// Identifies for commands in + /// Identifies for commands in /// a . /// /// @@ -50,7 +50,9 @@ public RunMethodDescriptor(string id, MethodInfo methodInfo) /// /// THIS METHOD IS PART OF INTERNAL INFRASTRUCTURE AND IS NOT INTENDED FOR DIRECT USE BY APPLICATION CODE. /// - public async Task RunAsync(CommandRunner commandRunner, ICommandContext context) where TResult : CommandRunnerResult + public async Task RunAsync(CommandRunner commandRunner, TContext context) + where TContext : ICommandContext + where TResult : CommandRunnerResult { // Ensure command matches the passed context ParsedCommand parsedCommand = context.GetParsedCommand(); diff --git a/src/OneImlx.Terminal/Commands/Runners/CommandRunner.cs b/src/OneImlx.Terminal/Commands/Runners/CommandRunner.cs index f5487723..f0ecc826 100644 --- a/src/OneImlx.Terminal/Commands/Runners/CommandRunner.cs +++ b/src/OneImlx.Terminal/Commands/Runners/CommandRunner.cs @@ -15,7 +15,9 @@ namespace OneImlx.Terminal.Commands.Runners /// The framework resolves and invokes the appropriate runner for each command, enabling /// modular and isolated command execution within a terminal application. /// - public abstract class CommandRunner : IDelegateCommandRunner, ICommandRunner where TResult : CommandRunnerResult + public abstract class CommandRunner : IDelegateCommandRunner, ICommandRunner + where TContext : ICommandContext + where TResult : CommandRunnerResult { /// public async Task DelegateHelpAsync(ICommandContext context, ITerminalHelpProvider helpProvider, ILogger? logger = null) @@ -26,7 +28,7 @@ public async Task DelegateHelpAsync(ICommandContext context Command command = context.GetCommand(); logger?.LogDebug("Run help. command={0}", command.Id); - await RunHelpAsync(context).ConfigureAwait(false); + await RunHelpAsync((TContext)context).ConfigureAwait(false); return (TResult)new CommandRunnerResult(); } @@ -38,25 +40,26 @@ public async Task DelegateRunAsync(ICommandContext context, Command command = context.GetCommand(); logger?.LogDebug("Run command. command={0} type={1}", command.Id, command.Descriptor.Type); + TContext typedContext = (TContext)context; TResult result; RunMethodDescriptor? runMethodDescriptor = command.Descriptor.RunMethod; if (runMethodDescriptor != null) { - result = await runMethodDescriptor.RunAsync(this, context).ConfigureAwait(false); + result = await runMethodDescriptor.RunAsync(this, typedContext).ConfigureAwait(false); } else { - result = await RunCommandAsync(context).ConfigureAwait(false); + result = await RunCommandAsync(typedContext).ConfigureAwait(false); } return result; } /// - public abstract Task RunCommandAsync(ICommandContext context); + public abstract Task RunCommandAsync(TContext context); /// - public virtual Task RunHelpAsync(ICommandContext context) + public virtual Task RunHelpAsync(TContext context) { if (helpProvider == null) { diff --git a/src/OneImlx.Terminal/Commands/Runners/ICommandRunner.cs b/src/OneImlx.Terminal/Commands/Runners/ICommandRunner.cs index fa47f645..f8f5f54d 100644 --- a/src/OneImlx.Terminal/Commands/Runners/ICommandRunner.cs +++ b/src/OneImlx.Terminal/Commands/Runners/ICommandRunner.cs @@ -10,20 +10,22 @@ namespace OneImlx.Terminal.Commands.Runners /// /// An abstraction of a command runner. /// - public interface ICommandRunner where TResult : CommandRunnerResult + public interface ICommandRunner + where TContext : ICommandContext + where TResult : CommandRunnerResult { /// /// Runs a command asynchronously. /// /// The runner context. /// The runner result. - Task RunCommandAsync(ICommandContext context); + Task RunCommandAsync(TContext context); /// /// Runs a command help asynchronously. /// /// The runner context. /// The runner result. - Task RunHelpAsync(ICommandContext context); + Task RunHelpAsync(TContext context); } } \ No newline at end of file diff --git a/src/OneImlx.Terminal/Commands/Runners/LicenseInfoRunner.cs b/src/OneImlx.Terminal/Commands/Runners/LicenseInfoRunner.cs index 5a1e6b73..5467782d 100644 --- a/src/OneImlx.Terminal/Commands/Runners/LicenseInfoRunner.cs +++ b/src/OneImlx.Terminal/Commands/Runners/LicenseInfoRunner.cs @@ -14,7 +14,7 @@ namespace OneImlx.Terminal.Commands.Runners /// /// The default license info runner that outputs the current licensing information to the . /// - public class LicenseInfoRunner : CommandRunner + public class LicenseInfoRunner : CommandRunner { /// /// Initialize a new instance. @@ -27,7 +27,7 @@ public LicenseInfoRunner(ITerminalConsole terminalConsole, ILicenseExtractor lic } /// - public override async Task RunCommandAsync(ICommandContext context) + public override async Task RunCommandAsync(CommandContext context) { // Recheck the license to get the current consumption. // TODO: This should be tolerant of over-consumption since it is printing the usage. At present CheckLicenseAsync diff --git a/src/OneImlx.Terminal/Extensions/ITerminalBuilderExtensions.cs b/src/OneImlx.Terminal/Extensions/ITerminalBuilderExtensions.cs index 9d7fe89c..b16b70b9 100644 --- a/src/OneImlx.Terminal/Extensions/ITerminalBuilderExtensions.cs +++ b/src/OneImlx.Terminal/Extensions/ITerminalBuilderExtensions.cs @@ -379,7 +379,7 @@ public static ITerminalBuilder AddTextHandler(this ITerminalBuilde /// The command runner type. /// The configured . /// The configured . - public static ICommandBuilder DefineCommand(this ITerminalBuilder builder, string id, string name, string description, int commandType) where TRunner : ICommandRunner + public static ICommandBuilder DefineCommand(this ITerminalBuilder builder, string id, string name, string description, int commandType) where TRunner : ICommandRunner { return DefineCommand(builder, id, name, description, typeof(CommandChecker), typeof(TRunner), commandType); } diff --git a/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockCommandRunnerGenericsInner.cs b/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockCommandRunnerGenericsInner.cs index c7b5d894..d803446e 100644 --- a/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockCommandRunnerGenericsInner.cs +++ b/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockCommandRunnerGenericsInner.cs @@ -2,13 +2,13 @@ // For license, terms, and data policies, go to: // https://terms.perpetualintelligence.com/articles/intro.html -using System.Threading.Tasks; using OneImlx.Terminal.Commands.Runners; using OneImlx.Terminal.Shared; +using System.Threading.Tasks; namespace OneImlx.Terminal.Commands.Handlers.Mocks { - internal class MockGenericCommandRunnerInner : CommandRunner + internal class MockGenericCommandRunnerInner : CommandRunner { public bool Called { get; private set; } diff --git a/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockCommandRunnerInner.cs b/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockCommandRunnerInner.cs index ca18c584..a1e8e7da 100644 --- a/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockCommandRunnerInner.cs +++ b/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockCommandRunnerInner.cs @@ -11,7 +11,7 @@ namespace OneImlx.Terminal.Commands.Handlers.Mocks { - internal class MockCommandRunnerInner : IDelegateCommandRunner, ICommandRunner + internal class MockCommandRunnerInner : IDelegateCommandRunner, ICommandRunner { public bool DelegateHelpCalled { get; private set; } diff --git a/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockErrorCommandRunnerInner.cs b/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockErrorCommandRunnerInner.cs index d8b59f1e..b2c459b2 100644 --- a/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockErrorCommandRunnerInner.cs +++ b/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockErrorCommandRunnerInner.cs @@ -10,7 +10,7 @@ namespace OneImlx.Terminal.Commands.Handlers.Mocks { - internal class MockErrorCommandRunnerInner : IDelegateCommandRunner, ICommandRunner + internal class MockErrorCommandRunnerInner : IDelegateCommandRunner, ICommandRunner { public async Task DelegateHelpAsync(ICommandContext context, ITerminalHelpProvider helpProvider, ILogger? logger = null) { diff --git a/test/OneImlx.Terminal.Tests/Commands/MockRunnerWithBaseResult.cs b/test/OneImlx.Terminal.Tests/Commands/MockRunnerWithBaseResult.cs index d2caf43d..1511b15c 100644 --- a/test/OneImlx.Terminal.Tests/Commands/MockRunnerWithBaseResult.cs +++ b/test/OneImlx.Terminal.Tests/Commands/MockRunnerWithBaseResult.cs @@ -8,11 +8,11 @@ namespace OneImlx.Terminal.Commands { - public class MockRunnerWithBaseResult : CommandRunner + public class MockRunnerWithBaseResult : CommandRunner { public bool MethodCalled { get; private set; } - public override Task RunCommandAsync(ICommandContext context) + public override Task RunCommandAsync(CommandContext context) { MethodCalled = false; return Task.FromResult(new CommandRunnerResult()); diff --git a/test/OneImlx.Terminal.Tests/Commands/MockRunnerWithDerivedResult.cs b/test/OneImlx.Terminal.Tests/Commands/MockRunnerWithDerivedResult.cs index ca243b6b..611207fe 100644 --- a/test/OneImlx.Terminal.Tests/Commands/MockRunnerWithDerivedResult.cs +++ b/test/OneImlx.Terminal.Tests/Commands/MockRunnerWithDerivedResult.cs @@ -8,7 +8,7 @@ namespace OneImlx.Terminal.Commands { - public class MockRunnerWithDerivedResult : CommandRunner + public class MockRunnerWithDerivedResult : CommandRunner { public bool MethodCalled { get; private set; } diff --git a/test/OneImlx.Terminal.Tests/Commands/Runners/Mocks/MockDefaultCommandRunner.cs b/test/OneImlx.Terminal.Tests/Commands/Runners/Mocks/MockDefaultCommandRunner.cs index 02ca7d79..09b2c0d4 100644 --- a/test/OneImlx.Terminal.Tests/Commands/Runners/Mocks/MockDefaultCommandRunner.cs +++ b/test/OneImlx.Terminal.Tests/Commands/Runners/Mocks/MockDefaultCommandRunner.cs @@ -1,9 +1,6 @@ -/* - Copyright © 2019-2025 Perpetual Intelligence L.L.C. All rights reserved. - - For license, terms, and data policies, go to: - https://terms.perpetualintelligence.com/articles/intro.html -*/ +// Copyright © 2019-2026 Perpetual Intelligence L.L.C. All rights reserved. +// For license, terms, and data policies, go to: +// https://terms.perpetualintelligence.com/articles/intro.html using System.Threading.Tasks; using OneImlx.Terminal.Commands.Handlers.Mocks; @@ -11,22 +8,22 @@ namespace OneImlx.Terminal.Commands.Runners.Mocks { - internal class MockDefaultCommandRunner : CommandRunner + internal class MockDefaultCommandRunner : CommandRunner { public bool HelpCalled { get; private set; } public bool RunCalled { get; private set; } - public override Task RunCommandAsync(ICommandContext context) + public override Task RunCommandAsync(CommandContext context) { RunCalled = true; return Task.FromResult((CommandRunnerResult)new MockCommandRunnerInnerResult()); } - public override async Task RunHelpAsync(ICommandContext context) + public override async Task RunHelpAsync(CommandContext context) { HelpCalled = true; await base.RunHelpAsync(context); } } -} +} \ No newline at end of file diff --git a/test/OneImlx.Terminal.Tests/Mocks/MockCommandRunner.cs b/test/OneImlx.Terminal.Tests/Mocks/MockCommandRunner.cs index 226e3b60..5073ec8b 100644 --- a/test/OneImlx.Terminal.Tests/Mocks/MockCommandRunner.cs +++ b/test/OneImlx.Terminal.Tests/Mocks/MockCommandRunner.cs @@ -1,17 +1,14 @@ -/* - Copyright © 2019-2025 Perpetual Intelligence L.L.C. All rights reserved. +// Copyright © 2019-2026 Perpetual Intelligence L.L.C. All rights reserved. +// For license, terms, and data policies, go to: +// https://terms.perpetualintelligence.com/articles/intro.html - For license, terms, and data policies, go to: - https://terms.perpetualintelligence.com/articles/intro.html -*/ - -using System.Threading.Tasks; using OneImlx.Terminal.Commands.Runners; using OneImlx.Terminal.Shared; +using System.Threading.Tasks; namespace OneImlx.Terminal.Mocks { - public class MockCommandRunner : ICommandRunner + public class MockCommandRunner : ICommandRunner { public bool HelpCalled { get; set; } @@ -26,7 +23,7 @@ public Task RunCommandAsync(ICommandContext context) public Task RunHelpAsync(ICommandContext context) { HelpCalled = true; - return Task.FromResult(new CommandRunnerResult()); + return Task.CompletedTask; } } -} +} \ No newline at end of file diff --git a/test/OneImlx.Terminal.Tests/Mocks/MockCommands.cs b/test/OneImlx.Terminal.Tests/Mocks/MockCommands.cs index b3359601..2a192634 100644 --- a/test/OneImlx.Terminal.Tests/Mocks/MockCommands.cs +++ b/test/OneImlx.Terminal.Tests/Mocks/MockCommands.cs @@ -74,116 +74,116 @@ static MockCommands() [ // Different name and prefix - NewCommandDefinition("id1", "name1", "desc1", CommandTypes.Leaf, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, + NewCommandDefinition("id1", "name1", "desc1", CommandTypes.Leaf, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Same name and prefix with args - NewCommandDefinition("id2", "name2", "desc2", CommandTypes.Leaf, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("id2", "name2", "desc2", CommandTypes.Leaf, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Multiple prefix names - NewCommandDefinition("id3", "name3", "desc3", CommandTypes.Leaf, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("id3", "name3", "desc3", CommandTypes.Leaf, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Command with no args NewCommandDefinition("id4", "name4", "desc4", CommandTypes.Leaf).Item1, // Command with no default arg - NewCommandDefinition("id5", "name5", "desc5", CommandTypes.Leaf, new OptionDescriptors(new TerminalTextHandler( StringComparison.OrdinalIgnoreCase, Encoding.Unicode )), typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("id5", "name5", "desc5", CommandTypes.Leaf, new OptionDescriptors(new TerminalTextHandler( StringComparison.OrdinalIgnoreCase, Encoding.Unicode )), typeof(CommandChecker), typeof(CommandRunner )).Item1, ]); GroupedCommands = new(unicodeTextHandler, [ // Different name and prefix - NewCommandDefinition("pi", "pi", "the top org grouped command", CommandTypes.Root, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("pi", "pi", "the top org grouped command", CommandTypes.Root, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Same name and prefix with args - NewCommandDefinition("auth", "pi:auth", "the auth grouped command", CommandTypes.IsolatedGroup, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("auth", "pi:auth", "the auth grouped command", CommandTypes.IsolatedGroup, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Same name and prefix with args - NewCommandDefinition("login", "pi:auth:login", "the login command within the auth group", CommandTypes.Leaf, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("login", "pi:auth:login", "the login command within the auth group", CommandTypes.Leaf, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Same name and prefix with args - NewCommandDefinition("slogin", "pi:auth:slogin", "the silent login command within the auth group", CommandTypes.IsolatedGroup, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("slogin", "pi:auth:slogin", "the silent login command within the auth group", CommandTypes.IsolatedGroup, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Same name and prefix with args - NewCommandDefinition("oidc", "pi:auth:slogin:oidc", "the slient oidc login command within the slogin group", CommandTypes.Leaf, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("oidc", "pi:auth:slogin:oidc", "the slient oidc login command within the slogin group", CommandTypes.Leaf, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Same name and prefix with args - NewCommandDefinition("outh", "pi:auth:slogin:oauth", "the slient oauth login command within the slogin group", CommandTypes.Leaf, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("outh", "pi:auth:slogin:oauth", "the slient oauth login command within the slogin group", CommandTypes.Leaf, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, ]); GroupedOptionsCommands = new(unicodeTextHandler, [ // Different name and prefix - NewCommandDefinition("orgid", "pi", "top org cmd group", CommandTypes.Leaf, TestOptionsDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("orgid", "pi", "top org cmd group", CommandTypes.Leaf, TestOptionsDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Same name and prefix with args - NewCommandDefinition("orgid:authid", "auth", "org auth cmd group", CommandTypes.Leaf, TestOptionsDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("orgid:authid", "auth", "org auth cmd group", CommandTypes.Leaf, TestOptionsDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Same name and prefix with args - NewCommandDefinition("orgid:authid:loginid", "login", "org auth login cmd", CommandTypes.Leaf, TestOptionsDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("orgid:authid:loginid", "login", "org auth login cmd", CommandTypes.Leaf, TestOptionsDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Same name and prefix with args - NewCommandDefinition("orgid:authid:sloginid", "slogin", "org auth slogin cmd", CommandTypes.Leaf, TestOptionsDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("orgid:authid:sloginid", "slogin", "org auth slogin cmd", CommandTypes.Leaf, TestOptionsDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Same name and prefix with args - NewCommandDefinition("orgid:authid:sloginid:oidc", "oidc", "org auth slogin oidc cmd", CommandTypes.Leaf, TestOptionsDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("orgid:authid:sloginid:oidc", "oidc", "org auth slogin oidc cmd", CommandTypes.Leaf, TestOptionsDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Same name and prefix with args - NewCommandDefinition("orgid:authid:sloginid:oauth", "oauth", "org auth slogin oauth cmd", CommandTypes.Leaf, TestOptionsDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("orgid:authid:sloginid:oauth", "oauth", "org auth slogin oauth cmd", CommandTypes.Leaf, TestOptionsDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, ]); LicensingCommands = new(unicodeTextHandler, [ // Different name and prefix - NewCommandDefinition("root1", "name1", "desc1", CommandTypes.Root, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("root1", "name1", "desc1", CommandTypes.Root, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Different name and prefix - NewCommandDefinition("root2", "name2", "desc2", CommandTypes.Root, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("root2", "name2", "desc2", CommandTypes.Root, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Different name and prefix - NewCommandDefinition("root3", "name3", "desc3", CommandTypes.Root, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("root3", "name3", "desc3", CommandTypes.Root, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Different name and prefix - NewCommandDefinition("grp1", "name1", "desc1", CommandTypes.IsolatedGroup, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("grp1", "name1", "desc1", CommandTypes.IsolatedGroup, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Different name and prefix - NewCommandDefinition("grp2", "name2", "desc2", CommandTypes.IsolatedGroup, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("grp2", "name2", "desc2", CommandTypes.IsolatedGroup, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Different name and prefix - NewCommandDefinition("grp3", "name3", "desc3", CommandTypes.IsolatedGroup, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("grp3", "name3", "desc3", CommandTypes.IsolatedGroup, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Different name and prefix - NewCommandDefinition("id1", "name1", "desc1", CommandTypes.Leaf, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("id1", "name1", "desc1", CommandTypes.Leaf, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Same name and prefix with args - NewCommandDefinition("id2", "name2", "desc2", CommandTypes.Leaf, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("id2", "name2", "desc2", CommandTypes.Leaf, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Multiple prefix names - NewCommandDefinition("id3", "name3", "desc3", CommandTypes.Leaf, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("id3", "name3", "desc3", CommandTypes.Leaf, TestOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Command with no args NewCommandDefinition("id4", "name4", "desc4", CommandTypes.Leaf).Item1, // Command with no default arg - NewCommandDefinition("id5", "name5", "desc5",CommandTypes.Leaf, new OptionDescriptors( new TerminalTextHandler( StringComparison.OrdinalIgnoreCase, Encoding.Unicode )), typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("id5", "name5", "desc5",CommandTypes.Leaf, new OptionDescriptors( new TerminalTextHandler( StringComparison.OrdinalIgnoreCase, Encoding.Unicode )), typeof(CommandChecker), typeof(CommandRunner )).Item1, ]); UnicodeCommands = new(unicodeTextHandler, [ // --- Hindi --- Root command - NewCommandDefinition("यूनिकोड", "यूनिकोड नाम", "यूनिकोड रूट कमांड", CommandTypes.Root, null, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("यूनिकोड", "यूनिकोड नाम", "यूनिकोड रूट कमांड", CommandTypes.Root, null, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Grouped command - NewCommandDefinition("परीक्षण", "परीक्षण नाम", "यूनिकोड समूहीकृत कमांड", CommandTypes.IsolatedGroup, null, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("परीक्षण", "परीक्षण नाम", "यूनिकोड समूहीकृत कमांड", CommandTypes.IsolatedGroup, null, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Subcommand - NewCommandDefinition("प्रिंट", "प्रिंट नाम", "प्रिंट कमांड", CommandTypes.Leaf, TestHindiUnicodeOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("प्रिंट", "प्रिंट नाम", "प्रिंट कमांड", CommandTypes.Leaf, TestHindiUnicodeOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, // Subcommand - NewCommandDefinition("दूसरा", "दूसरा नाम", "दूसरा आदेश", CommandTypes.Leaf, TestHindiUnicodeOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner < CommandRunnerResult >)).Item1, + NewCommandDefinition("दूसरा", "दूसरा नाम", "दूसरा आदेश", CommandTypes.Leaf, TestHindiUnicodeOptionDescriptors, typeof(CommandChecker), typeof(CommandRunner )).Item1, ]); } From 25a57af149727dccdc4853f7fddb139ce283ddbc Mon Sep 17 00:00:00 2001 From: "pi.admin" Date: Sun, 7 Jun 2026 13:04:13 -0700 Subject: [PATCH 4/9] Remove ICommandContext --- .../Checkers/Cmd3CommandChecker.cs | 2 +- apps/cli/TestConsole/Custom/CustomContext.cs | 3 +- apps/cli/TestConsole/Custom/CustomResult.cs | 8 ++ apps/cli/TestConsole/Runners/Grp1Runner.cs | 6 +- apps/cli/TestConsole/Runners/Grp2Runner.cs | 6 +- src/OneImlx.Terminal.Shared/CommandContext.cs | 27 +++++++ .../ICommandContext.cs | 19 ----- .../ICommandContextFactory.cs | 20 +++-- .../Checkers/ArgumentCheckerResult.cs | 12 +-- .../Commands/Checkers/CommandChecker.cs | 6 +- .../Commands/Checkers/ICommandChecker.cs | 2 +- .../Commands/Checkers/OptionCheckerResult.cs | 12 +-- .../Commands/CommandContext.cs | 51 ------------ .../Commands/CommandContextFactory.cs | 28 ++++--- .../Commands/CommandRouter.cs | 2 +- .../Commands/Handlers/CommandHandler.cs | 8 +- .../Commands/Handlers/ICommandHandler.cs | 2 +- .../Commands/ICommandRouter.cs | 2 +- .../Commands/Parsers/CommandParser.cs | 2 +- .../Commands/Parsers/ICommandParser.cs | 15 ++-- .../Commands/RunMethodDescriptor.cs | 2 +- .../Commands/Runners/CommandRunner.cs | 6 +- .../Commands/Runners/ICommandRunner.cs | 2 +- .../Runners/IDelegateCommandRunner.cs | 14 ++-- ...ensions.cs => CommandContextExtensions.cs} | 40 +++++----- .../Extensions/ITerminalBuilderExtensions.cs | 2 +- .../Runtime/TerminalProcessor.cs | 2 +- .../Commands/Checkers/CommandCheckerTests.cs | 4 +- .../Commands/CommandContextFactoryTests.cs | 2 +- .../Commands/CommandContextTests.cs | 15 +--- .../MockDeclarativeRunMethodsRunner.cs | 6 +- .../Commands/Handlers/CommandHandlerTests.cs | 4 +- .../Handlers/Mocks/MockCommandCheckerInner.cs | 2 +- .../Mocks/MockCommandRunnerGenericsInner.cs | 4 +- .../Handlers/Mocks/MockCommandRunnerInner.cs | 10 +-- .../Mocks/MockErrorCommandCheckerInner.cs | 2 +- .../Mocks/MockErrorCommandRunnerInner.cs | 10 +-- .../Commands/MockRunnerWithBaseResult.cs | 2 +- .../Commands/MockRunnerWithDerivedResult.cs | 6 +- .../Commands/Parsers/CommandParserTests.cs | 47 +++++------ .../Commands/Routers/CommandRouterTests.cs | 28 ++++--- .../Mocks/MockCommandExtractorInner.cs | 4 +- .../Routers/Mocks/MockCommandHandlerInner.cs | 4 +- .../Commands/RunMethodTests.cs | 7 +- .../Commands/Runners/CommandRunnerTests.cs | 10 ++- ...ts.cs => CommandContextExtensionsTests.cs} | 79 +++++++++---------- .../Mocks/MockCommandChecker.cs | 2 +- .../Mocks/MockCommandHandler.cs | 2 +- .../Mocks/MockCommandParser.cs | 2 +- .../Mocks/MockCommandRouter.cs | 4 +- .../Mocks/MockCommandRunner.cs | 6 +- .../Mocks/MockSocketCommandRouter.cs | 2 +- .../Mocks/MockTerminalConsole.cs | 13 ++- .../Runtime/TerminalProcessorTests.cs | 58 +++++++------- 54 files changed, 303 insertions(+), 333 deletions(-) create mode 100644 apps/cli/TestConsole/Custom/CustomResult.cs create mode 100644 src/OneImlx.Terminal.Shared/CommandContext.cs delete mode 100644 src/OneImlx.Terminal.Shared/ICommandContext.cs delete mode 100644 src/OneImlx.Terminal/Commands/CommandContext.cs rename src/OneImlx.Terminal/Extensions/{ICommandContextExtensions.cs => CommandContextExtensions.cs} (85%) rename test/OneImlx.Terminal.Tests/Extensions/{ICommandContextExtensionsTests.cs => CommandContextExtensionsTests.cs} (83%) diff --git a/apps/cli/TestConsole/Checkers/Cmd3CommandChecker.cs b/apps/cli/TestConsole/Checkers/Cmd3CommandChecker.cs index f4cad49c..8ad8c688 100644 --- a/apps/cli/TestConsole/Checkers/Cmd3CommandChecker.cs +++ b/apps/cli/TestConsole/Checkers/Cmd3CommandChecker.cs @@ -12,7 +12,7 @@ public Cmd3CommandChecker(ITerminalConsole terminalConsole) this.terminalConsole = terminalConsole; } - public Task CheckCommandAsync(ICommandContext context) + public Task CheckCommandAsync(CommandContext context) { terminalConsole.WriteLineAsync("Cmd3 custom checker called."); return Task.FromResult(new CommandCheckerResult()); diff --git a/apps/cli/TestConsole/Custom/CustomContext.cs b/apps/cli/TestConsole/Custom/CustomContext.cs index a9e7afd2..5e29a756 100644 --- a/apps/cli/TestConsole/Custom/CustomContext.cs +++ b/apps/cli/TestConsole/Custom/CustomContext.cs @@ -3,8 +3,7 @@ namespace OneImlx.Terminal.Apps.Test.Custom { - public class CustomContext(Dictionary properties) : ICommandContext + public class CustomContext(Dictionary properties) : CommandContext(properties) { - public Dictionary Properties { get; } = properties; } } \ No newline at end of file diff --git a/apps/cli/TestConsole/Custom/CustomResult.cs b/apps/cli/TestConsole/Custom/CustomResult.cs new file mode 100644 index 00000000..4b309b82 --- /dev/null +++ b/apps/cli/TestConsole/Custom/CustomResult.cs @@ -0,0 +1,8 @@ +using OneImlx.Terminal.Commands.Runners; + +namespace OneImlx.Terminal.Apps.Test.Custom +{ + public class CustomResult : CommandRunnerResult + { + } +} \ No newline at end of file diff --git a/apps/cli/TestConsole/Runners/Grp1Runner.cs b/apps/cli/TestConsole/Runners/Grp1Runner.cs index 92400a32..8bce2094 100644 --- a/apps/cli/TestConsole/Runners/Grp1Runner.cs +++ b/apps/cli/TestConsole/Runners/Grp1Runner.cs @@ -53,7 +53,7 @@ public override async Task RunCommandAsync(CommandContext c [CommandTags("command", "leaf")] [ArgumentDescriptor(1, "arg1", nameof(String), "First argument", BehaviorFlags.None)] [OptionDescriptor("opt1", nameof(String), "Option 1", BehaviorFlags.None)] - public async Task Cmd1Async(ICommandContext context) + public async Task Cmd1Async(CommandContext context) { logger.LogInformation("Executing grp1 cmd1"); string arg1 = context.GetCommand().GetRequiredArgumentValue("arg1"); @@ -67,7 +67,7 @@ public async Task Cmd1Async(ICommandContext context) [ArgumentDescriptor(1, "arg1", nameof(Int32), "Integer argument", BehaviorFlags.Required)] [ArgumentValidation("arg1", typeof(RequiredAttribute))] [OptionDescriptor("opt1", nameof(Boolean), "Boolean option", BehaviorFlags.None)] - public async Task Cmd2Async(ICommandContext context) + public async Task Cmd2Async(CommandContext context) { logger.LogInformation("Executing grp1 cmd2"); int arg1 = context.GetCommand().GetRequiredArgumentValue("arg1"); @@ -82,7 +82,7 @@ public async Task Cmd2Async(ICommandContext context) [ArgumentValidation("arg1", typeof(RequiredAttribute))] [ArgumentValidation("arg1", typeof(StringLengthAttribute), 50)] [OptionDescriptor("opt1", nameof(String), "String option", BehaviorFlags.None)] - public async Task Cmd3Async(ICommandContext context) + public async Task Cmd3Async(CommandContext context) { logger.LogInformation("Executing grp1 cmd3"); string arg1 = context.GetCommand().GetRequiredArgumentValue("arg1"); diff --git a/apps/cli/TestConsole/Runners/Grp2Runner.cs b/apps/cli/TestConsole/Runners/Grp2Runner.cs index dab3c71b..c0d68b57 100644 --- a/apps/cli/TestConsole/Runners/Grp2Runner.cs +++ b/apps/cli/TestConsole/Runners/Grp2Runner.cs @@ -49,7 +49,7 @@ public override async Task RunCommandAsync(CommandContext c [ArgumentDescriptor(1, "arg1", nameof(String), "First argument", BehaviorFlags.None)] [ArgumentDescriptor(2, "arg2", nameof(String), "Second argument", BehaviorFlags.None)] [OptionDescriptor("opt1", nameof(String), "Option 1", BehaviorFlags.None)] - public async Task Cmd4Async(ICommandContext context) + public async Task Cmd4Async(CommandContext context) { logger.LogInformation("Cmd4 (Leaf under composite grp2)"); context.GetCommand().TryGetArgumentValue("arg1", out string? arg1); @@ -65,7 +65,7 @@ public async Task Cmd4Async(ICommandContext context) [ArgumentValidation("arg1", typeof(RequiredAttribute))] [ArgumentValidation("arg1", typeof(RangeAttribute), 1, 100)] [OptionDescriptor("opt1", nameof(Boolean), "Boolean option", BehaviorFlags.Required)] - public async Task Cmd5Async(ICommandContext context) + public async Task Cmd5Async(CommandContext context) { logger.LogInformation("Executing grp1 grp2 cmd5"); int arg1 = context.GetCommand().GetRequiredArgumentValue("arg1"); @@ -79,7 +79,7 @@ public async Task Cmd5Async(ICommandContext context) [ArgumentDescriptor(1, "arg1", nameof(Boolean), "Boolean argument", BehaviorFlags.Required)] [OptionDescriptor("opt1", nameof(String), "String option", BehaviorFlags.Required)] [OptionValidation("opt1", typeof(RequiredAttribute))] - public async Task Cmd6Async(ICommandContext context) + public async Task Cmd6Async(CommandContext context) { logger.LogInformation("Executing grp1 grp2 cmd6"); bool arg1 = context.GetCommand().GetRequiredArgumentValue("arg1"); diff --git a/src/OneImlx.Terminal.Shared/CommandContext.cs b/src/OneImlx.Terminal.Shared/CommandContext.cs new file mode 100644 index 00000000..bb18955d --- /dev/null +++ b/src/OneImlx.Terminal.Shared/CommandContext.cs @@ -0,0 +1,27 @@ +// Copyright © 2019-2026 Perpetual Intelligence L.L.C. All rights reserved. +// For license, terms, and data policies, go to: +// https://terms.perpetualintelligence.com/articles/intro.html + +using System; +using System.Collections.Generic; + +namespace OneImlx.Terminal.Shared +{ + /// + /// The generic command router context. + /// + /// + /// The command string. + /// + /// + /// Initialize a new instance of . + /// + /// The additional router properties. + public class CommandContext(Dictionary properties) + { + /// + /// The additional router properties. + /// + public Dictionary Properties { get; } = properties ?? throw new ArgumentNullException(nameof(properties)); + } +} \ No newline at end of file diff --git a/src/OneImlx.Terminal.Shared/ICommandContext.cs b/src/OneImlx.Terminal.Shared/ICommandContext.cs deleted file mode 100644 index 9d0cdee4..00000000 --- a/src/OneImlx.Terminal.Shared/ICommandContext.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright © 2019-2026 Perpetual Intelligence L.L.C. All rights reserved. -// For license, terms, and data policies, go to: -// https://terms.perpetualintelligence.com/articles/intro.html - -using System.Collections.Generic; - -namespace OneImlx.Terminal.Shared -{ - /// - /// An abstraction for the command execution context. - /// - public interface ICommandContext - { - /// - /// The additional router properties. - /// - public Dictionary Properties { get; } - } -} \ No newline at end of file diff --git a/src/OneImlx.Terminal.Shared/ICommandContextFactory.cs b/src/OneImlx.Terminal.Shared/ICommandContextFactory.cs index 8cb0965f..cf05fd9f 100644 --- a/src/OneImlx.Terminal.Shared/ICommandContextFactory.cs +++ b/src/OneImlx.Terminal.Shared/ICommandContextFactory.cs @@ -7,17 +7,27 @@ namespace OneImlx.Terminal.Shared { /// - /// An abstraction for a factory. + /// An abstraction for a factory. /// public interface ICommandContextFactory { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The request to process. - /// The terminal routing context. + /// The terminal routing context. /// The additional router properties. - /// A new instance of . - public ICommandContext Create(CommandRequest request, TerminalRouterContext context, Dictionary properties); + /// A new instance of . + public CommandContext Create(CommandRequest request, TerminalRouterContext routerContext, Dictionary properties); + + /// + /// Creates a new instance of of the specified type. + /// + /// The context type. Must inherit from . + /// The request to process. + /// The terminal routing context. + /// The additional router properties. + /// A new instance of . + public TContext Create(CommandRequest request, TerminalRouterContext routerContext, Dictionary properties) where TContext : CommandContext; } } \ No newline at end of file diff --git a/src/OneImlx.Terminal/Commands/Checkers/ArgumentCheckerResult.cs b/src/OneImlx.Terminal/Commands/Checkers/ArgumentCheckerResult.cs index 72dca83a..73110d5f 100644 --- a/src/OneImlx.Terminal/Commands/Checkers/ArgumentCheckerResult.cs +++ b/src/OneImlx.Terminal/Commands/Checkers/ArgumentCheckerResult.cs @@ -1,9 +1,6 @@ -/* - Copyright © 2019-2025 Perpetual Intelligence L.L.C. All rights reserved. - - For license, terms, and data policies, go to: - https://terms.perpetualintelligence.com/articles/intro.html -*/ +// Copyright © 2019-2026 Perpetual Intelligence L.L.C. All rights reserved. +// For license, terms, and data policies, go to: +// https://terms.perpetualintelligence.com/articles/intro.html using System; @@ -13,7 +10,6 @@ namespace OneImlx.Terminal.Commands.Checkers /// The argument checker result. /// /// - /// public class ArgumentCheckerResult { /// @@ -30,4 +26,4 @@ public ArgumentCheckerResult(Type mappedType) /// public Type MappedType { get; } } -} +} \ No newline at end of file diff --git a/src/OneImlx.Terminal/Commands/Checkers/CommandChecker.cs b/src/OneImlx.Terminal/Commands/Checkers/CommandChecker.cs index 444e43e6..40e9f0f3 100644 --- a/src/OneImlx.Terminal/Commands/Checkers/CommandChecker.cs +++ b/src/OneImlx.Terminal/Commands/Checkers/CommandChecker.cs @@ -33,7 +33,7 @@ public CommandChecker(IOptionChecker optionChecker, IArgumentChecker argumentChe } /// - public virtual async Task CheckCommandAsync(ICommandContext context) + public virtual async Task CheckCommandAsync(CommandContext context) { Command command = context.GetCommand(); logger.LogDebug("Check command. command={0}", command.Id); @@ -45,7 +45,7 @@ public virtual async Task CheckCommandAsync(ICommandContex return new CommandCheckerResult(); } - private async Task CheckArgumentsAsync(ICommandContext context) + private async Task CheckArgumentsAsync(CommandContext context) { // Cache commonly accessed properties var command = context.GetCommand(); @@ -92,7 +92,7 @@ private async Task CheckArgumentsAsync(ICommandContext context) } } - private async Task CheckOptionsAsync(ICommandContext context) + private async Task CheckOptionsAsync(CommandContext context) { // Cache commonly accessed properties var command = context.GetCommand(); diff --git a/src/OneImlx.Terminal/Commands/Checkers/ICommandChecker.cs b/src/OneImlx.Terminal/Commands/Checkers/ICommandChecker.cs index e05af281..3a12ab60 100644 --- a/src/OneImlx.Terminal/Commands/Checkers/ICommandChecker.cs +++ b/src/OneImlx.Terminal/Commands/Checkers/ICommandChecker.cs @@ -17,6 +17,6 @@ public interface ICommandChecker /// /// The command check context. /// The instance. - public Task CheckCommandAsync(ICommandContext context); + public Task CheckCommandAsync(CommandContext context); } } \ No newline at end of file diff --git a/src/OneImlx.Terminal/Commands/Checkers/OptionCheckerResult.cs b/src/OneImlx.Terminal/Commands/Checkers/OptionCheckerResult.cs index 35e4478b..5e47e002 100644 --- a/src/OneImlx.Terminal/Commands/Checkers/OptionCheckerResult.cs +++ b/src/OneImlx.Terminal/Commands/Checkers/OptionCheckerResult.cs @@ -1,9 +1,6 @@ -/* - Copyright © 2019-2025 Perpetual Intelligence L.L.C. All rights reserved. - - For license, terms, and data policies, go to: - https://terms.perpetualintelligence.com/articles/intro.html -*/ +// Copyright © 2019-2026 Perpetual Intelligence L.L.C. All rights reserved. +// For license, terms, and data policies, go to: +// https://terms.perpetualintelligence.com/articles/intro.html using System; @@ -13,7 +10,6 @@ namespace OneImlx.Terminal.Commands.Checkers /// The option checker result. /// /// - /// public class OptionCheckerResult { /// @@ -30,4 +26,4 @@ public OptionCheckerResult(Type mappedType) /// public Type MappedType { get; } } -} +} \ No newline at end of file diff --git a/src/OneImlx.Terminal/Commands/CommandContext.cs b/src/OneImlx.Terminal/Commands/CommandContext.cs deleted file mode 100644 index 89dc82d6..00000000 --- a/src/OneImlx.Terminal/Commands/CommandContext.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright © 2019-2026 Perpetual Intelligence L.L.C. All rights reserved. -// For license, terms, and data policies, go to: -// https://terms.perpetualintelligence.com/articles/intro.html - -using OneImlx.Terminal.Extensions; -using OneImlx.Terminal.Shared; -using System; -using System.Collections.Generic; - -namespace OneImlx.Terminal.Commands -{ - /// - /// The generic command router context. - /// - /// - /// The command string. - /// - public sealed class CommandContext : ICommandContext - { - /// - /// The additional router properties. - /// - public Dictionary Properties { get; } - - /// - /// Initialize a new instance of . - /// - /// The request to process. - /// The terminal routing context. - /// The additional router properties. - internal CommandContext( - CommandRequest request, - TerminalRouterContext context, - Dictionary properties) - { - if (request is null) - { - throw new ArgumentNullException(nameof(request)); - } - - if (context is null) - { - throw new ArgumentNullException(nameof(context)); - } - - Properties = properties ?? throw new ArgumentNullException(nameof(properties)); - this.SetCommandRequest(request); - this.SetRouterContext(context); - } - } -} \ No newline at end of file diff --git a/src/OneImlx.Terminal/Commands/CommandContextFactory.cs b/src/OneImlx.Terminal/Commands/CommandContextFactory.cs index 7d28c7e6..ab25bfba 100644 --- a/src/OneImlx.Terminal/Commands/CommandContextFactory.cs +++ b/src/OneImlx.Terminal/Commands/CommandContextFactory.cs @@ -2,27 +2,33 @@ // For license, terms, and data policies, go to: // https://terms.perpetualintelligence.com/articles/intro.html +using OneImlx.Terminal.Extensions; using OneImlx.Terminal.Shared; using System.Collections.Generic; namespace OneImlx.Terminal.Commands { /// - /// Default implementation of . + /// Default . /// + /// + /// expects the to have a constructor that accepts a for properties. If the context type does not have such a constructor, an exception will be thrown at runtime. + /// public sealed class CommandContextFactory : ICommandContextFactory { - /// - /// Creates a new . - /// - /// The command request. - /// The . - /// Additional properties. - /// A new instance. - /// - public ICommandContext Create(CommandRequest request, TerminalRouterContext context, Dictionary properties) + /// + public CommandContext Create(CommandRequest request, TerminalRouterContext routerContext, Dictionary properties) { - return new CommandContext(request, context, properties); + return Create(request, routerContext, properties); + } + + /// + public TContext Create(CommandRequest request, TerminalRouterContext routerContext, Dictionary properties) where TContext : CommandContext + { + TContext context = (TContext)System.Activator.CreateInstance(typeof(TContext), properties); + context.SetCommandRequest(request); + context.SetRouterContext(routerContext); + return context; } } } \ No newline at end of file diff --git a/src/OneImlx.Terminal/Commands/CommandRouter.cs b/src/OneImlx.Terminal/Commands/CommandRouter.cs index 177334e0..75aa82e1 100644 --- a/src/OneImlx.Terminal/Commands/CommandRouter.cs +++ b/src/OneImlx.Terminal/Commands/CommandRouter.cs @@ -43,7 +43,7 @@ public CommandRouter(TerminalOptions terminalOptions, ICommandParser commandPars /// /// The router context. /// The instance. - public async Task RouteCommandAsync(ICommandContext context) + public async Task RouteCommandAsync(CommandContext context) { ParsedCommand? parsedCommand = null; CommandResult? commandResult = null!; diff --git a/src/OneImlx.Terminal/Commands/Handlers/CommandHandler.cs b/src/OneImlx.Terminal/Commands/Handlers/CommandHandler.cs index 23224f0d..91d92ed9 100644 --- a/src/OneImlx.Terminal/Commands/Handlers/CommandHandler.cs +++ b/src/OneImlx.Terminal/Commands/Handlers/CommandHandler.cs @@ -34,7 +34,7 @@ public CommandHandler(ICommandResolver commandResolver, IOptions - public async Task HandleCommandAsync(ICommandContext context) + public async Task HandleCommandAsync(CommandContext context) { CommandRequest commandRequest = context.GetCommandRequest(); logger.LogDebug("Handle request. request={0}", commandRequest.Id); @@ -46,7 +46,7 @@ public async Task HandleCommandAsync(ICommandContext context) context.SetCommandResult(new CommandResult(checkerResult, runnerResult)); } - private async Task<(CommandCheckerResult, CommandRunnerResult)> CheckAndRunCommandInnerAsync(ICommandContext context) + private async Task<(CommandCheckerResult, CommandRunnerResult)> CheckAndRunCommandInnerAsync(CommandContext context) { Command command = context.GetCommand(); @@ -66,7 +66,7 @@ public async Task HandleCommandAsync(ICommandContext context) return (checkerResult, runResult); } - private async Task CheckCommandInnerAsync(ICommandContext context, Command command) + private async Task CheckCommandInnerAsync(CommandContext context, Command command) { // Issue a before check event if configured if (terminalEventHandler != null) @@ -89,7 +89,7 @@ private async Task CheckCommandInnerAsync(ICommandContext return result; } - private async Task RunCommandInnerAsync(ICommandContext context, Command command, bool runHelp) + private async Task RunCommandInnerAsync(CommandContext context, Command command, bool runHelp) { // Issue a before run event if configured if (terminalEventHandler != null) diff --git a/src/OneImlx.Terminal/Commands/Handlers/ICommandHandler.cs b/src/OneImlx.Terminal/Commands/Handlers/ICommandHandler.cs index 7cd520a1..b3804d8a 100644 --- a/src/OneImlx.Terminal/Commands/Handlers/ICommandHandler.cs +++ b/src/OneImlx.Terminal/Commands/Handlers/ICommandHandler.cs @@ -17,6 +17,6 @@ public interface ICommandHandler /// /// The handler context. /// The handler result. - Task HandleCommandAsync(ICommandContext context); + Task HandleCommandAsync(CommandContext context); } } \ No newline at end of file diff --git a/src/OneImlx.Terminal/Commands/ICommandRouter.cs b/src/OneImlx.Terminal/Commands/ICommandRouter.cs index e46cccfe..dd54d710 100644 --- a/src/OneImlx.Terminal/Commands/ICommandRouter.cs +++ b/src/OneImlx.Terminal/Commands/ICommandRouter.cs @@ -17,6 +17,6 @@ public interface ICommandRouter /// /// The router context. /// The router result. - Task RouteCommandAsync(ICommandContext context); + Task RouteCommandAsync(CommandContext context); } } \ No newline at end of file diff --git a/src/OneImlx.Terminal/Commands/Parsers/CommandParser.cs b/src/OneImlx.Terminal/Commands/Parsers/CommandParser.cs index 8e344906..2bdc97a7 100644 --- a/src/OneImlx.Terminal/Commands/Parsers/CommandParser.cs +++ b/src/OneImlx.Terminal/Commands/Parsers/CommandParser.cs @@ -39,7 +39,7 @@ public CommandParser(ITerminalRequestParser terminalRequestParser, ITerminalText } /// - public async Task ParseCommandAsync(ICommandContext context) + public async Task ParseCommandAsync(CommandContext context) { CommandRequest commandRequest = context.GetCommandRequest(); logger.LogDebug("Parse request. request={0} raw={1}", commandRequest.Id, commandRequest.Raw); diff --git a/src/OneImlx.Terminal/Commands/Parsers/ICommandParser.cs b/src/OneImlx.Terminal/Commands/Parsers/ICommandParser.cs index 545a0eb7..ca7f571b 100644 --- a/src/OneImlx.Terminal/Commands/Parsers/ICommandParser.cs +++ b/src/OneImlx.Terminal/Commands/Parsers/ICommandParser.cs @@ -1,12 +1,9 @@ -/* - Copyright © 2019-2025 Perpetual Intelligence L.L.C. All rights reserved. +// Copyright © 2019-2026 Perpetual Intelligence L.L.C. All rights reserved. +// For license, terms, and data policies, go to: +// https://terms.perpetualintelligence.com/articles/intro.html - For license, terms, and data policies, go to: - https://terms.perpetualintelligence.com/articles/intro.html -*/ - -using OneImlx.Terminal.Shared; using System.Threading.Tasks; +using OneImlx.Terminal.Shared; namespace OneImlx.Terminal.Commands.Parsers { @@ -19,6 +16,6 @@ public interface ICommandParser /// Extracts asynchronously. /// /// The option extraction context. - public Task ParseCommandAsync(ICommandContext context); + public Task ParseCommandAsync(CommandContext context); } -} +} \ No newline at end of file diff --git a/src/OneImlx.Terminal/Commands/RunMethodDescriptor.cs b/src/OneImlx.Terminal/Commands/RunMethodDescriptor.cs index 0423401c..8d696d89 100644 --- a/src/OneImlx.Terminal/Commands/RunMethodDescriptor.cs +++ b/src/OneImlx.Terminal/Commands/RunMethodDescriptor.cs @@ -51,7 +51,7 @@ public RunMethodDescriptor(string id, MethodInfo methodInfo) /// THIS METHOD IS PART OF INTERNAL INFRASTRUCTURE AND IS NOT INTENDED FOR DIRECT USE BY APPLICATION CODE. /// public async Task RunAsync(CommandRunner commandRunner, TContext context) - where TContext : ICommandContext + where TContext : CommandContext where TResult : CommandRunnerResult { // Ensure command matches the passed context diff --git a/src/OneImlx.Terminal/Commands/Runners/CommandRunner.cs b/src/OneImlx.Terminal/Commands/Runners/CommandRunner.cs index f0ecc826..60767e65 100644 --- a/src/OneImlx.Terminal/Commands/Runners/CommandRunner.cs +++ b/src/OneImlx.Terminal/Commands/Runners/CommandRunner.cs @@ -16,11 +16,11 @@ namespace OneImlx.Terminal.Commands.Runners /// modular and isolated command execution within a terminal application. /// public abstract class CommandRunner : IDelegateCommandRunner, ICommandRunner - where TContext : ICommandContext + where TContext : CommandContext where TResult : CommandRunnerResult { /// - public async Task DelegateHelpAsync(ICommandContext context, ITerminalHelpProvider helpProvider, ILogger? logger = null) + public async Task DelegateHelpAsync(CommandContext context, ITerminalHelpProvider helpProvider, ILogger? logger = null) { this.helpProvider = helpProvider; this.logger = logger; @@ -33,7 +33,7 @@ public async Task DelegateHelpAsync(ICommandContext context } /// - public async Task DelegateRunAsync(ICommandContext context, ILogger? logger = null) + public async Task DelegateRunAsync(CommandContext context, ILogger? logger = null) { this.logger = logger; diff --git a/src/OneImlx.Terminal/Commands/Runners/ICommandRunner.cs b/src/OneImlx.Terminal/Commands/Runners/ICommandRunner.cs index f8f5f54d..2d1d8197 100644 --- a/src/OneImlx.Terminal/Commands/Runners/ICommandRunner.cs +++ b/src/OneImlx.Terminal/Commands/Runners/ICommandRunner.cs @@ -11,7 +11,7 @@ namespace OneImlx.Terminal.Commands.Runners /// An abstraction of a command runner. /// public interface ICommandRunner - where TContext : ICommandContext + where TContext : CommandContext where TResult : CommandRunnerResult { /// diff --git a/src/OneImlx.Terminal/Commands/Runners/IDelegateCommandRunner.cs b/src/OneImlx.Terminal/Commands/Runners/IDelegateCommandRunner.cs index 084f92be..37019e2b 100644 --- a/src/OneImlx.Terminal/Commands/Runners/IDelegateCommandRunner.cs +++ b/src/OneImlx.Terminal/Commands/Runners/IDelegateCommandRunner.cs @@ -10,29 +10,29 @@ namespace OneImlx.Terminal.Commands.Runners { /// - /// An abstraction to delegate to . + /// An abstraction to delegate to . /// /// - /// The enables the use of generics with . - /// All implementations must delegate by calling without any business logic. + /// The enables the use of generics with . + /// All implementations must delegate by calling without any business logic. /// public interface IDelegateCommandRunner { /// - /// Delegates to asynchronously. + /// Delegates to asynchronously. /// /// The runner context. /// The help provider. /// The logger. /// The runner result. - Task DelegateHelpAsync(ICommandContext context, ITerminalHelpProvider helpProvider, ILogger? logger = null); + Task DelegateHelpAsync(CommandContext context, ITerminalHelpProvider helpProvider, ILogger? logger = null); /// - /// Delegates to asynchronously. + /// Delegates to asynchronously. /// /// The runner context. /// The logger. /// The runner result. - Task DelegateRunAsync(ICommandContext context, ILogger? logger = null); + Task DelegateRunAsync(CommandContext context, ILogger? logger = null); } } \ No newline at end of file diff --git a/src/OneImlx.Terminal/Extensions/ICommandContextExtensions.cs b/src/OneImlx.Terminal/Extensions/CommandContextExtensions.cs similarity index 85% rename from src/OneImlx.Terminal/Extensions/ICommandContextExtensions.cs rename to src/OneImlx.Terminal/Extensions/CommandContextExtensions.cs index feb7c77d..06ef1d0d 100644 --- a/src/OneImlx.Terminal/Extensions/ICommandContextExtensions.cs +++ b/src/OneImlx.Terminal/Extensions/CommandContextExtensions.cs @@ -9,9 +9,9 @@ namespace OneImlx.Terminal.Extensions { /// - /// extension methods for working with parsed commands, arguments, and options. + /// extension methods for working with parsed commands, arguments, and options. /// - public static class ICommandContextExtensions + public static class CommandContextExtensions { /// /// Gets the parsed command from the context. @@ -19,7 +19,7 @@ public static class ICommandContextExtensions /// The command context. /// The parsed command. /// Thrown when the parsed command is not available in the context. - public static ParsedCommand GetParsedCommand(this ICommandContext commandContext) + public static ParsedCommand GetParsedCommand(this CommandContext commandContext) { commandContext.Properties.TryGetValue(TerminalIdentifiers.ParsedCommand, out object? parsedCommand); if (parsedCommand is not ParsedCommand currentCommand || parsedCommand == null) @@ -35,7 +35,7 @@ public static ParsedCommand GetParsedCommand(this ICommandContext commandContext /// The command context. /// The command request. /// Thrown when the command request is not available in the context. - public static CommandRequest GetCommandRequest(this ICommandContext commandContext) + public static CommandRequest GetCommandRequest(this CommandContext commandContext) { commandContext.Properties.TryGetValue(TerminalIdentifiers.CommandRequest, out object? result); if (result is not CommandRequest commandRequest) @@ -51,7 +51,7 @@ public static CommandRequest GetCommandRequest(this ICommandContext commandConte /// The command context. /// The command request if found; otherwise, . /// if the command request was found; otherwise, . - public static bool TryGetCommandRequest(this ICommandContext commandContext, out CommandRequest? commandRequest) + public static bool TryGetCommandRequest(this CommandContext commandContext, out CommandRequest? commandRequest) { bool found = commandContext.Properties.TryGetValue(TerminalIdentifiers.CommandRequest, out object? result); commandRequest = result as CommandRequest; @@ -63,7 +63,7 @@ public static bool TryGetCommandRequest(this ICommandContext commandContext, out /// /// The command context. /// The command request to set. - public static void SetCommandRequest(this ICommandContext commandContext, CommandRequest commandRequest) + public static void SetCommandRequest(this CommandContext commandContext, CommandRequest commandRequest) { commandContext.Properties[TerminalIdentifiers.CommandRequest] = commandRequest; } @@ -74,7 +74,7 @@ public static void SetCommandRequest(this ICommandContext commandContext, Comman /// The command context. /// The executing command. /// Thrown when the parsed command is not available in the context. - public static Command GetCommand(this ICommandContext commandContext) + public static Command GetCommand(this CommandContext commandContext) { ParsedCommand parsedCommand = commandContext.GetParsedCommand(); return parsedCommand.Command; @@ -86,7 +86,7 @@ public static Command GetCommand(this ICommandContext commandContext) /// The command context. /// The command result. /// Thrown when the command result is not available in the context. - public static CommandResult GetCommandResult(this ICommandContext commandContext) + public static CommandResult GetCommandResult(this CommandContext commandContext) { commandContext.Properties.TryGetValue(TerminalIdentifiers.CommandResult, out object? result); if (result is not CommandResult commandResult) @@ -102,7 +102,7 @@ public static CommandResult GetCommandResult(this ICommandContext commandContext /// The command context. /// The command result if found; otherwise, . /// if the command result was found; otherwise, . - public static bool TryGetCommandResult(this ICommandContext commandContext, out CommandResult? commandResult) + public static bool TryGetCommandResult(this CommandContext commandContext, out CommandResult? commandResult) { bool found = commandContext.Properties.TryGetValue(TerminalIdentifiers.CommandResult, out object? result); commandResult = result as CommandResult; @@ -115,7 +115,7 @@ public static bool TryGetCommandResult(this ICommandContext commandContext, out /// The command context. /// The router context. /// Thrown when the router context is not available in the context. - public static TerminalRouterContext GetRouterContext(this ICommandContext commandContext) + public static TerminalRouterContext GetRouterContext(this CommandContext commandContext) { bool found = commandContext.Properties.TryGetValue(TerminalIdentifiers.RouterContext, out object? result); if (result is not TerminalRouterContext routerContext) @@ -131,7 +131,7 @@ public static TerminalRouterContext GetRouterContext(this ICommandContext comman /// The command context. /// The router context if found; otherwise, . /// if the router context was found; otherwise, . - public static bool TryGetRouterContext(this ICommandContext commandContext, out TerminalRouterContext? routerContext) + public static bool TryGetRouterContext(this CommandContext commandContext, out TerminalRouterContext? routerContext) { bool found = commandContext.Properties.TryGetValue(TerminalIdentifiers.RouterContext, out object? result); routerContext = result as TerminalRouterContext; @@ -143,7 +143,7 @@ public static bool TryGetRouterContext(this ICommandContext commandContext, out /// /// The command context. /// The router context to set. - public static void SetRouterContext(this ICommandContext commandContext, TerminalRouterContext routerContext) + public static void SetRouterContext(this CommandContext commandContext, TerminalRouterContext routerContext) { commandContext.Properties[TerminalIdentifiers.RouterContext] = routerContext; } @@ -154,7 +154,7 @@ public static void SetRouterContext(this ICommandContext commandContext, Termina /// The command context. /// The parsed command if found; otherwise, . /// if the parsed command was found; otherwise, . - public static bool TryGetParsedCommand(this ICommandContext commandContext, out ParsedCommand? parsedCommand) + public static bool TryGetParsedCommand(this CommandContext commandContext, out ParsedCommand? parsedCommand) { bool found = commandContext.Properties.TryGetValue(TerminalIdentifiers.ParsedCommand, out object? parsedObject); parsedCommand = parsedObject as ParsedCommand; @@ -166,7 +166,7 @@ public static bool TryGetParsedCommand(this ICommandContext commandContext, out /// /// The command context. /// The parsed command to set. - public static void SetParsedCommand(this ICommandContext commandContext, ParsedCommand parsedCommand) + public static void SetParsedCommand(this CommandContext commandContext, ParsedCommand parsedCommand) { commandContext.Properties[TerminalIdentifiers.ParsedCommand] = parsedCommand; } @@ -176,7 +176,7 @@ public static void SetParsedCommand(this ICommandContext commandContext, ParsedC /// /// The command context. /// The command result to set. - public static void SetCommandResult(this ICommandContext commandContext, CommandResult commandResult) + public static void SetCommandResult(this CommandContext commandContext, CommandResult commandResult) { commandContext.Properties[TerminalIdentifiers.CommandResult] = commandResult; } @@ -188,7 +188,7 @@ public static void SetCommandResult(this ICommandContext commandContext, Command /// The argument identifier. /// The argument value. /// Thrown when the argument is not found or the value cannot be cast to . - public static TValue GetRequiredArgumentValue(this ICommandContext commandContext, string argId) + public static TValue GetRequiredArgumentValue(this CommandContext commandContext, string argId) { Command command = GetCommand(commandContext); return command.GetRequiredArgumentValue(argId); @@ -201,7 +201,7 @@ public static TValue GetRequiredArgumentValue(this ICommandContext comma /// The zero-based argument index. /// The argument value. /// Thrown when the argument is not found or the value cannot be cast to . - public static TValue GetRequiredArgumentValue(this ICommandContext commandContext, int argIndex) + public static TValue GetRequiredArgumentValue(this CommandContext commandContext, int argIndex) { Command command = GetCommand(commandContext); return command.GetRequiredArgumentValue(argIndex); @@ -214,7 +214,7 @@ public static TValue GetRequiredArgumentValue(this ICommandContext comma /// The argument identifier. /// The argument value if found; otherwise, the default value of . /// if the argument was found; otherwise, . - public static bool TryGetArgumentValue(this ICommandContext commandContext, string argId, out TValue? value) + public static bool TryGetArgumentValue(this CommandContext commandContext, string argId, out TValue? value) { Command command = GetCommand(commandContext); return command.TryGetArgumentValue(argId, out value); @@ -227,7 +227,7 @@ public static bool TryGetArgumentValue(this ICommandContext commandConte /// The option identifier or alias. /// The option value. /// Thrown when the option is not found or the value cannot be cast to . - public static TValue GetRequiredOptionValue(this ICommandContext commandContext, string idOrAlias) + public static TValue GetRequiredOptionValue(this CommandContext commandContext, string idOrAlias) { Command command = GetCommand(commandContext); return command.GetRequiredOptionValue(idOrAlias); @@ -240,7 +240,7 @@ public static TValue GetRequiredOptionValue(this ICommandContext command /// The option identifier or alias. /// The option value if found; otherwise, the default value of . /// if the option was found; otherwise, . - public static bool TryGetOptionValue(this ICommandContext commandContext, string idOrAlias, out TValue? value) + public static bool TryGetOptionValue(this CommandContext commandContext, string idOrAlias, out TValue? value) { Command command = GetCommand(commandContext); return command.TryGetOptionValue(idOrAlias, out value); diff --git a/src/OneImlx.Terminal/Extensions/ITerminalBuilderExtensions.cs b/src/OneImlx.Terminal/Extensions/ITerminalBuilderExtensions.cs index b16b70b9..58568b6c 100644 --- a/src/OneImlx.Terminal/Extensions/ITerminalBuilderExtensions.cs +++ b/src/OneImlx.Terminal/Extensions/ITerminalBuilderExtensions.cs @@ -379,7 +379,7 @@ public static ITerminalBuilder AddTextHandler(this ITerminalBuilde /// The command runner type. /// The configured . /// The configured . - public static ICommandBuilder DefineCommand(this ITerminalBuilder builder, string id, string name, string description, int commandType) where TRunner : ICommandRunner + public static ICommandBuilder DefineCommand(this ITerminalBuilder builder, string id, string name, string description, int commandType) where TRunner : ICommandRunner { return DefineCommand(builder, id, name, description, typeof(CommandChecker), typeof(TRunner), commandType); } diff --git a/src/OneImlx.Terminal/Runtime/TerminalProcessor.cs b/src/OneImlx.Terminal/Runtime/TerminalProcessor.cs index 43460f28..2575614c 100644 --- a/src/OneImlx.Terminal/Runtime/TerminalProcessor.cs +++ b/src/OneImlx.Terminal/Runtime/TerminalProcessor.cs @@ -277,7 +277,7 @@ private async Task RouteRequestsAsync(TerminalInputOutput terminalOutput, Termin } logger.LogDebug("Routing the command. raw={0} sender={1}", request.Raw, senderId); - ICommandContext context = commandContextFactory.Create(request, terminalRouterContext, properties); + CommandContext context = commandContextFactory.Create(request, terminalRouterContext, properties); var routeTask = commandRouter.RouteCommandAsync(context); if (await Task.WhenAny(routeTask, Task.Delay(timeout)).ConfigureAwait(false) == routeTask) { diff --git a/test/OneImlx.Terminal.Tests/Commands/Checkers/CommandCheckerTests.cs b/test/OneImlx.Terminal.Tests/Commands/Checkers/CommandCheckerTests.cs index 3ab5918e..4b404f2d 100644 --- a/test/OneImlx.Terminal.Tests/Commands/Checkers/CommandCheckerTests.cs +++ b/test/OneImlx.Terminal.Tests/Commands/Checkers/CommandCheckerTests.cs @@ -25,6 +25,8 @@ public CommandCheckerTests() { LoggerFactory loggerFactory = new(); + CommandContextFactory contextFactory = new(); + request = new CommandRequest(Guid.NewGuid().ToString(), "test_raw"); terminalOptions = MockTerminalOptions.NewLegacyOptions(); textHandler = new TerminalTextHandler(StringComparison.OrdinalIgnoreCase, Encoding.ASCII); @@ -37,7 +39,7 @@ public CommandCheckerTests() terminalTokenSource = new CancellationTokenSource(); commandTokenSource = new CancellationTokenSource(); routerContext = new MockTerminalRouterContext(TerminalStartMode.Custom, commandTokenSource.Token); - commandContext = new CommandContext(request, routerContext, []); + commandContext = contextFactory.Create(request, routerContext, []); } [Fact] diff --git a/test/OneImlx.Terminal.Tests/Commands/CommandContextFactoryTests.cs b/test/OneImlx.Terminal.Tests/Commands/CommandContextFactoryTests.cs index 62adaca3..7faca634 100644 --- a/test/OneImlx.Terminal.Tests/Commands/CommandContextFactoryTests.cs +++ b/test/OneImlx.Terminal.Tests/Commands/CommandContextFactoryTests.cs @@ -22,7 +22,7 @@ public void Create_SetsRequestContextAndProperties() var routerContext = new MockRoutingContext(TerminalStartMode.Console, CancellationToken.None); var properties = new Dictionary { ["key"] = "value" }; - var result = factory.Create(request, routerContext, properties); + var result = factory.Create(request, routerContext, properties); result.Properties.Should().BeSameAs(properties); result.GetCommandRequest().Should().BeSameAs(request); diff --git a/test/OneImlx.Terminal.Tests/Commands/CommandContextTests.cs b/test/OneImlx.Terminal.Tests/Commands/CommandContextTests.cs index a76d2395..a809534c 100644 --- a/test/OneImlx.Terminal.Tests/Commands/CommandContextTests.cs +++ b/test/OneImlx.Terminal.Tests/Commands/CommandContextTests.cs @@ -2,12 +2,9 @@ // For license, terms, and data policies, go to: // https://terms.perpetualintelligence.com/articles/intro.html -using System.Collections.Generic; -using System.Threading; using FluentAssertions; -using OneImlx.Terminal.Extensions; -using OneImlx.Terminal.Mocks; using OneImlx.Terminal.Shared; +using System.Collections.Generic; using Xunit; namespace OneImlx.Terminal.Commands @@ -15,17 +12,11 @@ namespace OneImlx.Terminal.Commands public class CommandContextTests { [Fact] - public void Constructor_Sets_Request_RouterContext_And_Properties() + public void Constructor_Sets_Properties() { - var request = new CommandRequest("req-1", "test command"); - var routerContext = new MockRoutingContext(TerminalStartMode.Console, CancellationToken.None); var properties = new Dictionary { ["key"] = "value" }; - - ICommandContext context = new CommandContextFactory().Create(request, routerContext, properties); - + CommandContext context = new(properties); context.Properties.Should().BeSameAs(properties); - context.GetCommandRequest().Should().BeSameAs(request); - context.GetRouterContext().Should().BeSameAs(routerContext); } } } \ No newline at end of file diff --git a/test/OneImlx.Terminal.Tests/Commands/Declarative/MockDeclarativeRunMethodsRunner.cs b/test/OneImlx.Terminal.Tests/Commands/Declarative/MockDeclarativeRunMethodsRunner.cs index 58985151..fb088e36 100644 --- a/test/OneImlx.Terminal.Tests/Commands/Declarative/MockDeclarativeRunMethodsRunner.cs +++ b/test/OneImlx.Terminal.Tests/Commands/Declarative/MockDeclarativeRunMethodsRunner.cs @@ -43,7 +43,7 @@ internal class MockDeclarativeRunMethodsRunner : IDeclarativeRunner [ArgumentDescriptor(1, "m1_arg1", nameof(String), "method1 arg1 desc", BehaviorFlags.None)] [ArgumentDescriptor(2, "m1_arg2", nameof(Int32), "method1 arg2 desc", BehaviorFlags.Required)] [ArgumentValidation("m1_arg2", typeof(RangeAttribute), 10, 100)] - public Task TestMethod1Async(ICommandContext context) + public Task TestMethod1Async(CommandContext context) { Method1Called = true; LastMethodCalled = nameof(TestMethod1Async); @@ -60,7 +60,7 @@ public Task TestMethod1Async(ICommandContext context) [ArgumentValidation("m2_arg1", typeof(RequiredAttribute))] [ArgumentValidation("m2_arg1", typeof(OneOfAttribute), "m2val1", "m2val2")] [ArgumentDescriptor(2, "m2_arg2", nameof(Boolean), "method2 arg2 desc", BehaviorFlags.None)] - public Task TestMethod2Async(ICommandContext context) + public Task TestMethod2Async(CommandContext context) { Method2Called = true; LastMethodCalled = nameof(TestMethod2Async); @@ -80,7 +80,7 @@ public Task TestMethod2Async(ICommandContext context) [ArgumentDescriptor(2, "m3_arg2", nameof(String), "method3 arg2 desc", BehaviorFlags.None)] [ArgumentDescriptor(3, "m3_arg3", nameof(Int32), "method3 arg3 desc", BehaviorFlags.Required | BehaviorFlags.Disabled)] [ArgumentValidation("m3_arg3", typeof(RangeAttribute), 1, 10)] - public Task TestMethod3Async(ICommandContext context) + public Task TestMethod3Async(CommandContext context) { Method3Called = true; LastMethodCalled = nameof(TestMethod3Async); diff --git a/test/OneImlx.Terminal.Tests/Commands/Handlers/CommandHandlerTests.cs b/test/OneImlx.Terminal.Tests/Commands/Handlers/CommandHandlerTests.cs index d6201d86..5dd75075 100644 --- a/test/OneImlx.Terminal.Tests/Commands/Handlers/CommandHandlerTests.cs +++ b/test/OneImlx.Terminal.Tests/Commands/Handlers/CommandHandlerTests.cs @@ -324,7 +324,8 @@ public ValueTask InitializeAsync() licenseChecker = new MockLicenseCheckerInner(); command = MockCommands.NewCommandDefinition("id1", "name1", "desc1", CommandTypes.Leaf); routerContext = new MockTerminalRouterContext(TerminalStartMode.Custom, commandTokenSource.Token); - commandContext = new CommandContext(new(Guid.NewGuid().ToString(), "test"), routerContext, []); + commandContextFactory = new CommandContextFactory(); + commandContext = commandContextFactory.Create(new(Guid.NewGuid().ToString(), "test"), routerContext, []); commandRuntime = new MockCommandResolver(); terminalHelpProvider = new MockTerminalHelpProvider(); terminalEventHandler = new MockTerminalEventHandler(); @@ -480,5 +481,6 @@ public async Task ValidCheckerAndRunnerShould_Not_CheckLicenseAsync() private MockTerminalHelpProvider terminalHelpProvider = null!; private IOptions terminalOptions = null!; private CancellationTokenSource terminalTokenSource = null!; + private CommandContextFactory commandContextFactory = null!; } } \ No newline at end of file diff --git a/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockCommandCheckerInner.cs b/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockCommandCheckerInner.cs index b39b2b39..5cbfa7d2 100644 --- a/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockCommandCheckerInner.cs +++ b/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockCommandCheckerInner.cs @@ -17,7 +17,7 @@ public class MockCommandCheckerInner : ICommandChecker public bool ThrowException { get; set; } - public Task CheckCommandAsync(ICommandContext context) + public Task CheckCommandAsync(CommandContext context) { Called = true; diff --git a/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockCommandRunnerGenericsInner.cs b/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockCommandRunnerGenericsInner.cs index d803446e..a16b044b 100644 --- a/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockCommandRunnerGenericsInner.cs +++ b/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockCommandRunnerGenericsInner.cs @@ -8,13 +8,13 @@ namespace OneImlx.Terminal.Commands.Handlers.Mocks { - internal class MockGenericCommandRunnerInner : CommandRunner + internal class MockGenericCommandRunnerInner : CommandRunner { public bool Called { get; private set; } public bool ThrowException { get; set; } - public override Task RunCommandAsync(ICommandContext context) + public override Task RunCommandAsync(CommandContext context) { Called = true; diff --git a/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockCommandRunnerInner.cs b/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockCommandRunnerInner.cs index a1e8e7da..19d24abe 100644 --- a/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockCommandRunnerInner.cs +++ b/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockCommandRunnerInner.cs @@ -11,7 +11,7 @@ namespace OneImlx.Terminal.Commands.Handlers.Mocks { - internal class MockCommandRunnerInner : IDelegateCommandRunner, ICommandRunner + internal class MockCommandRunnerInner : IDelegateCommandRunner, ICommandRunner { public bool DelegateHelpCalled { get; private set; } @@ -21,7 +21,7 @@ internal class MockCommandRunnerInner : IDelegateCommandRunner, ICommandRunner DelegateHelpAsync(ICommandContext context, ITerminalHelpProvider helpProvider, ILogger? logger = null) + public async Task DelegateHelpAsync(CommandContext context, ITerminalHelpProvider helpProvider, ILogger? logger = null) { this.helpProvider = helpProvider; DelegateHelpCalled = true; @@ -29,19 +29,19 @@ public async Task DelegateHelpAsync(ICommandContext context return new CommandRunnerResult(); } - public Task DelegateRunAsync(ICommandContext context, ILogger? logger = null) + public Task DelegateRunAsync(CommandContext context, ILogger? logger = null) { DelegateRunCalled = true; return RunCommandAsync(context); } - public Task RunCommandAsync(ICommandContext context) + public Task RunCommandAsync(CommandContext context) { RunCalled = true; return Task.FromResult(new MockCommandRunnerInnerResult()); } - public async Task RunHelpAsync(ICommandContext context) + public async Task RunHelpAsync(CommandContext context) { await helpProvider.ProvideHelpAsync(new TerminalHelpProviderContext(context.GetParsedCommand().Command)); HelpCalled = true; diff --git a/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockErrorCommandCheckerInner.cs b/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockErrorCommandCheckerInner.cs index efe99e3d..4bffce61 100644 --- a/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockErrorCommandCheckerInner.cs +++ b/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockErrorCommandCheckerInner.cs @@ -13,7 +13,7 @@ namespace OneImlx.Terminal.Commands.Handlers.Mocks { internal class MockErrorCommandCheckerInner : ICommandChecker { - public Task CheckCommandAsync(ICommandContext context) + public Task CheckCommandAsync(CommandContext context) { throw new TerminalException("test_checker_error", "test_checker_error_desc"); } diff --git a/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockErrorCommandRunnerInner.cs b/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockErrorCommandRunnerInner.cs index b2c459b2..f6559c88 100644 --- a/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockErrorCommandRunnerInner.cs +++ b/test/OneImlx.Terminal.Tests/Commands/Handlers/Mocks/MockErrorCommandRunnerInner.cs @@ -10,25 +10,25 @@ namespace OneImlx.Terminal.Commands.Handlers.Mocks { - internal class MockErrorCommandRunnerInner : IDelegateCommandRunner, ICommandRunner + internal class MockErrorCommandRunnerInner : IDelegateCommandRunner, ICommandRunner { - public async Task DelegateHelpAsync(ICommandContext context, ITerminalHelpProvider helpProvider, ILogger? logger = null) + public async Task DelegateHelpAsync(CommandContext context, ITerminalHelpProvider helpProvider, ILogger? logger = null) { await RunHelpAsync(context); return new CommandRunnerResult(); } - public Task DelegateRunAsync(ICommandContext context, ILogger? logger = null) + public Task DelegateRunAsync(CommandContext context, ILogger? logger = null) { return RunCommandAsync(context); } - public Task RunCommandAsync(ICommandContext context) + public Task RunCommandAsync(CommandContext context) { throw new TerminalException("test_runner_error", "test_runner_error_desc"); } - public Task RunHelpAsync(ICommandContext context) + public Task RunHelpAsync(CommandContext context) { throw new TerminalException("test_runner_help_error", "test_runner_help_error_desc"); } diff --git a/test/OneImlx.Terminal.Tests/Commands/MockRunnerWithBaseResult.cs b/test/OneImlx.Terminal.Tests/Commands/MockRunnerWithBaseResult.cs index 1511b15c..4c45b6a7 100644 --- a/test/OneImlx.Terminal.Tests/Commands/MockRunnerWithBaseResult.cs +++ b/test/OneImlx.Terminal.Tests/Commands/MockRunnerWithBaseResult.cs @@ -18,7 +18,7 @@ public override Task RunCommandAsync(CommandContext context return Task.FromResult(new CommandRunnerResult()); } - public Task TestMethodBase(ICommandContext context) + public Task TestMethodBase(CommandContext context) { MethodCalled = true; return Task.FromResult(new CommandRunnerResult()); diff --git a/test/OneImlx.Terminal.Tests/Commands/MockRunnerWithDerivedResult.cs b/test/OneImlx.Terminal.Tests/Commands/MockRunnerWithDerivedResult.cs index 611207fe..1ecfb46a 100644 --- a/test/OneImlx.Terminal.Tests/Commands/MockRunnerWithDerivedResult.cs +++ b/test/OneImlx.Terminal.Tests/Commands/MockRunnerWithDerivedResult.cs @@ -8,17 +8,17 @@ namespace OneImlx.Terminal.Commands { - public class MockRunnerWithDerivedResult : CommandRunner + public class MockRunnerWithDerivedResult : CommandRunner { public bool MethodCalled { get; private set; } - public override Task RunCommandAsync(ICommandContext context) + public override Task RunCommandAsync(CommandContext context) { MethodCalled = false; return Task.FromResult(new MockRunnerDerviedResult()); } - public Task TestMethodDerived(ICommandContext context) + public Task TestMethodDerived(CommandContext context) { MethodCalled = true; return Task.FromResult(new MockRunnerDerviedResult()); diff --git a/test/OneImlx.Terminal.Tests/Commands/Parsers/CommandParserTests.cs b/test/OneImlx.Terminal.Tests/Commands/Parsers/CommandParserTests.cs index 8a7c4ad2..7c26afa5 100644 --- a/test/OneImlx.Terminal.Tests/Commands/Parsers/CommandParserTests.cs +++ b/test/OneImlx.Terminal.Tests/Commands/Parsers/CommandParserTests.cs @@ -64,6 +64,8 @@ public CommandParserTests() parser = new CommandParser(requestParser, textHandler, commandStore, terminalIOptions, logger); terminalContext = new Mock(TerminalStartMode.Custom, null!, null!).Object; + + commandContextFactory = new CommandContextFactory(); } [Fact] @@ -73,7 +75,7 @@ public async Task Arguments_And_Options_Processed_Correctly() terminalOptions.Parser.OptionValueSeparator = ' '; CommandRequest request = new("id1", "root2 grp2 cmd2 arg1 arg2 --opt1 val1 --opt2 23 -o3 -o4 36.69"); - ICommandContext context = new CommandContext(request, terminalContext, []); + CommandContext context = commandContextFactory.Create(request, terminalContext, []); await parser.ParseCommandAsync(context); @@ -114,7 +116,7 @@ public async Task Arguments_And_Options_Processed_Correctly() public async Task Hiearchy_Null_For_Root() { CommandRequest request = new(Guid.NewGuid().ToString(), "root1"); - ICommandContext context = new CommandContext(request, terminalContext, []); + CommandContext context = commandContextFactory.Create(request, terminalContext, []); await parser.ParseCommandAsync(context); ParsedCommand parsedCommand = context.GetParsedCommand(); @@ -132,7 +134,7 @@ public async Task No_Options_Processes_Correctly() terminalOptions.Parser.OptionValueSeparator = ' '; CommandRequest request = new("id1", "root1 grp1 arg1 arg2"); - ICommandContext context = new CommandContext(request, terminalContext, []); + CommandContext context = commandContextFactory.Create(request, terminalContext, []); await parser.ParseCommandAsync(context); ParsedCommand parsedCommand = context.GetParsedCommand(); @@ -150,7 +152,7 @@ public async Task Options_Processed_Correctly() terminalOptions.Parser.OptionPrefix = '-'; terminalOptions.Parser.OptionValueSeparator = TerminalIdentifiers.SpaceSeparator; - var context = new CommandContext(new CommandRequest("id1", "root2 grp2 cmd2 --opt1 val1 --opt2 23 --opt3 --opt4 36.69"), terminalContext, []); + var context = commandContextFactory.Create(new CommandRequest("id1", "root2 grp2 cmd2 --opt1 val1 --opt2 23 --opt3 --opt4 36.69"), terminalContext, []); await parser.ParseCommandAsync(context); ParsedCommand parsedCommand = context.GetParsedCommand(); @@ -170,7 +172,7 @@ public async Task Options_Processed_Correctly() [Fact] public async Task Root_Processed_Correctly() { - var context = new CommandContext(new CommandRequest("id1", "root2"), terminalContext, []); + var context = commandContextFactory.Create(new CommandRequest("id1", "root2"), terminalContext, []); await parser.ParseCommandAsync(context); ParsedCommand parsedCommand = context.GetParsedCommand(); @@ -189,7 +191,7 @@ public async Task Root_With_Owner_Does_Not_Throw() // NOTE: This test should fail. But the root owner check is not performed at the parser level, it is // validated during the builder setup. CommandRequest request = new(Guid.NewGuid().ToString(), "root2 root3"); - ICommandContext context = new CommandContext(request, terminalContext, []); + CommandContext context = commandContextFactory.Create(request, terminalContext, []); await parser.ParseCommandAsync(context); ParsedCommand parsedCommand = context.GetParsedCommand(); @@ -199,7 +201,7 @@ public async Task Root_With_Owner_Does_Not_Throw() [Fact] public async Task Single_Non_Root_Processed_Correctly() { - var context = new CommandContext(new CommandRequest("id1", "cmd_nr1"), terminalContext, []); + var context = commandContextFactory.Create(new CommandRequest("id1", "cmd_nr1"), terminalContext, []); await parser.ParseCommandAsync(context); ParsedCommand parsedCommand = context.GetParsedCommand(); @@ -215,7 +217,7 @@ public async Task Single_Non_Root_Processed_Correctly() [Fact] public async Task Throws_If_Alias_Is_Unsupported() { - var context = new CommandContext(new CommandRequest("id1", "root2 grp2 cmd2 --opt1 Val1 -invalid_alias 25 --opt3 --opt4 \"val2\" --opt5"), terminalContext, []); + var context = commandContextFactory.Create(new CommandRequest("id1", "root2 grp2 cmd2 --opt1 Val1 -invalid_alias 25 --opt3 --opt4 \"val2\" --opt5"), terminalContext, []); Func act = async () => await parser.ParseCommandAsync(context); await act.Should().ThrowAsync() .WithErrorCode("unsupported_option") @@ -228,7 +230,7 @@ public async Task Throws_If_Alias_Prefix_Is_Specified_For_Option() terminalOptions.Parser.OptionPrefix = '-'; terminalOptions.Parser.OptionValueSeparator = TerminalIdentifiers.SpaceSeparator; - var context = new CommandContext(new CommandRequest("id1", "root2 grp2 cmd2 --opt1 val1 --opt2 23 -opt3 --opt4 36.69"), terminalContext, []); + var context = commandContextFactory.Create(new CommandRequest("id1", "root2 grp2 cmd2 --opt1 val1 --opt2 23 -opt3 --opt4 36.69"), terminalContext, []); Func act = async () => await parser.ParseCommandAsync(context); await act.Should().ThrowAsync() .WithErrorCode("invalid_option") @@ -241,7 +243,7 @@ public async Task Throws_If_Arguments_Found_Without_Command() terminalOptions.Parser.OptionPrefix = '-'; terminalOptions.Parser.OptionValueSeparator = TerminalIdentifiers.SpaceSeparator; - var context = new CommandContext(new CommandRequest("id1", "arg1 arg2 arg3"), terminalContext, []); + var context = commandContextFactory.Create(new CommandRequest("id1", "arg1 arg2 arg3"), terminalContext, []); Func act = async () => await parser.ParseCommandAsync(context); await act.Should().ThrowAsync() .WithErrorCode("missing_command") @@ -251,7 +253,7 @@ await act.Should().ThrowAsync() [Fact] public async Task Throws_If_Command_Does_Not_Define_An_Owner() { - var context = new CommandContext(new CommandRequest("id1", "root1 root2 grp2"), terminalContext, []); + var context = commandContextFactory.Create(new CommandRequest("id1", "root1 root2 grp2"), terminalContext, []); Func act = async () => await parser.ParseCommandAsync(context); await act.Should().ThrowAsync() .WithErrorCode("invalid_command") @@ -276,7 +278,7 @@ public async Task Throws_If_Command_Found_But_Returns_Null_Descriptor() var iOptions = Microsoft.Extensions.Options.Options.Create(terminalOptions); parser = new CommandParser(parserMock.Object, textHandler, storeMock.Object, iOptions, logger); - var context = new CommandContext(new CommandRequest("id1", "root1 grp1 cmd1"), terminalContext, []); + var context = commandContextFactory.Create(new CommandRequest("id1", "root1 grp1 cmd1"), terminalContext, []); Func act = async () => await parser.ParseCommandAsync(context); await act.Should().ThrowAsync() .WithErrorCode("invalid_command") @@ -286,7 +288,7 @@ await act.Should().ThrowAsync() [Fact] public async Task Throws_If_Command_Is_Present_In_Arguments() { - var context = new CommandContext(new CommandRequest("id1", "root1 grp1 arg1 cmd1"), terminalContext, []); + var context = commandContextFactory.Create(new CommandRequest("id1", "root1 grp1 arg1 cmd1"), terminalContext, []); Func act = async () => await parser.ParseCommandAsync(context); await act.Should().ThrowAsync() .WithErrorCode("invalid_argument") @@ -296,7 +298,7 @@ await act.Should().ThrowAsync() [Fact] public async Task Throws_If_Commands_Are_Duplicated() { - var context = new CommandContext(new CommandRequest("id1", "root1 grp1 cmd1 cmd1"), terminalContext, []); + var context = commandContextFactory.Create(new CommandRequest("id1", "root1 grp1 cmd1 cmd1"), terminalContext, []); Func act = async () => await parser.ParseCommandAsync(context); await act.Should().ThrowAsync() .WithErrorCode("invalid_command") @@ -306,7 +308,7 @@ await act.Should().ThrowAsync() [Fact] public async Task Throws_If_More_Than_Supported_Arguments() { - var context = new CommandContext(new CommandRequest("id1", "root1 grp1 arg1 arg2 arg3"), terminalContext, []); + var context = commandContextFactory.Create(new CommandRequest("id1", "root1 grp1 arg1 arg2 arg3"), terminalContext, []); Func act = async () => await parser.ParseCommandAsync(context); await act.Should().ThrowAsync() .WithErrorCode("unsupported_argument") @@ -316,7 +318,7 @@ await act.Should().ThrowAsync() [Fact] public async Task Throws_If_No_Root_Is_Not_Specified() { - var context = new CommandContext(new CommandRequest("id1", "grp1 cmd1 cmd1"), terminalContext, []); + var context = commandContextFactory.Create(new CommandRequest("id1", "grp1 cmd1 cmd1"), terminalContext, []); Func act = async () => await parser.ParseCommandAsync(context); await act.Should().ThrowAsync() .WithErrorCode("missing_command") @@ -329,7 +331,7 @@ public async Task Throws_If_Option_Is_Unsupported() terminalOptions.Parser.OptionPrefix = '-'; terminalOptions.Parser.OptionValueSeparator = TerminalIdentifiers.SpaceSeparator; - var context = new CommandContext(new CommandRequest("id1", "root2 grp2 cmd2 --opt1 val1 --invalid_opt1 23 --opt3 --opt4 36.69"), terminalContext, []); + var context = commandContextFactory.Create(new CommandRequest("id1", "root2 grp2 cmd2 --opt1 val1 --invalid_opt1 23 --opt3 --opt4 36.69"), terminalContext, []); Func act = async () => await parser.ParseCommandAsync(context); await act.Should().ThrowAsync() .WithErrorCode("unsupported_option") @@ -342,7 +344,7 @@ public async Task Throws_If_Option_Prefix_Is_Specified_For_Alias() terminalOptions.Parser.OptionPrefix = '-'; terminalOptions.Parser.OptionValueSeparator = TerminalIdentifiers.SpaceSeparator; - Func act = async () => await parser.ParseCommandAsync(new CommandContext(new CommandRequest("id1", "root2 grp2 cmd2 --opt1 val1 --opt2 23 --o3 --opt4 36.69"), terminalContext, [])); + Func act = async () => await parser.ParseCommandAsync(commandContextFactory.Create(new CommandRequest("id1", "root2 grp2 cmd2 --opt1 val1 --opt2 23 --o3 --opt4 36.69"), terminalContext, [])); await act.Should().ThrowAsync() .WithErrorCode("invalid_option") .WithErrorDescription("The option prefix is not valid for an alias. option=opt3 alias=o3"); @@ -351,7 +353,7 @@ await act.Should().ThrowAsync() [Fact] public async Task Throws_If_Owner_Is_Invalid() { - var context = new CommandContext(new CommandRequest("id1", "root2 grp1 cmd1"), terminalContext, []); + var context = commandContextFactory.Create(new CommandRequest("id1", "root2 grp1 cmd1"), terminalContext, []); Func act = async () => await parser.ParseCommandAsync(context); await act.Should().ThrowAsync() .WithErrorCode("invalid_command") @@ -361,7 +363,7 @@ await act.Should().ThrowAsync() [Fact] public async Task Throws_If_Owner_Is_Missing() { - var context = new CommandContext(new CommandRequest("id1", "grp1 cmd1"), terminalContext, []); + var context = commandContextFactory.Create(new CommandRequest("id1", "grp1 cmd1"), terminalContext, []); Func act = async () => await parser.ParseCommandAsync(context); await act.Should().ThrowAsync() .WithErrorCode("missing_command") @@ -371,7 +373,7 @@ await act.Should().ThrowAsync() [Fact] public async Task Throws_If_Unsupported_Arguments() { - var context = new CommandContext(new CommandRequest("id1", "root1 grp1 cmd1 arg1 arg2"), terminalContext, []); + var context = commandContextFactory.Create(new CommandRequest("id1", "root1 grp1 cmd1 arg1 arg2"), terminalContext, []); Func act = async () => await parser.ParseCommandAsync(context); await act.Should().ThrowAsync() .WithErrorCode("unsupported_argument") @@ -381,7 +383,7 @@ await act.Should().ThrowAsync() [Fact] public async Task Throws_If_Unsupported_Options() { - var context = new CommandContext(new CommandRequest("id1", "root1 grp1 cmd1 --opt1 val1 --opt2 23"), terminalContext, []); + var context = commandContextFactory.Create(new CommandRequest("id1", "root1 grp1 cmd1 --opt1 val1 --opt2 23"), terminalContext, []); Func act = async () => await parser.ParseCommandAsync(context); await act.Should().ThrowAsync() .WithErrorCode("unsupported_option") @@ -395,6 +397,7 @@ await act.Should().ThrowAsync() private readonly ITerminalRequestParser requestParser; private readonly TerminalOptions terminalOptions; private readonly ITerminalTextHandler textHandler; + private readonly CommandContextFactory commandContextFactory; private CommandParser parser; } } \ No newline at end of file diff --git a/test/OneImlx.Terminal.Tests/Commands/Routers/CommandRouterTests.cs b/test/OneImlx.Terminal.Tests/Commands/Routers/CommandRouterTests.cs index a1e39c58..e882f801 100644 --- a/test/OneImlx.Terminal.Tests/Commands/Routers/CommandRouterTests.cs +++ b/test/OneImlx.Terminal.Tests/Commands/Routers/CommandRouterTests.cs @@ -2,12 +2,9 @@ // For license, terms, and data policies, go to: // https://terms.perpetualintelligence.com/articles/intro.html -using System; -using System.Threading; -using System.Threading.Tasks; +using FluentAssertions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using FluentAssertions; using OneImlx.Terminal.Commands.Parsers; using OneImlx.Terminal.Commands.Routers.Mocks; using OneImlx.Terminal.Configuration.Options; @@ -15,6 +12,9 @@ using OneImlx.Terminal.Mocks; using OneImlx.Terminal.Shared; using OneImlx.Test.FluentAssertions; +using System; +using System.Threading; +using System.Threading.Tasks; using Xunit; namespace OneImlx.Terminal.Commands.Routers @@ -33,7 +33,7 @@ public async Task ExtractorErrorShouldNotRouteFurtherAsync() { commandParser.SetExplicitError = true; - CommandContext commandContext = new(new(Guid.NewGuid().ToString(), "test_command_string"), routerContext, []); + CommandContext commandContext = commandContextFactory.Create(new(Guid.NewGuid().ToString(), "test_command_string"), routerContext, []); Func func = async () => await commandRouter.RouteCommandAsync(commandContext); await func.Should().ThrowAsync().WithErrorCode("test_parser_error").WithErrorDescription("test_parser_error_desc"); commandParser.Called.Should().BeTrue(); @@ -45,7 +45,7 @@ public async Task ExtractorNoExtractedCommandDescriptorShouldNotRouteFurtherAsyn { commandParser.DoNotSetCommandDescriptor = true; - CommandContext commandContext = new(new(Guid.NewGuid().ToString(), "test_command_string"), routerContext, []); + CommandContext commandContext = commandContextFactory.Create(new(Guid.NewGuid().ToString(), "test_command_string"), routerContext, []); Func act = async () => await commandRouter.RouteCommandAsync(commandContext); await act.Should().ThrowAsync(); @@ -58,7 +58,7 @@ public async Task HandlerErrorShouldNotRouteFurtherAsync() { commandHandler.IsExplicitError = true; - CommandContext commandContext = new(new(Guid.NewGuid().ToString(), "test_command_string"), routerContext, []); + CommandContext commandContext = commandContextFactory.Create(new(Guid.NewGuid().ToString(), "test_command_string"), routerContext, []); Func func = async () => await commandRouter.RouteCommandAsync(commandContext); await func.Should().ThrowAsync().WithErrorCode("test_handler_error").WithErrorDescription("test_handler_error_desc"); commandHandler.Called.Should().BeTrue(); @@ -78,6 +78,7 @@ public ValueTask InitializeAsync() terminalTokenSource = new CancellationTokenSource(); commandTokenSource = new CancellationTokenSource(); routerContext = new MockTerminalRouterContext(TerminalStartMode.Custom, commandTokenSource.Token); + commandContextFactory = new(); return ValueTask.CompletedTask; } @@ -90,7 +91,7 @@ public async Task ParserFails_But_Invokes_Handler() commandParser.Called.Should().BeFalse(); commandHandler.Called.Should().BeFalse(); - CommandContext commandContext = new(new(Guid.NewGuid().ToString(), "test_command_string"), routerContext, []); + CommandContext commandContext = commandContextFactory.Create(new(Guid.NewGuid().ToString(), "test_command_string"), routerContext, []); commandContext.TryGetParsedCommand(out _).Should().BeFalse(); await commandRouter.RouteCommandAsync(commandContext); @@ -107,7 +108,7 @@ public async Task Router_Calls_Parser_And_Handler() commandParser.Called.Should().BeFalse(); commandHandler.Called.Should().BeFalse(); - CommandContext commandContext = new(new(Guid.NewGuid().ToString(), "test_command_string"), routerContext, []); + CommandContext commandContext = commandContextFactory.Create(new(Guid.NewGuid().ToString(), "test_command_string"), routerContext, []); await commandRouter.RouteCommandAsync(commandContext); ; commandParser.Called.Should().BeTrue(); @@ -122,7 +123,7 @@ public async Task Router_Passes_Updated_Context_From_Parser_To_Handler() { commandHandler.PassedContext.Should().BeNull(); - CommandContext commandContext = new(new(Guid.NewGuid().ToString(), "test_command_string"), routerContext, []); + CommandContext commandContext = commandContextFactory.Create(new(Guid.NewGuid().ToString(), "test_command_string"), routerContext, []); commandContext.TryGetParsedCommand(out _).Should().BeFalse(); await commandRouter.RouteCommandAsync(commandContext); @@ -155,7 +156,7 @@ public async Task ShouldCallRouterEventIfConfigured() eventHandler.BeforeRouteCalled.Should().BeFalse(); eventHandler.AfterRouteCalled.Should().BeFalse(); - CommandContext commandContext = new(new(Guid.NewGuid().ToString(), "test_command_string"), routerContext, []); + CommandContext commandContext = commandContextFactory.Create(new(Guid.NewGuid().ToString(), "test_command_string"), routerContext, []); await commandRouter.RouteCommandAsync(commandContext); eventHandler.BeforeRouteCalled.Should().BeTrue(); @@ -178,7 +179,7 @@ public async Task ShouldCallRouterEventInCaseOfExceptionIfConfigured() try { - CommandContext commandContext = new(new(Guid.NewGuid().ToString(), "test_command_string"), routerContext, []); + CommandContext commandContext = commandContextFactory.Create(new(Guid.NewGuid().ToString(), "test_command_string"), routerContext, []); await commandRouter.RouteCommandAsync(commandContext); } catch (TerminalException eex) @@ -205,7 +206,7 @@ public async Task ShouldNotCallRouterEventIfNotConfigured() eventHandler.AfterRouteCalled.Should().BeFalse(); commandRouter = new CommandRouter(terminalOptions, commandParser, commandHandler, logger, asyncEventHandler: null); - CommandContext commandContext = new(new(Guid.NewGuid().ToString(), "test_command_string"), routerContext, []); + CommandContext commandContext = commandContextFactory.Create(new(Guid.NewGuid().ToString(), "test_command_string"), routerContext, []); await commandRouter.RouteCommandAsync(commandContext); eventHandler.BeforeRouteCalled.Should().BeFalse(); @@ -226,5 +227,6 @@ public async Task ShouldNotCallRouterEventIfNotConfigured() private MockTerminalRouterContext routerContext = null!; private TerminalOptions terminalOptions = null!; private CancellationTokenSource terminalTokenSource = null!; + private CommandContextFactory commandContextFactory = null!; } } \ No newline at end of file diff --git a/test/OneImlx.Terminal.Tests/Commands/Routers/Mocks/MockCommandExtractorInner.cs b/test/OneImlx.Terminal.Tests/Commands/Routers/Mocks/MockCommandExtractorInner.cs index b23cfedb..194ed968 100644 --- a/test/OneImlx.Terminal.Tests/Commands/Routers/Mocks/MockCommandExtractorInner.cs +++ b/test/OneImlx.Terminal.Tests/Commands/Routers/Mocks/MockCommandExtractorInner.cs @@ -17,11 +17,11 @@ internal class MockCommandParserInner : ICommandParser public bool DoNotSetParsedCommand { get; set; } - public ICommandContext? PassedContext { get; internal set; } + public CommandContext? PassedContext { get; internal set; } public bool SetExplicitError { get; set; } - public Task ParseCommandAsync(ICommandContext context) + public Task ParseCommandAsync(CommandContext context) { Called = true; PassedContext = context; diff --git a/test/OneImlx.Terminal.Tests/Commands/Routers/Mocks/MockCommandHandlerInner.cs b/test/OneImlx.Terminal.Tests/Commands/Routers/Mocks/MockCommandHandlerInner.cs index 1ca52519..f5e166c7 100644 --- a/test/OneImlx.Terminal.Tests/Commands/Routers/Mocks/MockCommandHandlerInner.cs +++ b/test/OneImlx.Terminal.Tests/Commands/Routers/Mocks/MockCommandHandlerInner.cs @@ -17,11 +17,11 @@ public MockCommandHandlerInner() public bool Called { get; set; } - public ICommandContext PassedContext { get; internal set; } + public CommandContext PassedContext { get; internal set; } public bool IsExplicitError { get; internal set; } - public Task HandleCommandAsync(ICommandContext context) + public Task HandleCommandAsync(CommandContext context) { Called = true; diff --git a/test/OneImlx.Terminal.Tests/Commands/RunMethodTests.cs b/test/OneImlx.Terminal.Tests/Commands/RunMethodTests.cs index 93e39dfa..36b93685 100644 --- a/test/OneImlx.Terminal.Tests/Commands/RunMethodTests.cs +++ b/test/OneImlx.Terminal.Tests/Commands/RunMethodTests.cs @@ -23,7 +23,9 @@ public RunMethodTests() parsedCommand = new ParsedCommand(command); commandTokenSource = new CancellationTokenSource(); routingContext = new MockTerminalRouterContext(TerminalStartMode.Custom, commandTokenSource.Token); - routerContext = new CommandContext(new(Guid.NewGuid().ToString(), "test"), routingContext, []); + + commandContextFactory = new CommandContextFactory(); + routerContext = commandContextFactory.Create(new(Guid.NewGuid().ToString(), "test"), routingContext, []); routerContext.SetParsedCommand(parsedCommand); } @@ -122,7 +124,7 @@ public async Task RunAsync_Throws_WhenParsedCommandIsNull() { MockRunnerWithBaseResult runner = new(); var runMethod = new RunMethodDescriptor("cmd-id", nameof(MockRunnerWithBaseResult.TestMethodBase)); - var contextWithoutParsedCommand = new CommandContext(new(Guid.NewGuid().ToString(), "test"), routingContext, []); + var contextWithoutParsedCommand = commandContextFactory.Create(new(Guid.NewGuid().ToString(), "test"), routingContext, []); Func act = async () => await runMethod.RunAsync(runner, contextWithoutParsedCommand); await act.Should().ThrowAsync().WithMessage("The parsed command is missing in the context."); } @@ -163,5 +165,6 @@ public async Task RunAsync_Throws_WhenBothMethodInfoAndMethodNameAreNull() private readonly CancellationTokenSource commandTokenSource = null!; private readonly CommandContext routerContext = null!; private readonly TerminalRouterContext routingContext = null!; + private readonly CommandContextFactory commandContextFactory = null!; } } \ No newline at end of file diff --git a/test/OneImlx.Terminal.Tests/Commands/Runners/CommandRunnerTests.cs b/test/OneImlx.Terminal.Tests/Commands/Runners/CommandRunnerTests.cs index 854a5eb9..da46d2f1 100644 --- a/test/OneImlx.Terminal.Tests/Commands/Runners/CommandRunnerTests.cs +++ b/test/OneImlx.Terminal.Tests/Commands/Runners/CommandRunnerTests.cs @@ -2,9 +2,6 @@ // For license, terms, and data policies, go to: // https://terms.perpetualintelligence.com/articles/intro.html -using System; -using System.Threading; -using System.Threading.Tasks; using FluentAssertions; using OneImlx.Terminal.Commands.Handlers.Mocks; using OneImlx.Terminal.Commands.Parsers; @@ -12,6 +9,9 @@ using OneImlx.Terminal.Extensions; using OneImlx.Terminal.Mocks; using OneImlx.Terminal.Shared; +using System; +using System.Threading; +using System.Threading.Tasks; using Xunit; namespace OneImlx.Terminal.Commands.Runners @@ -23,7 +23,8 @@ public CommandRunnerTests() terminalTokenSource = new CancellationTokenSource(); commandTokenSource = new CancellationTokenSource(); routerContext = new MockTerminalRouterContext(TerminalStartMode.Custom, commandTokenSource.Token); - commandContext = new CommandContext(new(Guid.NewGuid().ToString(), "test"), routerContext, []); + commandContextFactory = new CommandContextFactory(); + commandContext = commandContextFactory.Create(new(Guid.NewGuid().ToString(), "test"), routerContext, []); } [Fact] @@ -74,5 +75,6 @@ public async Task HelpShouldThrowIfIHelpProviderIsNullAsync() private readonly CommandContext commandContext = null!; private readonly TerminalRouterContext routerContext = null!; private readonly CancellationTokenSource terminalTokenSource = null!; + private readonly CommandContextFactory commandContextFactory = null!; } } \ No newline at end of file diff --git a/test/OneImlx.Terminal.Tests/Extensions/ICommandContextExtensionsTests.cs b/test/OneImlx.Terminal.Tests/Extensions/CommandContextExtensionsTests.cs similarity index 83% rename from test/OneImlx.Terminal.Tests/Extensions/ICommandContextExtensionsTests.cs rename to test/OneImlx.Terminal.Tests/Extensions/CommandContextExtensionsTests.cs index c30f62f8..cdd1fc77 100644 --- a/test/OneImlx.Terminal.Tests/Extensions/ICommandContextExtensionsTests.cs +++ b/test/OneImlx.Terminal.Tests/Extensions/CommandContextExtensionsTests.cs @@ -17,12 +17,12 @@ namespace OneImlx.Terminal.Tests.Extensions { - public class ICommandContextExtensionsTests + public class CommandContextExtensionsTests { private readonly Command command; private readonly ParsedCommand parsedCommand; - public ICommandContextExtensionsTests() + public CommandContextExtensionsTests() { ITerminalTextHandler textHandler = new Mock().Object; @@ -39,19 +39,16 @@ public ICommandContextExtensionsTests() parsedCommand = new ParsedCommand(command, null); } - private static ICommandContext Context(Dictionary? properties = null) + private static CommandContext Context(Dictionary? properties = null) { - Mock mock = new(); - Dictionary props = properties ?? new Dictionary(); - mock.Setup(x => x.Properties).Returns(props); - return mock.Object; + return new CommandContext(properties ?? []); } [Fact] public void GetParsedCommand_Returns_WhenAvailable() { Dictionary properties = new() { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; - ICommandContext context = Context(properties); + CommandContext context = Context(properties); ParsedCommand result = context.GetParsedCommand(); result.Should().Be(parsedCommand); } @@ -59,7 +56,7 @@ public void GetParsedCommand_Returns_WhenAvailable() [Fact] public void GetParsedCommand_Throws_WhenMissing() { - ICommandContext context = Context(); + CommandContext context = Context(); Action act = () => context.GetParsedCommand(); act.Should().Throw().WithErrorCode(TerminalErrors.ServerError) .WithErrorDescription("The parsed command is missing in the context."); @@ -69,7 +66,7 @@ public void GetParsedCommand_Throws_WhenMissing() public void GetParsedCommand_Throws_WhenWrongType() { Dictionary properties = new() { { TerminalIdentifiers.ParsedCommand, "wrong" } }; - ICommandContext context = Context(properties); + CommandContext context = Context(properties); Action act = () => context.GetParsedCommand(); act.Should().Throw().WithErrorCode(TerminalErrors.ServerError); } @@ -78,7 +75,7 @@ public void GetParsedCommand_Throws_WhenWrongType() public void TryGetParsedCommand_ReturnsTrue_WhenAvailable() { Dictionary properties = new() { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; - ICommandContext context = Context(properties); + CommandContext context = Context(properties); bool found = context.TryGetParsedCommand(out ParsedCommand? result); found.Should().BeTrue(); result.Should().Be(parsedCommand); @@ -87,7 +84,7 @@ public void TryGetParsedCommand_ReturnsTrue_WhenAvailable() [Fact] public void TryGetParsedCommand_ReturnsFalse_WhenMissing() { - ICommandContext context = Context(); + CommandContext context = Context(); bool found = context.TryGetParsedCommand(out ParsedCommand? result); found.Should().BeFalse(); result.Should().BeNull(); @@ -96,7 +93,7 @@ public void TryGetParsedCommand_ReturnsFalse_WhenMissing() [Fact] public void SetParsedCommand_SetsValue() { - ICommandContext context = Context(new Dictionary()); + CommandContext context = Context([]); context.SetParsedCommand(parsedCommand); context.Properties[TerminalIdentifiers.ParsedCommand].Should().Be(parsedCommand); } @@ -105,7 +102,7 @@ public void SetParsedCommand_SetsValue() public void GetCommand_Returns_WhenAvailable() { Dictionary properties = new() { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; - ICommandContext context = Context(properties); + CommandContext context = Context(properties); Command result = context.GetCommand(); result.Should().Be(command); } @@ -113,7 +110,7 @@ public void GetCommand_Returns_WhenAvailable() [Fact] public void GetCommand_Throws_WhenParsedCommandMissing() { - ICommandContext context = Context(); + CommandContext context = Context(); Action act = () => context.GetCommand(); act.Should().Throw().WithErrorCode(TerminalErrors.ServerError); } @@ -123,7 +120,7 @@ public void GetCommandRequest_Returns_WhenAvailable() { CommandRequest request = new("req-1", "cmd"); Dictionary properties = new() { { TerminalIdentifiers.CommandRequest, request } }; - ICommandContext context = Context(properties); + CommandContext context = Context(properties); CommandRequest result = context.GetCommandRequest(); result.Should().BeSameAs(request); } @@ -131,7 +128,7 @@ public void GetCommandRequest_Returns_WhenAvailable() [Fact] public void GetCommandRequest_Throws_WhenMissing() { - ICommandContext context = Context(); + CommandContext context = Context(); Action act = () => context.GetCommandRequest(); act.Should().Throw().WithErrorCode(TerminalErrors.ServerError) .WithErrorDescription("The command request is missing in the context."); @@ -142,7 +139,7 @@ public void TryGetCommandRequest_ReturnsTrue_WhenAvailable() { CommandRequest request = new("req-1", "cmd"); Dictionary properties = new() { { TerminalIdentifiers.CommandRequest, request } }; - ICommandContext context = Context(properties); + CommandContext context = Context(properties); bool found = context.TryGetCommandRequest(out CommandRequest? result); found.Should().BeTrue(); result.Should().BeSameAs(request); @@ -151,7 +148,7 @@ public void TryGetCommandRequest_ReturnsTrue_WhenAvailable() [Fact] public void TryGetCommandRequest_ReturnsFalse_WhenMissing() { - ICommandContext context = Context(); + CommandContext context = Context(); bool found = context.TryGetCommandRequest(out CommandRequest? result); found.Should().BeFalse(); result.Should().BeNull(); @@ -161,7 +158,7 @@ public void TryGetCommandRequest_ReturnsFalse_WhenMissing() public void SetCommandRequest_SetsValue() { CommandRequest request = new("req-1", "cmd"); - ICommandContext context = Context(new Dictionary()); + CommandContext context = Context([]); context.SetCommandRequest(request); context.Properties[TerminalIdentifiers.CommandRequest].Should().BeSameAs(request); } @@ -171,7 +168,7 @@ public void GetRouterContext_Returns_WhenAvailable() { MockRoutingContext routerContext = new(TerminalStartMode.Console, CancellationToken.None); Dictionary properties = new() { { TerminalIdentifiers.RouterContext, routerContext } }; - ICommandContext context = Context(properties); + CommandContext context = Context(properties); TerminalRouterContext result = context.GetRouterContext(); result.Should().BeSameAs(routerContext); } @@ -179,9 +176,10 @@ public void GetRouterContext_Returns_WhenAvailable() [Fact] public void GetRouterContext_Throws_WhenMissing() { - ICommandContext context = Context(); + CommandContext context = Context(); Action act = () => context.GetRouterContext(); - act.Should().Throw().WithErrorCode(TerminalErrors.ServerError).WithErrorDescription("The router context is missing in the context."); + act.Should().Throw().WithErrorCode(TerminalErrors.ServerError) + .WithErrorDescription("The router context is missing in the context."); } [Fact] @@ -189,7 +187,7 @@ public void TryGetRouterContext_ReturnsTrue_WhenAvailable() { MockRoutingContext routerContext = new(TerminalStartMode.Console, CancellationToken.None); Dictionary properties = new() { { TerminalIdentifiers.RouterContext, routerContext } }; - ICommandContext context = Context(properties); + CommandContext context = Context(properties); bool found = context.TryGetRouterContext(out TerminalRouterContext? result); found.Should().BeTrue(); result.Should().BeSameAs(routerContext); @@ -198,7 +196,7 @@ public void TryGetRouterContext_ReturnsTrue_WhenAvailable() [Fact] public void TryGetRouterContext_ReturnsFalse_WhenMissing() { - ICommandContext context = Context(); + CommandContext context = Context(); bool found = context.TryGetRouterContext(out TerminalRouterContext? result); found.Should().BeFalse(); result.Should().BeNull(); @@ -208,7 +206,7 @@ public void TryGetRouterContext_ReturnsFalse_WhenMissing() public void SetRouterContext_SetsValue() { MockRoutingContext routerContext = new(TerminalStartMode.Console, CancellationToken.None); - ICommandContext context = Context(new Dictionary()); + CommandContext context = Context([]); context.SetRouterContext(routerContext); context.Properties[TerminalIdentifiers.RouterContext].Should().BeSameAs(routerContext); } @@ -218,7 +216,7 @@ public void GetCommandResult_Returns_WhenAvailable() { CommandResult commandResult = new(); Dictionary properties = new() { { TerminalIdentifiers.CommandResult, commandResult } }; - ICommandContext context = Context(properties); + CommandContext context = Context(properties); CommandResult result = context.GetCommandResult(); result.Should().Be(commandResult); } @@ -226,9 +224,10 @@ public void GetCommandResult_Returns_WhenAvailable() [Fact] public void GetCommandResult_Throws_WhenMissing() { - ICommandContext context = Context(); + CommandContext context = Context(); Action act = () => context.GetCommandResult(); - act.Should().Throw().WithErrorCode(TerminalErrors.ServerError).WithErrorDescription("The command result is missing in the context."); + act.Should().Throw().WithErrorCode(TerminalErrors.ServerError) + .WithErrorDescription("The command result is missing in the context."); } [Fact] @@ -236,7 +235,7 @@ public void TryGetCommandResult_ReturnsTrue_WhenAvailable() { CommandResult commandResult = new(); Dictionary properties = new() { { TerminalIdentifiers.CommandResult, commandResult } }; - ICommandContext context = Context(properties); + CommandContext context = Context(properties); bool found = context.TryGetCommandResult(out CommandResult? result); found.Should().BeTrue(); result.Should().Be(commandResult); @@ -245,7 +244,7 @@ public void TryGetCommandResult_ReturnsTrue_WhenAvailable() [Fact] public void TryGetCommandResult_ReturnsFalse_WhenMissing() { - ICommandContext context = Context(); + CommandContext context = Context(); bool found = context.TryGetCommandResult(out CommandResult? result); found.Should().BeFalse(); result.Should().BeNull(); @@ -255,7 +254,7 @@ public void TryGetCommandResult_ReturnsFalse_WhenMissing() public void SetCommandResult_SetsValue() { CommandResult commandResult = new(); - ICommandContext context = Context(new Dictionary()); + CommandContext context = Context([]); context.SetCommandResult(commandResult); context.Properties[TerminalIdentifiers.CommandResult].Should().Be(commandResult); } @@ -264,7 +263,7 @@ public void SetCommandResult_SetsValue() public void GetRequiredOptionValue_Returns_WhenAvailable() { Dictionary properties = new() { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; - ICommandContext context = Context(properties); + CommandContext context = Context(properties); int value = context.GetRequiredOptionValue("opt1"); value.Should().Be(123); } @@ -278,7 +277,7 @@ public void GetRequiredOptionValue_Throws_WhenOptionsNull() Command cmd = new(commandDescriptor, arguments, null); ParsedCommand parsedCmd = new(cmd, null); Dictionary properties = new() { { TerminalIdentifiers.ParsedCommand, parsedCmd } }; - ICommandContext context = Context(properties); + CommandContext context = Context(properties); Action act = () => context.GetRequiredOptionValue("opt1"); act.Should().Throw().WithErrorCode(TerminalErrors.UnsupportedOption); } @@ -287,7 +286,7 @@ public void GetRequiredOptionValue_Throws_WhenOptionsNull() public void TryGetOptionValue_ReturnsTrue_WhenAvailable() { Dictionary properties = new() { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; - ICommandContext context = Context(properties); + CommandContext context = Context(properties); bool found = context.TryGetOptionValue("opt1", out int value); found.Should().BeTrue(); value.Should().Be(123); @@ -297,7 +296,7 @@ public void TryGetOptionValue_ReturnsTrue_WhenAvailable() public void TryGetOptionValue_ReturnsFalse_WhenNotAvailable() { Dictionary properties = new() { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; - ICommandContext context = Context(properties); + CommandContext context = Context(properties); bool found = context.TryGetOptionValue("notfound", out int value); found.Should().BeFalse(); } @@ -306,7 +305,7 @@ public void TryGetOptionValue_ReturnsFalse_WhenNotAvailable() public void GetRequiredArgumentValue_ById_Returns_WhenAvailable() { Dictionary properties = new() { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; - ICommandContext context = Context(properties); + CommandContext context = Context(properties); int value = context.GetRequiredArgumentValue("arg1"); value.Should().Be(42); } @@ -315,7 +314,7 @@ public void GetRequiredArgumentValue_ById_Returns_WhenAvailable() public void GetRequiredArgumentValue_ByIndex_Returns_WhenAvailable() { Dictionary properties = new() { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; - ICommandContext context = Context(properties); + CommandContext context = Context(properties); int value = context.GetRequiredArgumentValue(0); value.Should().Be(42); } @@ -324,7 +323,7 @@ public void GetRequiredArgumentValue_ByIndex_Returns_WhenAvailable() public void TryGetArgumentValue_ReturnsTrue_WhenAvailable() { Dictionary properties = new() { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; - ICommandContext context = Context(properties); + CommandContext context = Context(properties); bool found = context.TryGetArgumentValue("arg1", out int value); found.Should().BeTrue(); value.Should().Be(42); @@ -334,7 +333,7 @@ public void TryGetArgumentValue_ReturnsTrue_WhenAvailable() public void TryGetArgumentValue_ReturnsFalse_WhenNotAvailable() { Dictionary properties = new() { { TerminalIdentifiers.ParsedCommand, parsedCommand } }; - ICommandContext context = Context(properties); + CommandContext context = Context(properties); bool found = context.TryGetArgumentValue("notfound", out int value); found.Should().BeFalse(); } diff --git a/test/OneImlx.Terminal.Tests/Mocks/MockCommandChecker.cs b/test/OneImlx.Terminal.Tests/Mocks/MockCommandChecker.cs index 0e0c496b..df684e10 100644 --- a/test/OneImlx.Terminal.Tests/Mocks/MockCommandChecker.cs +++ b/test/OneImlx.Terminal.Tests/Mocks/MockCommandChecker.cs @@ -15,7 +15,7 @@ public class MockCommandChecker : ICommandChecker { public bool Called { get; set; } - public Task CheckCommandAsync(ICommandContext context) + public Task CheckCommandAsync(CommandContext context) { Called = true; return Task.FromResult(new CommandCheckerResult()); diff --git a/test/OneImlx.Terminal.Tests/Mocks/MockCommandHandler.cs b/test/OneImlx.Terminal.Tests/Mocks/MockCommandHandler.cs index 4aed8865..63dbee08 100644 --- a/test/OneImlx.Terminal.Tests/Mocks/MockCommandHandler.cs +++ b/test/OneImlx.Terminal.Tests/Mocks/MockCommandHandler.cs @@ -15,7 +15,7 @@ public class MockCommandHandler : ICommandHandler { public bool Called { get; set; } - public Task HandleCommandAsync(ICommandContext context) + public Task HandleCommandAsync(CommandContext context) { Called = true; return Task.CompletedTask; diff --git a/test/OneImlx.Terminal.Tests/Mocks/MockCommandParser.cs b/test/OneImlx.Terminal.Tests/Mocks/MockCommandParser.cs index 5cd44e84..761ee615 100644 --- a/test/OneImlx.Terminal.Tests/Mocks/MockCommandParser.cs +++ b/test/OneImlx.Terminal.Tests/Mocks/MockCommandParser.cs @@ -12,7 +12,7 @@ namespace OneImlx.Terminal.Mocks { public class MockCommandParser : ICommandParser { - public Task ParseCommandAsync(ICommandContext context) + public Task ParseCommandAsync(CommandContext context) { Called = true; diff --git a/test/OneImlx.Terminal.Tests/Mocks/MockCommandRouter.cs b/test/OneImlx.Terminal.Tests/Mocks/MockCommandRouter.cs index e4d96ac5..e9695229 100644 --- a/test/OneImlx.Terminal.Tests/Mocks/MockCommandRouter.cs +++ b/test/OneImlx.Terminal.Tests/Mocks/MockCommandRouter.cs @@ -28,7 +28,7 @@ public MockCommandRouter(int? routeDelay = null, CancellationTokenSource? cancel public List MultipleRawString { get; set; } - public ICommandContext? PassedContext { get; private set; } + public CommandContext? PassedContext { get; private set; } public string? RawCommandString { get; set; } @@ -39,7 +39,7 @@ public MockCommandRouter(int? routeDelay = null, CancellationTokenSource? cancel //This is used in the context of singleton Router public int RouteCounter { get; set; } - public async Task RouteCommandAsync(ICommandContext context) + public async Task RouteCommandAsync(CommandContext context) { // For testing this is a singleton router so make sure it is thread safe await routeLock.WaitAsync(); diff --git a/test/OneImlx.Terminal.Tests/Mocks/MockCommandRunner.cs b/test/OneImlx.Terminal.Tests/Mocks/MockCommandRunner.cs index 5073ec8b..3a0f75ee 100644 --- a/test/OneImlx.Terminal.Tests/Mocks/MockCommandRunner.cs +++ b/test/OneImlx.Terminal.Tests/Mocks/MockCommandRunner.cs @@ -8,19 +8,19 @@ namespace OneImlx.Terminal.Mocks { - public class MockCommandRunner : ICommandRunner + public class MockCommandRunner : ICommandRunner { public bool HelpCalled { get; set; } public bool RunCalled { get; set; } - public Task RunCommandAsync(ICommandContext context) + public Task RunCommandAsync(CommandContext context) { RunCalled = true; return Task.FromResult(new CommandRunnerResult()); } - public Task RunHelpAsync(ICommandContext context) + public Task RunHelpAsync(CommandContext context) { HelpCalled = true; return Task.CompletedTask; diff --git a/test/OneImlx.Terminal.Tests/Mocks/MockSocketCommandRouter.cs b/test/OneImlx.Terminal.Tests/Mocks/MockSocketCommandRouter.cs index 76c7193c..072ea779 100644 --- a/test/OneImlx.Terminal.Tests/Mocks/MockSocketCommandRouter.cs +++ b/test/OneImlx.Terminal.Tests/Mocks/MockSocketCommandRouter.cs @@ -34,7 +34,7 @@ public MockSocketCommandRouter(int? routeDelay = null, CancellationTokenSource? //This is used in the context of singleton Router public int RouteCounter { get; set; } - public async Task RouteCommandAsync(ICommandContext context) + public async Task RouteCommandAsync(CommandContext context) { // Stats CommandRequest commandRequest = context.GetCommandRequest(); diff --git a/test/OneImlx.Terminal.Tests/Mocks/MockTerminalConsole.cs b/test/OneImlx.Terminal.Tests/Mocks/MockTerminalConsole.cs index 45d8c61a..e25f08a4 100644 --- a/test/OneImlx.Terminal.Tests/Mocks/MockTerminalConsole.cs +++ b/test/OneImlx.Terminal.Tests/Mocks/MockTerminalConsole.cs @@ -1,14 +1,11 @@ -/* - Copyright 2024 (c) Perpetual Intelligence L.L.C. All Rights Reserved. +// Copyright © 2019-2026 Perpetual Intelligence L.L.C. All rights reserved. +// For license, terms, and data policies, go to: +// https://terms.perpetualintelligence.com/articles/intro.html - For license, terms, and data policies, go to: - https://terms.perpetualintelligence.com/articles/intro.html -*/ - -using OneImlx.Terminal.Runtime; using System; using System.Collections.Generic; using System.Threading.Tasks; +using OneImlx.Terminal.Runtime; namespace OneImlx.Terminal.Mocks { @@ -98,4 +95,4 @@ public Task WriteLineColorAsync(ConsoleColor foregroundColor, string value, para return Task.CompletedTask; } } -} +} \ No newline at end of file diff --git a/test/OneImlx.Terminal.Tests/Runtime/TerminalProcessorTests.cs b/test/OneImlx.Terminal.Tests/Runtime/TerminalProcessorTests.cs index 94890a60..d97e13c5 100644 --- a/test/OneImlx.Terminal.Tests/Runtime/TerminalProcessorTests.cs +++ b/test/OneImlx.Terminal.Tests/Runtime/TerminalProcessorTests.cs @@ -81,8 +81,8 @@ public async Task AddAsync_Batch_Commands_HandlesConcurrentCalls() { // Mock the setup for the command router List routedCommands = []; - _mockCommandRouter.Setup(r => r.RouteCommandAsync(It.IsAny())) - .Callback(c => routedCommands.Add(c.GetCommandRequest().Raw)); + _mockCommandRouter.Setup(r => r.RouteCommandAsync(It.IsAny())) + .Callback(c => routedCommands.Add(c.GetCommandRequest().Raw)); _terminalProcessor.StartProcessing(_mockTerminalRouterContext.Object, background: true); int idx = 0; @@ -123,8 +123,8 @@ public async Task AddAsync_Does_Add_When_BatchDelimiter_Missing_In_Non_BatchMode // Setup that the mock command router was invoked List routedCommands = []; - _mockCommandRouter.Setup(r => r.RouteCommandAsync(It.IsAny())) - .Callback(c => routedCommands.Add(c.GetCommandRequest().Raw)); + _mockCommandRouter.Setup(r => r.RouteCommandAsync(It.IsAny())) + .Callback(c => routedCommands.Add(c.GetCommandRequest().Raw)); // Act await _terminalProcessor.AddAsync(TerminalInputOutput.Single("id1", "command1", "sender", "endpoint")); @@ -140,8 +140,8 @@ public async Task AddAsync_Handles_ConcurrentCalls() { // Mock the setup for the command router List routedCommands = []; - _mockCommandRouter.Setup(r => r.RouteCommandAsync(It.IsAny())) - .Callback(c => routedCommands.Add(c.GetCommandRequest().Raw)); + _mockCommandRouter.Setup(r => r.RouteCommandAsync(It.IsAny())) + .Callback(c => routedCommands.Add(c.GetCommandRequest().Raw)); _terminalProcessor.StartProcessing(_mockTerminalRouterContext.Object, background: true); int idx = 1; @@ -165,8 +165,8 @@ public async Task AddAsync_Processes_BatchCommand_In_Order() // Setup that the mock command router was invoked List routedCommands = []; - _mockCommandRouter.Setup(r => r.RouteCommandAsync(It.IsAny())) - .Callback(c => routedCommands.Add(c.GetCommandRequest().Raw)); + _mockCommandRouter.Setup(r => r.RouteCommandAsync(It.IsAny())) + .Callback(c => routedCommands.Add(c.GetCommandRequest().Raw)); OrderedDictionary commands1 = []; for (int i = 0; i < 1000; i++) @@ -287,8 +287,8 @@ public async Task AddAsync_Processes_Large_Batch() // Setup that the mock command router was invoked Dictionary routedCommands = []; - _mockCommandRouter.Setup(r => r.RouteCommandAsync(It.IsAny())) - .Callback(c => routedCommands.Add(c.GetCommandRequest().Id, c.GetCommandRequest().Raw)); + _mockCommandRouter.Setup(r => r.RouteCommandAsync(It.IsAny())) + .Callback(c => routedCommands.Add(c.GetCommandRequest().Id, c.GetCommandRequest().Raw)); // Send batch of 100000 commands by using TerminalServices Dictionary allCommands = []; @@ -355,9 +355,9 @@ public async Task ExecuteAsync_Routes_Batched_Commands_And_Processes_In_Order() CommandResult routerResult3 = new(new CommandCheckerResult(), new CommandRunnerResult("sender_result3")); // Arrange - ICommandContext? routeContext = null; - _mockCommandRouter.Setup(r => r.RouteCommandAsync(It.IsAny())) - .Callback(c => + CommandContext? routeContext = null; + _mockCommandRouter.Setup(r => r.RouteCommandAsync(It.IsAny())) + .Callback(c => { routeContext = c; switch (c.GetCommandRequest().Raw) @@ -425,9 +425,9 @@ public async Task ExecuteAsync_Routes_Batched_Commands_With_Null_Value_Result() CommandResult routerResult5 = new(new CommandCheckerResult(), new CommandRunnerResult("sender_result5")); // Arrange - ICommandContext? routeContext = null; - _mockCommandRouter.Setup(r => r.RouteCommandAsync(It.IsAny())) - .Callback(c => + CommandContext? routeContext = null; + _mockCommandRouter.Setup(r => r.RouteCommandAsync(It.IsAny())) + .Callback(c => { routeContext = c; switch (c.GetCommandRequest().Raw) @@ -503,9 +503,9 @@ public async Task ExecuteAsync_Routes_Command_And_Returns_Result() CommandResult routerResult = new(new CommandCheckerResult(), new CommandRunnerResult("sender_result")); // Arrange - ICommandContext? routeContext = null; - _mockCommandRouter.Setup(r => r.RouteCommandAsync(It.IsAny())) - .Callback(c => + CommandContext? routeContext = null; + _mockCommandRouter.Setup(r => r.RouteCommandAsync(It.IsAny())) + .Callback(c => { routeContext = c; c.SetCommandResult(routerResult); @@ -569,8 +569,8 @@ public async Task StartProcessing_HandlesRouterException() [Fact] public async Task StartProcessing_Populates_Router_Context() { - ICommandContext? routeContext = null; - _mockCommandRouter.Setup(r => r.RouteCommandAsync(It.IsAny())).Callback(c => routeContext = c); + CommandContext? routeContext = null; + _mockCommandRouter.Setup(r => r.RouteCommandAsync(It.IsAny())).Callback(c => routeContext = c); _terminalProcessor.StartProcessing(_mockTerminalRouterContext.Object, background: true); @@ -699,8 +699,8 @@ public async Task Stream_Does_No_Processes_Partial_Batch() List processedCommands = []; var lockObj = new object(); - _mockCommandRouter.Setup(x => x.RouteCommandAsync(It.IsAny())) - .Callback(ctx => + _mockCommandRouter.Setup(x => x.RouteCommandAsync(It.IsAny())) + .Callback(ctx => { processedCommands.Add(ctx.GetCommandRequest().Raw); }); @@ -742,8 +742,8 @@ public async Task Stream_Does_Not_Process_Non_Delimited_Input() string? processedCommand = null; var lockObj = new object(); - _mockCommandRouter.Setup(x => x.RouteCommandAsync(It.IsAny())) - .Callback(ctx => + _mockCommandRouter.Setup(x => x.RouteCommandAsync(It.IsAny())) + .Callback(ctx => { processedCommand = ctx.GetCommandRequest().Raw; }); @@ -775,8 +775,8 @@ public async Task Stream_Processes_Chunks_In_Order() var processedCommands = new ConcurrentQueue(); var lockObj = new object(); - _mockCommandRouter.Setup(x => x.RouteCommandAsync(It.IsAny())) - .Callback(ctx => + _mockCommandRouter.Setup(x => x.RouteCommandAsync(It.IsAny())) + .Callback(ctx => { processedCommands.Enqueue(ctx.GetCommandRequest().Raw); }); @@ -822,8 +822,8 @@ public async Task StreamRequestAsync_Processes_Delimited_Input() var processedCommand = ""; var lockObj = new object(); - _mockCommandRouter.Setup(x => x.RouteCommandAsync(It.IsAny())) - .Callback(ctx => + _mockCommandRouter.Setup(x => x.RouteCommandAsync(It.IsAny())) + .Callback(ctx => { processedCommand = ctx.GetCommandRequest().Raw; }); From b33f22fb011c8271e94afbab63fc6ea32968e251 Mon Sep 17 00:00:00 2001 From: "pi.admin" Date: Sun, 7 Jun 2026 13:19:52 -0700 Subject: [PATCH 5/9] Demo custom context --- ...stomContext.cs => CustomCommandContext.cs} | 2 +- .../Custom/CustomCommandContextFactory.cs | 28 ++++++++++++++++++ ...{CustomResult.cs => CustomRunnerResult.cs} | 2 +- apps/cli/TestConsole/Program.cs | 5 ++-- apps/cli/TestConsole/Runners/ClearRunner.cs | 9 +++--- apps/cli/TestConsole/Runners/Cmd7Runner.cs | 8 +++-- apps/s2s/TestClient/Runners/ClsRunner.cs | 18 +++++------- .../Commands/Runners/CommandRunnerResult.cs | 21 +++----------- .../Runners/CommandRunnerResultTests.cs | 29 ++++--------------- 9 files changed, 60 insertions(+), 62 deletions(-) rename apps/cli/TestConsole/Custom/{CustomContext.cs => CustomCommandContext.cs} (54%) create mode 100644 apps/cli/TestConsole/Custom/CustomCommandContextFactory.cs rename apps/cli/TestConsole/Custom/{CustomResult.cs => CustomRunnerResult.cs} (64%) diff --git a/apps/cli/TestConsole/Custom/CustomContext.cs b/apps/cli/TestConsole/Custom/CustomCommandContext.cs similarity index 54% rename from apps/cli/TestConsole/Custom/CustomContext.cs rename to apps/cli/TestConsole/Custom/CustomCommandContext.cs index 5e29a756..2366c440 100644 --- a/apps/cli/TestConsole/Custom/CustomContext.cs +++ b/apps/cli/TestConsole/Custom/CustomCommandContext.cs @@ -3,7 +3,7 @@ namespace OneImlx.Terminal.Apps.Test.Custom { - public class CustomContext(Dictionary properties) : CommandContext(properties) + public class CustomCommandContext(Dictionary properties) : CommandContext(properties) { } } \ No newline at end of file diff --git a/apps/cli/TestConsole/Custom/CustomCommandContextFactory.cs b/apps/cli/TestConsole/Custom/CustomCommandContextFactory.cs new file mode 100644 index 00000000..67c32e52 --- /dev/null +++ b/apps/cli/TestConsole/Custom/CustomCommandContextFactory.cs @@ -0,0 +1,28 @@ +using OneImlx.Terminal.Extensions; +using OneImlx.Terminal.Shared; +using System; +using System.Collections.Generic; +using System.Text; + +namespace OneImlx.Terminal.Apps.Test.Custom +{ + internal class CustomCommandContextFactory : ICommandContextFactory + { + public CommandContext Create(CommandRequest request, TerminalRouterContext routerContext, Dictionary properties) + { + var context = new CustomCommandContext(properties); + context.SetCommandRequest(request); + context.SetRouterContext(routerContext); + return context; + + } + + public TContext Create(CommandRequest request, TerminalRouterContext routerContext, Dictionary properties) where TContext : CommandContext + { + var context = new CustomCommandContext(properties); + context.SetCommandRequest(request); + context.SetRouterContext(routerContext); + return (TContext) (CommandContext) context; + } + } +} diff --git a/apps/cli/TestConsole/Custom/CustomResult.cs b/apps/cli/TestConsole/Custom/CustomRunnerResult.cs similarity index 64% rename from apps/cli/TestConsole/Custom/CustomResult.cs rename to apps/cli/TestConsole/Custom/CustomRunnerResult.cs index 4b309b82..e650eac2 100644 --- a/apps/cli/TestConsole/Custom/CustomResult.cs +++ b/apps/cli/TestConsole/Custom/CustomRunnerResult.cs @@ -2,7 +2,7 @@ namespace OneImlx.Terminal.Apps.Test.Custom { - public class CustomResult : CommandRunnerResult + public class CustomRunnerResult : CommandRunnerResult { } } \ No newline at end of file diff --git a/apps/cli/TestConsole/Program.cs b/apps/cli/TestConsole/Program.cs index e92e1f06..f5b391cc 100644 --- a/apps/cli/TestConsole/Program.cs +++ b/apps/cli/TestConsole/Program.cs @@ -9,6 +9,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using OneImlx.Shared.Licensing; +using OneImlx.Terminal.Apps.Test.Custom; using OneImlx.Terminal.Apps.Test.Runners; using OneImlx.Terminal.Commands; using OneImlx.Terminal.Extensions; @@ -64,8 +65,8 @@ private static void ConfigureOneImlxTerminal(IServiceCollection collection) options.Router.Caret = "> "; }); - // Default command context - terminalBuilder.AddCommandContextFactory(); + // Custom command context + terminalBuilder.AddCommandContextFactory(); // Add commands using declarative syntax. terminalBuilder.AddDeclarativeAssembly(); diff --git a/apps/cli/TestConsole/Runners/ClearRunner.cs b/apps/cli/TestConsole/Runners/ClearRunner.cs index a4abe182..3a17bc0a 100644 --- a/apps/cli/TestConsole/Runners/ClearRunner.cs +++ b/apps/cli/TestConsole/Runners/ClearRunner.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using OneImlx.Terminal.Apps.Test.Custom; using OneImlx.Terminal.Commands; using OneImlx.Terminal.Commands.Runners; using OneImlx.Terminal.Runtime; @@ -11,7 +12,7 @@ namespace OneImlx.Terminal.Apps.Test.Runners /// Clears the console. /// [CommandDescriptor("cls", "Clear Console", "Clears the console.", CommandTypes.Native)] - public class ClearRunner : CommandRunner , IDeclarativeRunner + public class ClearRunner : CommandRunner, IDeclarativeRunner { /// /// Initializes a new instance of the class. @@ -23,12 +24,12 @@ public ClearRunner(ITerminalConsole terminalConsole) } /// - public override async Task RunCommandAsync(CommandContext context) + public override async Task RunCommandAsync(CustomCommandContext context) { await terminalConsole.ClearAsync(); - return await CommandRunnerResult.EmptyAsync(); + return new CustomRunnerResult(); } private readonly ITerminalConsole terminalConsole; } -} +} \ No newline at end of file diff --git a/apps/cli/TestConsole/Runners/Cmd7Runner.cs b/apps/cli/TestConsole/Runners/Cmd7Runner.cs index 8154c4ee..2fc94c45 100644 --- a/apps/cli/TestConsole/Runners/Cmd7Runner.cs +++ b/apps/cli/TestConsole/Runners/Cmd7Runner.cs @@ -1,6 +1,8 @@ using Microsoft.Extensions.Logging; +using OneImlx.Terminal.Apps.Test.Custom; using OneImlx.Terminal.Commands; using OneImlx.Terminal.Commands.Runners; +using OneImlx.Terminal.Extensions; using OneImlx.Terminal.Runtime; using OneImlx.Terminal.Shared; using OneImlx.Terminal.Shared.Declarative; @@ -13,7 +15,7 @@ namespace OneImlx.Terminal.Apps.Test.Runners /// [CommandOwners("grp3")] [CommandDescriptor("cmd7", "Command 7", "Command 7 under grp3.", CommandTypes.Leaf)] - public class Cmd7Runner : CommandRunner , IDeclarativeRunner + public class Cmd7Runner : CommandRunner, IDeclarativeRunner { private readonly ITerminalConsole terminalConsole; private readonly ILogger logger; @@ -24,12 +26,12 @@ public Cmd7Runner(ITerminalConsole terminalConsole, ILogger logger) this.logger = logger; } - public override async Task RunCommandAsync(CommandContext context) + public override async Task RunCommandAsync(CustomCommandContext context) { logger.LogInformation("Executing grp3 cmd7"); await terminalConsole.WriteLineAsync("Executing: grp3 cmd7"); await terminalConsole.WriteLineAsync("This is a leaf command under isolated group grp3."); - return new CommandRunnerResult(); + return new CustomRunnerResult(); } } } \ No newline at end of file diff --git a/apps/s2s/TestClient/Runners/ClsRunner.cs b/apps/s2s/TestClient/Runners/ClsRunner.cs index db83c2d1..d7ab6586 100644 --- a/apps/s2s/TestClient/Runners/ClsRunner.cs +++ b/apps/s2s/TestClient/Runners/ClsRunner.cs @@ -10,25 +10,21 @@ namespace OneImlx.Terminal.Apps.TestClient.Runners /// /// Clears the console. /// + /// + /// Initializes a new instance of the class. + /// + /// The terminal console. [CommandDescriptor("cls", "Clear Console", "Clears the console.", CommandTypes.Native)] - public class ClsRunner : CommandRunner, IDeclarativeRunner + public class ClsRunner(ITerminalConsole terminalConsole) : CommandRunner, IDeclarativeRunner { - /// - /// Initializes a new instance of the class. - /// - /// The terminal console. - public ClsRunner(ITerminalConsole terminalConsole) - { - this.terminalConsole = terminalConsole; - } /// public override async Task RunCommandAsync(CommandContext context) { await terminalConsole.ClearAsync(); - return await CommandRunnerResult.EmptyAsync(); + return CommandRunnerResult.Empty(); } - private readonly ITerminalConsole terminalConsole; + private readonly ITerminalConsole terminalConsole = terminalConsole; } } \ No newline at end of file diff --git a/src/OneImlx.Terminal/Commands/Runners/CommandRunnerResult.cs b/src/OneImlx.Terminal/Commands/Runners/CommandRunnerResult.cs index 66373b98..acc888af 100644 --- a/src/OneImlx.Terminal/Commands/Runners/CommandRunnerResult.cs +++ b/src/OneImlx.Terminal/Commands/Runners/CommandRunnerResult.cs @@ -1,13 +1,9 @@ -/* - Copyright © 2019-2025 Perpetual Intelligence L.L.C. All rights reserved. - - For license, terms, and data policies, go to: - https://terms.perpetualintelligence.com/articles/intro.html -*/ +// Copyright © 2019-2026 Perpetual Intelligence L.L.C. All rights reserved. +// For license, terms, and data policies, go to: +// https://terms.perpetualintelligence.com/articles/intro.html using OneImlx.Terminal.Shared; using System; -using System.Threading.Tasks; namespace OneImlx.Terminal.Commands.Runners { @@ -61,15 +57,6 @@ public static CommandRunnerResult Empty() return new CommandRunnerResult(); } - /// - /// Creates an empty with no value. - /// - /// A task that creates a new instance of . - public static Task EmptyAsync() - { - return Task.FromResult(new CommandRunnerResult()); - } - /// /// Gets the value as the specified type. /// @@ -87,4 +74,4 @@ public TValue As() private readonly object? value; } -} +} \ No newline at end of file diff --git a/test/OneImlx.Terminal.Tests/Commands/Runners/CommandRunnerResultTests.cs b/test/OneImlx.Terminal.Tests/Commands/Runners/CommandRunnerResultTests.cs index 2b6acd44..fc35d231 100644 --- a/test/OneImlx.Terminal.Tests/Commands/Runners/CommandRunnerResultTests.cs +++ b/test/OneImlx.Terminal.Tests/Commands/Runners/CommandRunnerResultTests.cs @@ -1,15 +1,12 @@ -/* - Copyright © 2019-2025 Perpetual Intelligence L.L.C. All rights reserved. - - For license, terms, and data policies, go to: - https://terms.perpetualintelligence.com/articles/intro.html -*/ +// Copyright © 2019-2026 Perpetual Intelligence L.L.C. All rights reserved. +// For license, terms, and data policies, go to: +// https://terms.perpetualintelligence.com/articles/intro.html +using System; +using System.Threading.Tasks; using FluentAssertions; using OneImlx.Terminal.Shared; using OneImlx.Test.FluentAssertions; -using System; -using System.Threading.Tasks; using Xunit; namespace OneImlx.Terminal.Commands.Runners @@ -72,20 +69,6 @@ public async Task Empty_Returns_New_Instance() var result1 = CommandRunnerResult.Empty(); var result2 = CommandRunnerResult.Empty(); result1.Should().NotBeSameAs(result2); - - var result3 = await CommandRunnerResult.EmptyAsync(); - var result4 = await CommandRunnerResult.EmptyAsync(); - result3.Should().NotBeSameAs(result4); - - result1.Should().NotBeSameAs(result3); - } - - [Fact] - public Task EmptyAysnc_Returns_Task() - { - var result = CommandRunnerResult.EmptyAsync(); - result.Should().BeOfType>(); - return result; } [Fact] @@ -98,4 +81,4 @@ public void Value_ShouldThrowInvalidOperationException_WhenValueIsNotSet() .WithErrorDescription("The value is not set on the command runner result."); } } -} +} \ No newline at end of file From 4228910098a1b29c076b4f204cc2f436fd26cce5 Mon Sep 17 00:00:00 2001 From: "pi.admin" Date: Sun, 7 Jun 2026 14:31:41 -0700 Subject: [PATCH 6/9] Update app/cli for custom context --- .../TestConsole/Custom/CustomRunnerResult.cs | 7 ++++++ apps/cli/TestConsole/Runners/Cmd8Runner.cs | 7 +++--- apps/cli/TestConsole/Runners/Cmd9Runner.cs | 8 +++---- apps/cli/TestConsole/Runners/ExitRunner.cs | 19 +++++++-------- apps/cli/TestConsole/Runners/Grp1Runner.cs | 23 ++++++++----------- apps/cli/TestConsole/Runners/Grp2Runner.cs | 19 +++++++-------- apps/cli/TestConsole/Runners/Grp3Runner.cs | 7 +++--- apps/cli/TestConsole/Runners/HelpRunner.cs | 21 ++++++++--------- apps/cli/TestConsole/Runners/RunRunner.cs | 14 ++++------- apps/cli/TestConsole/Runners/TestRunner.cs | 12 ++++------ 10 files changed, 66 insertions(+), 71 deletions(-) diff --git a/apps/cli/TestConsole/Custom/CustomRunnerResult.cs b/apps/cli/TestConsole/Custom/CustomRunnerResult.cs index e650eac2..4da3ecf1 100644 --- a/apps/cli/TestConsole/Custom/CustomRunnerResult.cs +++ b/apps/cli/TestConsole/Custom/CustomRunnerResult.cs @@ -4,5 +4,12 @@ namespace OneImlx.Terminal.Apps.Test.Custom { public class CustomRunnerResult : CommandRunnerResult { + public CustomRunnerResult() + { + } + + public CustomRunnerResult(object value) : base(value) + { + } } } \ No newline at end of file diff --git a/apps/cli/TestConsole/Runners/Cmd8Runner.cs b/apps/cli/TestConsole/Runners/Cmd8Runner.cs index ffaf967e..c7dd4670 100644 --- a/apps/cli/TestConsole/Runners/Cmd8Runner.cs +++ b/apps/cli/TestConsole/Runners/Cmd8Runner.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Logging; +using OneImlx.Terminal.Apps.Test.Custom; using OneImlx.Terminal.Commands; using OneImlx.Terminal.Commands.Runners; using OneImlx.Terminal.Runtime; @@ -13,7 +14,7 @@ namespace OneImlx.Terminal.Apps.Test.Runners /// [CommandOwners("grp3")] [CommandDescriptor("cmd8", "Command 8", "Command 8 under grp3.", CommandTypes.Leaf)] - public class Cmd8Runner : CommandRunner , IDeclarativeRunner + public class Cmd8Runner : CommandRunner, IDeclarativeRunner { private readonly ITerminalConsole terminalConsole; private readonly ILogger logger; @@ -24,12 +25,12 @@ public Cmd8Runner(ITerminalConsole terminalConsole, ILogger logger) this.logger = logger; } - public override async Task RunCommandAsync(CommandContext context) + public override async Task RunCommandAsync(CustomCommandContext context) { logger.LogInformation("Executing grp3 cmd8"); await terminalConsole.WriteLineAsync("Executing: grp3 cmd8"); await terminalConsole.WriteLineAsync("This is a leaf command under isolated group grp3."); - return new CommandRunnerResult(); + return new CustomRunnerResult(); } } } \ No newline at end of file diff --git a/apps/cli/TestConsole/Runners/Cmd9Runner.cs b/apps/cli/TestConsole/Runners/Cmd9Runner.cs index adacc7ab..4eb6dcb1 100644 --- a/apps/cli/TestConsole/Runners/Cmd9Runner.cs +++ b/apps/cli/TestConsole/Runners/Cmd9Runner.cs @@ -1,7 +1,7 @@ using Microsoft.Extensions.Logging; using OneImlx.Terminal.Apps.Test.Checkers; +using OneImlx.Terminal.Apps.Test.Custom; using OneImlx.Terminal.Commands; -using OneImlx.Terminal.Commands.Checkers; using OneImlx.Terminal.Commands.Runners; using OneImlx.Terminal.Runtime; using OneImlx.Terminal.Shared; @@ -16,7 +16,7 @@ namespace OneImlx.Terminal.Apps.Test.Runners [CommandOwners("grp3")] [CommandDescriptor("cmd9", "Command 9", "Command 9 under grp3 with custom checker.", CommandTypes.Leaf)] [CommandChecker(typeof(Cmd3CommandChecker))] - public class Cmd9Runner : CommandRunner , IDeclarativeRunner + public class Cmd9Runner : CommandRunner, IDeclarativeRunner { private readonly ITerminalConsole terminalConsole; private readonly ILogger logger; @@ -27,12 +27,12 @@ public Cmd9Runner(ITerminalConsole terminalConsole, ILogger logger) this.logger = logger; } - public override async Task RunCommandAsync(CommandContext context) + public override async Task RunCommandAsync(CustomCommandContext context) { logger.LogInformation("Executing grp3 cmd9"); await terminalConsole.WriteLineAsync("Executing: grp3 cmd9"); await terminalConsole.WriteLineAsync("This is a leaf command with custom checker under grp3."); - return new CommandRunnerResult(); + return new CustomRunnerResult(); } } } diff --git a/apps/cli/TestConsole/Runners/ExitRunner.cs b/apps/cli/TestConsole/Runners/ExitRunner.cs index 563db6a7..f64c641e 100644 --- a/apps/cli/TestConsole/Runners/ExitRunner.cs +++ b/apps/cli/TestConsole/Runners/ExitRunner.cs @@ -1,22 +1,20 @@ -using Microsoft.Extensions.Hosting; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using OneImlx.Terminal.Apps.Test.Custom; using OneImlx.Terminal.Commands; using OneImlx.Terminal.Commands.Runners; using OneImlx.Terminal.Runtime; using OneImlx.Terminal.Shared; using OneImlx.Terminal.Shared.Declarative; -using System.Threading.Tasks; namespace OneImlx.Terminal.Apps.Test.Runners { /// - /// Runs native OS commands. + /// Exits the client terminal application. /// [CommandDescriptor("exit", "Exit", "Exits the client terminal application.", CommandTypes.Native)] - public class ExitRunner : CommandRunner , IDeclarativeRunner + public class ExitRunner : CommandRunner, IDeclarativeRunner { - /// - /// Initializes a new instance of the class. - /// public ExitRunner(ITerminalConsole terminalConsole, IHostApplicationLifetime applicationLifetime) { this.terminalConsole = terminalConsole; @@ -24,7 +22,7 @@ public ExitRunner(ITerminalConsole terminalConsole, IHostApplicationLifetime app } /// - public override async Task RunCommandAsync(CommandContext context) + public override async Task RunCommandAsync(CustomCommandContext context) { string answer = await terminalConsole.ReadAnswerAsync("Are you sure you want to exit ?", "y", "Y"); if (answer == "y" || answer == "Y") @@ -32,11 +30,10 @@ public override async Task RunCommandAsync(CommandContext c _applicationLifetime.StopApplication(); await Task.Delay(2000); } - - return CommandRunnerResult.Empty(); + return new CustomRunnerResult(); } private readonly IHostApplicationLifetime _applicationLifetime; private readonly ITerminalConsole terminalConsole; } -} +} \ No newline at end of file diff --git a/apps/cli/TestConsole/Runners/Grp1Runner.cs b/apps/cli/TestConsole/Runners/Grp1Runner.cs index 8bce2094..d99d3fb0 100644 --- a/apps/cli/TestConsole/Runners/Grp1Runner.cs +++ b/apps/cli/TestConsole/Runners/Grp1Runner.cs @@ -1,11 +1,8 @@ -// Copyright © 2019-2026 Perpetual Intelligence L.L.C. All rights reserved. -// For license, terms, and data policies, go to: -// https://terms.perpetualintelligence.com/articles/intro.html - using System; using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using OneImlx.Terminal.Apps.Test.Custom; using OneImlx.Terminal.Commands; using OneImlx.Terminal.Commands.Checkers; using OneImlx.Terminal.Commands.Runners; @@ -23,7 +20,7 @@ namespace OneImlx.Terminal.Apps.Test.Runners [CommandDescriptor("grp1", "Group 1", "Group 1 as a composite group", CommandTypes.CompositeGroup)] [CommandChecker(typeof(CommandChecker))] [CommandTags("group", "composite")] - public class Grp1Runner : CommandRunner , IDeclarativeRunner + public class Grp1Runner : CommandRunner, IDeclarativeRunner { private readonly ITerminalConsole terminalConsole; private readonly ILogger logger; @@ -34,7 +31,7 @@ public Grp1Runner(ITerminalConsole terminalConsole, ILogger logger) this.logger = logger; } - public override async Task RunCommandAsync(CommandContext context) + public override async Task RunCommandAsync(CustomCommandContext context) { logger.LogInformation("Executing grp1 base command"); await terminalConsole.WriteLineAsync("Group 1 (CompositeGroup)"); @@ -46,20 +43,20 @@ public override async Task RunCommandAsync(CommandContext c await terminalConsole.WriteLineAsync(" grp2 - Sub-group 2"); await terminalConsole.WriteLineAsync(""); await terminalConsole.WriteLineAsync("Usage: grp1 [arguments] [options]"); - return new CommandRunnerResult(); + return new(); } [CommandDescriptor("cmd1", "Command 1", "Command 1 in grp1.", CommandTypes.Leaf)] [CommandTags("command", "leaf")] [ArgumentDescriptor(1, "arg1", nameof(String), "First argument", BehaviorFlags.None)] [OptionDescriptor("opt1", nameof(String), "Option 1", BehaviorFlags.None)] - public async Task Cmd1Async(CommandContext context) + public async Task Cmd1Async(CustomCommandContext context) { logger.LogInformation("Executing grp1 cmd1"); string arg1 = context.GetCommand().GetRequiredArgumentValue("arg1"); string opt1 = context.GetCommand().GetRequiredOptionValue("opt1"); await terminalConsole.WriteLineAsync($"grp1 cmd1 executed: arg1={arg1}, opt1={opt1}"); - return new CommandRunnerResult(); + return new CustomRunnerResult(); } [CommandDescriptor("cmd2", "Command 2", "Command 2 in grp1.", CommandTypes.Leaf)] @@ -67,13 +64,13 @@ public async Task Cmd1Async(CommandContext context) [ArgumentDescriptor(1, "arg1", nameof(Int32), "Integer argument", BehaviorFlags.Required)] [ArgumentValidation("arg1", typeof(RequiredAttribute))] [OptionDescriptor("opt1", nameof(Boolean), "Boolean option", BehaviorFlags.None)] - public async Task Cmd2Async(CommandContext context) + public async Task Cmd2Async(CustomCommandContext context) { logger.LogInformation("Executing grp1 cmd2"); int arg1 = context.GetCommand().GetRequiredArgumentValue("arg1"); bool opt1 = context.GetCommand().GetRequiredOptionValue("opt1"); await terminalConsole.WriteLineAsync($"grp1 cmd2 executed: arg1={arg1}, opt1={opt1}"); - return new CommandRunnerResult(); + return new CustomRunnerResult(); } [CommandDescriptor("cmd3", "Command 3", "Command 3 in grp1.", CommandTypes.Leaf)] @@ -82,13 +79,13 @@ public async Task Cmd2Async(CommandContext context) [ArgumentValidation("arg1", typeof(RequiredAttribute))] [ArgumentValidation("arg1", typeof(StringLengthAttribute), 50)] [OptionDescriptor("opt1", nameof(String), "String option", BehaviorFlags.None)] - public async Task Cmd3Async(CommandContext context) + public async Task Cmd3Async(CustomCommandContext context) { logger.LogInformation("Executing grp1 cmd3"); string arg1 = context.GetCommand().GetRequiredArgumentValue("arg1"); string opt1 = context.GetCommand().GetRequiredOptionValue("opt1"); await terminalConsole.WriteLineAsync($"grp1 cmd3 executed: arg1={arg1}, opt1={opt1}"); - return new CommandRunnerResult(); + return new CustomRunnerResult(); } } } \ No newline at end of file diff --git a/apps/cli/TestConsole/Runners/Grp2Runner.cs b/apps/cli/TestConsole/Runners/Grp2Runner.cs index c0d68b57..92a4c53e 100644 --- a/apps/cli/TestConsole/Runners/Grp2Runner.cs +++ b/apps/cli/TestConsole/Runners/Grp2Runner.cs @@ -2,6 +2,7 @@ using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using OneImlx.Terminal.Apps.Test.Custom; using OneImlx.Terminal.Commands; using OneImlx.Terminal.Commands.Checkers; using OneImlx.Terminal.Commands.Runners; @@ -19,7 +20,7 @@ namespace OneImlx.Terminal.Apps.Test.Runners [CommandDescriptor("grp2", "Group 2", "Group 2 CompositeGroup under grp1 with cmd4, cmd5, cmd6.", CommandTypes.CompositeGroup)] [CommandChecker(typeof(CommandChecker))] [CommandTags("group", "composite", "nested")] - public class Grp2Runner : CommandRunner , IDeclarativeRunner + public class Grp2Runner : CommandRunner, IDeclarativeRunner { private readonly ITerminalConsole terminalConsole; private readonly ILogger logger; @@ -30,7 +31,7 @@ public Grp2Runner(ITerminalConsole terminalConsole, ILogger logger) this.logger = logger; } - public override async Task RunCommandAsync(CommandContext context) + public override async Task RunCommandAsync(CustomCommandContext context) { logger.LogInformation("Executing grp2 base command"); await terminalConsole.WriteLineAsync("Group 2 (CompositeGroup under grp1)"); @@ -41,7 +42,7 @@ public override async Task RunCommandAsync(CommandContext c await terminalConsole.WriteLineAsync(" cmd6 - Command 6"); await terminalConsole.WriteLineAsync(""); await terminalConsole.WriteLineAsync("Usage: grp1 grp2 [arguments] [options]"); - return new CommandRunnerResult(); + return new CustomRunnerResult(); } [CommandDescriptor("cmd4", "Command 4", "Command 4 in grp2.", CommandTypes.Leaf)] @@ -49,14 +50,14 @@ public override async Task RunCommandAsync(CommandContext c [ArgumentDescriptor(1, "arg1", nameof(String), "First argument", BehaviorFlags.None)] [ArgumentDescriptor(2, "arg2", nameof(String), "Second argument", BehaviorFlags.None)] [OptionDescriptor("opt1", nameof(String), "Option 1", BehaviorFlags.None)] - public async Task Cmd4Async(CommandContext context) + public async Task Cmd4Async(CustomCommandContext context) { logger.LogInformation("Cmd4 (Leaf under composite grp2)"); context.GetCommand().TryGetArgumentValue("arg1", out string? arg1); context.GetCommand().TryGetArgumentValue("arg2", out string? arg2); context.GetCommand().TryGetOptionValue("opt1", out string? opt1); await terminalConsole.WriteLineAsync($"grp1 grp2 cmd4 executed: arg1={arg1}, arg2={arg2}, opt1={opt1}"); - return new CommandRunnerResult(); + return new CustomRunnerResult(); } [CommandDescriptor("cmd5", "Command 5", "Command 5 in grp2.", CommandTypes.Leaf)] @@ -65,13 +66,13 @@ public async Task Cmd4Async(CommandContext context) [ArgumentValidation("arg1", typeof(RequiredAttribute))] [ArgumentValidation("arg1", typeof(RangeAttribute), 1, 100)] [OptionDescriptor("opt1", nameof(Boolean), "Boolean option", BehaviorFlags.Required)] - public async Task Cmd5Async(CommandContext context) + public async Task Cmd5Async(CustomCommandContext context) { logger.LogInformation("Executing grp1 grp2 cmd5"); int arg1 = context.GetCommand().GetRequiredArgumentValue("arg1"); bool opt1 = context.GetCommand().GetRequiredOptionValue("opt1"); await terminalConsole.WriteLineAsync($"grp1 grp2 cmd5 executed: arg1={arg1}, opt1={opt1}"); - return new CommandRunnerResult(); + return new CustomRunnerResult(); } [CommandDescriptor("cmd6", "Command 6", "Command 6 in grp2.", CommandTypes.Leaf)] @@ -79,13 +80,13 @@ public async Task Cmd5Async(CommandContext context) [ArgumentDescriptor(1, "arg1", nameof(Boolean), "Boolean argument", BehaviorFlags.Required)] [OptionDescriptor("opt1", nameof(String), "String option", BehaviorFlags.Required)] [OptionValidation("opt1", typeof(RequiredAttribute))] - public async Task Cmd6Async(CommandContext context) + public async Task Cmd6Async(CustomCommandContext context) { logger.LogInformation("Executing grp1 grp2 cmd6"); bool arg1 = context.GetCommand().GetRequiredArgumentValue("arg1"); string opt1 = context.GetCommand().GetRequiredOptionValue("opt1"); await terminalConsole.WriteLineAsync($"grp1 grp2 cmd6 executed: arg1={arg1}, opt1={opt1}"); - return new CommandRunnerResult(); + return new CustomRunnerResult(); } } } \ No newline at end of file diff --git a/apps/cli/TestConsole/Runners/Grp3Runner.cs b/apps/cli/TestConsole/Runners/Grp3Runner.cs index 5e9e29d0..879ded62 100644 --- a/apps/cli/TestConsole/Runners/Grp3Runner.cs +++ b/apps/cli/TestConsole/Runners/Grp3Runner.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Logging; +using OneImlx.Terminal.Apps.Test.Custom; using OneImlx.Terminal.Commands; using OneImlx.Terminal.Commands.Checkers; using OneImlx.Terminal.Commands.Runners; @@ -16,7 +17,7 @@ namespace OneImlx.Terminal.Apps.Test.Runners [CommandDescriptor("grp3", "Group 3", "Group 3 IsolatedGroup (independent) with cmd7, cmd8, cmd9.", CommandTypes.IsolatedGroup)] [CommandChecker(typeof(CommandChecker))] [CommandTags("group", "isolated", "independent")] - public class Grp3Runner : CommandRunner , IDeclarativeRunner + public class Grp3Runner : CommandRunner, IDeclarativeRunner { private readonly ITerminalConsole terminalConsole; private readonly ILogger logger; @@ -27,7 +28,7 @@ public Grp3Runner(ITerminalConsole terminalConsole, ILogger logger) this.logger = logger; } - public override async Task RunCommandAsync(CommandContext context) + public override async Task RunCommandAsync(CustomCommandContext context) { logger.LogInformation("Executing grp3 command"); await terminalConsole.WriteLineAsync("Group 3 (IsolatedGroup - independent)"); @@ -39,7 +40,7 @@ public override async Task RunCommandAsync(CommandContext c await terminalConsole.WriteLineAsync(" cmd9 - Command 9"); await terminalConsole.WriteLineAsync(""); await terminalConsole.WriteLineAsync("Usage: grp3 "); - return new CommandRunnerResult(); + return new CustomRunnerResult(); } } } diff --git a/apps/cli/TestConsole/Runners/HelpRunner.cs b/apps/cli/TestConsole/Runners/HelpRunner.cs index 3898177c..0fdf1038 100644 --- a/apps/cli/TestConsole/Runners/HelpRunner.cs +++ b/apps/cli/TestConsole/Runners/HelpRunner.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using OneImlx.Terminal.Apps.Test.Custom; using OneImlx.Terminal.Commands; using OneImlx.Terminal.Commands.Runners; using OneImlx.Terminal.Runtime; @@ -9,33 +10,29 @@ namespace OneImlx.Terminal.Apps.Test.Runners { /// - /// Runs native OS commands. + /// Displays all supported commands. /// [CommandDescriptor("help", "Help Command", "Displays all supported commands.", CommandTypes.Native)] - public class HelpRunner : CommandRunner , IDeclarativeRunner + public class HelpRunner : CommandRunner, IDeclarativeRunner { - /// - /// Initializes a new instance of the class. - /// public HelpRunner(ITerminalConsole console, ITerminalCommandStore commandStore) { _console = console; - this.commandStore = commandStore; + _commandStore = commandStore; } /// - public override async Task RunCommandAsync(CommandContext context) + public override async Task RunCommandAsync(CustomCommandContext context) { - var commands = await commandStore.AllAsync(); - + var commands = await _commandStore.AllAsync(); foreach (var command in commands) { await _console.WriteLineAsync($"{command.Key} ({command.Value.Name}) --> {command.Value.Description}"); } - return CommandRunnerResult.Empty(); + return new CustomRunnerResult(); } private readonly ITerminalConsole _console; - private readonly ITerminalCommandStore commandStore; + private readonly ITerminalCommandStore _commandStore; } -} +} \ No newline at end of file diff --git a/apps/cli/TestConsole/Runners/RunRunner.cs b/apps/cli/TestConsole/Runners/RunRunner.cs index a3fc2d53..2e1e8de5 100644 --- a/apps/cli/TestConsole/Runners/RunRunner.cs +++ b/apps/cli/TestConsole/Runners/RunRunner.cs @@ -1,9 +1,7 @@ -// Copyright © 2019-2026 Perpetual Intelligence L.L.C. All rights reserved. -// For license, terms, and data policies, go to: -// https://terms.perpetualintelligence.com/articles/intro.html -using System; +using System; using System.Diagnostics; using System.Threading.Tasks; +using OneImlx.Terminal.Apps.Test.Custom; using OneImlx.Terminal.Commands; using OneImlx.Terminal.Commands.Runners; using OneImlx.Terminal.Extensions; @@ -18,7 +16,7 @@ namespace OneImlx.Terminal.Apps.Test.Runners /// [CommandDescriptor("run", "Run Command", "Runs a native OS command.", CommandTypes.Native)] [ArgumentDescriptor(0, "cmd", nameof(String), "The full native command to execute, e.g., 'ls -all'", BehaviorFlags.Required)] - public class RunRunner : CommandRunner , IDeclarativeRunner + public class RunRunner : CommandRunner, IDeclarativeRunner { /// /// Initializes a new instance of the class. @@ -29,7 +27,7 @@ public RunRunner(ITerminalConsole terminalConsole) } /// - public override async Task RunCommandAsync(CommandContext context) + public override async Task RunCommandAsync(CustomCommandContext context) { var command = context.GetParsedCommand().Command; var osCommand = command.GetRequiredArgumentValue("cmd"); @@ -47,12 +45,10 @@ public override async Task RunCommandAsync(CommandContext c }; using var process = new Process { StartInfo = processStartInfo }; - process.Start(); var output = await process.StandardOutput.ReadToEndAsync(); var error = await process.StandardError.ReadToEndAsync(); - await process.WaitForExitAsync(); if (!string.IsNullOrWhiteSpace(error)) @@ -61,7 +57,7 @@ public override async Task RunCommandAsync(CommandContext c } await terminalConsole.WriteLineColorAsync(ConsoleColor.Green, $"OS command complete: {output}"); - return new CommandRunnerResult(output); + return new CustomRunnerResult(output); } private readonly ITerminalConsole terminalConsole; diff --git a/apps/cli/TestConsole/Runners/TestRunner.cs b/apps/cli/TestConsole/Runners/TestRunner.cs index 6aaaff21..c95e8488 100644 --- a/apps/cli/TestConsole/Runners/TestRunner.cs +++ b/apps/cli/TestConsole/Runners/TestRunner.cs @@ -1,9 +1,7 @@ -// Copyright © 2019-2026 Perpetual Intelligence L.L.C. All rights reserved. -// For license, terms, and data policies, go to: -// https://terms.perpetualintelligence.com/articles/intro.html -using System; +using System; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using OneImlx.Terminal.Apps.Test.Custom; using OneImlx.Terminal.Commands; using OneImlx.Terminal.Commands.Checkers; using OneImlx.Terminal.Commands.Runners; @@ -20,7 +18,7 @@ namespace OneImlx.Terminal.Apps.Test.Runners [CommandDescriptor("test", "Test App", "Test application description.", CommandTypes.Root)] [OptionDescriptor("version", nameof(String), "Test version description", BehaviorFlags.None, "v")] [CommandChecker(typeof(CommandChecker))] - public class TestRunner : CommandRunner , IDeclarativeRunner + public class TestRunner : CommandRunner, IDeclarativeRunner { private readonly ITerminalConsole terminalConsole; private readonly ILogger logger; @@ -31,7 +29,7 @@ public TestRunner(ITerminalConsole terminalConsole, ILogger logger) this.logger = logger; } - public override async Task RunCommandAsync(CommandContext context) + public override async Task RunCommandAsync(CustomCommandContext context) { await terminalConsole.WriteLineAsync("Test root command called."); @@ -41,7 +39,7 @@ public override async Task RunCommandAsync(CommandContext c await terminalConsole.WriteLineAsync("Version option passed."); } - return new CommandRunnerResult(); + return new CustomRunnerResult(); } } } \ No newline at end of file From 2ac71dd3991eed40c7f948440ae78a426d3750db Mon Sep 17 00:00:00 2001 From: "pi.admin" Date: Wed, 10 Jun 2026 17:08:55 -0700 Subject: [PATCH 7/9] minor cleanup --- apps/auth/TestMsal/Program.cs | 2 +- src/OneImlx.Terminal/Commands/Arguments.cs | 13 +++----- .../Commands/Checkers/CommandChecker.cs | 19 +++++------ .../Commands/Checkers/IValueChecker.cs | 11 +++---- .../Commands/CommandRouter.cs | 5 ++- .../Commands/Runners/CommandRunner.cs | 2 +- .../Licensing/LicenseChecker.cs | 2 +- .../Licensing/LicenseQuota.cs | 33 ++++++++++++------- .../Runtime/TerminalProcessor.cs | 8 ++--- .../Licensing/LicenseQuotaTests.cs | 29 +++++++++------- 10 files changed, 64 insertions(+), 60 deletions(-) diff --git a/apps/auth/TestMsal/Program.cs b/apps/auth/TestMsal/Program.cs index 69ad08e6..fb8b2df3 100644 --- a/apps/auth/TestMsal/Program.cs +++ b/apps/auth/TestMsal/Program.cs @@ -36,7 +36,7 @@ private static void ConfigureLoggingDelegate(HostBuilderContext context, ILoggin // Configure logging of your choice, here we are configuring Serilog var loggerConfig = new LoggerConfiguration(); - loggerConfig.MinimumLevel.Error(); + loggerConfig.MinimumLevel.Verbose(); loggerConfig.WriteTo.Console(); Log.Logger = loggerConfig.CreateLogger(); builder.AddSerilog(Log.Logger); diff --git a/src/OneImlx.Terminal/Commands/Arguments.cs b/src/OneImlx.Terminal/Commands/Arguments.cs index 7fadf243..2a8fe796 100644 --- a/src/OneImlx.Terminal/Commands/Arguments.cs +++ b/src/OneImlx.Terminal/Commands/Arguments.cs @@ -1,13 +1,10 @@ -/* - Copyright (c) 2023 Perpetual Intelligence L.L.C. All Rights Reserved. +// Copyright © 2019-2026 Perpetual Intelligence L.L.C. All rights reserved. +// For license, terms, and data policies, go to: +// https://terms.perpetualintelligence.com/articles/intro.html - For license, terms, and data policies, go to: - https://terms.perpetualintelligence.com/articles/intro.html -*/ - -using OneImlx.Terminal.Shared; using System.Collections; using System.Collections.Generic; +using OneImlx.Terminal.Shared; namespace OneImlx.Terminal.Commands { @@ -45,7 +42,6 @@ public Arguments(ITerminalTextHandler textHandler, IEnumerable argumen /// /// The argument identifier. /// - public Argument this[string id] { get @@ -59,7 +55,6 @@ public Argument this[string id] /// /// The argument index. /// - public Argument this[int index] { get diff --git a/src/OneImlx.Terminal/Commands/Checkers/CommandChecker.cs b/src/OneImlx.Terminal/Commands/Checkers/CommandChecker.cs index 40e9f0f3..cac3aafd 100644 --- a/src/OneImlx.Terminal/Commands/Checkers/CommandChecker.cs +++ b/src/OneImlx.Terminal/Commands/Checkers/CommandChecker.cs @@ -2,13 +2,13 @@ // For license, terms, and data policies, go to: // https://terms.perpetualintelligence.com/articles/intro.html +using System.Collections.Generic; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; using OneImlx.Terminal.Configuration.Options; using OneImlx.Terminal.Extensions; using OneImlx.Terminal.Shared; using OneImlx.Terminal.Shared.Extensions; -using System.Collections.Generic; -using System.Threading.Tasks; namespace OneImlx.Terminal.Commands.Checkers { @@ -35,20 +35,20 @@ public CommandChecker(IOptionChecker optionChecker, IArgumentChecker argumentChe /// public virtual async Task CheckCommandAsync(CommandContext context) { - Command command = context.GetCommand(); - logger.LogDebug("Check command. command={0}", command.Id); + var request = context.GetCommandRequest(); + var command = context.GetCommand(); + logger.LogDebug("Check request. request={0}", request.Id); - await CheckArgumentsAsync(context); + await CheckArgumentsAsync(command); - await CheckOptionsAsync(context); + await CheckOptionsAsync(command); return new CommandCheckerResult(); } - private async Task CheckArgumentsAsync(CommandContext context) + private async Task CheckArgumentsAsync(Command command) { // Cache commonly accessed properties - var command = context.GetCommand(); ArgumentDescriptors? argumentDescriptors = command.Descriptor.ArgumentDescriptors; // If the command itself does not support any arguments then there's nothing to check. Parser will reject @@ -92,10 +92,9 @@ private async Task CheckArgumentsAsync(CommandContext context) } } - private async Task CheckOptionsAsync(CommandContext context) + private async Task CheckOptionsAsync(Command command) { // Cache commonly accessed properties - var command = context.GetCommand(); OptionDescriptors? optionDescriptors = command.Descriptor.OptionDescriptors; // If the command itself does not support any options then there's nothing to check. Parser will reject any diff --git a/src/OneImlx.Terminal/Commands/Checkers/IValueChecker.cs b/src/OneImlx.Terminal/Commands/Checkers/IValueChecker.cs index d7794815..3c74b468 100644 --- a/src/OneImlx.Terminal/Commands/Checkers/IValueChecker.cs +++ b/src/OneImlx.Terminal/Commands/Checkers/IValueChecker.cs @@ -1,9 +1,6 @@ -/* - Copyright © 2019-2025 Perpetual Intelligence L.L.C. All rights reserved. - - For license, terms, and data policies, go to: - https://terms.perpetualintelligence.com/articles/intro.html -*/ +// Copyright © 2019-2026 Perpetual Intelligence L.L.C. All rights reserved. +// For license, terms, and data policies, go to: +// https://terms.perpetualintelligence.com/articles/intro.html using System; using System.Threading.Tasks; @@ -28,4 +25,4 @@ public interface IValueChecker where T : ICommandValue /// public Type GetRawType(); } -} +} \ No newline at end of file diff --git a/src/OneImlx.Terminal/Commands/CommandRouter.cs b/src/OneImlx.Terminal/Commands/CommandRouter.cs index 75aa82e1..1a646aa3 100644 --- a/src/OneImlx.Terminal/Commands/CommandRouter.cs +++ b/src/OneImlx.Terminal/Commands/CommandRouter.cs @@ -24,7 +24,6 @@ public sealed class CommandRouter : ICommandRouter /// Initializes a new instance. /// /// The configuration options. - /// The license extractor. /// The command parser. /// The command handler. /// The logger. @@ -52,7 +51,7 @@ public async Task RouteCommandAsync(CommandContext context) try { - logger.LogDebug("Start command router. type={0} request={1}", GetType().Name, requestId); + logger.LogDebug("Start route request. request={0}", requestId); // Issue a before request event if configured if (asyncEventHandler != null) @@ -78,7 +77,7 @@ public async Task RouteCommandAsync(CommandContext context) await asyncEventHandler.AfterCommandRouteAsync(request, parsedCommand?.Command, commandResult).ConfigureAwait(false); } - logger.LogDebug("End command router. request={0}", requestId); + logger.LogDebug("End route request. request={0}", requestId); } } diff --git a/src/OneImlx.Terminal/Commands/Runners/CommandRunner.cs b/src/OneImlx.Terminal/Commands/Runners/CommandRunner.cs index 60767e65..9fd38589 100644 --- a/src/OneImlx.Terminal/Commands/Runners/CommandRunner.cs +++ b/src/OneImlx.Terminal/Commands/Runners/CommandRunner.cs @@ -38,7 +38,7 @@ public async Task DelegateRunAsync(CommandContext context, this.logger = logger; Command command = context.GetCommand(); - logger?.LogDebug("Run command. command={0} type={1}", command.Id, command.Descriptor.Type); + logger?.LogDebug("Run command. id={0} type={1}", command.Id, command.Descriptor.Type); TContext typedContext = (TContext)context; TResult result; diff --git a/src/OneImlx.Terminal/Licensing/LicenseChecker.cs b/src/OneImlx.Terminal/Licensing/LicenseChecker.cs index afea2402..b33704f8 100644 --- a/src/OneImlx.Terminal/Licensing/LicenseChecker.cs +++ b/src/OneImlx.Terminal/Licensing/LicenseChecker.cs @@ -108,7 +108,7 @@ private Task CheckLimitsAsync(License license) private Task CheckOptionsAsync(License license) { - // Follow the pricing http://localhost:8080/articles/pi-cli/pricing.html We drive all customization through + // Follow the pricing http://localhost:8080/articles/pi-cli/pricing.html. We drive all customization through // options and the License sets the allowed options. So here we don't need to check the license plan, just // check the options value with license value. LicenseQuota quota = license.Quota; diff --git a/src/OneImlx.Terminal/Licensing/LicenseQuota.cs b/src/OneImlx.Terminal/Licensing/LicenseQuota.cs index 0b488134..f617ef97 100644 --- a/src/OneImlx.Terminal/Licensing/LicenseQuota.cs +++ b/src/OneImlx.Terminal/Licensing/LicenseQuota.cs @@ -1,14 +1,11 @@ -/* - Copyright © 2019-2025 Perpetual Intelligence L.L.C. All rights reserved. +// Copyright © 2019-2026 Perpetual Intelligence L.L.C. All rights reserved. +// For license, terms, and data policies, go to: +// https://terms.perpetualintelligence.com/articles/intro.html - For license, terms, and data policies, go to: - https://terms.perpetualintelligence.com/articles/intro.html -*/ - -using System; -using System.Collections.Generic; using OneImlx.Shared.Licensing; using OneImlx.Terminal.Shared; +using System; +using System.Collections.Generic; namespace OneImlx.Terminal.Licensing { @@ -115,6 +112,11 @@ public int? TerminalLimit } } + /// + /// The declarations. + /// + public IReadOnlyList Declarations => Features["declarations"]; + /// /// Creates a new instance of based on the specified SaaS plan. /// @@ -196,8 +198,9 @@ private static LicenseQuota ForCorporate() { "authentications", new[] { "msal", "oauth", "oidc", "none" } }, { "encodings", new [] { "ascii", "utf8", "utf16", "utf32" } }, { "stores", new [] { "memory", "custom" } }, - { "routers", new [] { "console", "tcp", "udp", "grpc", "http", "custom" } }, + { "routers", new [] { "console", "tcp", "udp", "grpc", "http", "pulsar", "custom" } }, { "deployments", new [] { "standard", "air_gapped" } }, + { "declarations", new [] {"standard", "custom" } } } }; } @@ -230,6 +233,7 @@ private static LicenseQuota ForCustom(IDictionary customClaims) { "stores", (string[]) customClaims["stores"] }, { "routers", (string[]) customClaims["routers"] }, { "deployments", (string[]) customClaims["deployments"] }, + { "declarations", (string[]) customClaims["declarations"] }, } }; } @@ -260,8 +264,9 @@ private static LicenseQuota ForDemo() { "authentications", new[] { "msal", "oauth", "oidc", "none" } }, { "encodings", new [] { "ascii", "utf8", "utf16", "utf32" } }, { "stores", new [] { "memory" } }, - { "routers", new [] { "console", "tcp", "udp", "grpc", "http" } }, + { "routers", new [] { "console", "tcp", "udp", "grpc", "http", "pulsar" } }, { "deployments", new [] { "standard" } }, + { "declarations", new [] {"standard" } } } }; } @@ -292,8 +297,9 @@ private static LicenseQuota ForEnterprise() { "authentications", new[] { "msal", "oauth", "oidc", "none" } }, { "encodings", new [] { "ascii", "utf8", "utf16", "utf32" } }, { "stores", new [] { "memory", "custom" } }, - { "routers", new [] { "console", "tcp", "udp", "grpc", "http", "custom" } }, + { "routers", new [] { "console", "tcp", "udp", "grpc", "http", "pulsar", "custom" } }, { "deployments", new [] { "standard", "air_gapped" } }, + { "declarations", new [] {"standard", "custom" } } } }; } @@ -326,6 +332,7 @@ private static LicenseQuota ForMicro() { "stores", new [] { "memory" } }, { "routers", new [] { "console" } }, { "deployments", new [] { "standard" } }, + { "declarations", new [] {"standard" } } } }; } @@ -358,6 +365,7 @@ private static LicenseQuota ForSmb() { "stores", new [] { "memory" } }, { "routers", new [] { "console", "tcp", "udp" } }, { "deployments", new [] { "standard" } }, + { "declarations", new [] {"standard" } } } }; } @@ -390,8 +398,9 @@ private static LicenseQuota ForSolo() { "stores", new [] { "memory" } }, { "routers", new [] { "console" } }, { "deployments", new [] { "standard" } }, + { "declarations", new [] {"standard" } } } }; } } -} +} \ No newline at end of file diff --git a/src/OneImlx.Terminal/Runtime/TerminalProcessor.cs b/src/OneImlx.Terminal/Runtime/TerminalProcessor.cs index 2575614c..fd1ace9e 100644 --- a/src/OneImlx.Terminal/Runtime/TerminalProcessor.cs +++ b/src/OneImlx.Terminal/Runtime/TerminalProcessor.cs @@ -127,7 +127,7 @@ public async Task ExecuteAsync(TerminalInputOutput terminalIO) throw new TerminalException(TerminalErrors.ServerError, "The terminal processor is not running."); } - await RouteRequestsAsync(terminalIO, terminalRouterContext).ConfigureAwait(false); + await ProcessRequestsAsync(terminalIO, terminalRouterContext).ConfigureAwait(false); } /// @@ -241,7 +241,7 @@ private void RegisterResponseHandler(Func handler) this.handler = handler ?? throw new TerminalException(TerminalErrors.InvalidRequest, "The response handler cannot be null."); } - private async Task RouteRequestsAsync(TerminalInputOutput terminalOutput, TerminalRouterContext terminalRouterContext) + private async Task ProcessRequestsAsync(TerminalInputOutput terminalOutput, TerminalRouterContext terminalRouterContext) { // Cache options for the loop to avoid repeated property access int maxLength = terminalOptions.Value.Router.MaxLength; @@ -276,7 +276,7 @@ private async Task RouteRequestsAsync(TerminalInputOutput terminalOutput, Termin throw new TerminalException(TerminalErrors.InvalidRequest, "The command length exceeds the maximum allowed. max={0}", maxLength); } - logger.LogDebug("Routing the command. raw={0} sender={1}", request.Raw, senderId); + logger.LogDebug("Process request. raw={0} sender={1}", request.Raw, senderId); CommandContext context = commandContextFactory.Create(request, terminalRouterContext, properties); var routeTask = commandRouter.RouteCommandAsync(context); if (await Task.WhenAny(routeTask, Task.Delay(timeout)).ConfigureAwait(false) == routeTask) @@ -336,7 +336,7 @@ private async Task StartRequestProcessingAsync(TerminalRouterContext terminalRou if (output != null) { // Request is processed and results are populated in the output - await RouteRequestsAsync(output, terminalRouterContext).ConfigureAwait(false); + await ProcessRequestsAsync(output, terminalRouterContext).ConfigureAwait(false); } else { diff --git a/test/OneImlx.Terminal.Tests/Licensing/LicenseQuotaTests.cs b/test/OneImlx.Terminal.Tests/Licensing/LicenseQuotaTests.cs index bf1e6b21..2abbb0d0 100644 --- a/test/OneImlx.Terminal.Tests/Licensing/LicenseQuotaTests.cs +++ b/test/OneImlx.Terminal.Tests/Licensing/LicenseQuotaTests.cs @@ -1,9 +1,6 @@ -/* - Copyright © 2019-2025 Perpetual Intelligence L.L.C. All rights reserved. - - For license, terms, and data policies, go to: - https://terms.perpetualintelligence.com/articles/intro.html -*/ +// Copyright © 2019-2026 Perpetual Intelligence L.L.C. All rights reserved. +// For license, terms, and data policies, go to: +// https://terms.perpetualintelligence.com/articles/intro.html using FluentAssertions; using OneImlx.Shared.Licensing; @@ -50,8 +47,9 @@ public void Corporate_Sets_Quota_Correctly() quota.Features["authentications"].Should().BeEquivalentTo(["msal", "oauth", "oidc", "none"]); quota.Features["encodings"].Should().BeEquivalentTo(["ascii", "utf8", "utf16", "utf32"]); quota.Features["stores"].Should().BeEquivalentTo(["memory", "custom"]); - quota.Features["routers"].Should().BeEquivalentTo(["console", "tcp", "udp", "grpc", "http", "custom"]); + quota.Features["routers"].Should().BeEquivalentTo(["console", "tcp", "udp", "grpc", "http", "pulsar", "custom"]); quota.Features["deployments"].Should().BeEquivalentTo(["standard", "air_gapped"]); + quota.Features["declarations"].Should().BeEquivalentTo(["standard", "custom"]); } [Fact] @@ -81,7 +79,8 @@ public void Custom_Sets_Quota_Correctly() { "encodings", new[] { "ascii", "utf8", "utf16", "utf32" } }, { "stores", new[] { "memory", "custom" } }, { "routers", new[] { "console", "tcp", "udp", "grpc", "http", "custom" } }, - { "deployments", new[] { "standard", "air_gapped" } } + { "deployments", new[] { "standard", "air_gapped" } }, + { "declarations", new[] { "standard", "custom" } } }; LicenseQuota quota = LicenseQuota.Create(ProductCatalog.TerminalPlanCustom, claims); @@ -101,6 +100,7 @@ public void Custom_Sets_Quota_Correctly() quota.Features["stores"].Should().BeEquivalentTo(["memory", "custom"]); quota.Features["routers"].Should().BeEquivalentTo(["console", "tcp", "udp", "grpc", "http", "custom"]); quota.Features["deployments"].Should().BeEquivalentTo(["standard", "air_gapped"]); + quota.Features["declarations"].Should().BeEquivalentTo(["standard", "custom"]); } [Fact] @@ -121,8 +121,9 @@ public void Demo_Sets_Quota_Correctly() quota.Features["authentications"].Should().BeEquivalentTo(["msal", "oauth", "oidc", "none"]); quota.Features["encodings"].Should().BeEquivalentTo(["ascii", "utf8", "utf16", "utf32"]); quota.Features["stores"].Should().BeEquivalentTo(["memory"]); - quota.Features["routers"].Should().BeEquivalentTo(["console", "tcp", "udp", "grpc", "http"]); + quota.Features["routers"].Should().BeEquivalentTo(["console", "tcp", "udp", "grpc", "http", "pulsar"]); quota.Features["deployments"].Should().BeEquivalentTo(["standard"]); + quota.Features["declarations"].Should().BeEquivalentTo(["standard"]); } [Fact] @@ -143,8 +144,9 @@ public void Enterprise_Sets_Limits() quota.Features["authentications"].Should().BeEquivalentTo(["msal", "oauth", "oidc", "none"]); quota.Features["encodings"].Should().BeEquivalentTo(["ascii", "utf8", "utf16", "utf32"]); quota.Features["stores"].Should().BeEquivalentTo(["memory", "custom"]); - quota.Features["routers"].Should().BeEquivalentTo(["console", "tcp", "udp", "grpc", "http", "custom"]); + quota.Features["routers"].Should().BeEquivalentTo(["console", "tcp", "udp", "grpc", "http", "pulsar", "custom"]); quota.Features["deployments"].Should().BeEquivalentTo(["standard", "air_gapped"]); + quota.Features["declarations"].Should().BeEquivalentTo(["standard", "custom"]); } [Fact] @@ -176,6 +178,7 @@ public void Micro_Sets_Quota_Correctly() quota.Features["stores"].Should().BeEquivalentTo(["memory"]); quota.Features["routers"].Should().BeEquivalentTo(["console"]); quota.Features["deployments"].Should().BeEquivalentTo(["standard"]); + quota.Features["declarations"].Should().BeEquivalentTo(["standard"]); } [Fact] @@ -196,7 +199,8 @@ public void Properties_Returns_Correctly() { "encodings", new[] { "ascii", "utf8", "utf16", "utf32" } }, { "stores", new[] { "memory", "custom" } }, { "routers", new[] { "console", "tcp", "udp", "grpc", "http", "custom" } }, - { "deployments", new[] { "standard", "air_gapped" } } + { "deployments", new[] { "standard", "air_gapped" } }, + { "declarations", new [] {"standard", "custom"} } }; LicenseQuota quota = LicenseQuota.Create(ProductCatalog.TerminalPlanCustom, claims); @@ -216,6 +220,7 @@ public void Properties_Returns_Correctly() quota.Stores.Should().BeEquivalentTo(["memory", "custom"]); quota.Routers.Should().BeEquivalentTo(["console", "tcp", "udp", "grpc", "http", "custom"]); quota.Deployments.Should().BeEquivalentTo(["standard", "air_gapped"]); + quota.Declarations.Should().BeEquivalentTo(["standard", "custom"]); } [Fact] @@ -262,4 +267,4 @@ public void Solo_Sets_Quota_Correctly() quota.Features["deployments"].Should().BeEquivalentTo(["standard"]); } } -} +} \ No newline at end of file From e66dd5a3eeceab418b8c026c8d1c20d88ccda0bc Mon Sep 17 00:00:00 2001 From: "pi.admin" Date: Wed, 10 Jun 2026 17:11:03 -0700 Subject: [PATCH 8/9] Minor log --- src/OneImlx.Terminal/Commands/Parsers/CommandParser.cs | 2 +- src/OneImlx.Terminal/Runtime/TerminalProcessor.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OneImlx.Terminal/Commands/Parsers/CommandParser.cs b/src/OneImlx.Terminal/Commands/Parsers/CommandParser.cs index 2bdc97a7..da075c34 100644 --- a/src/OneImlx.Terminal/Commands/Parsers/CommandParser.cs +++ b/src/OneImlx.Terminal/Commands/Parsers/CommandParser.cs @@ -42,7 +42,7 @@ public CommandParser(ITerminalRequestParser terminalRequestParser, ITerminalText public async Task ParseCommandAsync(CommandContext context) { CommandRequest commandRequest = context.GetCommandRequest(); - logger.LogDebug("Parse request. request={0} raw={1}", commandRequest.Id, commandRequest.Raw); + logger.LogDebug("Parse request. request={0}", commandRequest.Id); TerminalParsedRequest parsedOutput = await terminalRequestParser.ParseRequestAsync(commandRequest).ConfigureAwait(false); context.SetParsedCommand(await MapParsedRequestAsync(commandRequest, parsedOutput).ConfigureAwait(false)); } diff --git a/src/OneImlx.Terminal/Runtime/TerminalProcessor.cs b/src/OneImlx.Terminal/Runtime/TerminalProcessor.cs index fd1ace9e..941c7e2a 100644 --- a/src/OneImlx.Terminal/Runtime/TerminalProcessor.cs +++ b/src/OneImlx.Terminal/Runtime/TerminalProcessor.cs @@ -276,7 +276,7 @@ private async Task ProcessRequestsAsync(TerminalInputOutput terminalOutput, Term throw new TerminalException(TerminalErrors.InvalidRequest, "The command length exceeds the maximum allowed. max={0}", maxLength); } - logger.LogDebug("Process request. raw={0} sender={1}", request.Raw, senderId); + logger.LogDebug("Process request. raw={0} request={1} sender={2}", request.Raw, request.Id, senderId); CommandContext context = commandContextFactory.Create(request, terminalRouterContext, properties); var routeTask = commandRouter.RouteCommandAsync(context); if (await Task.WhenAny(routeTask, Task.Delay(timeout)).ConfigureAwait(false) == routeTask) From ad0fd014f5ffd91d4f9162e54e73cdf2051a8371 Mon Sep 17 00:00:00 2001 From: "pi.admin" Date: Wed, 10 Jun 2026 21:12:15 -0700 Subject: [PATCH 9/9] remove empty --- apps/s2s/TestClient/Runners/ClsRunner.cs | 2 +- apps/s2s/TestClient/Runners/ExitRunner.cs | 2 +- apps/s2s/TestClient/Runners/HelpRunner.cs | 2 +- .../Commands/Runners/CommandRunnerResult.cs | 11 +---------- .../Commands/Runners/CommandRunnerResultTests.cs | 4 ++-- 5 files changed, 6 insertions(+), 15 deletions(-) diff --git a/apps/s2s/TestClient/Runners/ClsRunner.cs b/apps/s2s/TestClient/Runners/ClsRunner.cs index d7ab6586..81d9ec8f 100644 --- a/apps/s2s/TestClient/Runners/ClsRunner.cs +++ b/apps/s2s/TestClient/Runners/ClsRunner.cs @@ -22,7 +22,7 @@ public class ClsRunner(ITerminalConsole terminalConsole) : CommandRunner RunCommandAsync(CommandContext context) { await terminalConsole.ClearAsync(); - return CommandRunnerResult.Empty(); + return new CommandRunnerResult(); } private readonly ITerminalConsole terminalConsole = terminalConsole; diff --git a/apps/s2s/TestClient/Runners/ExitRunner.cs b/apps/s2s/TestClient/Runners/ExitRunner.cs index 6c32ab80..7ba87685 100644 --- a/apps/s2s/TestClient/Runners/ExitRunner.cs +++ b/apps/s2s/TestClient/Runners/ExitRunner.cs @@ -33,7 +33,7 @@ public override async Task RunCommandAsync(CommandContext c await Task.Delay(2000); } - return CommandRunnerResult.Empty(); + return new CommandRunnerResult(); } private readonly IHostApplicationLifetime _applicationLifetime; diff --git a/apps/s2s/TestClient/Runners/HelpRunner.cs b/apps/s2s/TestClient/Runners/HelpRunner.cs index a2f099cd..0d5d0a83 100644 --- a/apps/s2s/TestClient/Runners/HelpRunner.cs +++ b/apps/s2s/TestClient/Runners/HelpRunner.cs @@ -32,7 +32,7 @@ public override async Task RunCommandAsync(CommandContext c { await _console.WriteLineAsync($"{command.Key} ({command.Value.Name}) --> {command.Value.Description}"); } - return CommandRunnerResult.Empty(); + return new CommandRunnerResult(); } private readonly ITerminalConsole _console; diff --git a/src/OneImlx.Terminal/Commands/Runners/CommandRunnerResult.cs b/src/OneImlx.Terminal/Commands/Runners/CommandRunnerResult.cs index acc888af..851b4e09 100644 --- a/src/OneImlx.Terminal/Commands/Runners/CommandRunnerResult.cs +++ b/src/OneImlx.Terminal/Commands/Runners/CommandRunnerResult.cs @@ -2,8 +2,8 @@ // For license, terms, and data policies, go to: // https://terms.perpetualintelligence.com/articles/intro.html -using OneImlx.Terminal.Shared; using System; +using OneImlx.Terminal.Shared; namespace OneImlx.Terminal.Commands.Runners { @@ -48,15 +48,6 @@ public object Value } } - /// - /// Creates an empty with no value. - /// - /// A new instance of . - public static CommandRunnerResult Empty() - { - return new CommandRunnerResult(); - } - /// /// Gets the value as the specified type. /// diff --git a/test/OneImlx.Terminal.Tests/Commands/Runners/CommandRunnerResultTests.cs b/test/OneImlx.Terminal.Tests/Commands/Runners/CommandRunnerResultTests.cs index fc35d231..61f80325 100644 --- a/test/OneImlx.Terminal.Tests/Commands/Runners/CommandRunnerResultTests.cs +++ b/test/OneImlx.Terminal.Tests/Commands/Runners/CommandRunnerResultTests.cs @@ -66,8 +66,8 @@ public void Constructor_ShouldThrowArgumentNullException_WhenValueIsNull() [Fact] public async Task Empty_Returns_New_Instance() { - var result1 = CommandRunnerResult.Empty(); - var result2 = CommandRunnerResult.Empty(); + var result1 = new CommandRunnerResult(); + var result2 = new CommandRunnerResult(); result1.Should().NotBeSameAs(result2); }