Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions Modules/Sources/Storage/Tools/StorageType+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,24 @@ public extension StorageType {
return firstObject(ofType: SystemPlugin.self, matching: predicate)
}

/// Returns a system plugin with a specified `siteID` and `fileNameWithoutExtension`.
///
/// - Parameters:
/// - siteID: The site ID to filter by.
/// - fileNameWithoutExtension: The plugin file name to match without extension (e.g., "woocommerce", "woocommerce-payments").
/// - active: Optional active state filter. If provided, only plugins with matching active state are returned. If nil, active state is ignored.
/// - Returns: The matching system plugin, or nil if not found.
func loadSystemPlugin(siteID: Int64, fileNameWithoutExtension: String, active: Bool? = nil) -> SystemPlugin? {
let escapedFileName = NSRegularExpression.escapedPattern(for: fileNameWithoutExtension)
let regexPattern = "(.*/)?\(escapedFileName)\\.[^/]+$"
let predicate = if let active {
NSPredicate(format: "siteID == %lld AND plugin MATCHES %@ AND active == %@", siteID, regexPattern, NSNumber(value: active))
} else {
NSPredicate(format: "siteID == %lld AND plugin MATCHES %@", siteID, regexPattern)
}
return firstObject(ofType: SystemPlugin.self, matching: predicate)
}

/// Returns stored system plugins for a provided `siteID` matching the given plugin `paths`.
///
func loadSystemPlugins(siteID: Int64, matchingPaths paths: [String]) -> [SystemPlugin] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ public final class POSSystemStatusService: POSSystemStatusServiceProtocol {
})

