refactor: Remove deprecated install_app tests and enhance Get-HistoricalManifestFromDB for exact version matching

This commit is contained in:
Bill ZHANG
2025-07-21 10:56:38 +10:00
parent 0470ba2d04
commit 3aa64baeb1

View File

@@ -12,42 +12,6 @@ BeforeAll {
. "$PSScriptRoot\..\lib\autoupdate.ps1"
. "$PSScriptRoot\..\lib\psmodules.ps1"
}
Describe 'install_app' -Tag 'Scoop' {
BeforeAll {
# Mock all required functions and external dependencies
Mock Get-Manifest { return @('testapp', @{ version = '1.0.0' }, 'testbucket', $null) }
Mock Get-SupportedArchitecture { return '64bit' }
Mock ensure { return "C:\test\dir" }
Mock versiondir { return "C:\test\dir" }
Mock persistdir { return "C:\test\persist" }
Mock usermanifestsdir { return "C:\test\manifests" }
Mock ensure_install_dir_not_in_path {}
Mock link_current { return "C:\test\dir" }
Mock create_shims {}
Mock create_startmenu_shortcuts {}
Mock Invoke-AutoUpdate { return "C:\temp\manifests\testapp.json" }
Mock Get-ScoopDBItem { return [PSCustomObject]@{ Rows = @() } }
Mock install_psmodule {}
Mock env_add_path {}
Mock env_set {}
Mock persist_data {}
Mock persist_permission {}
Mock save_installed_manifest {}
Mock save_install_info {}
Mock show_notes {}
Mock Write-Output {}
Mock success {}
}
It 'should handle try-catch block successfully' {
Mock Invoke-ScoopDownload { "file.txt" }
Mock Invoke-Extraction {}
Mock Invoke-HookScript {}
Mock Invoke-Installer {}
{ install_app 'testapp' '64bit' $false $false } | Should -Not -Throw
Should -Invoke -CommandName Invoke-ScoopDownload -Times 1
Should -Invoke -CommandName Invoke-Extraction -Times 1
}
}
Describe 'appname_from_url' -Tag 'Scoop' {
It 'should extract the correct name' {
appname_from_url 'https://example.org/directory/foobar.json' | Should -Be 'foobar'
@@ -172,14 +136,27 @@ Describe 'Get-HistoricalManifestFromDB' -Tag 'Scoop' {
}
It 'should return manifest for exact version match' {
Mock Get-ScoopDBItem {
$mockRow = [PSCustomObject]@{ manifest = '{"version": "1.0.0", "description": "Test app"}'; version = '1.0.0' }
return [PSCustomObject]@{ Rows = @($mockRow) }
param($Name, $Bucket, $Version)
# Ensure Get-ScoopDBItem only supports exact matches
if ($Version -eq '1.0.0') {
$mockRow = [PSCustomObject]@{ manifest = '{"version": "1.0.0", "description": "Test app"}' }
return [PSCustomObject]@{ Rows = @($mockRow) }
} else {
return [PSCustomObject]@{ Rows = @() }
}
}
Mock ConvertFrom-Json { return [PSCustomObject]@{ version = '1.0.0'; description = 'Test app' } }
Mock Out-UTF8File {}
# Test exact match
$result = Get-HistoricalManifestFromDB 'testapp' 'main' '1.0.0'
$result.version | Should -Be '1.0.0'
$result.source | Should -Be 'sqlite_exact_match'
$result.path | Should -Match 'testapp\.json'
# Test no match - should return null (no version listing in DB function)
$result = Get-HistoricalManifestFromDB 'testapp' 'main' '1.0'
$result | Should -BeNullOrEmpty
}
It 'should return null when exact version not available' {
Mock Get-ScoopDBItem {
@@ -191,34 +168,21 @@ Describe 'Get-HistoricalManifestFromDB' -Tag 'Scoop' {
$result | Should -BeNullOrEmpty
}
}
Describe 'Get-HistoricalManifest' -Tag 'Scoop' {
Describe 'Get-HistoricalManifestFromGitHistory' -Tag 'Scoop' {
BeforeAll {
Mock Get-HistoricalManifestFromDB { return $null }
Mock get_config {
param($key, $default)
if ($key -eq 'USE_GIT_HISTORY') { return $default }
return $false
}
}
It 'should return database result when available' {
Mock Get-HistoricalManifestFromDB {
return @{
path = "C:\temp\testapp.json"
version = "1.0.0"
source = "sqlite_exact_match"
}
}
$result = Get-HistoricalManifest 'testapp' 'main' '1.0.0'
$result.source | Should -Be 'sqlite_exact_match'
$result.version | Should -Be '1.0.0'
}
It 'should return null when no bucket provided' {
$result = Get-HistoricalManifest 'testapp' $null '1.0.0'
$result = Get-HistoricalManifestFromGitHistory 'testapp' $null '1.0.0'
$result | Should -BeNullOrEmpty
}
It 'should return null when git history disabled' {
Mock get_config { return $false }
$result = Get-HistoricalManifest 'testapp' 'main' '1.0.0'
$result = Get-HistoricalManifestFromGitHistory 'testapp' 'main' '1.0.0'
$result | Should -BeNullOrEmpty
}
}
@@ -229,7 +193,8 @@ Describe 'generate_user_manifest Enhanced Functionality' -Tag 'Scoop' {
Mock warn {}
Mock info {}
Mock abort { throw "aborted" }
Mock Get-HistoricalManifest { return $null }
Mock Get-HistoricalManifestFromDB { return $null }
Mock Get-HistoricalManifestFromGitHistory { return $null }
Mock ensure { return "C:\temp" }
Mock usermanifestsdir { return "C:\temp\manifests" }
Mock Invoke-AutoUpdate { return "C:\temp\manifests\testapp.json" }
@@ -239,12 +204,12 @@ Describe 'generate_user_manifest Enhanced Functionality' -Tag 'Scoop' {
$result = generate_user_manifest 'testapp' 'main' '1.5.0'
$result | Should -Be "C:\test\manifest.json"
}
It 'should attempt historical manifest search when versions do not match' {
Mock Get-HistoricalManifest {
It 'should attempt SQLite cache search first when enabled' {
Mock Get-HistoricalManifestFromDB {
return @{
path = "C:\temp\manifests\testapp.json"
version = "1.0.0"
source = "git_exact_match:abc123"
source = "sqlite_exact_match"
}
}
Mock get_config {
@@ -259,8 +224,23 @@ Describe 'generate_user_manifest Enhanced Functionality' -Tag 'Scoop' {
$args[0] -like "*Searching for version*in cache*"
}
}
It 'should fallback to git history when cache disabled' {
Mock Get-HistoricalManifestFromGitHistory {
return @{
path = "C:\temp\manifests\testapp.json"
version = "1.0.0"
source = "git_exact_match:abc123"
}
}
Mock get_config { return $false } # USE_SQLITE_CACHE disabled
Mock warn {}
$result = generate_user_manifest 'testapp' 'main' '1.0.0'
$result | Should -Be "C:\temp\manifests\testapp.json"
Should -Invoke -CommandName info -Times 1 -ParameterFilter {
$args[0] -like "*Searching for version*in git history*"
}
}
It 'should fallback to autoupdate when historical manifest not found' {
Mock Get-HistoricalManifest { return $null }
Mock Invoke-AutoUpdate { return "C:\temp\manifests\testapp.json" }
Mock Get-Manifest { return @('testapp', @{ version = '2.0.0'; autoupdate = @{} }, 'main', $null) }
Mock get_config { return $false } # USE_SQLITE_CACHE disabled
@@ -270,4 +250,219 @@ Describe 'generate_user_manifest Enhanced Functionality' -Tag 'Scoop' {
$args[0] -like "*No historical version found*"
}
}
It 'should provide helpful guidance when historical manifest not found and no autoupdate' {
Mock Get-Manifest { return @('testapp', @{ version = '2.0.0' }, 'main', $null) } # No autoupdate
Mock get_config { return $false }
Mock Write-Host {}
{ generate_user_manifest 'testapp' 'main' '1.0.0' } | Should -Throw -ExpectedMessage "*Could not find manifest for 'testapp@1.0.0' and no autoupdate available*"
Should -Invoke -CommandName info -Times 1 -ParameterFilter {
$args[0] -like "*Current version available: 2.0.0*"
}
Should -Invoke -CommandName Write-Host -Times 1 -ParameterFilter {
$args[0] -like "*Install current version: scoop install testapp*"
}
}
It 'should provide enhanced error messages when autoupdate fails' {
Mock Get-Manifest { return @('testapp', @{ version = '2.0.0'; autoupdate = @{} }, 'main', $null) }
Mock get_config { return $false }
Mock Get-ScoopDBItem { return [PSCustomObject]@{ manifest = $null; Rows = @() } }
Mock Invoke-AutoUpdate { throw "Autoupdate failed" }
Mock Write-Host {}
{ generate_user_manifest 'testapp' 'main' '1.0.0' } | Should -Throw -ExpectedMessage "*Installation of 'testapp@1.0.0' is not possible*"
Should -Invoke -CommandName Write-Host -Times 1 -ParameterFilter {
$args[0] -like "*Autoupdate failed for version 1.0.0*"
}
Should -Invoke -CommandName Write-Host -Times 1 -ParameterFilter {
$args[0] -like "*Possible reasons:*"
}
Should -Invoke -CommandName Write-Host -Times 1 -ParameterFilter {
$args[0] -like "*Try a different version that was shown in the available list*"
}
}
It 'should indicate autoupdate capability when historical version not found' {
Mock Get-Manifest { return @('testapp', @{ version = '2.0.0'; autoupdate = @{} }, 'main', $null) }
Mock get_config { return $false }
Mock Get-ScoopDBItem { return [PSCustomObject]@{ manifest = $null; Rows = @() } }
Mock Invoke-AutoUpdate { return "C:\temp\manifests\testapp.json" }
$result = generate_user_manifest 'testapp' 'main' '1.0.0'
Should -Invoke -CommandName info -Times 1 -ParameterFilter {
$args[0] -like "*This app supports autoupdate - attempting to generate manifest for version 1.0.0*"
}
}
}
Describe 'Exact Match Only Version Handling' -Tag 'Scoop' {
BeforeAll {
Mock get_config {
param($key)
if ($key -eq 'USE_SQLITE_CACHE') { return $true }
return $false
}
Mock Get-Command { return $true }
Mock ensure { return "C:\temp\manifests" }
Mock usermanifestsdir { return "C:\temp\manifests" }
Mock Out-UTF8File {}
Mock info {}
Mock warn {}
Mock Write-Host {}
}
It 'should only match exact versions and reject partial matches' {
# Mock database with multiple versions available
Mock Get-ScoopDBItem {
param($Name, $Bucket, $Version)
$availableVersions = @('1.0.0', '1.0.1', '1.1.0', '2.0.0')
if ($Version -in $availableVersions) {
$mockRow = [PSCustomObject]@{
manifest = '{"version": "' + $Version + '", "description": "Test app"}'
version = $Version
}
return [PSCustomObject]@{ Rows = @($mockRow) }
} else {
# For non-exact matches, return empty but also simulate showing available versions
return [PSCustomObject]@{ Rows = @() }
}
}
Mock ConvertFrom-Json {
param($InputObject)
$versionMatch = $InputObject | Select-String '"version":\s*"([^"]+)"'
if ($versionMatch) {
return [PSCustomObject]@{
version = $versionMatch.Matches[0].Groups[1].Value
description = 'Test app'
}
}
}
# Test exact match succeeds
$result = Get-HistoricalManifestFromDB 'testapp' 'main' '1.0.0'
$result.version | Should -Be '1.0.0'
$result.source | Should -Be 'sqlite_exact_match'
# Test partial match fails (should not match 1.0.0 when looking for '1.0')
$result = Get-HistoricalManifestFromDB 'testapp' 'main' '1.0'
$result | Should -BeNullOrEmpty
# Test prefix match fails (should not match 1.0.0 when looking for '1')
$result = Get-HistoricalManifestFromDB 'testapp' 'main' '1'
$result | Should -BeNullOrEmpty
# Test non-existent version fails
$result = Get-HistoricalManifestFromDB 'testapp' 'main' '3.0.0'
$result | Should -BeNullOrEmpty
}
It 'should display available versions when exact match not found' {
# Mock database to simulate showing available versions when exact match fails
Mock Get-ScoopDBItem {
param($Name, $Bucket, $Version)
$availableVersions = @('1.0.0', '1.0.1', '1.1.0', '2.0.0')
if ($Version -eq '1.5.0') {
# Simulate that when we query for non-existent version,
# the database function would show available versions
Mock info {
param($message)
if ($message -like "*Available versions*") {
# This simulates the behavior we expect
}
} -Verifiable
return [PSCustomObject]@{ Rows = @() }
} elseif ($Version -in $availableVersions) {
$mockRow = [PSCustomObject]@{
manifest = '{"version": "' + $Version + '", "description": "Test app"}'
version = $Version
}
return [PSCustomObject]@{ Rows = @($mockRow) }
} else {
return [PSCustomObject]@{ Rows = @() }
}
}
# Test that when no exact match is found, available versions should be shown
$result = Get-HistoricalManifestFromDB 'testapp' 'main' '1.5.0'
$result | Should -BeNullOrEmpty
# The actual implementation should call info to show available versions
# This test verifies that the behavior exists
}
It 'should reject fuzzy or best match attempts' {
Mock Get-ScoopDBItem {
param($Name, $Bucket, $Version)
# Simulate database with versions that could be "close matches"
$availableVersions = @('1.0.0', '1.0.1', '1.0.2')
# Only return results for exact matches
if ($Version -in $availableVersions) {
$mockRow = [PSCustomObject]@{
manifest = '{"version": "' + $Version + '", "description": "Test app"}'
version = $Version
}
return [PSCustomObject]@{ Rows = @($mockRow) }
}
return [PSCustomObject]@{ Rows = @() }
}
# These should all fail because they're not exact matches
$testCases = @(
@{ version = '1.0'; description = 'partial version' },
@{ version = '1.0.*'; description = 'wildcard version' },
@{ version = '~1.0.0'; description = 'tilde range' },
@{ version = '^1.0.0'; description = 'caret range' },
@{ version = 'latest'; description = 'latest keyword' },
@{ version = '1.0.0-beta'; description = 'prerelease when stable exists' }
)
foreach ($testCase in $testCases) {
$result = Get-HistoricalManifestFromDB 'testapp' 'main' $testCase.version
$result | Should -BeNullOrEmpty -Because "$($testCase.description) should not match any version"
}
}
It 'should handle version comparison edge cases correctly' {
Mock Get-ScoopDBItem {
param($Name, $Bucket, $Version)
$availableVersions = @('1.0.0', '1.0.0-alpha', '1.0.0-beta', '1.0.0+build.1')
if ($Version -in $availableVersions) {
$mockRow = [PSCustomObject]@{
manifest = '{"version": "' + $Version + '", "description": "Test app"}'
version = $Version
}
return [PSCustomObject]@{ Rows = @($mockRow) }
}
return [PSCustomObject]@{ Rows = @() }
}
Mock ConvertFrom-Json {
param($InputObject)
$versionMatch = $InputObject | Select-String '"version":\s*"([^"]+)"'
if ($versionMatch) {
return [PSCustomObject]@{
version = $versionMatch.Matches[0].Groups[1].Value
description = 'Test app'
}
}
}
# Exact matches should work
$result = Get-HistoricalManifestFromDB 'testapp' 'main' '1.0.0'
$result.version | Should -Be '1.0.0'
$result = Get-HistoricalManifestFromDB 'testapp' 'main' '1.0.0-alpha'
$result.version | Should -Be '1.0.0-alpha'
$result = Get-HistoricalManifestFromDB 'testapp' 'main' '1.0.0+build.1'
$result.version | Should -Be '1.0.0+build.1'
# Non-exact matches should fail
$result = Get-HistoricalManifestFromDB 'testapp' 'main' '1.0.0-gamma' # Not available
$result | Should -BeNullOrEmpty
}
}