diff --git a/Keybinding.Test/CommandIdWhitespaceTests.cs b/Keybinding.Test/CommandIdWhitespaceTests.cs new file mode 100644 index 0000000..8b59204 --- /dev/null +++ b/Keybinding.Test/CommandIdWhitespaceTests.cs @@ -0,0 +1,51 @@ +// Copyright (c) 2023-2026 ktsu-dev contributors + +namespace ktsu.Keybinding.Test; + +using ktsu.Keybinding.Core.Models; +using ktsu.Keybinding.Core.Services; + +[TestClass] +public class CommandIdWhitespaceTests +{ + private static Command CreatePaddedCommand() => + new(CommandId.Create(" file.save "), CommandName.Create("Save")); + + [TestMethod] + public void Constructor_CommandIdWithWhitespace_TrimsId() + { + Assert.AreEqual("file.save", CreatePaddedCommand().Id.ToString()); + } + + [TestMethod] + public void Constructor_CommandIdWithWhitespace_EqualsStringConstructedCommand() + { + Assert.AreEqual(new Command(" file.save ", "Save"), CreatePaddedCommand()); + } + + [TestMethod] + public void Constructor_WhitespaceOnlyCommandId_Throws() + { + Assert.ThrowsExactly(() => new Command(CommandId.Create(" "), CommandName.Create("Save"))); + } + + [TestMethod] + public void Register_CommandIdWithWhitespace_CanBeFoundBoundAndUnregistered() + { + CommandRegistry registry = new(); + Assert.IsTrue(registry.RegisterCommand(CreatePaddedCommand())); + + Assert.IsTrue(registry.IsCommandRegistered("file.save")); + Assert.IsTrue(registry.IsCommandRegistered(" file.save ")); + Assert.IsNotNull(registry.GetCommand("file.save")); + + ProfileManager profiles = new(); + profiles.CreateProfile("p", "Profile"); + profiles.SetActiveProfile("p"); + KeybindingService service = new(registry, profiles); + Assert.IsTrue(service.BindChord(" file.save ", Chord.Parse("Ctrl+S"))); + + Assert.IsTrue(registry.UnregisterCommand(" file.save ")); + Assert.IsFalse(registry.IsCommandRegistered("file.save")); + } +} diff --git a/Keybinding/Models/Command.cs b/Keybinding/Models/Command.cs index 2ed0653..8a9d851 100644 --- a/Keybinding/Models/Command.cs +++ b/Keybinding/Models/Command.cs @@ -20,7 +20,16 @@ public Command(CommandId id, CommandName name, CommandDescription? description = Ensure.NotNull(id); Ensure.NotNull(name); - Id = id; + // Trim as the string constructor does: the registry's lookups trim the id they are given, so an + // untrimmed id would be stored under a key no lookup can produce. + string rawId = id.ToString(); + string trimmedId = rawId.Trim(); + if (trimmedId.Length == 0) + { + throw new ArgumentException("Command ID cannot be null or whitespace", nameof(id)); + } + + Id = trimmedId == rawId ? id : CommandId.Create(trimmedId); Name = name; Description = description; Category = category;