Skip to content

Commit c2f7436

Browse files
committed
Support single file plugin path by using regex match in predicate with more edge case coverage in unit tests.
1 parent 55d769b commit c2f7436

File tree

2 files changed

+54
-8
lines changed

2 files changed

+54
-8
lines changed

Modules/Sources/Storage/Tools/StorageType+Extensions.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -863,11 +863,12 @@ public extension StorageType {
863863
/// - active: Optional active state filter. If provided, only plugins with matching active state are returned. If nil, active state is ignored.
864864
/// - Returns: The matching system plugin, or nil if not found.
865865
func loadSystemPlugin(siteID: Int64, fileNameWithoutExtension: String, active: Bool? = nil) -> SystemPlugin? {
866-
let pathPattern = "*/\(fileNameWithoutExtension).*"
866+
let escapedFileName = NSRegularExpression.escapedPattern(for: fileNameWithoutExtension)
867+
let regexPattern = "(.*/)?\(escapedFileName)\\.[^/]+$"
867868
let predicate = if let active {
868-
NSPredicate(format: "siteID == %lld AND plugin LIKE %@ AND active == %@", siteID, pathPattern, NSNumber(value: active))
869+
NSPredicate(format: "siteID == %lld AND plugin MATCHES %@ AND active == %@", siteID, regexPattern, NSNumber(value: active))
869870
} else {
870-
NSPredicate(format: "siteID == %lld AND plugin LIKE %@", siteID, pathPattern)
871+
NSPredicate(format: "siteID == %lld AND plugin MATCHES %@", siteID, regexPattern)
871872
}
872873
return firstObject(ofType: SystemPlugin.self, matching: predicate)
873874
}

Modules/Tests/StorageTests/Tools/StorageTypeExtensionsTests.swift

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1559,14 +1559,14 @@ final class StorageTypeExtensionsTests: XCTestCase {
15591559

15601560
func test_loadSystemPlugin_by_fileNameWithoutExtension_returns_matching_plugin_when_plugin_path_is_in_different_valid_formats() throws {
15611561
let validPluginPaths = [
1562+
"woocommerce.php",
1563+
"woocommerce.swift",
15621564
"woocommerce/woocommerce.php",
15631565
"woocommerce/woocommerce.swift",
1564-
"woocommerce//woocommerce.php",
15651566
"test-plugin/test-plugin/woocommerce.php",
15661567
"test-plugin/test-plugin/test-plugin/woocommerce.tmp.php",
15671568
"test-plugin/test-plugin/woocommerce.tmp.php",
1568-
"test-plugin/woocommerce.tmp.php",
1569-
"/woocommerce.tmp.php"
1569+
"test-plugin/woocommerce.tmp.php"
15701570
]
15711571

15721572
for pluginPath in validPluginPaths {
@@ -1588,11 +1588,13 @@ final class StorageTypeExtensionsTests: XCTestCase {
15881588

15891589
func test_loadSystemPlugin_by_fileNameWithoutExtension_returns_nil_when_plugin_path_is_in_different_invalid_formats() throws {
15901590
let invalidPluginPaths = [
1591+
"woocommerce",
15911592
"woocommerce/woocommerce",
1593+
"woocommerce//woocommerce",
1594+
"woocommerce-dev.php",
15921595
"woocommerce/woocommerce-dev.php",
15931596
"test-plugin/woocommerce-dev.php",
1594-
"test-plugin/test-plugin/woocommerce-dev.php",
1595-
"woocommerce.tmp.php"
1597+
"test-plugin/test-plugin/woocommerce-dev.php"
15961598
]
15971599

15981600
for pluginPath in invalidPluginPaths {
@@ -1612,6 +1614,49 @@ final class StorageTypeExtensionsTests: XCTestCase {
16121614
}
16131615
}
16141616

1617+
func test_loadSystemPlugin_by_fileNameWithoutExtension_handles_regex_special_characters() throws {
1618+
let specialCharacterTests = [
1619+
// Filename with regex special characters, plugin path, should match
1620+
("plugin.name", "test-folder/plugin.name.php", true),
1621+
("plugin+test", "plugin-dir/plugin+test.swift", true),
1622+
("plugin*wild", "nested/plugin*wild.css", true),
1623+
("plugin[bracket]", "plugin[bracket].php", true),
1624+
("plugin(paren)", "folder/plugin(paren).min.js", true),
1625+
("plugin^caret", "plugin^caret.tmp", true),
1626+
("plugin$dollar", "deep/nested/plugin$dollar.ext", true),
1627+
// Filenames with spaces
1628+
("test plugin", "some folder with spaces/test plugin.css", true),
1629+
("plugin name", "plugin name.js", true),
1630+
// Should not match when filename is different
1631+
("plugin.name", "test-folder/pluginXname.php", false),
1632+
("plugin+test", "plugin-dir/plugin-test.js", false),
1633+
("plugin*wild", "nested/pluginXwild.css", false),
1634+
("my plugin", "folder/my-plugin.php", false),
1635+
("plugin name", "plugin-name.js", false)
1636+
]
1637+
1638+
for (searchTerm, pluginPath, shouldMatch) in specialCharacterTests {
1639+
// Given
1640+
let plugin = storage.insertNewObject(ofType: SystemPlugin.self)
1641+
plugin.plugin = pluginPath
1642+
plugin.siteID = sampleSiteID
1643+
1644+
// When
1645+
let foundPlugin = storage.loadSystemPlugin(siteID: sampleSiteID, fileNameWithoutExtension: searchTerm)
1646+
1647+
// Then
1648+
if shouldMatch {
1649+
XCTAssertEqual(foundPlugin, plugin, "Should find plugin for search term '\(searchTerm)' in path '\(pluginPath)'")
1650+
} else {
1651+
XCTAssertNil(foundPlugin, "Should NOT find plugin for search term '\(searchTerm)' in path '\(pluginPath)'")
1652+
}
1653+
1654+
// Cleanup for the next iteration
1655+
storage.deleteObject(plugin)
1656+
}
1657+
}
1658+
1659+
16151660
func test_loadSystemPlugin_by_fileNameWithoutExtension_returns_matching_plugin_when_active_state_is_set() throws {
16161661
// Given
16171662
let inactivePlugin = storage.insertNewObject(ofType: SystemPlugin.self)

0 commit comments

Comments
 (0)