commit fb540a28c54a4ec9fbd688985570cd2fcc118c1d
parent 11b19469932e7511c4e1b1d30945804276d5fc4b
Author: Ryan <69221034+ryfu-msft@users.noreply.github.com>
Date: Mon, 4 Dec 2023 11:24:04 -0800
Add WingetDSC E2E tests (#3939)
Diffstat:
5 files changed, 303 insertions(+), 56 deletions(-)
diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt
@@ -189,6 +189,7 @@ nonexistentsetting
norestart
normalizednameandpublisher
normalizedpackagenameandpublisher
+notcontains
NTSTATUS
nullsoft
nunit
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
@@ -466,6 +466,13 @@ jobs:
Contents: '**\*'
TargetFolder: '$(Build.ArtifactStagingDirectory)'
+ - task: CopyFiles@2
+ displayName: 'Copy Microsoft.WinGet.DSC module to staging directory'
+ inputs:
+ SourceFolder: '$(Build.SourcesDirectory)\src\PowerShell\Microsoft.WinGet.DSC'
+ Contents: '**\*'
+ TargetFolder: '$(Build.ArtifactStagingDirectory)\Microsoft.WinGet.DSC'
+
- task: PowerShell@2
displayName: Install Tests Dependencies
inputs:
diff --git a/src/PowerShell/Microsoft.WinGet.DSC/Microsoft.WinGet.DSC.psm1 b/src/PowerShell/Microsoft.WinGet.DSC/Microsoft.WinGet.DSC.psm1
@@ -221,10 +221,11 @@ class WinGetSources
foreach ($packageCatalogReference in $packageCatalogReferences)
{
$source = @{
- Arg = $packageCatalogReference.Info.Argument
- Identifier = $packageCatalogReference.Info.Id
- Name = $packageCatalogReference.Info.Name
- Type = $packageCatalogReference.Info.Type
+ $packageCatalogReference.Name = @{
+ Identifier = $packageCatalogReference.Id
+ Arg = $packageCatalogReference.Argument
+ Type = $packageCatalogReference.Type
+ }
}
$wingetSources.Add($source)
}
@@ -240,46 +241,39 @@ class WinGetSources
[bool] Test()
{
$currentSources = $this.Get().Sources
+ $currentState = [Ensure]::Present
- # If this is a full match and the counts are different give up.
- if (($this.Action -eq [WinGetAction]::Full) -and ($this.Sources.Count -ne $currentSources.Count))
+ # If this is a full match and the counts are different return false. This only applies if we want to ensure the full source is present.
+ if (($this.Action -eq [WinGetAction]::Full) -and ($this.Sources.Count -ne $currentSources.Count) -and ($this.Ensure -eq [Ensure]::Present))
{
return $false
}
- # There's no need to differentiate between Partial and Full anymore.
- foreach ($source in $this.Sources)
+ foreach ($sourceName in $this.Sources.Keys)
{
- # Require Name and Arg.
- if ((-not $source.ContainsKey("Name")) -or [string]::IsNullOrWhiteSpace($source.Name))
- {
- # TODO: Localize.
- throw "Invalid source input. Name is required."
- }
-
- if ((-not $source.ContainsKey("Arg")) -or [string]::IsNullOrWhiteSpace($source.Arg))
+ #Check if the source name exists, if it doesn't, then return false.
+ $result = $currentSources.Keys | Where-Object { $_ -eq $sourceName }
+ if ($null -eq $result)
{
- # TODO: Localize.
- throw "Invalid source input. Arg is required."
+ $currentState = [Ensure]::Absent
}
# Type has a default value.
- $sourceType = "Microsoft.PreIndexed.Package"
+ $source = $this.Sources.$($sourceName)
+ $sourceType = "Microsoft.PreIndexed.Package" # default source type
if ($source.ContainsKey("Type") -and (-not([string]::IsNullOrWhiteSpace($source.Type))))
{
$sourceType = $source.Type
}
- $result = $currentSources | Where-Object { $_.Name -eq $source.Name -and $_.Arg -eq $source.Arg -and $_.Type -eq $sourceType }
-
- # Source not found.
- if ($null -eq $result)
+ $existingSource = $currentSources.$($sourceName)
+ if ($source.Arg -ne $existingSource.Arg -or $sourceType -ne $existingSource.Type)
{
- return $false
+ $currentState = [Ensure]::Absent
}
}
- return $true
+ return $currentState -eq $this.Ensure
}
# Sets the desired properties.
@@ -290,40 +284,37 @@ class WinGetSources
Assert-WinGetCommand "Reset-WinGetSource"
Assert-WinGetCommand "Remove-WinGetSource"
- foreach ($source in $this.Sources)
+ if (-not $this.Test())
{
- $sourceType = "Microsoft.PreIndexed.Package"
-
- # Require Name and Arg.
- if ((-not $source.ContainsKey("Name")) -or [string]::IsNullOrWhiteSpace($source.Name))
- {
- # TODO: Localize.
- throw "Invalid source input. Name is required."
- }
-
- if ((-not $source.ContainsKey("Arg")) -or [string]::IsNullOrWhiteSpace($source.Arg))
- {
- # TODO: Localize.
- throw "Invalid source input. Arg is required."
- }
-
- if ($source.ContainsKey("Type") -and (-not([string]::IsNullOrWhiteSpace($source.Type))))
- {
- $sourceType = $source.Type
- }
-
- if ($this.Ensure -eq [Ensure]::Present)
+ foreach ($sourceName in $this.Sources.Keys)
{
- Add-WinGetSource -Name $source.Name -Argument $source.Argument -Type $sourceType
-
- if ($this.Reset)
+ $sourceType = "Microsoft.PreIndexed.Package"
+ $source = $this.Sources.$($sourceName)
+
+ if ((-not $source.ContainsKey("Arg")) -or [string]::IsNullOrWhiteSpace($source.Arg))
{
- Reset-WinGetSource -Name $source.Name
+ # TODO: Localize.
+ throw "Invalid source input. Arg is required."
+ }
+
+ if ($source.ContainsKey("Type") -and (-not([string]::IsNullOrWhiteSpace($source.Type))))
+ {
+ $sourceType = $source.Type
+ }
+
+ if ($this.Ensure -eq [Ensure]::Present)
+ {
+ Add-WinGetSource -Name $sourceName -Argument $source.Arg -Type $sourceType
+
+ if ($this.Reset)
+ {
+ Reset-WinGetSource -Name $sourceName
+ }
+ }
+ else
+ {
+ Remove-WinGetSource -Name $sourceName
}
- }
- else
- {
- Remove-WinGetSource -Name $source.Name
}
}
}
diff --git a/src/PowerShell/tests/Microsoft.WinGet.DSC.Tests.ps1 b/src/PowerShell/tests/Microsoft.WinGet.DSC.Tests.ps1
@@ -0,0 +1,241 @@
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License.
+
+<#
+.Synopsis
+ Pester tests related to the Microsoft.WinGet.DSC PowerShell module.
+ The tests require the localhost web server to be running and serving the test data.
+ 'Invoke-Pester' should be called in an admin PowerShell window.
+#>
+
+BeforeAll {
+ Install-Module -Name PSDesiredStateConfiguration -Force -SkipPublisherCheck
+ Import-Module Microsoft.WinGet.Client
+ Import-Module Microsoft.WinGet.DSC
+
+ # Helper function for calling Invoke-DscResource on the Microsoft.WinGet.DSC module.
+ function InvokeWinGetDSC() {
+ param (
+ [Parameter()]
+ [string]$Name,
+
+ [Parameter()]
+ [string]$Method,
+
+ [Parameter()]
+ [hashtable]$Property
+ )
+
+ return Invoke-DscResource -Name $Name -ModuleName Microsoft.WinGet.DSC -Method $Method -Property $Property
+ }
+}
+
+Describe 'List available DSC resources'{
+ It 'Shows DSC Resources'{
+ $expectedDSCResources = "WinGetAdminSettings", "WinGetPackage", "WinGetPackageManager", "WinGetSources", "WinGetUserSettings"
+ $availableDSCResources = (Get-DscResource -Module Microsoft.WinGet.DSC).Name
+ $availableDSCResources.length | Should -Be 5
+ $availableDSCResources | Where-Object {$expectedDSCResources -notcontains $_} | Should -BeNullOrEmpty -ErrorAction Stop
+ }
+}
+
+Describe 'WinGetAdminSettings' {
+
+ BeforeAll {
+ $initialAdminSettings = (Get-WinGetSettings).adminSettings
+ $adminSettingsHash = @{
+ BypassCertificatePinningForMicrosoftStore = !$initialAdminSettings.BypassCertificatePinningForMicrosoftStore;
+ InstallerHashOverride = !$initialAdminSettings.InstallerHashOverride;
+ LocalManifestFiles = !$initialAdminSettings.LocalManifestFiles;
+ LocalArchiveMalwareScanOverride = !$initialAdminSettings.LocalArchiveMalwareScanOverride;
+ }
+ }
+
+ It 'Get admin settings' {
+ $result = InvokeWinGetDSC -Name WinGetAdminSettings -Method Get -Property @{ Settings = $adminSettingsHash }
+ $adminSettings = $result.Settings
+ $adminSettings.BypassCertificatePinningForMicrosoftStore | Should -Be $initialAdminSettings.BypassCertificatePinningForMicrosoftStore
+ $adminSettings.InstallerHashOverride | Should -Be $initialAdminSettings.InstallerHashOverride
+ $adminSettings.LocalManifestFiles | Should -Be $initialAdminSettings.LocalManifestFiles
+ $adminSettings.LocalArchiveMalwareScanOverride | Should -Be $initialAdminSettings.LocalArchiveMalwareScanOverride
+ }
+
+ It 'Test admin settings' {
+ $result = InvokeWinGetDSC -Name WinGetAdminSettings -Method Test -Property @{ Settings = $adminSettingsHash }
+ $result.InDesiredState | Should -Be $false
+ }
+
+ It 'Set admin settings' {
+ InvokeWinGetDSC -Name WinGetAdminSettings -Method Set -Property @{ Settings = $adminSettingsHash }
+
+ # Verify settings were applied.
+ $result = InvokeWinGetDSC -Name WinGetAdminSettings -Method Get -Property @{ Settings = $adminSettingsHash }
+ $adminSettings = $result.Settings
+ $adminSettings.BypassCertificatePinningForMicrosoftStore | Should -Not -Be $initialAdminSettings.BypassCertificatePinningForMicrosoftStore
+ $adminSettings.InstallerHashOverride | Should -Not -Be $initialAdminSettings.InstallerHashOverride
+ $adminSettings.LocalManifestFiles | Should -Not -Be $initialAdminSettings.LocalManifestFiles
+ $adminSettings.LocalArchiveMalwareScanOverride | Should -Not -Be $initialAdminSettings.LocalArchiveMalwareScanOverride
+
+ $testResult = InvokeWinGetDSC -Name WinGetAdminSettings -Method Test -Property @{ Settings = $adminSettingsHash }
+ $testResult | Should -Be $true
+ }
+
+ AfterAll {
+ InvokeWinGetDSC -Name WinGetAdminSettings -Method Set -Property @{ Settings = $initialAdminSettings }
+ }
+}
+
+Describe 'WinGetUserSettings' {
+ BeforeAll {
+ # Delete existing user settings file.
+ $settingsFilePath = (Get-WinGetSettings).userSettingsFile
+ $backupSettingsFilePath = $settingsFilePath + ".backup"
+
+ if (Test-Path -Path $settingsFilePath)
+ {
+ Remove-Item $settingsFilePath
+ }
+
+ if (Test-Path -Path $backupSettingsFilePath)
+ {
+ Remove-Item $backupSettingsFilePath
+ }
+
+ $userSettingsHash = @{
+ experimentalFeatures = @{ directMSI = $true };
+ installBehavior = @{ Preferences = @{ Scope = 'User' }}
+ }
+ }
+
+ It 'Get user settings' {
+ $result = InvokeWinGetDSC -Name WinGetUserSettings -Method Get -Property @{ Settings = $userSettingsHash }
+ $result.Settings.Count | Should -Be 0
+ }
+
+ It 'Test user settings' {
+ $result = InvokeWinGetDSC -Name WinGetUserSettings -Method Test -Property @{ Settings = $userSettingsHash }
+ $result.InDesiredState | Should -Be $false
+ }
+
+ It 'Set user settings' {
+ InvokeWinGetDSC -Name WinGetUserSettings -Method Set -Property @{ Settings = $userSettingsHash }
+
+ # Verify user settings were applied.
+ $result = InvokeWinGetDSC -Name WinGetUserSettings -Method Get -Property @{ Settings = $userSettingsHash }
+ $userSettings = $result.Settings
+ $userSettings.experimentalFeatures.directMSI | Should -Be $true
+ $userSettings.installBehavior.Preferences.Scope | Should -Be 'User'
+ }
+}
+
+Describe 'WinGetSources' {
+ BeforeAll {
+ $testSourceName = 'TestSource'
+
+ $testSourceValue = @{
+ Type = 'Microsoft.PreIndexed.Package'
+ Arg = 'https://localhost:5001/TestKit/'
+ }
+
+ InvokeWinGetDSC -Name WinGetSources -Method Set -Property @{ Action = 'Partial'; Ensure = 'Absent'; Sources = @{ $testSourceName = $testSourceValue }}
+ }
+
+ It 'Get WinGet source' {
+ $result = InvokeWinGetDSC -Name WinGetSources -Method Get -Property @{ Sources = @{ $testSourceName = $testSourceValue }}
+ $result.Sources.Keys | Should -Not -Contain $testSourceName
+ }
+
+ It 'Test WinGet source' {
+ $result = InvokeWinGetDSC -Name WinGetSources -Method Test -Property @{ Ensure='Present'; Sources = @{ $testSourceName = $testSourceValue }}
+ $result.InDesiredState | Should -Be $false
+ }
+
+ It 'Set WinGet source' {
+ InvokeWinGetDSC -Name WinGetSources -Method Set -Property @{ Ensure = 'Present'; Sources = @{ $testSourceName = $testSourceValue }}
+ $result = InvokeWinGetDSC -Name WinGetSources -Method Get -Property @{ Sources = @{ $testSourceName = $testSourceValue }}
+ $result.Sources.Keys | Should -Contain $testSourceName
+
+ $testSource = $result.Sources.$($testSourceName)
+ $testSource.Type | Should -Be 'Microsoft.PreIndexed.Package'
+ $testSource.Arg | Should -Be 'https://localhost:5001/TestKit/'
+ $testSource.Identifier | Should -Be $null
+ }
+}
+
+Describe 'WinGetPackage' {
+ BeforeAll {
+ $testPackageId = 'AppInstallerTest.TestExeInstaller'
+ $testPackageVersion = '1.0.0.0'
+
+ # Add test source.
+ InvokeWinGetDSC -Name WinGetSources -Method Set -Property @{ Action = 'Partial'; Ensure = 'Present'; Sources = @{ TestSource = @{ Arg = 'https://localhost:5001/TestKit/'; Type = 'Microsoft.PreIndexed.Package' }}}
+ }
+
+ It 'Get WinGetPackage' {
+ $result = InvokeWinGetDSC -Name WinGetPackage -Method Get -Property @{ Id = $testPackageId; Version = $testPackageVersion }
+ $result.IsInstalled | Should -Be $false
+ }
+
+ It 'Test WinGetPackage' {
+ $result = InvokeWinGetDSC -Name WinGetPackage -Method Test -Property @{ Id = $testPackageId; Version = $testPackageVersion }
+ $result.InDesiredState | Should -Be $false
+ }
+
+ It 'Install WinGetPackage' {
+ InvokeWinGetDSC -Name WinGetPackage -Method Set -Property @{ Id = $testPackageId; Version = $testPackageVersion }
+
+ # Verify package installed.
+ $result = InvokeWinGetDSC -Name WinGetPackage -Method Get -Property @{ Id = $testPackageId; Version = $testPackageVersion }
+ $result.IsInstalled | Should -Be $true
+ $result.IsUpdateAvailable | Should -Be $true
+ $result.InstalledVersion | Should -Be 1.0.0.0
+ }
+
+ It 'Update WinGetPackage' {
+ $testResult = InvokeWinGetDSC -Name WinGetPackage -Method Test -Property @{ Id = $testPackageId; UseLatest = $true }
+ $testResult.InDesiredState | Should -Be $false
+
+ InvokeWinGetDSC -Name WinGetPackage -Method Set -Property @{ Id = $testPackageId; UseLatest = $true }
+
+ # Verify package updated.
+ $result = InvokeWinGetDSC -Name WinGetPackage -Method Get -Property @{ Id = $testPackageId; UseLatest = $true }
+ $result.IsInstalled | Should -Be $true
+ $result.IsUpdateAvailable | Should -Be $false
+ $result.InstalledVersion | Should -Not -Be 1.0.0.0
+ }
+
+ It 'Uninstall WinGetPackage' {
+ InvokeWinGetDSC -Name WinGetPackage -Method Set -Property @{ Id = $testPackageId; UseLatest = $true }
+
+ $testResult = InvokeWinGetDSC -Name WinGetPackage -Method Test -Property @{ Ensure = 'Absent'; Id = $testPackageId }
+ $testResult.InDesiredState | Should -Be $false
+
+ InvokeWinGetDSC -Name WinGetPackage -Method Set -Property @{ Ensure = 'Absent'; Id = $testPackageId }
+
+ # Verify package uninstalled.
+ $result = InvokeWinGetDSC -Name WinGetPackage -Method Get -Property @{ Ensure = 'Absent'; Id = $testPackageId }
+ $result.IsInstalled | Should -Be $false
+ }
+
+ AfterAll {
+ InvokeWinGetDSC -Name WinGetPackage -Method Set -Property @{ Ensure = 'Absent'; Id = $testPackageId}
+ }
+}
+
+Describe 'WinGetPackageManager' {
+ It 'Get WinGet version' {
+ $result = InvokeWinGetDSC -Name WinGetPackageManager -Method Get -Property @{}
+ $result.Version | Should -Not -Be $null
+ }
+
+ It 'Test WinGet version' {
+ $result = InvokeWinGetDSC -Name WinGetPackageManager -Method Test -Property @{ Version = "1.2.3.4" }
+ $result.InDesiredState | Should -Be $false
+
+ $currentVersion = Get-WinGetVersion
+ $result = InvokeWinGetDSC -Name WinGetPackageManager -Method Test -Property @{ Version = $currentVersion }
+ $result.InDesiredState | Should -Be $true
+ }
+
+ # TODO: Add test to verify Set method for WinGetPackageManager
+}
diff --git a/src/PowerShell/tests/RunTests.ps1 b/src/PowerShell/tests/RunTests.ps1
@@ -69,5 +69,12 @@ if ($PSEdition -eq "Core")
$configConfig.Run.Container = New-PesterContainer -Path "$PSScriptRoot\Microsoft.WinGet.Configuration.Tests.ps1" -Data @{ ConfigurationTestDataPath = $ConfigurationTestDataPath }
Invoke-Pester -Configuration $configConfig
-}
+ $dscConfig = New-PesterConfiguration
+ $dscConfig.TestResult.OutputFormat = "NUnitXML"
+ $dscConfig.TestResult.OutputPath = "$outputPath\Tests-WinGetDSC.XML"
+ $dscConfig.TestResult.Enabled = $true
+ $dscConfig.Run.Container = New-PesterContainer -Path "$PSScriptRoot\Microsoft.WinGet.DSC.Tests.ps1"
+
+ Invoke-Pester -Configuration $dscConfig
+}