diff --git a/PowerToys.slnx b/PowerToys.slnx
index f6ceadcb65..185d0fac24 100644
--- a/PowerToys.slnx
+++ b/PowerToys.slnx
@@ -196,6 +196,10 @@
+
+
+
+
@@ -295,6 +299,10 @@
+
+
+
+
diff --git a/src/modules/cmdpal/CommandPalette.slnf b/src/modules/cmdpal/CommandPalette.slnf
index fc0288cc73..6ac089f0b4 100644
--- a/src/modules/cmdpal/CommandPalette.slnf
+++ b/src/modules/cmdpal/CommandPalette.slnf
@@ -15,6 +15,7 @@
"src\\modules\\cmdpal\\Microsoft.CmdPal.UI.ViewModels\\Microsoft.CmdPal.UI.ViewModels.csproj",
"src\\modules\\cmdpal\\Microsoft.CmdPal.UI\\Microsoft.CmdPal.UI.csproj",
"src\\modules\\cmdpal\\Microsoft.Terminal.UI\\Microsoft.Terminal.UI.vcxproj",
+ "src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Common.UnitTests\\Microsoft.CmdPal.Common.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Apps.UnitTests\\Microsoft.CmdPal.Ext.Apps.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Bookmarks.UnitTests\\Microsoft.CmdPal.Ext.Bookmarks.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Calc.UnitTests\\Microsoft.CmdPal.Ext.Calc.UnitTests.csproj",
diff --git a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/GlobalUsings.cs b/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/GlobalUsings.cs
deleted file mode 100644
index 022cf98f31..0000000000
--- a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/GlobalUsings.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright (c) Microsoft Corporation
-// The Microsoft Corporation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-global using System;
-global using System.Collections.Generic;
-global using System.Diagnostics.CodeAnalysis;
-global using System.Linq;
-global using Microsoft.VisualStudio.TestTools.UnitTesting;
diff --git a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Microsoft.CmdPal.Common.UnitTests.csproj b/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Microsoft.CmdPal.Common.UnitTests.csproj
deleted file mode 100644
index b85df53da4..0000000000
--- a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Microsoft.CmdPal.Common.UnitTests.csproj
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
- false
- true
- Microsoft.CmdPal.Common.UnitTests
- $(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal\tests\
- false
- false
- enable
- preview
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Services/Sanitizer/ConnectionStringRuleProviderTests.cs b/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Services/Sanitizer/ConnectionStringRuleProviderTests.cs
deleted file mode 100644
index 1c9615e950..0000000000
--- a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Services/Sanitizer/ConnectionStringRuleProviderTests.cs
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright (c) Microsoft Corporation
-// The Microsoft Corporation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using Microsoft.CmdPal.Common.Services.Sanitizer;
-using Microsoft.CmdPal.Common.Services.Sanitizer.Abstraction;
-using Microsoft.CmdPal.Common.UnitTests.TestUtils;
-
-namespace Microsoft.CmdPal.Common.UnitTests.Services.Sanitizer;
-
-[TestClass]
-public class ConnectionStringRuleProviderTests
-{
- [TestMethod]
- public void GetRules_ShouldReturnExpectedRules()
- {
- // Arrange
- var provider = new ConnectionStringRuleProvider();
-
- // Act
- var rules = provider.GetRules();
-
- // Assert
- var ruleList = new List(rules);
- Assert.AreEqual(1, ruleList.Count);
- Assert.AreEqual("Connection string parameters", ruleList[0].Description);
- }
-
- [DataTestMethod]
- [DataRow("Server=localhost;Database=mydb;User ID=admin;Password=secret123", "Server=[REDACTED];Database=[REDACTED];User ID=[REDACTED];Password=[REDACTED]")]
- [DataRow("Data Source=server.example.com;Initial Catalog=testdb;Uid=user;Pwd=pass", "Data Source=[REDACTED];Initial Catalog=[REDACTED];Uid=[REDACTED];Pwd=[REDACTED]")]
- [DataRow("Server=localhost;Password=my_secret", "Server=[REDACTED];Password=[REDACTED]")]
- [DataRow("No connection string here", "No connection string here")]
- public void ConnectionStringRules_ShouldMaskConnectionStringParameters(string input, string expected)
- {
- // Arrange
- var provider = new ConnectionStringRuleProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-
- [DataTestMethod]
- [DataRow("Password=\"complexPassword123!\"", "Password=[REDACTED]")]
- [DataRow("Password='myPassword'", "Password=[REDACTED]")]
- [DataRow("Password=unquotedSecret", "Password=[REDACTED]")]
- public void ConnectionStringRules_ShouldHandleQuotedAndUnquotedValues(string input, string expected)
- {
- // Arrange
- var provider = new ConnectionStringRuleProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-
- [DataTestMethod]
- [DataRow("SERVER=server1;PASSWORD=pass1", "SERVER=[REDACTED];PASSWORD=[REDACTED]")]
- [DataRow("server=server1;password=pass1", "server=[REDACTED];password=[REDACTED]")]
- [DataRow("Server=server1;Password=pass1", "Server=[REDACTED];Password=[REDACTED]")]
- public void ConnectionStringRules_ShouldBeCaseInsensitive(string input, string expected)
- {
- // Arrange
- var provider = new ConnectionStringRuleProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-
- [DataTestMethod]
- [DataRow("User ID=admin;Username=john;Password=secret", "User ID=[REDACTED];Username=[REDACTED];Password=[REDACTED]")]
- [DataRow("Database=mydb;Uid=user1;Pwd=pass1;Server=localhost", "Database=[REDACTED];Uid=[REDACTED];Pwd=[REDACTED];Server=[REDACTED]")]
- public void ConnectionStringRules_ShouldHandleMultipleParameters(string input, string expected)
- {
- // Arrange
- var provider = new ConnectionStringRuleProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-
- [DataTestMethod]
- [DataRow("Server = localhost ; Password = secret123", "Server=[REDACTED] ; Password=[REDACTED]")]
- [DataRow("Initial Catalog=db; User ID=admin; Password=pass", "Initial Catalog=[REDACTED]; User ID=[REDACTED]; Password=[REDACTED]")]
- public void ConnectionStringRules_ShouldHandleWhitespace(string input, string expected)
- {
- // Arrange
- var provider = new ConnectionStringRuleProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-}
diff --git a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Services/Sanitizer/ErrorReportSanitizerTests.TestData.cs b/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Services/Sanitizer/ErrorReportSanitizerTests.TestData.cs
deleted file mode 100644
index 54c7ba92d7..0000000000
--- a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Services/Sanitizer/ErrorReportSanitizerTests.TestData.cs
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright (c) Microsoft Corporation
-// The Microsoft Corporation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-namespace Microsoft.CmdPal.Common.UnitTests.Services.Sanitizer;
-
-public partial class ErrorReportSanitizerTests
-{
- internal static class TestData
- {
- internal static string Input =>
- $"""
- HRESULT: 0x80004005
- HRESULT: -2147467259
-
- Here is e-mail address
- IPv4 address: 192.168.100.1
- IPv4 loopback address: 127.0.0.1
- MAC address: 00-14-22-01-23-45
- IPv6 address: 2001:0db8:85a3:0000:0000:8a2e:0370:7334
- IPv6 loopback address: ::1
- Password: P@ssw0rd123!
- Password=secret
- Api key: 1234567890abcdef
- PostgreSQL connection string: Host=localhost;Username=postgres;Password=secret;Database=mydb
- InstrumentationKey=00000000-0000-0000-0000-000000000000;EndpointSuffix=ai.contoso.com;
- X-API-key: 1234567890abcdef
- Pet-Shop-Subscription-Key: 1234567890abcdef
- Here is a user name {Environment.UserName}
- And here is a profile path {Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}\RandomFolder
- Here is a local app data path {Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\Microsoft\PowerToys\CmdPal
- Here is machine name {Environment.MachineName}
- JWT token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30
- User email john.doe@company.com failed validation
- File not found: {Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)}\\secret.txt
- Connection string: Server=localhost;User ID=admin;Password=secret123;Database=test
- Phone number 555-123-4567 is invalid
- API key abc123def456ghi789jkl012mno345pqr678 expired
- Failed to connect to https://api.internal-company.com/users/12345?token=secret_abc123
- Error accessing file://C:/Users/john.doe/Documents/confidential.pdf
- JDBC connection failed: jdbc://database-server:5432/userdb?user=admin&password=secret
- FTP upload error: ftp://internal-server.company.com/uploads/user_data.csv
- Email service error: mailto:admin@internal-company.com?subject=Alert
- """;
-
- public const string Expected =
- $"""
- HRESULT: 0x80004005
- HRESULT: -2147467259
-
- Here is e-mail address <[EMAIL_REDACTED]>
- IPv4 address: 192.168.100.1
- IPv4 loopback address: 127.0.0.1
- MAC address: [MAC_ADDRESS_REDACTED]
- IPv6 address: [IP6_REDACTED]
- IPv6 loopback address: [IP6_REDACTED]
- Password: [REDACTED]
- Password= [REDACTED]
- Api key: [REDACTED]
- PostgreSQL connection string: [REDACTED]
- InstrumentationKey= [REDACTED]
- X-API-key: [REDACTED]
- Pet-Shop-Subscription-Key: [REDACTED]
- Here is a user name [USERNAME_REDACTED]
- And here is a profile path [USER_PROFILE_DIR]RandomFolder
- Here is a local app data path [LOCALAPPLICATIONDATA_DIR]Microsoft\PowerToys\CmdPal
- Here is machine name [MACHINE_NAME_REDACTED]
- JWT token: [REDACTED]
- User email [EMAIL_REDACTED] failed validation
- File not found: [MYDOCUMENTS_DIR]se****.txt
- Connection string: [REDACTED] ID=[REDACTED];Password= [REDACTED]
- Phone number [PHONE_REDACTED] is invalid
- API key [TOKEN_REDACTED] expired
- Failed to connect to [URL_REDACTED]
- Error accessing [URL_REDACTED]
- JDBC connection failed: [URL_REDACTED]
- FTP upload error: [URL_REDACTED]
- Email service error: mailto:[EMAIL_REDACTED]?subject=Alert
- """;
-
- internal static string Input2 =>
- $"""
- ============================================================
- Hello World! Command Palette is starting.
-
- Application:
- App version: 0.0.1.0
- Packaging flavor: Packaged
- Is elevated: no
-
- Environment:
- OS version: Microsoft Windows 10.0.26220
- OS architecture: X64
- Runtime identifier: win-x64
- Framework: .NET 9.0.13
- Process architecture: X64
- Culture: cs-CZ
- UI culture: en-US
-
- Paths:
- Log directory: {Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\Microsoft\PowerToys\CmdPal\Logs\0.0.1.0
- Config directory: {Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\Packages\Microsoft.CommandPalette.Dev_8wekyb3d8bbwe\LocalState
- ============================================================
- """;
-
- public const string Expected2 =
- """
- ============================================================
- Hello World! Command Palette is starting.
-
- Application:
- App version: 0.0.1.0
- Packaging flavor: Packaged
- Is elevated: no
-
- Environment:
- OS version: Microsoft Windows 10.0.26220
- OS architecture: X64
- Runtime identifier: win-x64
- Framework: .NET 9.0.13
- Process architecture: X64
- Culture: cs-CZ
- UI culture: en-US
-
- Paths:
- Log directory: [LOCALAPPLICATIONDATA_DIR]Microsoft\PowerToys\CmdPal\Logs\0.0.1.0
- Config directory: [LOCALAPPLICATIONDATA_DIR]Packages\Microsoft.CommandPalette.Dev_8wekyb3d8bbwe\LocalState
- ============================================================
- """;
- }
-}
diff --git a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Services/Sanitizer/ErrorReportSanitizerTests.cs b/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Services/Sanitizer/ErrorReportSanitizerTests.cs
deleted file mode 100644
index 4df8220965..0000000000
--- a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Services/Sanitizer/ErrorReportSanitizerTests.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) Microsoft Corporation
-// The Microsoft Corporation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using Microsoft.CmdPal.Common.Services.Sanitizer;
-
-namespace Microsoft.CmdPal.Common.UnitTests.Services.Sanitizer;
-
-[TestClass]
-public partial class ErrorReportSanitizerTests
-{
- [TestMethod]
- public void Sanitize_ShouldMaskPiiInErrorReport()
- {
- // Arrange
- var reportSanitizer = new ErrorReportSanitizer();
- var input = TestData.Input;
-
- // Act
- var result = reportSanitizer.Sanitize(input);
-
- // Assert
- Assert.AreEqual(TestData.Expected, result);
- }
-
- [TestMethod]
- public void Sanitize_ShouldNotMaskTooMuchPiiInErrorReport()
- {
- // Arrange
- var reportSanitizer = new ErrorReportSanitizer();
- var input = TestData.Input2;
-
- // Act
- var result = reportSanitizer.Sanitize(input);
-
- // Assert
- Assert.AreEqual(TestData.Expected2, result);
- }
-}
diff --git a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Services/Sanitizer/FilenameMaskRuleProviderTests.cs b/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Services/Sanitizer/FilenameMaskRuleProviderTests.cs
deleted file mode 100644
index 9d5abcb444..0000000000
--- a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Services/Sanitizer/FilenameMaskRuleProviderTests.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) Microsoft Corporation
-// The Microsoft Corporation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using Microsoft.CmdPal.Common.Services.Sanitizer;
-using Microsoft.CmdPal.Common.Services.Sanitizer.Abstraction;
-using Microsoft.CmdPal.Common.UnitTests.TestUtils;
-
-namespace Microsoft.CmdPal.Common.UnitTests.Services.Sanitizer;
-
-[TestClass]
-public class FilenameMaskRuleProviderTests
-{
- [TestMethod]
- public void GetRules_ShouldReturnExpectedRules()
- {
- // Arrange
- var provider = new FilenameMaskRuleProvider();
-
- // Act
- var rules = provider.GetRules();
-
- // Assert
- var ruleList = new List(rules);
- Assert.AreEqual(1, ruleList.Count);
- Assert.AreEqual("Mask filename in any path", ruleList[0].Description);
- }
-
- [DataTestMethod]
- [DataRow(@"C:\Users\Alice\Documents\secret.txt", @"C:\Users\Alice\Documents\se****.txt")]
- [DataRow(@"logs\error-report.log", @"logs\er**********.log")]
- [DataRow(@"/var/logs/trace.json", @"/var/logs/tr***.json")]
- public void FilenameRules_ShouldMaskFileNamesInPaths(string input, string expected)
- {
- // Arrange
- var provider = new FilenameMaskRuleProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-
- [DataTestMethod]
- [DataRow("C:\\Users\\Alice\\Documents\\", "C:\\Users\\Alice\\Documents\\")]
- [DataRow(@"C:\Users\Alice\PowerToys\CmdPal\Logs\1.2.3.4", @"C:\Users\Alice\PowerToys\CmdPal\Logs\1.2.3.4")]
- [DataRow(@"C:\Users\Alice\appsettings.json", @"C:\Users\Alice\appsettings.json")]
- [DataRow(@"C:\Users\Alice\.env", @"C:\Users\Alice\.env")]
- [DataRow(@"logs\readme", @"logs\readme")]
- public void FilenameRules_ShouldNotMaskNonSensitivePatterns(string input, string expected)
- {
- // Arrange
- var provider = new FilenameMaskRuleProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-}
diff --git a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Services/Sanitizer/PiiRuleProviderTests.cs b/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Services/Sanitizer/PiiRuleProviderTests.cs
deleted file mode 100644
index 89ce30f881..0000000000
--- a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Services/Sanitizer/PiiRuleProviderTests.cs
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright (c) Microsoft Corporation
-// The Microsoft Corporation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using Microsoft.CmdPal.Common.Services.Sanitizer;
-using Microsoft.CmdPal.Common.Services.Sanitizer.Abstraction;
-using Microsoft.CmdPal.Common.UnitTests.TestUtils;
-
-namespace Microsoft.CmdPal.Common.UnitTests.Services.Sanitizer;
-
-[TestClass]
-public class PiiRuleProviderTests
-{
- [TestMethod]
- public void GetRules_ShouldReturnExpectedRules()
- {
- // Arrange
- var provider = new PiiRuleProvider();
-
- // Act
- var rules = provider.GetRules();
-
- // Assert
- var ruleList = new List(rules);
- Assert.AreEqual(4, ruleList.Count);
- Assert.AreEqual("Email addresses", ruleList[0].Description);
- Assert.AreEqual("Social Security Numbers", ruleList[1].Description);
- Assert.AreEqual("Credit card numbers", ruleList[2].Description);
- Assert.AreEqual("Phone numbers", ruleList[3].Description);
- }
-
- [DataTestMethod]
- [DataRow("Contact me at john.doe@contoso.com", "Contact me at [EMAIL_REDACTED]")]
- [DataRow("Contact me at a_b-c%2@foo-bar.example.co.uk", "Contact me at [EMAIL_REDACTED]")]
- [DataRow("My email is john@sub-domain.contoso.com.", "My email is [EMAIL_REDACTED].")]
- [DataRow("Two: a@b.com and c@d.org", "Two: [EMAIL_REDACTED] and [EMAIL_REDACTED]")]
- [DataRow("No email here", "No email here")]
- public void EmailRules_ShouldMaskEmailAddresses(string input, string expected)
- {
- // Arrange
- var provider = new PiiRuleProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-
- [DataTestMethod]
- [DataRow("Call me at 123-456-7890", "Call me at [PHONE_REDACTED]")]
- [DataRow("My number is (123) 456-7890.", "My number is [PHONE_REDACTED].")]
- [DataRow("Office: +1 123 456 7890", "Office: [PHONE_REDACTED]")]
- [DataRow("Two numbers: 123-456-7890 and +420 777123456", "Two numbers: [PHONE_REDACTED] and [PHONE_REDACTED]")]
- [DataRow("Czech phone +420 777 123 456", "Czech phone [PHONE_REDACTED]")]
- [DataRow("Slovak phone +421 777 12 34 56", "Slovak phone [PHONE_REDACTED]")]
- [DataRow("Version 1.2.3.4", "Version 1.2.3.4")]
- [DataRow("OS version: Microsoft Windows 10.0.26220", "OS version: Microsoft Windows 10.0.26220")]
- [DataRow("No phone number here", "No phone number here")]
- public void PhoneRules_ShouldMaskPhoneNumbers(string input, string expected)
- {
- // Arrange
- var provider = new PiiRuleProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-
- [DataTestMethod]
- [DataRow("My SSN is 123-45-6789", "My SSN is [SSN_REDACTED]")]
- [DataRow("No SSN here", "No SSN here")]
- public void SsnRules_ShouldMaskSsn(string input, string expected)
- {
- // Arrange
- var provider = new PiiRuleProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-
- [DataTestMethod]
- [DataRow("My credit card number is 1234-5678-9012-3456", "My credit card number is [CARD_REDACTED]")]
- [DataRow("My credit card number is 1234567890123456", "My credit card number is [CARD_REDACTED]")]
- [DataRow("No credit card here", "No credit card here")]
- public void CreditCardRules_ShouldMaskCreditCardNumbers(string input, string expected)
- {
- // Arrange
- var provider = new PiiRuleProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-
- [DataTestMethod]
- [DataRow("Error code: 0x80070005", "Error code: 0x80070005")]
- [DataRow("Error code: -2147467262", "Error code: -2147467262")]
- [DataRow("GUID: 123e4567-e89b-12d3-a456-426614174000", "GUID: 123e4567-e89b-12d3-a456-426614174000")]
- [DataRow("Timestamp: 2023-10-05T14:32:10Z", "Timestamp: 2023-10-05T14:32:10Z")]
- [DataRow("Version: 1.2.3", "Version: 1.2.3")]
- [DataRow("Version: 1.2.3.4", "Version: 1.2.3.4")]
- [DataRow("Version: 0.2.3.4", "Version: 0.2.3.4")]
- [DataRow("Version: 10.0.22631.3448", "Version: 10.0.22631.3448")]
- [DataRow("MAC: 00:1A:2B:3C:4D:5E", "MAC: 00:1A:2B:3C:4D:5E")]
- [DataRow("Date: 2023-10-05", "Date: 2023-10-05")]
- [DataRow("Date: 05/10/2023", "Date: 05/10/2023")]
- public void PiiRuleProvider_ShouldNotOverRedact(string input, string expected)
- {
- // Arrange
- var provider = new PiiRuleProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-}
diff --git a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Services/Sanitizer/SecretKeyValueRulesProviderTests.cs b/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Services/Sanitizer/SecretKeyValueRulesProviderTests.cs
deleted file mode 100644
index 33ee54a32b..0000000000
--- a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Services/Sanitizer/SecretKeyValueRulesProviderTests.cs
+++ /dev/null
@@ -1,266 +0,0 @@
-// Copyright (c) Microsoft Corporation
-// The Microsoft Corporation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using Microsoft.CmdPal.Common.Services.Sanitizer;
-using Microsoft.CmdPal.Common.Services.Sanitizer.Abstraction;
-using Microsoft.CmdPal.Common.UnitTests.TestUtils;
-
-namespace Microsoft.CmdPal.Common.UnitTests.Services.Sanitizer;
-
-[TestClass]
-public class SecretKeyValueRulesProviderTests
-{
- [TestMethod]
- public void GetRules_ShouldReturnExpectedRules()
- {
- // Arrange
- var provider = new SecretKeyValueRulesProvider();
-
- // Act
- var rules = provider.GetRules();
-
- // Assert
- var ruleList = new List(rules);
- Assert.AreEqual(1, ruleList.Count);
- Assert.AreEqual("Sensitive key/value pairs", ruleList[0].Description);
- }
-
- [DataTestMethod]
- [DataRow("password=secret123", "password= [REDACTED]")]
- [DataRow("passphrase=myPassphrase", "passphrase= [REDACTED]")]
- [DataRow("pwd=test", "pwd= [REDACTED]")]
- [DataRow("passwd=pass1234", "passwd= [REDACTED]")]
- public void SecretKeyValueRules_ShouldMaskPasswordSecrets(string input, string expected)
- {
- // Arrange
- var provider = new SecretKeyValueRulesProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-
- [DataTestMethod]
- [DataRow("token=abc123def456", "token= [REDACTED]")]
- [DataRow("access_token=token_value", "access_token= [REDACTED]")]
- [DataRow("refresh-token=refresh_value", "refresh-token= [REDACTED]")]
- [DataRow("id token=id_token_value", "id token= [REDACTED]")]
- [DataRow("bearer token=bearer_value", "bearer token= [REDACTED]")]
- [DataRow("session token=session_value", "session token= [REDACTED]")]
- public void SecretKeyValueRules_ShouldMaskTokens(string input, string expected)
- {
- // Arrange
- var provider = new SecretKeyValueRulesProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-
- [DataTestMethod]
- [DataRow("api key=my_api_key", "api key= [REDACTED]")]
- [DataRow("api-key=key123", "api-key= [REDACTED]")]
- [DataRow("api_key=secret_key", "api_key= [REDACTED]")]
- [DataRow("x-api-key=api123", "x-api-key= [REDACTED]")]
- [DataRow("x api key=key456", "x api key= [REDACTED]")]
- [DataRow("client id=client123", "client id= [REDACTED]")]
- [DataRow("client-secret=secret123", "client-secret= [REDACTED]")]
- [DataRow("consumer secret=secret456", "consumer secret= [REDACTED]")]
- public void SecretKeyValueRules_ShouldMaskApiCredentials(string input, string expected)
- {
- // Arrange
- var provider = new SecretKeyValueRulesProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-
- [DataTestMethod]
- [DataRow("subscription key=sub_key_123", "subscription key= [REDACTED]")]
- [DataRow("instrumentation key=instr_key", "instrumentation key= [REDACTED]")]
- [DataRow("account key=account123", "account key= [REDACTED]")]
- [DataRow("storage account key=storage_key", "storage account key= [REDACTED]")]
- [DataRow("shared access key=sak123", "shared access key= [REDACTED]")]
- [DataRow("SAS token=sas123", "SAS token= [REDACTED]")]
- public void SecretKeyValueRules_ShouldMaskCloudPlatformKeys(string input, string expected)
- {
- // Arrange
- var provider = new SecretKeyValueRulesProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-
- [DataTestMethod]
- [DataRow("connection string=Server=localhost;Pwd=pass", "connection string= [REDACTED]")]
- [DataRow("conn string=conn_value", "conn string= [REDACTED]")]
- [DataRow("storage connection string=connection_value", "storage connection string= [REDACTED]")]
- public void SecretKeyValueRules_ShouldMaskConnectionStrings(string input, string expected)
- {
- // Arrange
- var provider = new SecretKeyValueRulesProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-
- [DataTestMethod]
- [DataRow("private key=pk123", "private key= [REDACTED]")]
- [DataRow("certificate password=cert_pass", "certificate password= [REDACTED]")]
- [DataRow("client certificate password=cert123", "client certificate password= [REDACTED]")]
- [DataRow("pfx password=pfx_pass", "pfx password= [REDACTED]")]
- public void SecretKeyValueRules_ShouldMaskCertificateSecrets(string input, string expected)
- {
- // Arrange
- var provider = new SecretKeyValueRulesProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-
- [DataTestMethod]
- [DataRow("aws access key id=AKIAIOSFODNN7EXAMPLE", "aws access key id= [REDACTED]")]
- [DataRow("aws secret access key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "aws secret access key= [REDACTED]")]
- [DataRow("aws session token=session_token_value", "aws session token= [REDACTED]")]
- public void SecretKeyValueRules_ShouldMaskAwsKeys(string input, string expected)
- {
- // Arrange
- var provider = new SecretKeyValueRulesProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-
- [DataTestMethod]
- [DataRow("password=\"complexPassword123!\"", "password= \"[REDACTED]\"")]
- [DataRow("api-key='secret-key'", "api-key= '[REDACTED]'")]
- [DataRow("token=\"bearer_token_value\"", "token= \"[REDACTED]\"")]
- public void SecretKeyValueRules_ShouldPreserveQuotesAroundRedactedValue(string input, string expected)
- {
- // Arrange
- var provider = new SecretKeyValueRulesProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-
- [DataTestMethod]
- [DataRow("PASSWORD=secret", "PASSWORD= [REDACTED]")]
- [DataRow("Api-Key=key123", "Api-Key= [REDACTED]")]
- [DataRow("CLIENT_ID=client123", "CLIENT_ID= [REDACTED]")]
- [DataRow("Pwd=pass123", "Pwd= [REDACTED]")]
- public void SecretKeyValueRules_ShouldBeCaseInsensitive(string input, string expected)
- {
- // Arrange
- var provider = new SecretKeyValueRulesProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-
- [DataTestMethod]
- [DataRow("regularKey=regularValue", "regularKey=regularValue")]
- [DataRow("config=myConfig", "config=myConfig")]
- [DataRow("hostname=server.example.com", "hostname=server.example.com")]
- [DataRow("port=8080", "port=8080")]
- public void SecretKeyValueRules_ShouldNotRedactNonSecretKeyValuePairs(string input, string expected)
- {
- // Arrange
- var provider = new SecretKeyValueRulesProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-
- [DataTestMethod]
- [DataRow("password:secret123", "password: [REDACTED]")]
- [DataRow("api key:api_key_value", "api key: [REDACTED]")]
- [DataRow("client_secret:secret_value", "client_secret: [REDACTED]")]
- public void SecretKeyValueRules_ShouldSupportColonSeparator(string input, string expected)
- {
- // Arrange
- var provider = new SecretKeyValueRulesProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-
- [DataTestMethod]
- [DataRow("password = secret123", "password= [REDACTED]")]
- [DataRow("api key = api_key_value", "api key= [REDACTED]")]
- [DataRow("token : token_value", "token: [REDACTED]")]
- public void SecretKeyValueRules_ShouldHandleWhitespace(string input, string expected)
- {
- // Arrange
- var provider = new SecretKeyValueRulesProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-
- [DataTestMethod]
- [DataRow("password=secret API_KEY=key config=myConfig", "password= [REDACTED] API_KEY= [REDACTED] config=myConfig")]
- [DataRow("client_id=id123 name=admin pwd=pass123", "client_id= [REDACTED] name=admin pwd= [REDACTED]")]
- public void SecretKeyValueRules_ShouldHandleMultipleKeyValuePairsInSingleString(string input, string expected)
- {
- // Arrange
- var provider = new SecretKeyValueRulesProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-
- [DataTestMethod]
- [DataRow("cosmos db key=cosmos_key", "cosmos db key= [REDACTED]")]
- [DataRow("service principal secret=sp_secret", "service principal secret= [REDACTED]")]
- [DataRow("shared access signature=sas_signature", "shared access signature= [REDACTED]")]
- public void SecretKeyValueRules_ShouldMaskServiceSpecificSecrets(string input, string expected)
- {
- // Arrange
- var provider = new SecretKeyValueRulesProvider();
-
- // Act
- var result = SanitizerTestHelper.ApplyRules(input, provider.GetRules());
-
- // Assert
- Assert.AreEqual(expected, result);
- }
-}
diff --git a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/TestUtils/SanitizerTestHelper.cs b/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/TestUtils/SanitizerTestHelper.cs
deleted file mode 100644
index 00de3fdad8..0000000000
--- a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/TestUtils/SanitizerTestHelper.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright (c) Microsoft Corporation
-// The Microsoft Corporation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Text.RegularExpressions;
-using Microsoft.CmdPal.Common.Services.Sanitizer.Abstraction;
-
-namespace Microsoft.CmdPal.Common.UnitTests.TestUtils;
-
-///
-/// Test-only helpers for applying SanitizationRule sets without relying on production ITextSanitizer implementation.
-///
-public static class SanitizerTestHelper
-{
- ///
- /// Applies the provided rules to the input, in order, mimicking the production sanitizer behavior closely
- /// but without any external dependencies.
- ///
- public static string ApplyRules(string? input, IEnumerable rules)
- {
- if (string.IsNullOrEmpty(input))
- {
- return input ?? string.Empty;
- }
-
- var result = input;
- foreach (var rule in rules ?? [])
- {
- try
- {
- var previous = result;
- result = rule.Evaluator is null
- ? rule.Regex.Replace(previous, rule.Replacement ?? string.Empty)
- : rule.Regex.Replace(previous, rule.Evaluator);
-
- // Guardrail to avoid accidental mass-redaction from a faulty rule
- if (result.Length < previous.Length * 0.3)
- {
- result = previous;
- }
- }
- catch (RegexMatchTimeoutException)
- {
- // Ignore timeouts in tests
- }
- }
-
- return result;
- }
-
- ///
- /// Creates a lightweight sanitizer instance backed by the given rules.
- /// Useful when a component expects an ITextSanitizer, but you want deterministic behavior in tests.
- ///
- public static ITextSanitizer CreateSanitizer(IEnumerable rules)
- => new InlineSanitizer(rules);
-
- private sealed class InlineSanitizer : ITextSanitizer
- {
- private readonly List _rules;
-
- public InlineSanitizer(IEnumerable rules)
- {
- _rules = rules?.ToList() ?? [];
- }
-
- public string Sanitize(string? input) => ApplyRules(input, _rules);
-
- public void AddRule(string pattern, string replacement, string description = "")
- {
- var rx = new Regex(pattern, RegexOptions.Compiled | RegexOptions.CultureInvariant);
- _rules.Add(new SanitizationRule(rx, replacement, description));
- }
-
- public void RemoveRule(string description)
- {
- _rules.RemoveAll(r => r.Description.Equals(description, StringComparison.OrdinalIgnoreCase));
- }
-
- public IReadOnlyList GetRules() => _rules.AsReadOnly();
-
- public string TestRule(string input, string ruleDescription)
- {
- var rule = _rules.FirstOrDefault(r => r.Description.Contains(ruleDescription, StringComparison.OrdinalIgnoreCase));
- if (rule.Regex is null)
- {
- return input;
- }
-
- try
- {
- if (rule.Evaluator is not null)
- {
- return rule.Regex.Replace(input, rule.Evaluator);
- }
-
- if (rule.Replacement is not null)
- {
- return rule.Regex.Replace(input, rule.Replacement);
- }
- }
- catch
- {
- // Ignore exceptions for test determinism
- }
-
- return input;
- }
- }
-}
diff --git a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Text/PrecomputedFuzzyMatcherEmojiTests.cs b/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Text/PrecomputedFuzzyMatcherEmojiTests.cs
deleted file mode 100644
index 0ba18634dc..0000000000
--- a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Text/PrecomputedFuzzyMatcherEmojiTests.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (c) Microsoft Corporation
-// The Microsoft Corporation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using Microsoft.CmdPal.Common.Text;
-
-namespace Microsoft.CmdPal.Common.UnitTests.Text;
-
-[TestClass]
-public sealed class PrecomputedFuzzyMatcherEmojiTests
-{
- private readonly PrecomputedFuzzyMatcher _matcher = new();
-
- [TestMethod]
- public void ExactMatch_SimpleEmoji_ReturnsScore()
- {
- const string needle = "🚀";
- const string haystack = "Launch 🚀 sequence";
-
- var query = _matcher.PrecomputeQuery(needle);
- var target = _matcher.PrecomputeTarget(haystack);
- var score = _matcher.Score(query, target);
-
- Assert.IsTrue(score > 0, "Expected match for simple emoji");
- }
-
- [TestMethod]
- public void ExactMatch_SkinTone_ReturnsScore()
- {
- const string needle = "👍🏽"; // Medium skin tone
- const string haystack = "Thumbs up 👍🏽 here";
-
- var query = _matcher.PrecomputeQuery(needle);
- var target = _matcher.PrecomputeTarget(haystack);
- var score = _matcher.Score(query, target);
-
- Assert.IsTrue(score > 0, "Expected match for emoji with skin tone");
- }
-
- [TestMethod]
- public void ZWJSequence_Family_Match()
- {
- const string needle = "👨👩👧👦"; // Family: Man, Woman, Girl, Boy
- const string haystack = "Emoji 👨👩👧👦 Test";
-
- var query = _matcher.PrecomputeQuery(needle);
- var target = _matcher.PrecomputeTarget(haystack);
- var score = _matcher.Score(query, target);
-
- Assert.IsTrue(score > 0, "Expected match for ZWJ sequence");
- }
-
- [TestMethod]
- public void Flags_Match()
- {
- const string needle = "🇺🇸"; // US Flag (Regional Indicator U + Regional Indicator S)
- const string haystack = "USA 🇺🇸";
-
- var query = _matcher.PrecomputeQuery(needle);
- var target = _matcher.PrecomputeTarget(haystack);
- var score = _matcher.Score(query, target);
-
- Assert.IsTrue(score > 0, "Expected match for flag emoji");
- }
-
- [TestMethod]
- public void Emoji_MixedWithText_Search()
- {
- const string needle = "t🌮o"; // "t" + taco + "o"
- const string haystack = "taco 🌮 on tuesday";
-
- var query = _matcher.PrecomputeQuery(needle);
- var target = _matcher.PrecomputeTarget(haystack);
- var score = _matcher.Score(query, target);
-
- Assert.IsTrue(score > 0, "Expected match for emoji mixed with text");
- }
-}
diff --git a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Text/PrecomputedFuzzyMatcherOptionsTests.cs b/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Text/PrecomputedFuzzyMatcherOptionsTests.cs
deleted file mode 100644
index 997c3d2c89..0000000000
--- a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Text/PrecomputedFuzzyMatcherOptionsTests.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (c) Microsoft Corporation
-// The Microsoft Corporation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using Microsoft.CmdPal.Common.Text;
-
-namespace Microsoft.CmdPal.Common.UnitTests.Text;
-
-[TestClass]
-public sealed class PrecomputedFuzzyMatcherOptionsTests
-{
- [TestMethod]
- public void Score_RemoveDiacriticsOption_AffectsMatching()
- {
- var withDiacriticsRemoved = new PrecomputedFuzzyMatcher(
- new PrecomputedFuzzyMatcherOptions { RemoveDiacritics = true });
- var withoutDiacriticsRemoved = new PrecomputedFuzzyMatcher(
- new PrecomputedFuzzyMatcherOptions { RemoveDiacritics = false });
-
- const string needle = "cafe";
- const string haystack = "CAFÉ";
-
- var scoreWithRemoval = withDiacriticsRemoved.Score(
- withDiacriticsRemoved.PrecomputeQuery(needle),
- withDiacriticsRemoved.PrecomputeTarget(haystack));
- var scoreWithoutRemoval = withoutDiacriticsRemoved.Score(
- withoutDiacriticsRemoved.PrecomputeQuery(needle),
- withoutDiacriticsRemoved.PrecomputeTarget(haystack));
-
- Assert.IsTrue(scoreWithRemoval > 0, "Expected match when diacritics are removed.");
- Assert.AreEqual(0, scoreWithoutRemoval, "Expected no match when diacritics are preserved.");
- }
-
- [TestMethod]
- public void Score_SkipWordSeparatorsOption_AffectsMatching()
- {
- var skipSeparators = new PrecomputedFuzzyMatcher(
- new PrecomputedFuzzyMatcherOptions { SkipWordSeparators = true });
- var keepSeparators = new PrecomputedFuzzyMatcher(
- new PrecomputedFuzzyMatcherOptions { SkipWordSeparators = false });
-
- const string needle = "a b";
- const string haystack = "ab";
-
- var scoreSkip = skipSeparators.Score(
- skipSeparators.PrecomputeQuery(needle),
- skipSeparators.PrecomputeTarget(haystack));
- var scoreKeep = keepSeparators.Score(
- keepSeparators.PrecomputeQuery(needle),
- keepSeparators.PrecomputeTarget(haystack));
-
- Assert.IsTrue(scoreSkip > 0, "Expected match when word separators are skipped.");
- Assert.AreEqual(0, scoreKeep, "Expected no match when word separators are preserved.");
- }
-
- [TestMethod]
- public void Score_IgnoreSameCaseBonusOption_AffectsLowercaseQuery()
- {
- var ignoreSameCase = new PrecomputedFuzzyMatcher(
- new PrecomputedFuzzyMatcherOptions
- {
- IgnoreSameCaseBonusIfQueryIsAllLowercase = true,
- SameCaseBonus = 10,
- });
- var applySameCase = new PrecomputedFuzzyMatcher(
- new PrecomputedFuzzyMatcherOptions
- {
- IgnoreSameCaseBonusIfQueryIsAllLowercase = false,
- SameCaseBonus = 10,
- });
-
- const string needle = "test";
- const string haystack = "test";
-
- var scoreIgnore = ignoreSameCase.Score(
- ignoreSameCase.PrecomputeQuery(needle),
- ignoreSameCase.PrecomputeTarget(haystack));
- var scoreApply = applySameCase.Score(
- applySameCase.PrecomputeQuery(needle),
- applySameCase.PrecomputeTarget(haystack));
-
- Assert.IsTrue(scoreApply > scoreIgnore, "Expected same-case bonus to apply when not ignored.");
- }
-}
diff --git a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Text/PrecomputedFuzzyMatcherSecondaryInputTests.cs b/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Text/PrecomputedFuzzyMatcherSecondaryInputTests.cs
deleted file mode 100644
index 5403453b4b..0000000000
--- a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Text/PrecomputedFuzzyMatcherSecondaryInputTests.cs
+++ /dev/null
@@ -1,227 +0,0 @@
-// Copyright (c) Microsoft Corporation
-// The Microsoft Corporation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using Microsoft.CmdPal.Common.Text;
-
-namespace Microsoft.CmdPal.Common.UnitTests.Text;
-
-[TestClass]
-public sealed class PrecomputedFuzzyMatcherSecondaryInputTests
-{
- private readonly PrecomputedFuzzyMatcher _matcher = new();
- private readonly StringFolder _folder = new();
- private readonly BloomFilter _bloom = new();
-
- [TestMethod]
- public void Score_PrimaryQueryMatchesSecondaryTarget_ShouldMatch()
- {
- // Scenario: Searching for "calc" should match a file "calculator.exe" where primary is filename, secondary is path
- var query = CreateQuery("calc");
- var target = CreateTarget(primary: "important.txt", secondary: "C:\\Programs\\Calculator\\");
-
- var score = _matcher.Score(query, target);
-
- Assert.IsTrue(score > 0, "Expected primary query to match secondary target");
- }
-
- [TestMethod]
- public void Score_SecondaryQueryMatchesPrimaryTarget_ShouldMatch()
- {
- // Scenario: User types "documents\\report" and we want to match against filename
- var query = CreateQuery(primary: "documents", secondary: "report");
- var target = CreateTarget(primary: "report.docx");
-
- var score = _matcher.Score(query, target);
-
- Assert.IsTrue(score > 0, "Expected secondary query to match primary target");
- }
-
- [TestMethod]
- public void Score_SecondaryQueryMatchesSecondaryTarget_ShouldMatch()
- {
- // Scenario: Both query and target have secondary info that matches
- var query = CreateQuery(primary: "test", secondary: "documents");
- var target = CreateTarget(primary: "something.txt", secondary: "C:\\Users\\Documents\\");
-
- var score = _matcher.Score(query, target);
-
- Assert.IsTrue(score > 0, "Expected secondary query to match secondary target");
- }
-
- [TestMethod]
- public void Score_PrimaryQueryMatchesBothTargets_ShouldReturnBestScore()
- {
- // The same query matches both primary and secondary of target
- var query = CreateQuery("test");
- var target = CreateTarget(primary: "test.txt", secondary: "test_folder\\");
-
- var score = _matcher.Score(query, target);
-
- Assert.IsTrue(score > 0, "Expected query to match when it appears in both primary and secondary");
- }
-
- [TestMethod]
- public void Score_NoSecondaryInQuery_MatchesSecondaryTarget()
- {
- // Query without secondary can still match target's secondary
- var query = CreateQuery("downloads");
- var target = CreateTarget(primary: "file.txt", secondary: "C:\\Downloads\\");
-
- var score = _matcher.Score(query, target);
-
- Assert.IsTrue(score > 0, "Expected primary query to match secondary target");
- }
-
- [TestMethod]
- public void Score_NoSecondaryInTarget_SecondaryQueryShouldNotMatch()
- {
- // Query with secondary but target without secondary - secondary query shouldn't interfere
- var query = CreateQuery(primary: "test", secondary: "extra");
- var target = CreateTarget(primary: "test.txt");
-
- var score = _matcher.Score(query, target);
-
- // Primary should still match, secondary query just doesn't contribute
- Assert.IsTrue(score > 0, "Expected primary query to match primary target");
- }
-
- [TestMethod]
- public void Score_SecondaryQueryNoMatch_PrimaryCanStillMatch()
- {
- // Secondary doesn't match anything, but primary does
- var query = CreateQuery(primary: "file", secondary: "nomatch");
- var target = CreateTarget(primary: "myfile.txt", secondary: "C:\\Documents\\");
-
- var score = _matcher.Score(query, target);
-
- Assert.IsTrue(score > 0, "Expected primary query to match even when secondary doesn't");
- }
-
- [TestMethod]
- public void Score_OnlySecondaryMatches_ShouldReturnScore()
- {
- // Only the secondary parts match, primary doesn't
- var query = CreateQuery(primary: "xyz", secondary: "documents");
- var target = CreateTarget(primary: "abc.txt", secondary: "C:\\Users\\Documents\\");
-
- var score = _matcher.Score(query, target);
-
- Assert.IsTrue(score > 0, "Expected match when only secondary parts match");
- }
-
- [TestMethod]
- public void Score_BothQueriesMatchDifferentTargets_ShouldReturnBestScore()
- {
- // Primary query matches secondary target, secondary query matches primary target
- var query = CreateQuery(primary: "docs", secondary: "report");
- var target = CreateTarget(primary: "report.pdf", secondary: "C:\\Documents\\");
-
- var score = _matcher.Score(query, target);
-
- Assert.IsTrue(score > 0, "Expected match when queries cross-match with targets");
- }
-
- [TestMethod]
- public void Score_CompletelyDifferent_ShouldNotMatch()
- {
- var query = CreateQuery(primary: "xyz", secondary: "abc");
- var target = CreateTarget(primary: "hello", secondary: "world");
-
- var score = _matcher.Score(query, target);
-
- Assert.AreEqual(0, score, "Expected no match when nothing matches");
- }
-
- [TestMethod]
- public void Score_EmptySecondaryInputs_ShouldMatchOnPrimary()
- {
- var query = CreateQuery(primary: "test", secondary: string.Empty);
- var target = CreateTarget(primary: "test.txt", secondary: string.Empty);
-
- var score = _matcher.Score(query, target);
-
- Assert.IsTrue(score > 0, "Expected match on primary when secondaries are empty");
- }
-
- [TestMethod]
- public void Score_WordSeparatorMatching_AcrossSecondary()
- {
- // Test that "Power Point" matches "PowerPoint" using secondary
- var query = CreateQuery(primary: "power", secondary: "point");
- var target = CreateTarget(primary: "PowerPoint.exe");
-
- var score = _matcher.Score(query, target);
-
- Assert.IsTrue(score > 0, "Expected 'power' + 'point' to match 'PowerPoint'");
- }
-
- private FuzzyQuery CreateQuery(string primary, string? secondary = null)
- {
- var primaryFolded = _folder.Fold(primary, removeDiacritics: true);
- var primaryBloom = _bloom.Compute(primaryFolded);
- var primaryEffectiveLength = primaryFolded.Length;
- var primaryIsAllLowercase = IsAllLowercaseAsciiOrNonLetter(primary);
-
- string? secondaryFolded = null;
- ulong secondaryBloom = 0;
- var secondaryEffectiveLength = 0;
- var secondaryIsAllLowercase = true;
-
- if (!string.IsNullOrEmpty(secondary))
- {
- secondaryFolded = _folder.Fold(secondary, removeDiacritics: true);
- secondaryBloom = _bloom.Compute(secondaryFolded);
- secondaryEffectiveLength = secondaryFolded.Length;
- secondaryIsAllLowercase = IsAllLowercaseAsciiOrNonLetter(secondary);
- }
-
- return new FuzzyQuery(
- original: primary,
- folded: primaryFolded,
- bloom: primaryBloom,
- effectiveLength: primaryEffectiveLength,
- isAllLowercaseAsciiOrNonLetter: primaryIsAllLowercase,
- secondaryOriginal: secondary,
- secondaryFolded: secondaryFolded,
- secondaryBloom: secondaryBloom,
- secondaryEffectiveLength: secondaryEffectiveLength,
- secondaryIsAllLowercaseAsciiOrNonLetter: secondaryIsAllLowercase);
- }
-
- private FuzzyTarget CreateTarget(string primary, string? secondary = null)
- {
- var primaryFolded = _folder.Fold(primary, removeDiacritics: true);
- var primaryBloom = _bloom.Compute(primaryFolded);
-
- string? secondaryFolded = null;
- ulong secondaryBloom = 0;
-
- if (!string.IsNullOrEmpty(secondary))
- {
- secondaryFolded = _folder.Fold(secondary, removeDiacritics: true);
- secondaryBloom = _bloom.Compute(secondaryFolded);
- }
-
- return new FuzzyTarget(
- original: primary,
- folded: primaryFolded,
- bloom: primaryBloom,
- secondaryOriginal: secondary,
- secondaryFolded: secondaryFolded,
- secondaryBloom: secondaryBloom);
- }
-
- private static bool IsAllLowercaseAsciiOrNonLetter(string s)
- {
- foreach (var c in s)
- {
- if ((uint)(c - 'A') <= ('Z' - 'A'))
- {
- return false;
- }
- }
-
- return true;
- }
-}
diff --git a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Text/PrecomputedFuzzyMatcherTests.cs b/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Text/PrecomputedFuzzyMatcherTests.cs
deleted file mode 100644
index 9cf3d5d38a..0000000000
--- a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Core.Common.UnitTests/Text/PrecomputedFuzzyMatcherTests.cs
+++ /dev/null
@@ -1,209 +0,0 @@
-// Copyright (c) Microsoft Corporation
-// The Microsoft Corporation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using Microsoft.CmdPal.Common.Text;
-
-namespace Microsoft.CmdPal.Common.UnitTests.Text;
-
-[TestClass]
-public class PrecomputedFuzzyMatcherTests
-{
- private readonly PrecomputedFuzzyMatcher _matcher = new();
-
- public static IEnumerable