diff --git a/src/actions/ini/add.ps1 b/src/actions/ini/add.ps1 index 2c15ecf6..a6480570 100644 --- a/src/actions/ini/add.ps1 +++ b/src/actions/ini/add.ps1 @@ -45,71 +45,6 @@ function Select-ExtensionPackageLink { return ($extensionLinks | Where-Object -FilterScript { $_.index -eq $packageIndex }) } -function Get-XdebugConfigV2 { - param ($XDebugPath) - - return @( - '[xdebug]' - ";zend_extension='$XDebugPath'" - 'xdebug.remote_enable=1' - 'xdebug.remote_host=127.0.0.1' - 'xdebug.remote_port=9000' - ) -} - -function Get-XdebugConfigV3 { - param ($XDebugPath) - - return @( - '[xdebug]' - ";zend_extension='$XDebugPath'" - 'xdebug.mode=debug' - 'xdebug.client_host=127.0.0.1' - 'xdebug.client_port=9003' - ) -} - -function Get-XDebugFromUrl { - param ($url, $version) - - try { - $html = Invoke-WebRequestWrapper -uri $url - $links = $html.Links - - # Return the filtered links (PHP version names) - $formattedList = @() - $links | ForEach-Object -Process { - if (-not $_.href) { return } - - $fileName = [System.IO.Path]::GetFileName($_.href) - - if ($fileName -notmatch '^php_xdebug-.*\.dll$') { return } - - if ($fileName -notmatch "php_xdebug-[\d\.a-zA-Z]+-$version-") { return } - - $xDebugVersion = '2.0' - if ($fileName -match 'php_xdebug-([^-]+)') { - $xDebugVersion = $matches[1] - } - - $formattedList += @{ - href = "$($PVMConfig.links.xdebugBase)$($_.href)" - version = $version - extVersion = $xDebugVersion; - arch = if ($fileName -match '(x86_64|x64)(?=\.dll$)') { 'x64' } else { 'x86' } - buildType = if ($fileName -match '(?i)(?:^|-)nts(?:-|\.dll$)') { 'NTS' } else { 'TS' } - compiler = if ($fileName -match '(?i)\b(vs|vc)\d+\b') { $matches[0].ToUpper() } else { 'unknown' } - fileName = $fileName - } - } - - return $formattedList - } catch { - $null = Add-LogEntry -data @{ header = "$($MyInvocation.MyCommand.Name) - Failed to fetch xdebug versions from $url"; exception = $_ } - return @() - } -} - function Get-PrereleaseSortKey { param ($Name) @@ -130,87 +65,6 @@ function Get-PrereleaseSortKey { return ($versionScore * 100000) + ($weight * 10000) + $number } -function Install-XDebugExtension { - param ($iniPath, $skipConfirmation = $false) - - try { - $currentVersionObj = Get-CurrentPHPVersion - $currentVersion = $currentVersionObj.version -replace '^(\d+\.\d+)\..*$', '$1' - $xDebugList = Get-OrUpdateCache -cacheFileName "packages_links_for_xdebug_php_$($currentVersion)_xdebug" -compute { - return Show-SpinnerWhileJob -argumentList @($currentVersion) -scriptBlock { - param ($currentVersion) - - $data = Get-XDebugFromUrl -url $PVMConfig.links.xdebugHistorical -version $currentVersion - return @{ pvmData = $data } - } -rethrow $true - } - - if ($null -eq $xDebugList -or $xDebugList.Count -eq 0) { - Show-Error -message "`nNo match was found, check the '$($PVMConfig.paths.files.logError)' for any potentiel errors" - return -1 - } - - $xDebugList = $xDebugList | Where-Object -FilterScript { - if ($null -ne $currentVersionObj.arch) { - if ($_.arch -ne $currentVersionObj.arch) { return $false } - } - - if ($null -ne $currentVersionObj.buildType) { - if ($_.buildType -ne $currentVersionObj.buildType) { return $false } - } - - return $true - } - - Show-Info -message "`nThis is a partial list. For a complete list, visit: $($PVMConfig.links.xdebugHistorical)" - $chosenItem = Select-ExtensionPackageLink -extName 'Xdebug' -extensionLinks $xDebugList - - if (-not $chosenItem) { - Show-Error -message "`nYou chose the wrong index" - return -1 - } - - $null = Invoke-WebRequestWrapper -uri $chosenItem.href -outFile $PVMConfig.paths.directories.php - $phpPath = Split-Path -Path $iniPath -Parent - - if (-not $skipConfirmation) { - if (Test-FileExists -path "$phpPath\ext\$($chosenItem.fileName)") { - $response = Read-HostWrapper -prompt "`n$($chosenItem.fileName) already exists. Would you like to overwrite it? (y/n)" - if (Test-NoResponse -response $response) { - Remove-ItemWrapper -path "$($PVMConfig.paths.directories.php)\$($chosenItem.fileName)" - Write-Gray -message "`nInstallation cancelled" - return -1 - } - } - } - - Move-ItemWrapper -path "$($PVMConfig.paths.directories.php)\$($chosenItem.fileName)" -destination "$phpPath\ext" - $xDebugConfig = Get-XdebugConfigV2 -XDebugPath $($chosenItem.fileName) - if ($chosenItem.extVersion -like '3.*') { - $xDebugConfig = Get-XdebugConfigV3 -XDebugPath $($chosenItem.fileName) - } - - $code = Add-MissingPHPExtensionToIni -iniPath $iniPath -extFileName $chosenItem.fileName -enable $false - if ($code -ne 0) { - Show-Error -message "`nFailed to add XDebug" - return -1 - } else { - $iniContent = Get-ContentWrapper -path $iniPath - if ($iniContent -notcontains '[xdebug]') { - $xDebugConfig = "`n$($xDebugConfig -join "`n")" - Add-ContentWrapper -path $iniPath -value $xDebugConfig - } - } - - Show-Success -message "`nXDebug installed successfully" - - return 0 - } catch { - $null = Add-LogEntry -data @{ header = "$($MyInvocation.MyCommand.Name) - Failed to install extension 'xdebug'"; exception = $_ } - return -1 - } -} - function Add-MissingPHPExtensionToIni { param ($iniPath, $extFileName, $enable = $true) @@ -235,15 +89,15 @@ function Add-MissingPHPExtensionToIni { return -1 } - $matchesList = Get-MatchingPHPExtensionsStatus -iniPath $iniPath -extName $extFileName -includeIniOnly $true -addToIniFileIfMissing $false - if ($matchesList.Length -gt 0) { + $matchesList = (Get-MatchingPHPExtensionsStatus -iniPath $iniPath -extName $extFileName -includeIniOnly $true -addToIniFileIfMissing $false) | Select-Object -First 1 + if ($matchesList.Length -gt 0 -and $matchesList.LineNumber -gt 0) { Show-Warning -message "- Extension '$extFileName' already exists in php.ini" return 0 } $lines = Get-ContentWrapper -path $iniPath $commented = if ($enable) { '' } else { ';' } - $isZendExtension = Get-ZendExtensionsList | Where-Object -FilterScript { $extFileName -like "*$_*" } + $isZendExtension = [bool](Get-ZendExtensionsList | Where-Object -FilterScript { $extFileName -like "*$_*" } | Select-Object -First 1) if ($isZendExtension) { $lines += "`n$commented" + "zend_extension=$extFileName" } else { @@ -263,9 +117,46 @@ function Install-Extension { param ($iniPath, $extName, $skipConfirmation = $false) try { - $currentVersionObj = Get-CurrentPHPVersion - $currentVersion = $currentVersionObj.version -replace '^(\d+\.\d+)\..*$', '$1' - $extensionLinksObj = Get-ExtensionPackages -extName $extName -version $currentVersion + New-Line + $sourceHandlers = (Get-ExtensionHandlers).SourceHandlers + $sourceNames = @($sourceHandlers.Keys) | Sort-Object + $index = 0 + $sourceNames | Foreach-Object -Process { + Show-Message -message "[$index] $_" + $index++ + } + $selectedIndex = Read-HostWrapper -prompt "`nEnter the [number] of your selection" + if ([string]::IsNullOrWhiteSpace($selectedIndex)) { + Write-Gray -message "`nInstallation cancelled" + return -1 + } + $choice = $null + if (-not [int]::TryParse($selectedIndex, [ref]$choice)) { + Show-Warning -message "`nYou answer is invalid!" + return -1 + } + if ($choice -lt 0 -or $choice -gt $sourceHandlers.Count - 1) { + Show-Warning -message "Number must be between 0 and $($sourceHandlers.Count - 1)." + return -1 + } + + $selectedSource = $sourceNames[$choice] + $handler = Get-SourceHandler -sourceUrl $selectedSource + + if (-not $handler) { + Show-Error -message "`nNo handler found for source: $source" + return -1 + } + + if ($handler.SupportedExtensions -and $handler.SupportedExtensions -notcontains '*') { + $normalizedExtName = ConvertTo-ExtensionId -name $extName + if ($handler.SupportedExtensions -notcontains $normalizedExtName) { + Show-Error -message "`nSource '$selectedSource' does not support extension '$extName'. Supported extensions: $($handler.SupportedExtensions -join ', ')" + return -1 + } + } + + $extensionLinksObj = & $handler.ResolveLinks -extName $extName if (($null -eq $extensionLinksObj) -or ($extensionLinksObj.Count -eq 0) -or ($null -eq $extensionLinksObj.data) -or ($extensionLinksObj.data.Count -eq 0)) { $extName = if ($extensionLinksObj) { $extensionLinksObj.extName } else { $extName } @@ -273,6 +164,7 @@ function Install-Extension { return -1 } + $currentVersionObj = Get-CurrentPHPVersion $extensionLinks = $extensionLinksObj.data | Where-Object -FilterScript { if ($null -ne $currentVersionObj.arch) { if ($_.arch -ne $currentVersionObj.arch) { return $false } @@ -291,10 +183,17 @@ function Install-Extension { } $extName = $extensionLinksObj.extName + $source = $extensionLinksObj.source + if ($extensionLinks.Length -eq 1) { $chosenItem = $($extensionLinks) } else { - Show-Info -message "`nThis is a partial list. For a complete list, visit: $($PVMConfig.links.peclPackageRoot)/$extName" + $moreInfoUrl = if ($handler.MoreInfoUrl -is [scriptblock]) { + & $handler.MoreInfoUrl $extName + } else { + $handler.MoreInfoUrl + } + Show-Info -message "`nThis is a partial list. For a complete list, visit: $moreInfoUrl" $chosenItem = Select-ExtensionPackageLink -extName $extName -extensionLinks $extensionLinks } @@ -303,39 +202,31 @@ function Install-Extension { return -1 } - $null = Invoke-WebRequestWrapper -uri $chosenItem.href -outFile $PVMConfig.paths.directories.php - $fileNamePath = $chosenItem.fileName -replace '.zip$', '' - $extractPath = "$($PVMConfig.paths.directories.php)\$fileNamePath" - Expand-Zip -zipPath "$extractPath.zip" -extractPath $extractPath -deleteZipAfter $true - $files = Get-ChildItemWrapper -path $extractPath - $extFile = $files | Where-Object -FilterScript { - ($_.Name -match "^php_$extName.*\.dll$") + $phpPath = Split-Path -Path $iniPath -Parent + + $downloadParams = @{ + chosenItem = $chosenItem + phpPath = $phpPath + skipConfirmation = $skipConfirmation + } + if ($source -eq 'pecl.php.net') { + $downloadParams.extName = $extName } + + $extFile = & $handler.Download @downloadParams if (-not $extFile) { - Show-Error -message "`nFailed to find $extName" + Show-Error -message "`nFailed to download $extName" return -1 } - $phpPath = Split-Path -Path $iniPath -Parent - - if (-not $skipConfirmation) { - if (Test-FileExists -path "$phpPath\ext\$($extFile.Name)") { - $response = Read-HostWrapper -prompt "`n$($extFile.Name) already exists. Would you like to overwrite it? (y/n)" - if (Test-NoResponse -response $response) { - Remove-ItemWrapper -path "$($PVMConfig.paths.directories.php)\$fileNamePath" - Write-Gray -message "`nInstallation cancelled" - return -1 - } - } - } + $configHandler = Get-ExtensionConfigHandler -extName $extFile.Name - Move-ItemWrapper -path $extFile.FullName -destination "$phpPath\ext" - Remove-ItemWrapper -path $extractPath - $code = Add-MissingPHPExtensionToIni -iniPath $iniPath -extFileName $extFile.Name -enable $false - if ($code -ne 0) { - Show-Error -message "`nFailed to add $extName" + $configCode = & $configHandler -iniPath $iniPath -fileName $extFile.Name -extVersion $chosenItem.extVersion + if ($configCode -ne 0) { + Show-Error -message "`nFailed to apply configuration for $extName" return -1 } + Show-Success -message "`n$extName installed successfully" return 0 @@ -354,16 +245,13 @@ function Install-IniExtension { return -1 } - $overallCode = 0 + $codes = @() foreach ($extName in $extNames) { - if ($extName -like '*xdebug*') { - $overallCode = Install-XDebugExtension -iniPath $iniPath -skipConfirmation $skipConfirmation - } else { - $overallCode = Install-Extension -iniPath $iniPath -extName $extName -skipConfirmation $skipConfirmation - } + $codes += Install-Extension -iniPath $iniPath -extName $extName -skipConfirmation $skipConfirmation } - return $overallCode + if ($codes | Where-Object -FilterScript { $_ -ne 0 }) { return -1 } + return 0 } catch { $null = Add-LogEntry -data @{ header = "$($MyInvocation.MyCommand.Name) - Failed to install '$($extNames -join ', ')'"; exception = $_ } return -1 diff --git a/src/actions/ini/helpers/fetch.ps1 b/src/actions/ini/helpers/fetch.ps1 index 286f1e43..d2c1a080 100644 --- a/src/actions/ini/helpers/fetch.ps1 +++ b/src/actions/ini/helpers/fetch.ps1 @@ -1,4 +1,262 @@  +function Get-ExtensionHandlers { + return @{ + SourceHandlers = @{ + 'xdebug.org' = @{ + SupportedExtensions = @('xdebug') + ResolveLinks = { + param ($extName) + + $currentVersionObj = Get-CurrentPHPVersion + $currentVersion = $currentVersionObj.version -replace '^(\d+\.\d+)\..*$', '$1' + + $handler = Get-SourceHandler -sourceUrl 'xdebug.org' + if (-not $handler) { + return @{ extName = 'xdebug'; data = $null; source = 'xdebug.org' } + } + + $formattedList = & $handler.GetPackages -version $currentVersion + if ($null -eq $formattedList) { + return @{ extName = 'xdebug'; data = $null; source = 'xdebug.org' } + } + + return @{ + extName = $extName + data = $formattedList + source = 'xdebug.org' + } + } + GetPackages = { + param ($version) + return Get-OrUpdateCache -cacheFileName "packages_links_for_xdebug_php_$($version)_xdebug" -compute { + return Show-SpinnerWhileJob -argumentList @($version) -scriptBlock { + param ($version) + $data = Get-XDebugFromUrl -url $PVMConfig.links.xdebugHistorical -version $version + return @{ pvmData = $data } + } -rethrow $true + } + } + Download = { + param ($chosenItem, $phpPath, $skipConfirmation) + + try { + $null = Invoke-WebRequestWrapper -uri $chosenItem.href -outFile $PVMConfig.paths.directories.php + $extFile = @{ + Name = $chosenItem.fileName + FullName = "$($PVMConfig.paths.directories.php)\$($chosenItem.fileName)" + } + + if (-not $skipConfirmation) { + if (Test-FileExists -path "$phpPath\ext\$($extFile.Name)") { + $response = Read-HostWrapper -prompt "`n$($extFile.Name) already exists. Would you like to overwrite it? (y/n)" + if (Test-NoResponse -response $response) { + Remove-ItemWrapper -path $extFile.FullName + Write-Gray -message "`nInstallation cancelled" + return $null + } + } + } + + Move-ItemWrapper -path $extFile.FullName -destination "$phpPath\ext" + return $extFile + } catch { + $null = Add-LogEntry -data @{ header = "Xdebug.org Handler - Failed to download extension"; exception = $_ } + return $null + } + } + MoreInfoUrl = $PVMConfig.links.xdebugHistorical + } + 'pecl.php.net' = @{ + SupportedExtensions = @('*') + ResolveLinks = { + param ($extName) + + $currentVersionObj = Get-CurrentPHPVersion + $currentVersion = $currentVersionObj.version -replace '^(\d+\.\d+)\..*$', '$1' + + return (Get-ExtensionPackages -extName $extName -version $currentVersion) + } + GetPackages = { + param ($version, $linksObj) + if (($null -eq $linksObj.links) -or ($linksObj.links.Count -eq 0)) { + return $null + } + return Get-OrUpdateCache -cacheFileName "packages_links_for_$($linksObj.extName)_php_$($version)_pecl" -compute { + return Show-SpinnerWhileJob -argumentList @($linksObj, $version) -scriptBlock { + param ($linksObj, $version) + $data = Get-PackagesFromSourceLinks -extName $linksObj.extName -version $version -links $linksObj.links + return @{ pvmData = $data } + } -rethrow $true + } + } + Download = { + param ($chosenItem, $phpPath, $skipConfirmation, $extName) + + try { + $null = Invoke-WebRequestWrapper -uri $chosenItem.href -outFile $PVMConfig.paths.directories.php + $fileNamePath = $chosenItem.fileName -replace '.zip$', '' + $extractPath = "$($PVMConfig.paths.directories.php)\$fileNamePath" + Expand-Zip -zipPath "$extractPath.zip" -extractPath $extractPath -deleteZipAfter $true + $files = Get-ChildItemWrapper -path $extractPath + $extFile = $files | Where-Object -FilterScript { + ($_.Name -match "^php_$extName.*\.dll$") + } + + if (-not $extFile) { + Remove-ItemWrapper -path $extractPath + return $null + } + + if (-not $skipConfirmation) { + if (Test-FileExists -path "$phpPath\ext\$($extFile.Name)") { + $response = Read-HostWrapper -prompt "`n$($extFile.Name) already exists. Would you like to overwrite it? (y/n)" + if (Test-NoResponse -response $response) { + Remove-ItemWrapper -path $extractPath + Write-Gray -message "`nInstallation cancelled" + return $null + } + } + } + + Move-ItemWrapper -path $extFile.FullName -destination "$phpPath\ext" + Remove-ItemWrapper -path $extractPath + return $extFile + } catch { + $null = Add-LogEntry -data @{ header = "PECL Handler - Failed to download extension"; exception = $_ } + return $null + } + } + MoreInfoUrl = { + param ($extName) + return "$($PVMConfig.links.peclPackageRoot)/$extName" + } + } + } + ExtensionConfigHandlers = @{ + 'xdebug' = { + param ($iniPath, $fileName, $extVersion) + + try { + $xDebugConfig = Get-XdebugConfigV2 -XDebugPath $fileName + if ($extVersion -like '3.*') { + $xDebugConfig = Get-XdebugConfigV3 -XDebugPath $fileName + } + + $iniContent = Get-ContentWrapper -path $iniPath + if ($iniContent -notcontains '[xdebug]') { + $xDebugConfig = "`n$($xDebugConfig -join "`n")" + Add-ContentWrapper -path $iniPath -value $xDebugConfig + } + + return 0 + } catch { + $null = Add-LogEntry -data @{ header = "Xdebug Config Handler - Failed to apply configuration"; exception = $_ } + return -1 + } + } + 'default' = { + param ($iniPath, $fileName, $extVersion) + + return (Add-MissingPHPExtensionToIni -iniPath $iniPath -extFileName $extFile.Name -enable $false) + } + } + } +} + +function Get-SourceHandler { + param ($sourceUrl) + + $handlers = Get-ExtensionHandlers + $sourceHandlers = $handlers.SourceHandlers + + if ($sourceHandlers.ContainsKey($sourceUrl)) { + return $sourceHandlers[$sourceUrl] + } + + # Default to PECL handler for unknown sources + return $sourceHandlers['pecl.php.net'] +} + +function Get-ExtensionConfigHandler { + param ($extName) + + $handlers = Get-ExtensionHandlers + $configHandlers = $handlers.ExtensionConfigHandlers + + $baseExtName = ConvertTo-ExtensionId -name $extName + + if ($configHandlers.ContainsKey($baseExtName)) { + return $configHandlers[$baseExtName] + } + + # Default handler - no special configuration needed + return $configHandlers['default'] +} + +function Get-XDebugFromUrl { + param ($url, $version) + + try { + $html = Invoke-WebRequestWrapper -uri $url + $links = $html.Links + + $formattedList = @() + $links | ForEach-Object -Process { + if (-not $_.href) { return } + + $fileName = [System.IO.Path]::GetFileName($_.href) + + if ($fileName -notmatch '^php_xdebug-.*\.dll$') { return } + + if ($fileName -notmatch "php_xdebug-[\d\.a-zA-Z]+-$version-") { return } + + $xDebugVersion = '2.0' + if ($fileName -match 'php_xdebug-([^-]+)') { + $xDebugVersion = $matches[1] + } + + $formattedList += @{ + href = "$($PVMConfig.links.xdebugBase)$($_.href)" + version = $version + extVersion = $xDebugVersion; + arch = if ($fileName -match '(x86_64|x64)(?=\.dll$)') { 'x64' } else { 'x86' } + buildType = if ($fileName -match '(?i)(?:^|-)nts(?:-|\.dll$)') { 'NTS' } else { 'TS' } + compiler = if ($fileName -match '(?i)\b(vs|vc)\d+\b') { $matches[0].ToUpper() } else { 'unknown' } + fileName = $fileName + } + } + + return $formattedList + } catch { + $null = Add-LogEntry -data @{ header = "$($MyInvocation.MyCommand.Name) - Failed to fetch xdebug versions from $url"; exception = $_ } + return @() + } +} + +function Get-XdebugConfigV2 { + param ($XDebugPath) + + return @( + '[xdebug]' + ";zend_extension='$XDebugPath'" + 'xdebug.remote_enable=1' + 'xdebug.remote_host=127.0.0.1' + 'xdebug.remote_port=9000' + ) +} + +function Get-XdebugConfigV3 { + param ($XDebugPath) + + return @( + '[xdebug]' + ";zend_extension='$XDebugPath'" + 'xdebug.mode=debug' + 'xdebug.client_host=127.0.0.1' + 'xdebug.client_port=9003' + ) +} + function Get-ExtensionCategoriesByPage { param ($extCategory, $link, $page = 1) @@ -237,6 +495,7 @@ function Resolve-ExtensionLinks { $links = Get-OrUpdateCache -cacheFileName "available_$($extName)_versions_$($version)_pecl" -compute { return Get-ExtensionAvailableReleasesLinks -extName $extName } + $source = 'pecl.php.net' } catch { Show-Message -message "`nDirect link for extension '$extName' not found, Loading matching extensions..." @@ -252,15 +511,23 @@ function Resolve-ExtensionLinks { if (-not $chosenItem) { return $null } $extName = $chosenItem.extName + $source = $chosenItem.source Show-Message -message "`nLoading links for '$extName'..." - $links = Get-OrUpdateCache -cacheFileName "available_$($extName)_versions_$($version)_pecl" -compute { - return Get-ExtensionAvailableReleasesLinks -extName $extName + + # Only get PECL links if the source is pecl.php.net + if ($source -eq 'pecl.php.net') { + $links = Get-OrUpdateCache -cacheFileName "available_$($extName)_versions_$($version)_pecl" -compute { + return Get-ExtensionAvailableReleasesLinks -extName $extName + } + } else { + $links = @() } } return @{ extName = $extName links = $links + source = $source } } @@ -269,22 +536,28 @@ function Get-ExtensionPackages { $linksObj = Resolve-ExtensionLinks -extName $extName -version $version - if (($null -eq $linksObj) -or ($linksObj.Count -eq 0) -or ($null -eq $linksObj.links) -or ($linksObj.links.Count -eq 0)) { - $extName = if ($linksObj -and $linksObj.extName) { $linksObj.extName } else { $extName } - return @{ extName = $extName; data = $null } + if ($null -eq $linksObj) { + return @{ extName = $extName; data = $null; source = 'unknown' } + } + + $handler = Get-SourceHandler -sourceUrl $linksObj.source + if (-not $handler) { + return @{ extName = $linksObj.extName; data = $null; source = $linksObj.source } } - $formattedList = Get-OrUpdateCache -cacheFileName "packages_links_for_$($linksObj.extName)_php_$($version)_pecl" -compute { - return Show-SpinnerWhileJob -argumentList @($linksObj, $version) -scriptBlock { - param ($linksObj, $version) + $formattedList = if ($linksObj.source -eq 'pecl.php.net') { + & $handler.GetPackages -version $version -linksObj $linksObj + } else { + & $handler.GetPackages -version $version + } - $data = Get-PackagesFromSourceLinks -extName $linksObj.extName -version $version -links $linksObj.links - return @{ pvmData = $data } - } -rethrow $true + if ($null -eq $formattedList) { + return @{ extName = $linksObj.extName; data = $null; source = $linksObj.source } } return @{ extName = $linksObj.extName data = $formattedList + source = $linksObj.source } } diff --git a/tests/actions/ini.tests.ps1 b/tests/actions/ini.tests.ps1 index 62a1e95f..ab3c0f5b 100644 --- a/tests/actions/ini.tests.ps1 +++ b/tests/actions/ini.tests.ps1 @@ -304,8 +304,7 @@ extension=php_curl.dll Mock Expand-Zip { } Mock Remove-ItemWrapper { } Mock Move-ItemWrapper { } - Mock Install-Extension { return 0 } - Mock Install-XDebugExtension { return 0 } + Mock Install-IniExtension { return 0 } } It "Installs extension" { @@ -331,7 +330,7 @@ extension=php_curl.dll $result = Invoke-IniAction -action 'add' -params @('pdo_mysql', '-y') $result | Should -Be 0 - Should -Invoke Install-Extension -Times 1 -ParameterFilter { + Should -Invoke Install-IniExtension -Times 1 -ParameterFilter { $skipConfirmation -eq $true } } @@ -341,7 +340,7 @@ extension=php_curl.dll $result = Invoke-IniAction -action 'add' -params @('xdebug', '-y') $result | Should -Be 0 - Should -Invoke Install-XDebugExtension -Times 1 -ParameterFilter { + Should -Invoke Install-IniExtension -Times 1 -ParameterFilter { $skipConfirmation -eq $true } } diff --git a/tests/actions/ini/add.tests.ps1 b/tests/actions/ini/add.tests.ps1 index d7c88c50..eabe0a29 100644 --- a/tests/actions/ini/add.tests.ps1 +++ b/tests/actions/ini/add.tests.ps1 @@ -18,6 +18,7 @@ BeforeAll { $script:PECL_PACKAGE_ROOT_URL = $PVMConfig.links.peclPackageRoot $script:PECL_WIN_EXT_DOWNLOAD_URL = $PVMConfig.links.peclWinExtDownload + Mock New-Line {} Mock Show-Warning {} Mock Show-Message {} Mock Show-Error {} @@ -95,407 +96,165 @@ AfterAll { $Global:PVMConfig = $PVMConfigBackup } -Describe "Get-XDebugFromUrl Tests" { - BeforeAll { - function Reset-MockState { - $script:MockRegistryThrowException = $false - $script:MockFileSystem.DownloadFails = $false - $script:MockFileSystem.WebResponses = @{} - $script:MockFileSystem.Files = @{} - $script:MockFileSystem.Directories = @() - } +Describe "Get-ExtensionHandlers Tests" { + It "Returns unified handler registry with both source and config handlers" { + $handlers = Get-ExtensionHandlers - function Set-MockWebResponse { - param ($url, $content, $links = @()) - $script:MockFileSystem.WebResponses[$url] = @{ - Content = $content - Links = $links - } - } + $handlers.SourceHandlers | Should -Not -BeNullOrEmpty + $handlers.ExtensionConfigHandlers | Should -Not -BeNullOrEmpty + $handlers.SourceHandlers.ContainsKey('xdebug.org') | Should -Be $true + $handlers.SourceHandlers.ContainsKey('pecl.php.net') | Should -Be $true + $handlers.ExtensionConfigHandlers.ContainsKey('xdebug') | Should -Be $true } - BeforeEach { - Reset-MockState - } - - It "Should parse XDebug versions correctly" { - $mockLinks = @( - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x86_64.dll" }, - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-2.9.0-8.1-vs16-x86_64.dll" }, - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-nts-vs16-x86_64.dll" }, - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-2.9.0-8.1-nts-vc16-x86_64.dll" }, - @{ href = "$XDEBUG_BASE_URL/download/php_random.dll" } - ) - Set-MockWebResponse -url 'https://test.com' -links $mockLinks +} - $result = Get-XDebugFromUrl -url 'https://test.com' -version '8.1' +Describe "Get-SourceHandler Tests" { + It "Returns correct handler for xdebug.org source" { + $handler = Get-SourceHandler -sourceUrl 'xdebug.org' - $result.Count | Should -Be 4 - $result[0].extVersion | Should -Be '3.1.0' - $result[1].extVersion | Should -Be '2.9.0' + $handler | Should -Not -BeNullOrEmpty + $handler.GetPackages | Should -Not -BeNullOrEmpty + $handler.Download | Should -Not -BeNullOrEmpty + $handler.MoreInfoUrl | Should -Not -BeNullOrEmpty } - It "Should handle network errors" { - $script:MockFileSystem.DownloadFails = $true - - $result = Get-XDebugFromUrl -url 'https://test.com' -version '8.1' + It "Returns correct handler for pecl.php.net source" { + $handler = Get-SourceHandler -sourceUrl 'pecl.php.net' - $result | Should -Be @() + $handler | Should -Not -BeNullOrEmpty + $handler.GetPackages | Should -Not -BeNullOrEmpty + $handler.Download | Should -Not -BeNullOrEmpty + $handler.MoreInfoUrl | Should -Not -BeNullOrEmpty } - It "Should parse xdebug with x86 architecture and unknown compiler" { - $mockLinks = @( - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-x86.dll" }, - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-2.9.0-8.1-nts-x86.dll" } - ) - Set-MockWebResponse -url 'https://test.com' -links $mockLinks - - $result = Get-XDebugFromUrl -url 'https://test.com' -version '8.1' + It "Returns default PECL handler for unknown sources" { + $handler = Get-SourceHandler -sourceUrl 'unknown.source.com' - $result.Count | Should -Be 2 - $result[0].arch | Should -Be 'x86' - $result[0].compiler | Should -Be 'unknown' - $result[1].arch | Should -Be 'x86' - $result[1].compiler | Should -Be 'unknown' + $handler | Should -Not -BeNullOrEmpty + # Should return the pecl.php.net handler as default + $handler.MoreInfoUrl | Should -Not -BeNullOrEmpty } } -Describe "Install-XDebugExtension" { - BeforeAll { - Mock Show-SpinnerWhileJob { - param ($scriptBlock, $message, $noClear, $argumentList, $rethrow) - $result = & $scriptBlock @argumentList - return $result.pvmData - } - Mock Get-CurrentPHPVersion { return @{ version = '8.1'; arch = 'x64'; buildType = 'ts'; path = "$TEST_DRIVE\php\8.1.0" } } - Mock Get-XDebugFromUrl { - return @( - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x64.dll"; arch = 'x64'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-vs16-x64.dll'; outerHTML = "php_xdebug-3.1.0-8.1-vs16-x64.dll" } - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-2.9.0-8.1-vs16-x64.dll"; arch = 'x64'; buildType = 'ts'; version = '8.1'; extVersion = '2.9.0'; fileName = 'php_xdebug-2.9.0-8.1-vs16-x86_64.dll'; outerHTML = "php_xdebug-2.9.0-8.1-vs16-x86_64.dll" } - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-nts-vs16-x64.dll"; arch = 'x64'; buildType = 'nts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-nts-vs16-x86_64.dll'; outerHTML = "php_xdebug-3.1.0-8.1-nts-vs16-x86_64.dll" } - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-2.9.0-8.1-nts-vc16-x64.dll"; arch = 'x64'; buildType = 'nts'; version = '8.1'; extVersion = '2.9.0'; fileName = 'php_xdebug-2.9.0-8.1-nts-vc16-x86_64.dll'; outerHTML = "php_xdebug-2.9.0-8.1-nts-vc16-x86_64.dll" } - ) - } - Mock Read-HostWrapper { - param ($Prompt) - if ($Prompt -eq "`nEnter the [number] of your selection") { - return '' - } - } - - function Reset-MockState { - $script:MockRegistryThrowException = $false - $script:MockFileSystem.DownloadFails = $false - $script:MockFileSystem.WebResponses = @{} - $script:MockFileSystem.Files = @{} - $script:MockFileSystem.Directories = @() - } - - function Add-ContentWrapper { - param ($Path, $Value) - if ($script:MockFileSystem.Files.ContainsKey($Path)) { - $script:MockFileSystem.Files[$Path] += "`n$Value" - } - else { - $script:MockFileSystem.Files[$Path] = $Value - } - } - - function Set-MockWebResponse { - param ($url, $content, $links = @()) - $script:MockFileSystem.WebResponses[$url] = @{ - Content = $content - Links = $links - } - } - } - - BeforeEach { - $script:MockFileSystem.Directories += "$TEST_DRIVE\php" - $script:MockFileSystem.Directories += "$TEST_DRIVE\php\ext" - $script:MockFileSystem.Files["$TEST_DRIVE\php\php.ini"] = @" -;extension_dir = "ext" -zend_extension = opcache -opcache.enable = 1 -"@ - Reset-MockState - $mockLinks = @( - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x64.dll" } - ) - Set-MockWebResponse -url $XDEBUG_HISTORICAL_URL -links $mockLinks - } +Describe "Get-ExtensionConfigHandler Tests" { + It "Returns xdebug config handler for xdebug extension" { + $handler = Get-ExtensionConfigHandler -extName 'php_xdebug.dll' - It "Returns -1 when user does not choose a dll extension version to install" { - $code = Install-XDebugExtension -iniPath $testIniPath - $code | Should -Be -1 + $handler | Should -Not -BeNullOrEmpty + # Should return the xdebug config handler scriptblock + $handler.GetType().Name | Should -Be 'ScriptBlock' } - It "Returns -1 when user does choose a non valid dll extension version to install" { - Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '-10' } - $code = Install-XDebugExtension -iniPath $testIniPath - $code | Should -Be -1 - } - - It "Returns -1 when user does not want to overwrite existing dll extension version" { - Set-MockWebResponse -url "$XDEBUG_DOWNLOAD_URL/php_xdebug-3.1.0-8.1-vs16-x64.dll" -content 'XDebug DLL content' - Mock Test-Path { return $true } - Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' } - Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nphp_xdebug-3.1.0-8.1-vs16-x64.dll already exists. Would you like to overwrite it? (y/n)" } -MockWith { return 'n' } - Mock Remove-ItemWrapper { } + It "Returns xdebug config handler for xdebug without prefix" { + $handler = Get-ExtensionConfigHandler -extName 'xdebug.dll' - $code = Install-XDebugExtension -iniPath $testIniPath - $code | Should -Be -1 + $handler | Should -Not -BeNullOrEmpty + $handler.GetType().Name | Should -Be 'ScriptBlock' } - It "Returns 0 when user wants to overwrite existing dll extension version" { - Set-MockWebResponse -url "$XDEBUG_DOWNLOAD_URL/php_xdebug-3.1.0-8.1-vs16-x64.dll" -content 'XDebug DLL content' - Set-MockWebResponse -url "$XDEBUG_DOWNLOAD_URL/php_xdebug-2.9.0-8.1-vs16-x64.dll" -content 'XDebug DLL content' - Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' } - Mock Test-FileExists -ParameterFilter { $path -eq "$TEST_DRIVE\ext\php_xdebug-3.1.0-8.1-vs16-x64.dll" } -MockWith { return $true } - Mock Read-HostWrapper -ParameterFilter { $prompt -like '*already exists. Would you like to overwrite it*' } -MockWith { return 'y' } - Mock Add-MissingPHPExtensionToIni { return 0 } - Mock Add-ContentWrapper { } - Mock Remove-ItemWrapper { } - Mock Move-ItemWrapper { } + It "Returns xdebug config handler for xdebug with version" { + $handler = Get-ExtensionConfigHandler -extName 'php_xdebug-3.1.0-8.1-vs16-x64.dll' - $code = Install-XDebugExtension -iniPath $testIniPath - $code | Should -Be 0 + $handler | Should -Not -BeNullOrEmpty + $handler.GetType().Name | Should -Be 'ScriptBlock' } - It "Handles exception gracefully" { - Mock Sort-Object { throw 'Error' } + It "Returns default handler for unknown extensions" { + $handler = Get-ExtensionConfigHandler -extName 'php_unknown.dll' - $code = Install-XDebugExtension -iniPath $testIniPath - $code | Should -Be -1 + $handler | Should -Not -BeNullOrEmpty + $handler.GetType().Name | Should -Be 'ScriptBlock' } - It "Returns -1 when no compatible extension version is found" { - Mock Test-CanUseCache { return $false } - Mock Get-XDebugFromUrl { return @() } + It "Returns default handler for empty input" { + $handler = Get-ExtensionConfigHandler -extName '' - $code = Install-XDebugExtension -iniPath $testIniPath - $code | Should -Be -1 - } - - It "Filters xdebug versions by architecture" { - Mock Get-CurrentPHPVersion { return @{ version = '8.1'; arch = 'x86'; buildType = 'ts'; path = "$TEST_DRIVE\php\8.1.0" } } - Mock Get-XDebugFromUrl { - return @( - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x64.dll"; arch = 'x64'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-vs16-x64.dll'; outerHTML = "test" } - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x86.dll"; arch = 'x86'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-vs16-x86.dll'; outerHTML = "test" } - ) - } - Mock Add-MissingPHPExtensionToIni { return 0 } - Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' } - Mock Invoke-WebRequestWrapper { } - Mock Move-ItemWrapper { } - Mock Get-ContentWrapper { return "zend_extension=opcache" } - Mock Set-ContentWrapper { } - Mock Remove-ItemWrapper { } - - $code = Install-XDebugExtension -iniPath $testIniPath - $code | Should -Be 0 + $handler | Should -Not -BeNullOrEmpty + $handler.GetType().Name | Should -Be 'ScriptBlock' } +} - It "Filters xdebug versions by build type" { - Mock Get-CurrentPHPVersion { return @{ version = '8.1'; arch = 'x64'; buildType = 'nts'; path = "$TEST_DRIVE\php\8.1.0" } } - Mock Get-XDebugFromUrl { - return @( - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-ts-x64.dll"; arch = 'x64'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-ts-x64.dll'; outerHTML = "test" } - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-nts-x64.dll"; arch = 'x64'; buildType = 'nts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-nts-x64.dll'; outerHTML = "test" } - ) +Describe "Install-Extension Tests" { + BeforeAll { + Mock Show-SpinnerWhileJob { + param ($scriptBlock, $message, $noClear, $argumentList, $rethrow) + $result = & $scriptBlock @argumentList + return $result.pvmData } - Mock Add-MissingPHPExtensionToIni { return 0 } - Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' } - Mock Invoke-WebRequestWrapper { } - Mock Move-ItemWrapper { } - Mock Get-ContentWrapper { return "zend_extension=opcache" } - Mock Set-ContentWrapper { } - Mock Remove-ItemWrapper { } - - $code = Install-XDebugExtension -iniPath $testIniPath - $code | Should -Be 0 - } - - It "Sorts prerelease versions correctly (alpha, beta, rc)" { - Mock Get-CurrentPHPVersion { return @{ version = '8.1'; arch = 'x64'; buildType = 'ts'; path = "$TEST_DRIVE\php\8.1.0" } } - Mock Get-XDebugFromUrl { - return @( - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x64.dll"; arch = 'x64'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-vs16-x64.dll'; outerHTML = "test" } - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0rc1-8.1-vs16-x64.dll"; arch = 'x64'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0rc1'; fileName = 'php_xdebug-3.1.0rc1-8.1-vs16-x64.dll'; outerHTML = "test" } - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0beta1-8.1-vs16-x64.dll"; arch = 'x64'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0beta1'; fileName = 'php_xdebug-3.1.0beta1-8.1-vs16-x64.dll'; outerHTML = "test" } - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0alpha1-8.1-vs16-x64.dll"; arch = 'x64'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0alpha1'; fileName = 'php_xdebug-3.1.0alpha1-8.1-vs16-x64.dll'; outerHTML = "test" } - ) + Mock Get-CurrentPHPVersion { return @{ version = '8.2'; arch = 'x64'; buildType = 'ts'; path = "$TEST_DRIVE\php\8.2.0" } } + Mock Read-HostWrapper { + param ($Prompt) + if ($Prompt -eq "`nEnter the [number] of your selection") { + return '0' + } } Mock Add-MissingPHPExtensionToIni { return 0 } - Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' } Mock Invoke-WebRequestWrapper { } Mock Move-ItemWrapper { } Mock Get-ContentWrapper { return "zend_extension=opcache" } - Mock Set-ContentWrapper { } - Mock Remove-ItemWrapper { } - - $code = Install-XDebugExtension -iniPath $testIniPath - $code | Should -Be 0 - } - - It "Returns -1 if fails to add xdebug to ini" { - Mock Get-CurrentPHPVersion { return @{ version = '8.1'; arch = 'x64'; buildType = 'ts'; path = "$TEST_DRIVE\php\8.1.0" } } - Mock Get-XDebugFromUrl { - return @( - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x64.dll"; arch = 'x64'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-vs16-x64.dll'; outerHTML = "test" } - ) - } - Mock Add-MissingPHPExtensionToIni { return -1 } - Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' } - Mock Invoke-WebRequestWrapper { } - Mock Move-ItemWrapper { } - Mock Remove-ItemWrapper { } - - $code = Install-XDebugExtension -iniPath $testIniPath - $code | Should -Be -1 - Should -Invoke Show-Error -ParameterFilter { $message -like '*Failed to add XDebug*' } - } - - It "Adds xdebug v3 config when no existing xdebug found" { - Mock Get-CurrentPHPVersion { return @{ version = '8.1'; arch = 'x64'; buildType = 'ts'; path = "$TEST_DRIVE\php\8.1.0" } } - Mock Get-XDebugFromUrl { - return @( - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x64.dll"; arch = 'x64'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-vs16-x64.dll'; outerHTML = "test" } - ) - } - Mock Add-MissingPHPExtensionToIni { return 0 } - Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' } - Mock Invoke-WebRequestWrapper { } - Mock Move-ItemWrapper { } - Mock Remove-ItemWrapper { } - Mock Get-ContentWrapper { return "zend_extension=opcache`nopache.enable = 1" } Mock Add-ContentWrapper { } - - $code = Install-XDebugExtension -iniPath $testIniPath - $code | Should -Be 0 - - # Verify Add-ContentWrapper was called for xdebug config - Should -Invoke Add-ContentWrapper -Times 1 -ParameterFilter { $Path -eq $testIniPath } } - It "Adds xdebug v2 config when version 2.x is selected" { - Mock Get-CurrentPHPVersion { return @{ version = '8.1'; arch = 'x64'; buildType = 'ts'; path = "$TEST_DRIVE\php\8.1.0" } } - Mock Get-XDebugFromUrl { - return @( - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-2.9.0-8.1-vs16-x64.dll"; arch = 'x64'; buildType = 'ts'; version = '8.1'; extVersion = '2.9.0'; fileName = 'php_xdebug-2.9.0-8.1-vs16-x64.dll'; outerHTML = "test" } - ) - } - Mock Add-MissingPHPExtensionToIni { return 0 } - Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' } - Mock Invoke-WebRequestWrapper { } + It "Successfully installs extension using source handler" { + Mock Invoke-WebRequestWrapper { return $null } + Mock Expand-Zip { } Mock Move-ItemWrapper { } Mock Remove-ItemWrapper { } - Mock Get-ContentWrapper { return "zend_extension=opcache`nopache.enable = 1" } - Mock Add-ContentWrapper { } - - $code = Install-XDebugExtension -iniPath $testIniPath - $code | Should -Be 0 - - # Verify Add-ContentWrapper was called with v2 config - Should -Invoke Add-ContentWrapper -Times 1 -ParameterFilter { - $Path -eq $testIniPath -and $Value -match 'xdebug.remote_enable' + $mockFile = @{ Name = 'php_curl.dll'; FullName = "$TEST_DRIVE\extracted\php_curl.dll" } + Mock Get-ChildItemWrapper { return @( $mockFile ) } + Mock Add-MissingPHPExtensionToIni { return 0 } + Mock Get-ExtensionPackages { + return @{ + extName = 'curl' + source = 'pecl.php.net' + data = @( + @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.1/php_curl-1.4.1-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' } + @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.1/php_curl-1.4.1-8.2-nts-vs16-x86.zip"; arch = 'x86'; buildType = 'nts' ; version = '8.2'; extVersion = '1.4.0' } + @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-nts-vs16-x64.zip"; arch = 'x64'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' } + ) + } } - } - It "Handles x86_64 architecture in sorting" { - Mock Get-CurrentPHPVersion { return @{ version = '8.1'; arch = $null; buildType = $null; path = "$TEST_DRIVE\php\8.1.0" } } - Mock Get-XDebugFromUrl { - return @( - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x86.dll"; arch = 'x86'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-vs16-x86.dll'; outerHTML = "test" } - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x86_64.dll"; arch = 'x86_64'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-vs16-x86_64.dll'; outerHTML = "test" } - ) - } - Mock Add-MissingPHPExtensionToIni { return 0 } - Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' } - Mock Invoke-WebRequestWrapper { } - Mock Move-ItemWrapper { } - Mock Remove-ItemWrapper { } - Mock Get-ContentWrapper { return "zend_extension=opcache" } - Mock Add-ContentWrapper { } + $code = Install-Extension -iniPath $testIniPath -extName 'curl' -skipConfirmation $true - $code = Install-XDebugExtension -iniPath $testIniPath $code | Should -Be 0 - } - - It "Handles unknown architecture in sorting" { - Mock Get-CurrentPHPVersion { return @{ version = '8.1'; arch = $null; buildType = $null; path = "$TEST_DRIVE\php\8.1.0" } } - Mock Get-XDebugFromUrl { - return @( - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x86.dll"; arch = 'x86'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-vs16-x86.dll'; outerHTML = "test" } - @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-arm64.dll"; arch = 'arm64'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-vs16-arm64.dll'; outerHTML = "test" } - ) + Should -Invoke Add-MissingPHPExtensionToIni -Exactly 1 + } + + It "Uses extension config handler for configuration" { + $mockFile = @{ Name = 'php_curl.dll'; FullName = "$TEST_DRIVE\extracted\php_curl.dll" } + Mock Get-ChildItemWrapper { return @( $mockFile ) } + Mock Get-ExtensionConfigHandler { + param ($extName) + return { + param ($iniPath, $fileName, $extVersion) + # Mock config handler + return 0 + } + } + Mock Get-ExtensionPackages { + return @{ + extName = 'xdebug' + source = 'xdebug.org' + data = @( + @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x64.dll"; arch = 'x64'; buildType = 'ts'; version = '8.1'; extVersion = '3.1.0'; fileName = 'php_xdebug-3.1.0-8.1-vs16-x64.dll' } + ) + } } - Mock Add-MissingPHPExtensionToIni { return 0 } - Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '1' } - Mock Invoke-WebRequestWrapper { } - Mock Move-ItemWrapper { } - Mock Remove-ItemWrapper { } - Mock Get-ContentWrapper { return "zend_extension=opcache" } - Mock Add-ContentWrapper { } - - $code = Install-XDebugExtension -iniPath $testIniPath - $code | Should -Be 0 - } - - It "Skips overwrite prompt and installs when skipConfirmation is true and file exists" { - Mock Test-CanUseCache { return $false } - Set-MockWebResponse -url "$XDEBUG_DOWNLOAD_URL/php_xdebug-3.1.0-8.1-vs16-x64.dll" -content 'XDebug DLL content' - Set-MockWebResponse -url "$XDEBUG_DOWNLOAD_URL/php_xdebug-2.9.0-8.1-vs16-x64.dll" -content 'XDebug DLL content' - Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' } - Mock Test-FileExists { return $true } - Mock Remove-ItemWrapper { } - Mock Move-ItemWrapper { } - Mock Get-ContentWrapper { return "zend_extension=opcache" } - Mock Add-ContentWrapper { } - Mock Add-MissingPHPExtensionToIni { return 0 } - $code = Install-XDebugExtension -iniPath $testIniPath -skipConfirmation $true + $code = Install-Extension -iniPath $testIniPath -extName 'xdebug' $code | Should -Be 0 - Should -Invoke Read-HostWrapper -Exactly 0 -ParameterFilter { - $Prompt -like '*already exists*' - } + Should -Invoke Get-ExtensionConfigHandler -Exactly 1 } - It "Prompts overwrite when skipConfirmation is false and file exists and user cancels" { - Set-MockWebResponse -url "$XDEBUG_DOWNLOAD_URL/php_xdebug-3.1.0-8.1-vs16-x64.dll" -content 'XDebug DLL content' - Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' } - Mock Test-FileExists { return $true } - Mock Read-HostWrapper -ParameterFilter { $prompt -like '*already exists*' } -MockWith { return 'n' } - Mock Remove-ItemWrapper { } + It "Returns -1 when no packages found" { + Mock Get-ExtensionPackages { return @{ extName = 'xdebug'; data = $null; source = 'xdebug.org' } } - $code = Install-XDebugExtension -iniPath $testIniPath -skipConfirmation $false + $code = Install-Extension -iniPath $testIniPath -extName 'xdebug' $code | Should -Be -1 - Should -Invoke Read-HostWrapper -Exactly 1 -ParameterFilter { - $Prompt -like '*already exists*' - } - } - - It "Prompts overwrite when skipConfirmation is false and file exists and user confirms" { - Mock Test-CanUseCache { return $false } - Set-MockWebResponse -url "$XDEBUG_DOWNLOAD_URL/php_xdebug-3.1.0-8.1-vs16-x64.dll" -content 'XDebug DLL content' - Set-MockWebResponse -url "$XDEBUG_DOWNLOAD_URL/php_xdebug-2.9.0-8.1-vs16-x64.dll" -content 'XDebug DLL content' - Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' } - Mock Test-FileExists { return $true } - Mock Read-HostWrapper -ParameterFilter { $prompt -like '*already exists*' } -MockWith { return 'y' } - Mock Remove-ItemWrapper { } - Mock Move-ItemWrapper { } - Mock Get-ContentWrapper { return "zend_extension=opcache" } - Mock Add-ContentWrapper { } - Mock Add-MissingPHPExtensionToIni { return 0 } - - $code = Install-XDebugExtension -iniPath $testIniPath -skipConfirmation $false - - $code | Should -Be 0 - Should -Invoke Read-HostWrapper -Exactly 1 -ParameterFilter { - $Prompt -like '*already exists*' - } + Should -Invoke Show-Error -Exactly 1 } } @@ -513,7 +272,17 @@ Describe "Add-MissingPHPExtensionToIni" { } It "Adds and configures xdebug in ini file" { - Mock Get-MatchingPHPExtensionsStatus { return @( @{ name = 'xdebug'; status = 'Enabled'; enabled = $true; color = 'DarkGreen' } )} + Mock Get-MatchingPHPExtensionsStatus { return @( @{ name = 'xdebug'; status = 'Enabled'; enabled = $true; color = 'DarkGreen'; LineNumber = 0 } )} + Mock Test-Path { return $true } + $result = Add-MissingPHPExtensionToIni -iniPath $testIniPath -extFileName 'php_xdebug.dll' + $result | Should -Be 0 + Should -Invoke Show-Success -Times 1 -ParameterFilter { + $message -like "- 'php_xdebug.dll' added successfully." + } + } + + It "Returns 0 and shows warning when extension already exists in ini file" { + Mock Get-MatchingPHPExtensionsStatus { return @( @{ name = 'xdebug'; status = 'Enabled'; enabled = $true; color = 'DarkGreen'; LineNumber = 150 } )} Mock Test-Path { return $true } $result = Add-MissingPHPExtensionToIni -iniPath $testIniPath -extFileName 'php_xdebug.dll' $result | Should -Be 0 @@ -707,6 +476,52 @@ Describe "Install-Extension" { } } + It "Returns -1 when user cancels the extension installation" { + Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { '' } + + $code = Install-Extension -iniPath $testIniPath -extName 'curl' + + $code | Should -Be -1 + Should -Invoke Write-Gray -Times 1 -ParameterFilter { $message -like '*Installation cancelled*' } + } + + It "Returns -1 when user enters an invalid selection" { + Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { 'unknown' } + + $code = Install-Extension -iniPath $testIniPath -extName 'curl' + + $code | Should -Be -1 + Should -Invoke Show-Warning -Times 1 -ParameterFilter { $message -like '*You answer is invalid*' } + } + + It "Returns -1 when user enters a negative selection" { + Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { -1 } + + $code = Install-Extension -iniPath $testIniPath -extName 'curl' + + $code | Should -Be -1 + Should -Invoke Show-Warning -Times 1 -ParameterFilter { $message -like '*Number must be between 0 and 1*' } + } + + It "Returns -1 when user enters a selection outside the valid range" { + Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { 5 } + + $code = Install-Extension -iniPath $testIniPath -extName 'curl' + + $code | Should -Be -1 + Should -Invoke Show-Warning -Times 1 -ParameterFilter { $message -like '*Number must be between 0 and 1*' } + } + + It "Returns -1 when no handler is found for the selected source" { + Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { 0 } + Mock Get-SourceHandler { return $null } + + $code = Install-Extension -iniPath $testIniPath -extName 'curl' + + $code | Should -Be -1 + Should -Invoke Show-Error -Times 1 -ParameterFilter { $message -like '*No handler found for source*' } + } + It "Returns -1 when gets empty list from extension" { $code = Install-Extension -iniPath $testIniPath -extName 'nonexistent_ext' $code | Should -Be -1 @@ -790,6 +605,7 @@ Describe "Install-Extension" { Mock Get-ExtensionPackages { return @{ extName = 'curl' + source = 'pecl.php.net' data = @( @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0'; fileName = 'php_curl-1.4.0-8.2-ts-vs16-x86.zip'; outerHTML = "8.2 Thread Safe (TS) x86" } @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-nts-vs16-x86.zip"; arch = 'x86'; buildType = 'nts' ; version = '8.2'; extVersion = '1.4.0'; fileName = 'php_curl-1.4.0-8.2-nts-vs16-x86.zip'; outerHTML = "8.2 Non Thread Safe (NTS) x86" } @@ -888,6 +704,7 @@ Describe "Install-Extension" { Mock Get-ExtensionPackages { return @{ extName = 'courierauth' + source = 'pecl.php.net' data = @( @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/courierauth/1.4.0/php_courierauth-1.4.0-8.2-ts-vs16-x64.zip"; arch = 'x64'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0'; fileName = 'php_courierauth-1.4.0-8.2-ts-vs16-x64.zip'; outerHTML = "8.2 Thread Safe (TS) x64" } @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/courierauth/1.4.0/php_courierauth-1.4.0-8.2-nts-vs16-x64.zip"; arch = 'x64'; buildType = 'nts' ; version = '8.2'; extVersion = '1.4.0'; fileName = 'php_courierauth-1.4.0-8.2-nts-vs16-x64.zip'; outerHTML = "8.2 Non Thread Safe (NTS) x64" } @@ -923,9 +740,15 @@ Describe "Install-Extension" { ) } } - Mock Select-ExtensionPackageLink { return $null } + $script:callCount = 0 + Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { + $script:callCount++ + if ($script:callCount -eq 1) { return '0' } + return '' + } $code = Install-Extension -iniPath $testIniPath -extName 'courierauth' $code | Should -Be -1 + Should -Invoke Show-Error -Times 1 -ParameterFilter { $message -like '*You chose the wrong index*' } } } @@ -940,6 +763,7 @@ Describe "Install-Extension" { Mock Get-ExtensionPackages { return @{ extName = 'curl' + source = 'pecl.php.net' data = @( @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.5.0/php_curl-1.5.0-8.2-ts-vs16-x64.zip"; arch = 'x64'; buildType = 'ts' ; version = '8.2'; extVersion = '1.5.0'; compiler = 'vs16' } @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.5.0rc1/php_curl-1.5.0rc1-8.2-ts-vs16-x64.zip"; arch = 'x64'; buildType = 'ts' ; version = '8.2'; extVersion = '1.5.0rc1'; compiler = 'vs16' } @@ -962,6 +786,7 @@ Describe "Install-Extension" { Mock Get-ExtensionPackages { return @{ extName = 'curl' + source = 'pecl.php.net' data = @( @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0'; compiler = 'vs16' } @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-ts-vs16-x86_64.zip"; arch = 'x86_64'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0'; compiler = 'vs16' } @@ -981,13 +806,14 @@ Describe "Install-Extension" { Mock Get-ExtensionPackages { return @{ extName = 'curl' + source = 'pecl.php.net' data = @( @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-ts-vs16-arm64.zip"; arch = 'arm64'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0'; compiler = 'vs16' } @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0'; compiler = 'vs16' } ) } } - Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '1' } + Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '0' } Mock Test-Path { return $false } Mock Add-MissingPHPExtensionToIni { return 0 } @@ -1000,6 +826,7 @@ Describe "Install-Extension" { Mock Get-ExtensionPackages { return @{ extName = 'curl' + source = 'pecl.php.net' data = @( @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0'; fileName = 'php_curl-1.4.0-8.2-ts-vs16-x86.zip'; outerHTML = "test" } ) @@ -1020,6 +847,7 @@ Describe "Install-Extension" { Mock Get-ExtensionPackages { return @{ extName = 'curl' + source = 'pecl.php.net' data = @( @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0'; fileName = 'php_curl-1.4.0-8.2-ts-vs16-x86.zip'; outerHTML = "test" } ) @@ -1041,6 +869,7 @@ Describe "Install-Extension" { Mock Get-ExtensionPackages { return @{ extName = 'curl' + source = 'pecl.php.net' data = @( @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0'; fileName = 'php_curl-1.4.0-8.2-ts-vs16-x86.zip'; outerHTML = "test" } ) @@ -1064,6 +893,7 @@ Describe "Install-Extension" { Mock Get-ExtensionPackages { return @{ extName = 'curl' + source = 'pecl.php.net' data = @( @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0'; fileName = 'php_curl-1.4.0-8.2-ts-vs16-x86.zip'; outerHTML = "test" } ) @@ -1086,6 +916,7 @@ Describe "Install-Extension" { Mock Get-ExtensionPackages { return @{ extName = 'curl' + source = 'pecl.php.net' data = @( @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts'; version = '8.2'; extVersion = '1.4.0'; fileName = 'php_curl-1.4.0-8.2-ts-vs16-x86.zip'; outerHTML = "test" } ) @@ -1112,6 +943,7 @@ Describe "Install-Extension" { Mock Get-ExtensionPackages { return @{ extName = 'curl' + source = 'pecl.php.net' data = @( @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts'; version = '8.2'; extVersion = '1.4.0'; fileName = 'php_curl-1.4.0-8.2-ts-vs16-x86.zip'; outerHTML = "test" } ) @@ -1137,6 +969,7 @@ Describe "Install-Extension" { Mock Get-ExtensionPackages { return @{ extName = 'curl' + source = 'pecl.php.net' data = @( @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts'; version = '8.2'; extVersion = '1.4.0'; fileName = 'php_curl-1.4.0-8.2-ts-vs16-x86.zip'; outerHTML = "test" } ) @@ -1164,6 +997,7 @@ Describe "Install-Extension" { Mock Get-ExtensionPackages { return @{ extName = 'curl' + source = 'pecl.php.net' data = @( @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.1/php_curl-1.4.1-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' } @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.1/php_curl-1.4.1-8.2-nts-vs16-x86.zip"; arch = 'x86'; buildType = 'nts' ; version = '8.2'; extVersion = '1.4.0' } @@ -1183,6 +1017,7 @@ Describe "Install-Extension" { Mock Get-ExtensionPackages { return @{ extName = 'curl' + source = 'pecl.php.net' data = @( @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.1/php_curl-1.4.1-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' } @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.1/php_curl-1.4.1-8.2-nts-vs16-x86.zip"; arch = 'x86'; buildType = 'nts' ; version = '8.2'; extVersion = '1.4.0' } @@ -1190,7 +1025,13 @@ Describe "Install-Extension" { ) } } - Mock Read-HostWrapper { '-1' } + + $script:callCount = 0 + Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { + $script:callCount++ + if ($script:callCount -eq 1) { return '0' } + return '-1' + } $code = Install-Extension -iniPath $testIniPath -extName 'curl' $code | Should -Be -1 @@ -1205,57 +1046,116 @@ Describe "Install-Extension" { $code | Should -Be -1 Should -Invoke Add-LogEntry } + + It "Returns -1 when no source supports the extension" { + Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '1' } + + $code = Install-Extension -iniPath $testIniPath -extName 'curl' + + $code | Should -Be -1 + Should -Invoke Show-Error -ParameterFilter { $message -like "*Source 'xdebug.org' does not support extension 'curl'*" } + } + + It "Shows the more info url" { + Mock Get-CurrentPHPVersion { return @{ version = '8.2.0'; path = "$TEST_DRIVE\php\8.2.0" } } + Mock Get-SourceHandler { + param ($sourceUrl) + return @{ + ResolveLinks = { + return @{ + extName = 'xdebug' + source = 'xdebug.org' + data = @( + @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-1.4.1-8.2-ts-vs16-x86.dll"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' } + @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-1.4.1-8.2-nts-vs16-x86.dll"; arch = 'x86'; buildType = 'nts' ; version = '8.2'; extVersion = '1.4.0' } + @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-1.4.0-8.2-nts-vs16-x64.dll"; arch = 'x64'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' } + ) + } + } + GetPackages = { + return @( + @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-1.4.1-8.2-ts-vs16-x86.dll"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' } + @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-1.4.1-8.2-nts-vs16-x86.dll"; arch = 'x86'; buildType = 'nts' ; version = '8.2'; extVersion = '1.4.0' } + @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-1.4.0-8.2-nts-vs16-x64.dll"; arch = 'x64'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' } + ) + } + Download = { return @{ Name = 'php_xdebug.dll'; FullName = "$TEST_DRIVE\extracted\php_xdebug.dll" } } + MoreInfoUrl = $XDEBUG_HISTORICAL_URL + } + } + Mock Get-ExtensionConfigHandler { + param ($extName) + return { return 0 } + } + Mock Get-ExtensionPackages { + return @{ + extName = 'xdebug' + source = 'xdebug.org' + data = @( + @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-1.4.1-8.2-ts-vs16-x86.dll"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' } + @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-1.4.1-8.2-nts-vs16-x86.dll"; arch = 'x86'; buildType = 'nts' ; version = '8.2'; extVersion = '1.4.0' } + @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-1.4.0-8.2-nts-vs16-x64.dll"; arch = 'x64'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' } + ) + } + } + Mock Read-HostWrapper -ParameterFilter { $prompt -eq "`nEnter the [number] of your selection" } -MockWith { return '1' } + + $code = Install-Extension -iniPath $testIniPath -extName 'xdebug' + + $code | Should -Be 0 + Should -Invoke Show-Info -ParameterFilter { $message -like "*This is a partial list. For a complete list, visit: $XDEBUG_HISTORICAL_URL*" } -Times 1 + } } Describe "Install-IniExtension" { It "Handles null extension name" { - $code = Install-IniExtension -iniPath $testIniPath -extName $null + $code = Install-IniExtension -iniPath $testIniPath -extNames $null $code | Should -Be -1 } It "Installs xdebug" { - Mock Install-XDebugExtension { return 0 } - $code = Install-IniExtension -iniPath $testIniPath -extName 'xdebug' + Mock Install-Extension { return 0 } + $code = Install-IniExtension -iniPath $testIniPath -extNames 'xdebug' $code | Should -Be 0 } - It "Installs extension" { + It "Installs pecl extension" { Mock Install-Extension { return 0 } - $code = Install-IniExtension -iniPath $testIniPath -extName 'curl' + $code = Install-IniExtension -iniPath $testIniPath -extNames 'curl' $code | Should -Be 0 } It "Returns -1 on error" { Mock Install-Extension { return -1 } - $code = Install-IniExtension -iniPath $testIniPath -extName 'curl' + $code = Install-IniExtension -iniPath $testIniPath -extNames 'curl' $code | Should -Be -1 } It "Handles thrown exception" { Mock Add-LogEntry { return 0 } Mock Install-Extension { throw 'Network error' } - $code = Install-IniExtension -iniPath $testIniPath -extName 'curl' + $code = Install-IniExtension -iniPath $testIniPath -extNames 'curl' $code | Should -Be -1 } - It "Passes skipConfirmation true to Install-XDebugExtension" { - Mock Install-XDebugExtension { return 0 } + It "Passes skipConfirmation true to Install-IniExtension" { + Mock Install-Extension { return 0 } $code = Install-IniExtension -iniPath $testIniPath -extNames @('xdebug') -skipConfirmation $true $code | Should -Be 0 - Should -Invoke Install-XDebugExtension -Exactly 1 -ParameterFilter { + Should -Invoke Install-Extension -Exactly 1 -ParameterFilter { $skipConfirmation -eq $true } } - It "Passes skipConfirmation false to Install-XDebugExtension by default" { - Mock Install-XDebugExtension { return 0 } + It "Passes skipConfirmation false to Install-IniExtension by default" { + Mock Install-Extension { return 0 } $code = Install-IniExtension -iniPath $testIniPath -extNames @('xdebug') $code | Should -Be 0 - Should -Invoke Install-XDebugExtension -Exactly 1 -ParameterFilter { + Should -Invoke Install-Extension -Exactly 1 -ParameterFilter { $skipConfirmation -eq $false } } @@ -1281,6 +1181,19 @@ Describe "Install-IniExtension" { $skipConfirmation -eq $false } } + + It "Returns -1 if one extension fails to install" { + Mock Install-Extension { + param ($extName) + + if ($extName -eq 'unknown') { return -1 } + return 0 + } + + $code = Install-IniExtension -iniPath $testIniPath -extNames @('curl', 'unknown') + + $code | Should -Be -1 + } } Describe "Get-PrereleaseSortKey" { diff --git a/tests/actions/ini/helpers/fetch.tests.ps1 b/tests/actions/ini/helpers/fetch.tests.ps1 index 846ffe98..d7a0f546 100644 --- a/tests/actions/ini/helpers/fetch.tests.ps1 +++ b/tests/actions/ini/helpers/fetch.tests.ps1 @@ -4,6 +4,9 @@ BeforeAll { $script:TEST_DRIVE = "$($PVMConfig.paths.directories.fakeStorage)\fetch-drive" $PVMConfig.test.setFakePaths.Invoke($TEST_DRIVE) + $script:testIniPath = "$TEST_DRIVE\php.ini" + $script:testPhpPath = "$TEST_DRIVE\php" + $script:XDEBUG_HISTORICAL_URL = $PVMConfig.links.xdebugHistorical $script:PECL_BASE_URL = $PVMConfig.links.peclBase $script:PECL_PACKAGES_URL = $PVMConfig.links.peclPackages $script:PECL_PACKAGE_ROOT_URL = $PVMConfig.links.peclPackageRoot @@ -52,6 +55,507 @@ AfterAll { $Global:PVMConfig = $PVMConfigBackup } +Describe "Get-ExtensionHandlers Tests" { + It "Returns unified handler registry with both source and config handlers" { + $handlers = Get-ExtensionHandlers + + $handlers.SourceHandlers | Should -Not -BeNullOrEmpty + $handlers.ExtensionConfigHandlers | Should -Not -BeNullOrEmpty + $handlers.SourceHandlers.ContainsKey('xdebug.org') | Should -Be $true + $handlers.SourceHandlers.ContainsKey('pecl.php.net') | Should -Be $true + $handlers.ExtensionConfigHandlers.ContainsKey('xdebug') | Should -Be $true + } +} + +Describe "Get-SourceHandler Tests" { + It "Returns correct handler for xdebug.org source" { + $handler = Get-SourceHandler -sourceUrl 'xdebug.org' + + $handler | Should -Not -BeNullOrEmpty + $handler.GetPackages | Should -Not -BeNullOrEmpty + $handler.Download | Should -Not -BeNullOrEmpty + $handler.MoreInfoUrl | Should -Not -BeNullOrEmpty + } + + It "Returns correct handler for pecl.php.net source" { + $handler = Get-SourceHandler -sourceUrl 'pecl.php.net' + + $handler | Should -Not -BeNullOrEmpty + $handler.GetPackages | Should -Not -BeNullOrEmpty + $handler.Download | Should -Not -BeNullOrEmpty + $handler.MoreInfoUrl | Should -Not -BeNullOrEmpty + } + + It "Returns default PECL handler for unknown sources" { + $handler = Get-SourceHandler -sourceUrl 'unknown.source.com' + + $handler | Should -Not -BeNullOrEmpty + # Should return the pecl.php.net handler as default + $handler.MoreInfoUrl | Should -Not -BeNullOrEmpty + } + + Context "When running source actions (download, configure and link) from xdebug source handler" { + BeforeEach { + Mock Show-SpinnerWhileJob { + param ($scriptBlock, $message, $noClear, $argumentList, $rethrow) + $result = & $scriptBlock @argumentList + return $result.pvmData + } + } + + It "Returns data null when no handler found for xdebug" { + Mock Get-CurrentPHPVersion { return @{ version = '8.2'; arch = 'x64'; buildType = 'ts'; path = "$TEST_DRIVE\php\8.2.0" } } + Mock Get-OrUpdateCache { return $null } + + $handler = Get-SourceHandler -sourceUrl 'xdebug.org' + + Mock Get-SourceHandler { return $null } + $result = & $handler.ResolveLinks -extName 'xdebug' + + $result.data | Should -BeNullOrEmpty + } + + It "Returns data null when no packages found" { + Mock Get-CurrentPHPVersion { return @{ version = '8.2'; arch = 'x64'; buildType = 'ts'; path = "$TEST_DRIVE\php\8.2.0" } } + Mock Get-OrUpdateCache { return $null } + + $handler = Get-SourceHandler -sourceUrl 'xdebug.org' + + $result = & $handler.ResolveLinks -extName 'xdebug' + + $result.data | Should -BeNullOrEmpty + } + + It "Resolves and returns xdebug links" { + Mock Get-CurrentPHPVersion { return @{ version = '8.2'; arch = 'x64'; buildType = 'ts'; path = "$TEST_DRIVE\php\8.2.0" } } + Mock Get-OrUpdateCache { + return @{ + extName = 'curl' + data = @( + @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.1/php_curl-1.4.1-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' } + @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.1/php_curl-1.4.1-8.2-nts-vs16-x86.zip"; arch = 'x86'; buildType = 'nts' ; version = '8.2'; extVersion = '1.4.0' } + @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-nts-vs16-x64.zip"; arch = 'x64'; buildType = 'nts' ; version = '8.2'; extVersion = '1.4.0' } + ) + } + } + + $handler = Get-SourceHandler -sourceUrl 'xdebug.org' + + $result = & $handler.ResolveLinks -extName 'xdebug' + + $result.source | Should -Be 'xdebug.org' + $result.data.Count | Should -Be 2 + $result.extName | Should -Be 'xdebug' + } + + It "Returns null when user cancels" { + Mock Get-XDebugFromUrl { return $null } + Mock Invoke-WebRequestWrapper { return $null } + Mock Test-FileExists { return $true } + $chosenItem = @{ fileName = 'php_xdebug-3.5.3-8.3-ts-vs16-x86_64.dll'; } + Mock Read-HostWrapper -ParameterFilter { $prompt -like "*$($chosenItem.fileName) already exists. Would you like to overwrite it?*" } -MockWith { return 'n' } + Mock Remove-ItemWrapper { } + + $handler = Get-SourceHandler -sourceUrl 'xdebug.org' + + $handler | Should -Not -BeNullOrEmpty + $handler.GetPackages | Should -Not -BeNullOrEmpty + $handler.Download | Should -Not -BeNullOrEmpty + $handler.MoreInfoUrl | Should -Be $XDEBUG_HISTORICAL_URL + + $null = & $handler.GetPackages -version '8.5' + $result = & $handler.Download -chosenItem $chosenItem -phpPath $testPhpPath -skipConfirmation $false + + $result | Should -BeNullOrEmpty + Should -Invoke Get-XDebugFromUrl -Times 1 + Should -Invoke Invoke-WebRequestWrapper -Times 1 + Should -Invoke Write-Gray -ParameterFilter { $message -like '*Installation cancelled*' } + } + + It "Returns downloaded file" { + Mock Get-XDebugFromUrl { return $null } + Mock Invoke-WebRequestWrapper { return $null } + $chosenItem = @{ fileName = 'php_xdebug-3.5.3-8.3-ts-vs16-x86_64.dll'; } + Mock Move-ItemWrapper { } + + $handler = Get-SourceHandler -sourceUrl 'xdebug.org' + + $handler | Should -Not -BeNullOrEmpty + $handler.GetPackages | Should -Not -BeNullOrEmpty + $handler.Download | Should -Not -BeNullOrEmpty + $handler.MoreInfoUrl | Should -Be $XDEBUG_HISTORICAL_URL + + $null = & $handler.GetPackages -version '8.5' + $result = & $handler.Download -chosenItem $chosenItem -phpPath $testPhpPath -skipConfirmation $true + + $result | Should -Not -BeNullOrEmpty + $result.Name | Should -Be $chosenItem.fileName + $result.FullName | Should -Be "$($PVMConfig.paths.directories.php)\$($chosenItem.fileName)" + Should -Invoke Get-XDebugFromUrl -Times 1 + Should -Invoke Invoke-WebRequestWrapper -Times 1 + Should -Invoke Move-ItemWrapper -Times 1 + } + + It "Handles exception gracefully" { + Mock Get-XDebugFromUrl { return $null } + Mock Invoke-WebRequestWrapper { throw 'Error' } + Mock Add-LogEntry { return 0 } + + $handler = Get-SourceHandler -sourceUrl 'xdebug.org' + + $handler | Should -Not -BeNullOrEmpty + $handler.GetPackages | Should -Not -BeNullOrEmpty + $handler.Download | Should -Not -BeNullOrEmpty + $handler.MoreInfoUrl | Should -Be $XDEBUG_HISTORICAL_URL + + $null = & $handler.GetPackages -version '8.5' + $result = & $handler.Download -chosenItem $chosenItem -phpPath $testPhpPath -skipConfirmation $true + + $result | Should -BeNullOrEmpty + Should -Invoke Get-XDebugFromUrl -Times 1 + Should -Invoke Invoke-WebRequestWrapper -Times 1 + Should -Invoke Add-LogEntry -Times 1 + } + } + + Context "When running source actions (download and configure) from pecl source handler" { + BeforeEach { + Mock Show-SpinnerWhileJob { + param ($scriptBlock, $message, $noClear, $argumentList, $rethrow) + $result = & $scriptBlock @argumentList + return $result.pvmData + } + } + + It "Resolves and returns extension links" { + Mock Get-CurrentPHPVersion { return @{ version = '8.2'; arch = 'x64'; buildType = 'ts'; path = "$TEST_DRIVE\php\8.2.0" } } + Mock Get-ExtensionPackages { + return @{ + extName = 'curl' + source = 'pecl.php.net' + data = @( + @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.1/php_curl-1.4.1-8.2-ts-vs16-x86.zip"; arch = 'x86'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' } + @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.1/php_curl-1.4.1-8.2-nts-vs16-x86.zip"; arch = 'x86'; buildType = 'nts' ; version = '8.2'; extVersion = '1.4.0' } + @{ href = "$PECL_WIN_EXT_DOWNLOAD_URL/curl/1.4.0/php_curl-1.4.0-8.2-nts-vs16-x64.zip"; arch = 'x64'; buildType = 'ts' ; version = '8.2'; extVersion = '1.4.0' } + ) + } + } + + $handler = Get-SourceHandler -sourceUrl 'pecl.php.net' + + $result = & $handler.ResolveLinks -extName 'curl' + + $result.source | Should -Be 'pecl.php.net' + $result.data.Count | Should -Be 3 + $result.extName | Should -Be 'curl' + } + + It "Returns null when no dll file found in downloaded zip" { + Mock Get-PackagesFromSourceLinks { return $null } + Mock Invoke-WebRequestWrapper { return $null } + $chosenItem = @{ fileName = 'php_xdebug-3.5.3-8.3-ts-vs16-x86_64.dll'; } + Mock Expand-Zip { } + Mock Get-ChildItemWrapper { return @() } + Mock Remove-ItemWrapper { } + + $handler = Get-SourceHandler -sourceUrl 'pecl.php.net' + + $handler | Should -Not -BeNullOrEmpty + $handler.GetPackages | Should -Not -BeNullOrEmpty + $handler.Download | Should -Not -BeNullOrEmpty + $handler.MoreInfoUrl | Should -Not -BeNullOrEmpty + + $links = @{ + extName = 'xdebug' + source = 'pecl.php.net' + links = @( + @{ href = "$PECL_BASE_URL/package/xdebug/3.4.0/windows" }, + @{ href = "$PECL_BASE_URL/package/xdebug/3.3.0/windows" }, + @{ href = "$PECL_BASE_URL/package/xdebug/3.2.0/windows" } + ) + } + + $null = & $handler.GetPackages -version '8.5' -linksObj $links + $result = & $handler.Download -chosenItem $chosenItem -phpPath $testPhpPath -skipConfirmation $true -extName 'xdebug' + + $result | Should -BeNullOrEmpty + Should -Invoke Get-PackagesFromSourceLinks -Times 1 + } + + It "Returns null when user cancels" { + Mock Get-PackagesFromSourceLinks { return $null } + Mock Invoke-WebRequestWrapper { return $null } + Mock Expand-Zip { } + $mockFile = @{ Name = 'php_xdebug.dll'; FullName = "$TEST_DRIVE\extracted\php_xdebug.dll" } + Mock Get-ChildItemWrapper { return @( $mockFile ) } + $chosenItem = @{ fileName = 'php_xdebug-3.5.3-8.3-ts-vs16-x86_64.dll'; } + Mock Test-FileExists { return $true } + Mock Read-HostWrapper -ParameterFilter { $prompt -like "*$($mockFile.Name) already exists. Would you like to overwrite it?*" } -MockWith { return 'n' } + Mock Remove-ItemWrapper { } + + $handler = Get-SourceHandler -sourceUrl 'pecl.php.net' + + $handler | Should -Not -BeNullOrEmpty + $handler.GetPackages | Should -Not -BeNullOrEmpty + $handler.Download | Should -Not -BeNullOrEmpty + $handler.MoreInfoUrl | Should -Not -BeNullOrEmpty + + $links = @{ + extName = 'xdebug' + source = 'pecl.php.net' + links = @( + @{ href = "$PECL_BASE_URL/package/xdebug/3.4.0/windows" }, + @{ href = "$PECL_BASE_URL/package/xdebug/3.3.0/windows" }, + @{ href = "$PECL_BASE_URL/package/xdebug/3.2.0/windows" } + ) + } + $null = & $handler.GetPackages -version '8.5' -linksObj $links + $result = & $handler.Download -chosenItem $chosenItem -phpPath $testPhpPath -skipConfirmation $false -extName 'xdebug' + $link = & $handler.MoreInfoUrl -extName 'xdebug' + + $result | Should -BeNullOrEmpty + $link | Should -Be "$PECL_PACKAGE_ROOT_URL/xdebug" + Should -Invoke Write-Gray -ParameterFilter { $message -like '*Installation cancelled*' } + Should -Invoke Get-PackagesFromSourceLinks -Times 1 + } + + It "Returns downloaded file" { + Mock Get-PackagesFromSourceLinks { return $null } + Mock Invoke-WebRequestWrapper { return $null } + Mock Expand-Zip { } + Mock Move-ItemWrapper { } + Mock Remove-ItemWrapper { } + $mockFile = @{ Name = 'php_xdebug.dll'; FullName = "$TEST_DRIVE\extracted\php_xdebug.dll" } + Mock Get-ChildItemWrapper { return @( $mockFile ) } + $chosenItem = @{ fileName = 'php_xdebug-3.5.3-8.3-ts-vs16-x86_64.dll'; } + + $handler = Get-SourceHandler -sourceUrl 'pecl.php.net' + + $handler | Should -Not -BeNullOrEmpty + $handler.GetPackages | Should -Not -BeNullOrEmpty + $handler.Download | Should -Not -BeNullOrEmpty + $handler.MoreInfoUrl | Should -Not -BeNullOrEmpty + + $links = @{ + extName = 'xdebug' + source = 'pecl.php.net' + links = @( + @{ href = "$PECL_BASE_URL/package/xdebug/3.4.0/windows" }, + @{ href = "$PECL_BASE_URL/package/xdebug/3.3.0/windows" }, + @{ href = "$PECL_BASE_URL/package/xdebug/3.2.0/windows" } + ) + } + $null = & $handler.GetPackages -version '8.5' -linksObj $links + $result = & $handler.Download -chosenItem $chosenItem -phpPath $testPhpPath -skipConfirmation $true -extName 'xdebug' + + $result.FullName | Should -Be $mockFile.FullName + $result.Name | Should -Be $mockFile.Name + Should -Invoke Get-PackagesFromSourceLinks -Times 1 + } + + It "Handles exception gracefully" { + Mock Get-PackagesFromSourceLinks { return $null } + Mock Invoke-WebRequestWrapper { throw 'Error' } + Mock Add-LogEntry { return 0 } + + $handler = Get-SourceHandler -sourceUrl 'pecl.php.net' + + $handler | Should -Not -BeNullOrEmpty + $handler.GetPackages | Should -Not -BeNullOrEmpty + $handler.Download | Should -Not -BeNullOrEmpty + $handler.MoreInfoUrl | Should -Not -BeNullOrEmpty + + $links = @{ + extName = 'xdebug' + source = 'pecl.php.net' + links = @( + @{ href = "$PECL_BASE_URL/package/xdebug/3.4.0/windows" }, + @{ href = "$PECL_BASE_URL/package/xdebug/3.3.0/windows" }, + @{ href = "$PECL_BASE_URL/package/xdebug/3.2.0/windows" } + ) + } + $null = & $handler.GetPackages -version '8.5' -linksObj $links + $result = & $handler.Download -chosenItem $chosenItem -phpPath $testPhpPath -skipConfirmation $true -extName 'xdebug' + + $result | Should -BeNullOrEmpty + Should -Invoke Get-PackagesFromSourceLinks -Times 1 + Should -Invoke Invoke-WebRequestWrapper -Times 1 + Should -Invoke Add-LogEntry -Times 1 + } + } +} + +Describe "Get-ExtensionConfigHandler Tests" { + It "Returns xdebug config handler for xdebug extension" { + $handler = Get-ExtensionConfigHandler -extName 'php_xdebug.dll' + + $handler | Should -Not -BeNullOrEmpty + # Should return the xdebug config handler scriptblock + $handler.GetType().Name | Should -Be 'ScriptBlock' + } + + It "Returns xdebug config handler for xdebug without prefix" { + $handler = Get-ExtensionConfigHandler -extName 'xdebug.dll' + + $handler | Should -Not -BeNullOrEmpty + $handler.GetType().Name | Should -Be 'ScriptBlock' + } + + It "Returns xdebug config handler for xdebug with version" { + $handler = Get-ExtensionConfigHandler -extName 'php_xdebug-3.1.0-8.1-vs16-x64.dll' + + $handler | Should -Not -BeNullOrEmpty + $handler.GetType().Name | Should -Be 'ScriptBlock' + } + + It "Returns default handler for unknown extensions" { + Mock Add-MissingPHPExtensionToIni { 0 } + + $handler = Get-ExtensionConfigHandler -extName 'php_unknown.dll' + + $handler | Should -Not -BeNullOrEmpty + $handler.GetType().Name | Should -Be 'ScriptBlock' + $code = & $handler $iniPath $null $fileName $null $extVersion $null + $code | Should -Be 0 + Should -Invoke Add-MissingPHPExtensionToIni -Times 1 + } + + It "Returns default handler for empty input" { + $handler = Get-ExtensionConfigHandler -extName '' + + $handler | Should -Not -BeNullOrEmpty + $handler.GetType().Name | Should -Be 'ScriptBlock' + } + + Context "When running config actions from selected config handler" { + It "Configures xdebug in ini file" { + Mock Get-ContentWrapper { return '' } + Mock Add-ContentWrapper { } + + $configHandler = Get-ExtensionConfigHandler -extName 'xdebug' + + $result = & $configHandler -iniPath $testIniPath -fileName 'php_xdebug.dll' -extVersion '3.5' + + $configHandler | Should -Not -BeNullOrEmpty + $result | Should -Be 0 + Should -Invoke Add-ContentWrapper -Times 1 + } + + It "Handles exception gracefully" { + Mock Get-ContentWrapper { throw 'Error' } + Mock Add-LogEntry { return 0 } + + $configHandler = Get-ExtensionConfigHandler -extName 'xdebug' + + $result = & $configHandler -iniPath $testIniPath -fileName 'php_xdebug.dll' -extVersion '3.5' + + $configHandler | Should -Not -BeNullOrEmpty + $result | Should -Be -1 + Should -Invoke Add-LogEntry -Times 1 + } + + It "Configures curl in ini file" { + Mock Add-MissingPHPExtensionToIni { return 0 } + + $configHandler = Get-ExtensionConfigHandler -extName 'curl' + + $result = & $configHandler -iniPath $testIniPath -fileName 'php_curl.dll' -extVersion '1.5' + + $configHandler | Should -Not -BeNullOrEmpty + $result | Should -Be 0 + Should -Invoke Add-MissingPHPExtensionToIni -Times 1 + } + } +} + +Describe "Get-XDebugFromUrl Tests" { + BeforeAll { + function Reset-MockState { + $script:MockRegistryThrowException = $false + $script:MockFileSystem.DownloadFails = $false + $script:MockFileSystem.WebResponses = @{} + $script:MockFileSystem.Files = @{} + $script:MockFileSystem.Directories = @() + } + + function Set-MockWebResponse { + param ($url, $content, $links = @()) + $script:MockFileSystem.WebResponses[$url] = @{ + Content = $content + Links = $links + } + } + } + BeforeEach { + Reset-MockState + } + + It "Should parse XDebug versions correctly" { + $mockLinks = @( + @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-vs16-x86_64.dll" }, + @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-2.9.0-8.1-vs16-x86_64.dll" }, + @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-nts-vs16-x86_64.dll" }, + @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-2.9.0-8.1-nts-vc16-x86_64.dll" }, + @{ href = "$XDEBUG_BASE_URL/download/php_random.dll" } + ) + Set-MockWebResponse -url 'https://test.com' -links $mockLinks + + $result = Get-XDebugFromUrl -url 'https://test.com' -version '8.1' + + $result.Count | Should -Be 4 + $result[0].extVersion | Should -Be '3.1.0' + $result[1].extVersion | Should -Be '2.9.0' + } + + It "Should handle network errors" { + $script:MockFileSystem.DownloadFails = $true + + $result = Get-XDebugFromUrl -url 'https://test.com' -version '8.1' + + $result | Should -Be @() + } + + It "Should parse xdebug with x86 architecture and unknown compiler" { + $mockLinks = @( + @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-3.1.0-8.1-x86.dll" }, + @{ href = "$XDEBUG_BASE_URL/download/php_xdebug-2.9.0-8.1-nts-x86.dll" } + ) + Set-MockWebResponse -url 'https://test.com' -links $mockLinks + + $result = Get-XDebugFromUrl -url 'https://test.com' -version '8.1' + + $result.Count | Should -Be 2 + $result[0].arch | Should -Be 'x86' + $result[0].compiler | Should -Be 'unknown' + $result[1].arch | Should -Be 'x86' + $result[1].compiler | Should -Be 'unknown' + } +} + +Describe "Get-XdebugConfigV2 Tests" { + It "Fetchs xdebug v2 config" { + $res = Get-XdebugConfigV2 -XDebugPath 'php_xdebug.dll' + + $res[0] | Should -Be '[xdebug]' + $res[1] | Should -Be ";zend_extension='php_xdebug.dll'" + $res[2] | Should -Be 'xdebug.remote_enable=1' + $res[3] | Should -Be 'xdebug.remote_host=127.0.0.1' + $res[4] | Should -Be 'xdebug.remote_port=9000' + } +} + +Describe "Get-XdebugConfigV2 Tests" { + It "Fetchs xdebug v3 config" { + $res = Get-XdebugConfigV3 -XDebugPath 'php_xdebug.dll' + + $res[0] | Should -Be '[xdebug]' + $res[1] | Should -Be ";zend_extension='php_xdebug.dll'" + $res[2] | Should -Be 'xdebug.mode=debug' + $res[3] | Should -Be 'xdebug.client_host=127.0.0.1' + $res[4] | Should -Be 'xdebug.client_port=9003' + } +} + Describe "Get-ExtensionCategoriesByPage Tests" { It "Returns extensions links by page" { Mock Invoke-WebRequestWrapper -ParameterFilter { $Uri -eq "$($PECL_PACKAGES_URL)?catpid=3&catname=Caching&pageID=1" } -MockWith { @@ -183,9 +687,9 @@ Describe "Get-ExtensionAvailableReleasesLinks" { $result = Get-ExtensionAvailableReleasesLinks -extName 'memcache' $result.Count | Should -Be 3 - $result[0].href | Should -Be "$($PVMConfig.links.peclBase)/package/memcache/3.4.0/windows" - $result[1].href | Should -Be "$($PVMConfig.links.peclBase)/package/memcache/3.3.0/windows" - $result[2].href | Should -Be "$($PVMConfig.links.peclBase)/package/memcache/3.2.0/windows" + $result[0].href | Should -Be "$PECL_BASE_URL/package/memcache/3.4.0/windows" + $result[1].href | Should -Be "$PECL_BASE_URL/package/memcache/3.3.0/windows" + $result[2].href | Should -Be "$PECL_BASE_URL/package/memcache/3.2.0/windows" } } @@ -669,13 +1173,25 @@ Describe "Resolve-ExtensionLinks Tests" { } It "Takes the only link found" { - Mock Get-ExtensionMatchingCategories { return @( @{ href = '/package/memcache'; extName = 'memcache' } ) } + Mock Get-ExtensionMatchingCategories { return @( @{ href = '/package/memcache'; extName = 'memcache'; source = 'pecl.php.net' } ) } $result = Resolve-ExtensionLinks -extName 'mem' -version '8.2' $result.extName | Should -Be 'memcache' $result.links.Count | Should -Be 3 } + + It "Should return empty links for sources other than pecl" { + Mock Get-ExtensionMatchingCategories { return @( + @{ href = "$XDEBUG_HISTORICAL_URL"; extName = 'xdebug'; source = 'xdebug.org' } + ) + } + + $result = Resolve-ExtensionLinks -extName 'debug' -version '8.2' + + $result.extName | Should -Be 'xdebug' + $result.links.Count | Should -Be 0 + } } It "Handles defensive check when chosen item is null" { @@ -711,6 +1227,7 @@ Describe "Get-ExtensionPackages Tests" { Mock Resolve-ExtensionLinks { return @{ extName = 'memcache' + source = 'pecl.php.net' links = @( @{ href = '/package/memcache/3.4.0/windows' }, @{ href = '/package/memcache/3.3.0/windows' }, @@ -739,11 +1256,12 @@ Describe "Get-ExtensionPackages Tests" { $result = Get-ExtensionPackages -extName 'cache' -version '8.2' $result.data | Should -Be $null + $result.source | Should -Be 'unknown' } It "Uses extName from linksObj when links are empty" { Mock Resolve-ExtensionLinks { - return @{ extName = 'memcache'; links = @() } + return @{ extName = 'memcache'; source = 'pecl.php.net'; links = @() } } $result = Get-ExtensionPackages -extName 'mem' -version '8.2' @@ -751,4 +1269,31 @@ Describe "Get-ExtensionPackages Tests" { $result.extName | Should -Be 'memcache' $result.data | Should -Be $null } + + It "Returns null when no handler found" { + Mock Resolve-ExtensionLinks { + return @{ extName = 'memcache'; source = 'pecl.php.net'; links = @() } + } + Mock Get-SourceHandler { return $null } + + $result = Get-ExtensionPackages -extName 'mem' -version '8.2' + + $result.extName | Should -Be 'memcache' + $result.data | Should -Be $null + $result.source | Should -Be 'pecl.php.net' + } + + It "Runs GetPackages for packages from sources other than pecl" { + Mock Get-OrUpdateCache { return $null } + Mock Resolve-ExtensionLinks { + return @{ extName = 'xdebug'; source = 'xdebug.org'; links = @() } + } + + $result = Get-ExtensionPackages -extName 'mem' -version '8.2' + + $result.extName | Should -Be 'xdebug' + $result.data | Should -Be $null + $result.source | Should -Be 'xdebug.org' + Should -Invoke Get-OrUpdateCache -Times 1 + } } diff --git a/tests/actions/ini/set.tests.ps1 b/tests/actions/ini/set.tests.ps1 index b3e6afd6..5984249d 100644 --- a/tests/actions/ini/set.tests.ps1 +++ b/tests/actions/ini/set.tests.ps1 @@ -181,4 +181,13 @@ memory_limit=2G $code = Set-IniSetting -iniPath $testIniPath -keys @('memory_limit=256M') $code | Should -Be -1 } + + It "Returns -1 if no match is found for any setting" { + @" +memory_limit=2G +"@ | Set-ContentWrapper -path $testIniPath + + $code = Set-IniSetting -iniPath $testIniPath -keys @('memory_limit=256M', 'unknown') + $code | Should -Be -1 + } } diff --git a/tests/actions/ini/status.tests.ps1 b/tests/actions/ini/status.tests.ps1 index 2f067afb..5ccd5c25 100644 --- a/tests/actions/ini/status.tests.ps1 +++ b/tests/actions/ini/status.tests.ps1 @@ -97,4 +97,17 @@ Describe "Get-IniExtensionStatus" { $code = Get-IniExtensionStatus -iniPath $testIniPath -extNames @('curl') $code | Should -Be -1 } + + It "Returns -1 if no match is found for any extension" { + Mock Get-MatchingPHPExtensionsStatus { + param ($extName) + + if ($extName -eq 'unknown') { return @() } + return @( @{ name = 'curl'; id='curl'; status='Enabled'; color='DarkGreen'; line=0; lineNamber=0; source='ext,ini' } ) + } + + $code = Get-IniExtensionStatus -iniPath $testIniPath -extNames @('curl', 'unknown') + + $code | Should -Be -1 + } }