Skip to content

Commit 3dc1668

Browse files
authored
Merge pull request #22 from pwshdevs/feature/self-updater
Adding self updater
2 parents 1fd99de + 08b01d8 commit 3dc1668

36 files changed

+3561
-67
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
BeforeAll {
2+
# Source the function under test
3+
. $PSScriptRoot\Update-DevSetup.ps1
4+
. $PSScriptRoot\..\Updater\Start-DevSetupSelfUpdate.ps1
5+
Mock Start-DevSetupSelfUpdate { }
6+
}
7+
8+
Describe "Update-DevSetup" {
9+
10+
Context "When Main parameter is specified" {
11+
It "Should call Start-DevSetupSelfUpdate with Main parameter" {
12+
Update-DevSetup -Main
13+
Assert-MockCalled Start-DevSetupSelfUpdate -Exactly 1 -Scope It -ParameterFilter { $Main -eq $true }
14+
}
15+
}
16+
17+
Context "When Develop parameter is specified" {
18+
It "Should call Start-DevSetupSelfUpdate with Develop parameter" {
19+
Update-DevSetup -Develop
20+
Assert-MockCalled Start-DevSetupSelfUpdate -Exactly 1 -Scope It -ParameterFilter { $Develop -eq $true }
21+
}
22+
}
23+
24+
Context "When Version parameter is specified" {
25+
It "Should call Start-DevSetupSelfUpdate with Version parameter" {
26+
Update-DevSetup -Version "1.0.0"
27+
Assert-MockCalled Start-DevSetupSelfUpdate -Exactly 1 -Scope It -ParameterFilter { $Version -eq "1.0.0" }
28+
}
29+
}
30+
31+
Context "When no parameters are specified (default)" {
32+
It "Should call Start-DevSetupSelfUpdate without parameters (letting it use its own defaults)" {
33+
Update-DevSetup
34+
Assert-MockCalled Start-DevSetupSelfUpdate -Exactly 1 -Scope It -ParameterFilter { $PSBoundParameters.Count -eq 0 }
35+
}
36+
}
37+
38+
Context "Parameter set validation" {
39+
It "Should allow Main parameter alone" {
40+
{ Update-DevSetup -Main } | Should -Not -Throw
41+
}
42+
43+
It "Should allow Develop parameter alone" {
44+
{ Update-DevSetup -Develop } | Should -Not -Throw
45+
}
46+
47+
It "Should allow Version parameter alone" {
48+
{ Update-DevSetup -Version "2.0.0" } | Should -Not -Throw
49+
}
50+
51+
It "Should not allow Main and Develop together" {
52+
{ Update-DevSetup -Main -Develop } | Should -Throw
53+
}
54+
55+
It "Should not allow Main and Version together" {
56+
{ Update-DevSetup -Main -Version "1.0.0" } | Should -Throw
57+
}
58+
59+
It "Should not allow Develop and Version together" {
60+
{ Update-DevSetup -Develop -Version "1.0.0" } | Should -Throw
61+
}
62+
}
63+
64+
Context "PSBoundParameters forwarding" {
65+
It "Should forward all parameters using splatting" {
66+
Mock Start-DevSetupSelfUpdate { } -ParameterFilter { $PSBoundParameters.Count -gt 0 }
67+
Update-DevSetup -Version "test"
68+
Assert-MockCalled Start-DevSetupSelfUpdate -Exactly 1 -Scope It
69+
}
70+
}
71+
72+
Context "Cross-platform compatibility" {
73+
It "Should work on Windows" {
74+
Update-DevSetup -Main
75+
Assert-MockCalled Start-DevSetupSelfUpdate -Exactly 1 -Scope It
76+
}
77+
78+
It "Should work on Linux" {
79+
Update-DevSetup -Develop
80+
Assert-MockCalled Start-DevSetupSelfUpdate -Exactly 1 -Scope It
81+
}
82+
83+
It "Should work on macOS" {
84+
Update-DevSetup -Version "1.0.0"
85+
Assert-MockCalled Start-DevSetupSelfUpdate -Exactly 1 -Scope It
86+
}
87+
}
88+
}
Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,13 @@
11
Function Update-DevSetup {
2-
[CmdletBinding()]
2+
[CmdletBinding(DefaultParameterSetName="ReleaseInstall")]
33
Param(
4-
[Parameter(Mandatory=$true, ParameterSetName="Main")]
4+
[Parameter(Mandatory=$true, ParameterSetName="MainWebInstall")]
55
[switch]$Main,
6-
[Parameter(Mandatory=$true, ParameterSetName="Develop")]
6+
[Parameter(Mandatory=$true, ParameterSetName="DevelopWebInstall")]
77
[switch]$Develop,
8-
[Parameter(Mandatory=$true, ParameterSetName="Version")]
9-
[string]$Version,
10-
[Parameter(Mandatory=$true, ParameterSetName="Latest")]
11-
[switch]$Latest
8+
[Parameter(Mandatory=$false, ParameterSetName="ReleaseInstall")]
9+
[string]$Version = "latest"
1210
)
1311

14-
$RemoteVersion = Get-DevSetupVersion -Remote
15-
$LocalVersion = Get-DevSetupVersion -Local
16-
if($RemoteVersion -gt $LocalVersion) {
17-
Write-Host "A new version of DevSetup is available: $RemoteVersion (current version: $LocalVersion)" -ForegroundColor Yellow
18-
} elseif ($RemoteVersion -eq $LocalVersion) {
19-
Write-Host "You are already running the latest version of DevSetup: $LocalVersion" -ForegroundColor Green
20-
return
21-
} else {
22-
Write-Host "You are running a newer version of DevSetup ($LocalVersion) than the latest release ($RemoteVersion)" -ForegroundColor Yellow
23-
return
24-
}
25-
Write-Host ""
26-
Write-Host "- Updating list of available environments..." -ForegroundColor Cyan
27-
Optimize-DevSetupEnvs | Out-Null
28-
Write-Host "- Available environments updated successfully" -ForegroundColor Green
29-
Write-Host ""
12+
Start-DevSetupSelfUpdate @PSBoundParameters
3013
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
BeforeAll {
2+
$global:LASTEXITCODE = 0
3+
Function Expand-Archive {
4+
param (
5+
[string]$Path,
6+
[string]$DestinationPath,
7+
[switch]$Force
8+
)
9+
}
10+
11+
Mock Expand-Archive {
12+
switch($Path) {
13+
(Join-Path $TestDrive "test.zip") {
14+
# Simulate successful expansion
15+
#Write-Output "test.zip expanded successfully"
16+
$global:LASTEXITCODE = 0
17+
return
18+
}
19+
(Join-Path $TestDrive "bad.zip") {
20+
#Write-Output "bad.zip encountered an error"
21+
# Simulate failed expansion
22+
throw "Simulated bad zip"
23+
return
24+
}
25+
(Join-Path $TestDrive "testdest.zip") {
26+
switch($DestinationPath) {
27+
(Join-Path $TestDrive "extracted") {
28+
#Write-Output "testdest.zip expanded successfully"
29+
# Simulate successful extraction
30+
$global:LASTEXITCODE = 0
31+
return
32+
}
33+
(Join-Path $TestDrive "badextract") {
34+
#Write-Output "testdest.zip encountered an error"
35+
# Simulate failed extraction
36+
throw "Simulated bad destination"
37+
return
38+
}
39+
default {
40+
#Write-Output "Invalid destination: $DestinationPath"
41+
# Simulate invalid destination
42+
$global:LASTEXITCODE = 1
43+
throw "Invalid destination: $DestinationPath"
44+
}
45+
}
46+
}
47+
default {
48+
Write-Error "File not found: $Path"
49+
# Simulate file not found
50+
$global:LASTEXITCODE = 1
51+
throw "File not found: $Path"
52+
}
53+
}
54+
# Simulate successful expansion
55+
$global:LASTEXITCODE = 1
56+
}
57+
. $PSScriptRoot\Expand-DevSetupUpdateArchive.ps1
58+
. $PSScriptRoot\..\Utils\Write-StatusMessage.ps1
59+
}
60+
61+
Describe "Expand-DevSetupUpdateArchive" {
62+
63+
Context "When the archive file does not exist" {
64+
It "Should return false and log an error" {
65+
Mock Write-StatusMessage { }
66+
$Archive = (Join-Path $TestDrive "nonexistent.zip")
67+
$result = Expand-DevSetupUpdateArchive -Path $Archive -DestinationPath (Join-Path $TestDrive "temp")
68+
$result | Should -Be $false
69+
Assert-MockCalled Write-StatusMessage -Exactly 1 -Scope It -ParameterFilter {
70+
$Message -match "Archive file not found at path: $([regex]::Escape($Archive))" -and $Verbosity -eq "Error"
71+
}
72+
}
73+
}
74+
75+
Context "When the archive expansion fails" {
76+
It "Should return false and log an error" {
77+
Mock Write-StatusMessage { }
78+
Mock Test-Path { $true } -ParameterFilter { $Path -eq (Join-Path $TestDrive "bad.zip") }
79+
$badArchive = (Join-Path $TestDrive "bad.zip")
80+
$goodDestination = (Join-Path $TestDrive "extracted")
81+
$result = Expand-DevSetupUpdateArchive -Path $badArchive -DestinationPath $goodDestination
82+
$result | Should -Be $false
83+
Assert-MockCalled Expand-Archive -Exactly 1 -Scope It -ParameterFilter {
84+
$Path -eq $badArchive -and $DestinationPath -eq $goodDestination -and $Force
85+
}
86+
Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter {
87+
$Message -match "Expanding archive file from $([regex]::Escape($badArchive)) to $([regex]::Escape($goodDestination))" -and $Verbosity -eq "Debug"
88+
} -Exactly 1
89+
}
90+
}
91+
92+
Context "When the archive expansion fails with bad destination" {
93+
It "Should return false and log an error" {
94+
Mock Write-StatusMessage { }
95+
Mock Test-Path { $true } -ParameterFilter { $Path -eq (Join-Path $TestDrive "testdest.zip") }
96+
$goodArchive = (Join-Path $TestDrive "testdest.zip")
97+
$badDestination = (Join-Path $TestDrive "badextract")
98+
$result = Expand-DevSetupUpdateArchive -Path $goodArchive -DestinationPath $badDestination
99+
$result | Should -Be $false
100+
Assert-MockCalled Expand-Archive -Exactly 1 -Scope It -ParameterFilter {
101+
$Path -eq $goodArchive -and $DestinationPath -eq $badDestination -and $Force
102+
}
103+
Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter {
104+
$Message -match "Expanding archive file from $([regex]::Escape($goodArchive)) to $([regex]::Escape($badDestination))" -and $Verbosity -eq "Debug"
105+
} -Exactly 1
106+
}
107+
}
108+
109+
Context "When the archive expansion fails with invalid destination" {
110+
It "Should return false and log an error" {
111+
Mock Write-StatusMessage { }
112+
Mock Test-Path { $true } -ParameterFilter { $Path -eq (Join-Path $TestDrive "testdest.zip") }
113+
$goodArchive = (Join-Path $TestDrive "testdest.zip")
114+
$invalidDestination = (Join-Path $TestDrive "invalid\path")
115+
$result = Expand-DevSetupUpdateArchive -Path $goodArchive -DestinationPath $invalidDestination
116+
$result | Should -Be $false
117+
Assert-MockCalled Expand-Archive -Exactly 1 -Scope It -ParameterFilter {
118+
$Path -eq $goodArchive -and $DestinationPath -eq $invalidDestination -and $Force
119+
}
120+
Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter {
121+
$Message -match "Expanding archive file from $([regex]::Escape($goodArchive)) to $([regex]::Escape($invalidDestination))" -and $Verbosity -eq "Debug"
122+
} -Exactly 1
123+
Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter {
124+
$Message -match "Failed to expand archive:" -and $Verbosity -eq "Error"
125+
} -Exactly 1
126+
Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter {
127+
$Verbosity -eq "Error"
128+
} -Exactly 2
129+
}
130+
}
131+
132+
Context "When the archive expansion succeeds" {
133+
It "Should return true and log debug messages" {
134+
Mock Write-StatusMessage { }
135+
Mock Test-Path { $true } -ParameterFilter { $Path -eq (Join-Path $TestDrive "test.zip") }
136+
$goodArchive = (Join-Path $TestDrive "test.zip")
137+
$goodDestination = (Join-Path $TestDrive "extracted")
138+
$result = Expand-DevSetupUpdateArchive -Path $goodArchive -DestinationPath $goodDestination
139+
$result | Should -Be $true
140+
Assert-MockCalled Expand-Archive -Exactly 1 -Scope It -ParameterFilter {
141+
$Path -eq $goodArchive -and $DestinationPath -eq $goodDestination -and $Force
142+
}
143+
Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter {
144+
$Message -match "Expanding archive file from $([regex]::Escape($goodArchive)) to $([regex]::Escape($goodDestination))" -and $Verbosity -eq "Debug"
145+
} -Exactly 1
146+
Assert-MockCalled Write-StatusMessage -Scope It -ParameterFilter {
147+
$Message -match "Expansion completed successfully." -and $Verbosity -eq "Debug"
148+
} -Exactly 1
149+
}
150+
}
151+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
Function Expand-DevSetupUpdateArchive {
2+
[CmdletBinding()]
3+
[OutputType([bool])]
4+
Param(
5+
[Parameter(Mandatory = $true, Position=0)]
6+
[ValidateNotNullOrEmpty()]
7+
[string]$Path,
8+
9+
[Parameter(Mandatory = $true, Position=1)]
10+
[ValidateNotNullOrEmpty()]
11+
[string]$DestinationPath
12+
)
13+
14+
if (-not (Test-Path $Path -ErrorAction SilentlyContinue)) {
15+
Write-StatusMessage "Archive file not found at path: $Path" -Verbosity Error
16+
return $false
17+
}
18+
19+
try {
20+
Write-StatusMessage "Expanding archive file from $Path to $DestinationPath" -Verbosity Debug
21+
Expand-Archive -Path $Path -DestinationPath $DestinationPath -Force
22+
Write-StatusMessage "Expansion completed successfully." -Verbosity Debug
23+
return $true
24+
} catch {
25+
Write-StatusMessage "Failed to expand archive: $_" -Verbosity Error
26+
Write-StatusMessage $_.ScriptStackTrace -Verbosity Error
27+
return $false
28+
}
29+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
BeforeAll {
2+
. $PSScriptRoot\Get-DevSetupModuleInstallPath.ps1
3+
. $PSScriptRoot\..\Providers\Powershell\Get-PowershellModuleScopeMap.ps1
4+
. $PSScriptRoot\..\Utils\Write-StatusMessage.ps1
5+
}
6+
7+
Describe "Get-DevSetupModuleInstallPath" {
8+
Context "When DevSetup module is installed in CurrentUser scope" {
9+
It "Should return the correct path" {
10+
$CurrentUserPath = (Join-Path (Join-Path (Join-Path $TestDrive "Documents" ) "PowerShell" ) "Modules")
11+
$AllUsersPath = (Join-Path (Join-Path (Join-Path $TestDrive "ProgramFiles" ) "PowerShell" ) "Modules")
12+
Mock Get-PowershellModuleScopeMap {
13+
return @(
14+
@{ Scope = "CurrentUser"; Path = $CurrentUserPath },
15+
@{ Scope = "AllUsers"; Path = $AllUsersPath }
16+
)
17+
}
18+
19+
$expectedPath = Join-Path -Path $CurrentUserPath -ChildPath "DevSetup"
20+
Mock Test-Path { return $true } -ParameterFilter { $Path -eq $expectedPath }
21+
22+
$result = Get-DevSetupModuleInstallPath
23+
$result | Should -Be $expectedPath
24+
}
25+
}
26+
27+
Context "When DevSetup module is installed in AllUsers scope" {
28+
It "Should return the correct path" {
29+
$CurrentUserPath = (Join-Path (Join-Path (Join-Path $TestDrive "Documents" ) "PowerShell" ) "Modules")
30+
$AllUsersPath = (Join-Path (Join-Path (Join-Path $TestDrive "ProgramFiles" ) "PowerShell" ) "Modules")
31+
Mock Get-PowershellModuleScopeMap {
32+
return @(
33+
@{ Scope = "CurrentUser"; Path = $CurrentUserPath },
34+
@{ Scope = "AllUsers"; Path = $AllUsersPath }
35+
)
36+
}
37+
38+
$expectedPath = Join-Path -Path $AllUsersPath -ChildPath "DevSetup"
39+
Mock Test-Path { return $true } -ParameterFilter { $Path -eq $expectedPath }
40+
41+
$result = Get-DevSetupModuleInstallPath
42+
$result | Should -Be $expectedPath
43+
}
44+
}
45+
46+
Context "When DevSetup module is not installed" {
47+
It "Should return the first scope path if module is not found" {
48+
$CurrentUserPath = (Join-Path (Join-Path (Join-Path $TestDrive "Documents" ) "PowerShell" ) "Modules")
49+
$AllUsersPath = (Join-Path (Join-Path (Join-Path $TestDrive "ProgramFiles" ) "PowerShell" ) "Modules")
50+
Mock Get-PowershellModuleScopeMap {
51+
return @(
52+
@{ Scope = "CurrentUser"; Path = $CurrentUserPath },
53+
@{ Scope = "AllUsers"; Path = $AllUsersPath }
54+
)
55+
}
56+
Mock Test-Path { return $false }
57+
$expectedPath = Join-Path -Path $CurrentUserPath -ChildPath "DevSetup"
58+
$result = Get-DevSetupModuleInstallPath
59+
$result | Should -Be $expectedPath
60+
}
61+
}
62+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
Function Get-DevSetupModuleInstallPath {
2+
[CmdletBinding()]
3+
Param()
4+
5+
# Get the module scope map
6+
$ScopeMap = Get-PowershellModuleScopeMap
7+
8+
foreach ($Scope in $ScopeMap) {
9+
$PotentialPath = Join-Path -Path $Scope.Path -ChildPath "DevSetup"
10+
if (Test-Path -Path $PotentialPath) {
11+
return $PotentialPath
12+
}
13+
}
14+
15+
return (Join-Path ($ScopeMap | Select-Object -First 1).Path -ChildPath "DevSetup")
16+
}

0 commit comments

Comments
 (0)