From 4d2853315ff9efe00f9a295c057576929e13fc00 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 30 Sep 2026 03:24:51 +0000 Subject: [PATCH] Return uncategorized commands for a null category in GetCommandsByCategory [patch] A null CommandCategory converts to "" rather than null, so comparing it with string.Equals made GetCommandsByCategory(null) match nothing while "" and " " returned the uncategorized commands. Null, empty and whitespace now all select the uncategorized commands. Fixes #140 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019UNSUUrn4o84JfkEVaD3xg --- Keybinding.Test/CommandCategoryFilterTests.cs | 56 +++++++++++++++++++ Keybinding/Contracts/ICommandRegistry.cs | 2 +- Keybinding/Services/CommandRegistry.cs | 8 ++- 3 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 Keybinding.Test/CommandCategoryFilterTests.cs diff --git a/Keybinding.Test/CommandCategoryFilterTests.cs b/Keybinding.Test/CommandCategoryFilterTests.cs new file mode 100644 index 0000000..5f767b1 --- /dev/null +++ b/Keybinding.Test/CommandCategoryFilterTests.cs @@ -0,0 +1,56 @@ +// Copyright (c) 2023-2026 ktsu-dev contributors + +namespace ktsu.Keybinding.Test; + +using ktsu.Keybinding.Core.Models; +using ktsu.Keybinding.Core.Services; + +[TestClass] +public class CommandCategoryFilterTests +{ + private static readonly string[] UncategorizedIds = ["file.save"]; + private static readonly string[] EditIds = ["edit.copy"]; + + private CommandRegistry _registry = null!; + + [TestInitialize] + public void Setup() + { + _registry = new CommandRegistry(); + _registry.RegisterCommand(new Command("file.save", "Save")); + _registry.RegisterCommand(new Command("edit.copy", "Copy", null, "Edit")); + } + + [TestMethod] + [DataRow(null)] + [DataRow("")] + [DataRow(" ")] + public void GetCommandsByCategory_BlankCategory_ReturnsOnlyUncategorizedCommands(string? category) + { + string[] ids = [.. _registry.GetCommandsByCategory(category).Select(c => (string)c.Id)]; + + CollectionAssert.AreEquivalent(UncategorizedIds, ids); + } + + [TestMethod] + [DataRow("Edit")] + [DataRow("edit")] + [DataRow(" Edit ")] + public void GetCommandsByCategory_NamedCategory_ReturnsOnlyThatCategory(string category) + { + string[] ids = [.. _registry.GetCommandsByCategory(category).Select(c => (string)c.Id)]; + + CollectionAssert.AreEquivalent(EditIds, ids); + } + + [TestMethod] + public void GetCommandsByCategory_PassingACommandsOwnCategoryBack_FindsThatCommand() + { + foreach (Command command in _registry.GetAllCommands()) + { + Assert.IsTrue( + _registry.GetCommandsByCategory(command.Category).Contains(command), + $"{command.Id} should be in the group for its own category."); + } + } +} diff --git a/Keybinding/Contracts/ICommandRegistry.cs b/Keybinding/Contracts/ICommandRegistry.cs index 1ed012f..04f3545 100644 --- a/Keybinding/Contracts/ICommandRegistry.cs +++ b/Keybinding/Contracts/ICommandRegistry.cs @@ -39,7 +39,7 @@ public interface ICommandRegistry /// /// Gets commands by category /// - /// The category to filter by + /// The category to filter by, compared case-insensitively. Null, empty or whitespace selects the uncategorized commands. /// Collection of commands in the specified category public IReadOnlyCollection GetCommandsByCategory(string? category); diff --git a/Keybinding/Services/CommandRegistry.cs b/Keybinding/Services/CommandRegistry.cs index 324b84a..34dce10 100644 --- a/Keybinding/Services/CommandRegistry.cs +++ b/Keybinding/Services/CommandRegistry.cs @@ -57,9 +57,13 @@ public IReadOnlyCollection GetCommandsByCategory(string? category) { lock (_lock) { - string? normalizedCategory = category?.Trim(); + // Compare explicitly: a null CommandCategory converts to "" rather than null, so passing + // it straight to string.Equals made null find nothing while "" found the uncategorized commands. + string? wanted = string.IsNullOrWhiteSpace(category) ? null : category.Trim(); return _commands.Values - .Where(c => string.Equals(c.Category, normalizedCategory, StringComparison.OrdinalIgnoreCase)) + .Where(c => wanted is null + ? string.IsNullOrWhiteSpace(c.Category) + : string.Equals(c.Category?.ToString(), wanted, StringComparison.OrdinalIgnoreCase)) .ToList() .AsReadOnly(); }