// Loads WooCommerce plugin from storage.
guard let wcPlugin = storageManager.viewStorage.loadSystemPlugin(siteID: siteID, path: Constants.wcPluginPath, active: true)?.toReadOnly() else {
guard let wcPlugin = storageManager.viewStorage.loadSystemPlugin(siteID: siteID,
fileNameWithoutExtension: Constants.wcPluginFileNameWithoutExtension,
active: true)?.toReadOnly() else {
return POSPluginAndFeatureInfo(wcPlugin: nil, featureValue: nil)
}

Expand All @@ -66,7 +68,7 @@ public final class POSSystemStatusService: POSSystemStatusServiceProtocol {

private extension POSSystemStatusService {
enum Constants {
static let wcPluginPath = "woocommerce/woocommerce.php"
static let wcPluginFileNameWithoutExtension = "woocommerce"
}
}

Expand Down
182 changes: 182 additions & 0 deletions Modules/Tests/StorageTests/Tools/StorageTypeExtensionsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1538,6 +1538,188 @@ final class StorageTypeExtensionsTests: XCTestCase {
XCTAssertNil(foundPlugin)
}

// MARK: - `loadSystemPlugin(siteID:fileNameWithoutExtension:)`

func test_loadSystemPlugin_by_fileNameWithoutExtension_returns_matching_plugin_when_two_plugins_in_storage() throws {
// Given
let systemPlugin1 = storage.insertNewObject(ofType: SystemPlugin.self)
systemPlugin1.plugin = "woocommerce-payments/woocommerce-payments.php"
systemPlugin1.siteID = sampleSiteID

let systemPlugin2 = storage.insertNewObject(ofType: SystemPlugin.self)
systemPlugin2.plugin = "test-plugin/woocommerce-gift-cards.php"
systemPlugin2.siteID = sampleSiteID

// When
let foundSystemPlugin = try XCTUnwrap(storage.loadSystemPlugin(siteID: sampleSiteID, fileNameWithoutExtension: "woocommerce-gift-cards"))

// Then
XCTAssertEqual(foundSystemPlugin, systemPlugin2)
}

func test_loadSystemPlugin_by_fileNameWithoutExtension_returns_matching_plugin_when_plugin_path_is_in_different_valid_formats() throws {
let validPluginPaths = [
"woocommerce.php",
"woocommerce.swift",
"./woocommerce.swift",
".././woocommerce.swift",
"woocommerce/woocommerce.php",
"woocommerce/woocommerce.swift",
"test-plugin/test-plugin/woocommerce.php",
"test-plugin/test-plugin/test-plugin/woocommerce.tmp.php",
"test-plugin/test-plugin/woocommerce.tmp.php",
"test-plugin/woocommerce.tmp.php"
]

for pluginPath in validPluginPaths {
// Given
let plugin = storage.insertNewObject(ofType: SystemPlugin.self)
plugin.plugin = pluginPath
plugin.siteID = sampleSiteID

// When
let foundPlugin = try XCTUnwrap(storage.loadSystemPlugin(siteID: sampleSiteID, fileNameWithoutExtension: "woocommerce"))

// Then
XCTAssertEqual(foundPlugin, plugin)

// Cleanup for next iteration
storage.deleteObject(plugin)
}
}

func test_loadSystemPlugin_by_fileNameWithoutExtension_returns_nil_when_plugin_path_is_in_different_invalid_formats() throws {
let invalidPluginPaths = [
"woocommerce",
"woocommerce/woocommerce",
"woocommerce//woocommerce",
"woocommerce-dev.php",
"woocommerce/woocommerce-dev.php",
"test-plugin/woocommerce-dev.php",
"test-plugin/test-plugin/woocommerce-dev.php"
]

for pluginPath in invalidPluginPaths {
// Given
let plugin = storage.insertNewObject(ofType: SystemPlugin.self)
plugin.plugin = pluginPath
plugin.siteID = sampleSiteID

// When
let foundPlugin = storage.loadSystemPlugin(siteID: sampleSiteID, fileNameWithoutExtension: "woocommerce")

// Then
XCTAssertNil(foundPlugin)

// Cleanup for next iteration
storage.deleteObject(plugin)
}
}

func test_loadSystemPlugin_by_fileNameWithoutExtension_handles_regex_special_characters() throws {
let specialCharacterTests = [
// Filename with regex special characters, plugin path, should match
("plugin.name", "test-folder/plugin.name.php", true),
("plugin+test", "plugin-dir/plugin+test.swift", true),
("plugin*wild", "nested/plugin*wild.css", true),
("plugin[bracket]", "plugin[bracket].php", true),
("plugin(paren)", "folder/plugin(paren).min.js", true),
("plugin^caret", "plugin^caret.tmp", true),
("plugin$dollar", "deep/nested/plugin$dollar.ext", true),
// Filenames with spaces
("test plugin", "some folder with spaces/test plugin.css", true),
("plugin name", "plugin name.js", true),
// Should not match when filename is different
("plugin.name", "test-folder/pluginXname.php", false),
("plugin+test", "plugin-dir/plugin-test.js", false),
("plugin*wild", "nested/pluginXwild.css", false),
("my plugin", "folder/my-plugin.php", false),
("plugin name", "plugin-name.js", false)
]

for (searchTerm, pluginPath, shouldMatch) in specialCharacterTests {
// Given
let plugin = storage.insertNewObject(ofType: SystemPlugin.self)
plugin.plugin = pluginPath
plugin.siteID = sampleSiteID

// When
let foundPlugin = storage.loadSystemPlugin(siteID: sampleSiteID, fileNameWithoutExtension: searchTerm)

// Then
if shouldMatch {
XCTAssertEqual(foundPlugin, plugin, "Should find plugin for search term '\(searchTerm)' in path '\(pluginPath)'")
} else {
XCTAssertNil(foundPlugin, "Should NOT find plugin for search term '\(searchTerm)' in path '\(pluginPath)'")
}

// Cleanup for the next iteration
storage.deleteObject(plugin)
}
}


func test_loadSystemPlugin_by_fileNameWithoutExtension_returns_matching_plugin_when_active_state_is_set() throws {
// Given
let inactivePlugin = storage.insertNewObject(ofType: SystemPlugin.self)
inactivePlugin.plugin = "test-plugin/woocommerce.php"
inactivePlugin.siteID = sampleSiteID
inactivePlugin.active = false

let activePlugin = storage.insertNewObject(ofType: SystemPlugin.self)
activePlugin.plugin = "woocommerce/woocommerce.php"
activePlugin.siteID = sampleSiteID
activePlugin.active = true

// When
let foundActivePlugin = try XCTUnwrap(storage.loadSystemPlugin(siteID: sampleSiteID, fileNameWithoutExtension: "woocommerce", active: true))

// Then
XCTAssertEqual(foundActivePlugin, activePlugin)

// When
let foundInactivePlugin = try XCTUnwrap(storage.loadSystemPlugin(siteID: sampleSiteID, fileNameWithoutExtension: "woocommerce", active: false))

// Then
XCTAssertEqual(foundInactivePlugin, inactivePlugin)
}

func test_loadSystemPlugin_by_fileNameWithoutExtension_returns_matching_plugin_when_active_state_is_nil() throws {
// Given
let activePlugin = storage.insertNewObject(ofType: SystemPlugin.self)
activePlugin.plugin = "test-plugin/woocommerce.php"
activePlugin.siteID = sampleSiteID
activePlugin.active = true

let inactivePlugin = storage.insertNewObject(ofType: SystemPlugin.self)
inactivePlugin.plugin = "jetpack/jetpack.php"
inactivePlugin.siteID = sampleSiteID
inactivePlugin.active = false

// When
let foundPlugin = storage.loadSystemPlugin(siteID: sampleSiteID, fileNameWithoutExtension: "woocommerce", active: nil)

// Then
XCTAssertNotNil(foundPlugin)
XCTAssertEqual(foundPlugin, activePlugin)
}

func test_loadSystemPlugin_by_fileNameWithoutExtension_returns_nil_when_active_state_does_not_match() {
// Given
let activePlugin = storage.insertNewObject(ofType: SystemPlugin.self)
activePlugin.plugin = "woocommerce/woocommerce.php"
activePlugin.siteID = sampleSiteID
activePlugin.active = true

// When
let foundPlugin = storage.loadSystemPlugin(siteID: sampleSiteID, fileNameWithoutExtension: "woocommerce", active: false)

// Then
XCTAssertNil(foundPlugin)
}

// MARK: - `loadSystemPlugins(siteID:matchingPaths:)`

func test_loadSystemPlugins_by_siteID_matching_paths() {
// Given
let systemPlugin1 = storage.insertNewObject(ofType: SystemPlugin.self)
Expand Down