Public/Tests/New-IntuneWin32Application.Tests.ps1
|
BeforeAll { $env:TCS_CONFIG_ROOT = Join-Path -Path $TestDrive -ChildPath 'config' $env:TCS_SKIP_UPDATE_CHECK = '1' $env:TCS_TELEMETRY_OPTOUT = '1' $ModuleRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent Import-Module -Name (Join-Path -Path $ModuleRoot -ChildPath 'tcs.intune.packaging.psd1') -Force } AfterAll { Remove-Module -Name tcs.intune.packaging -Force -ErrorAction SilentlyContinue } Describe 'New-IntuneWin32Application' { BeforeAll { . (Join-Path -Path $PSScriptRoot -ChildPath '../../../../tests/TestHelpers/New-TestIntuneWin.ps1') $script:Stubs = Add-GraphCommandStub $script:Package = (New-TestIntuneWin -Path (Join-Path -Path $TestDrive -ChildPath 'MyApp.intunewin') -SetupFile 'setup.msi').FullName $script:Detection = @{ '@odata.type' = '#microsoft.graph.win32LobAppProductCodeRule'; ruleType = 'detection'; productCode = '{ABC}'; productVersionOperator = 'notConfigured'; productVersion = $null } $script:Common = @{ Name = 'MyApp' Description = 'My application' Publisher = 'Contoso' InstallCommandLine = 'msiexec /i "setup.msi" /qn' UninstallCommandLine = 'msiexec /x "setup.msi" /qn' Rules = $script:Detection IntuneWinFilePath = $script:Package } } AfterAll { foreach ($name in $script:Stubs) { Remove-Item -Path "Function:\global:$name" -ErrorAction SilentlyContinue } } BeforeEach { Mock -ModuleName tcs.intune.packaging Get-MgContext { [PSCustomObject]@{ Account = 'admin@contoso.com'; TenantId = 't' } } Mock -ModuleName tcs.intune.packaging Invoke-MgGraphRequest { [PSCustomObject]@{ id = 'app-1'; displayName = 'MyApp' } } -ParameterFilter { $Method -eq 'POST' -and $Uri -eq 'v1.0/deviceAppManagement/mobileApps' } Mock -ModuleName tcs.intune.packaging Publish-IntuneWin32AppContent { '1' } } It 'Loads on every PowerShell edition and offers tab completion instead of a PowerShell 7-only ValidateSet class' { $parameter = (Get-Command -Name New-IntuneWin32Application).Parameters['ExistingPackage'] @($parameter.Attributes | Where-Object { $_ -is [System.Management.Automation.ArgumentCompleterAttribute] }).Count | Should -Be 1 } It 'Creates the win32LobApp with the documented properties and uploads the content' { $app = New-IntuneWin32Application @script:Common -Version '1.2.3' -Confirm:$false $app.id | Should -Be 'app-1' $app.committedContentVersion | Should -Be '1' Should -Invoke -ModuleName tcs.intune.packaging Publish-IntuneWin32AppContent -Times 1 -Exactly -ParameterFilter { $AppId -eq 'app-1' -and $IntuneWinPath -eq $script:Package } Should -Invoke -ModuleName tcs.intune.packaging Invoke-MgGraphRequest -Times 1 -Exactly -ParameterFilter { # Pester evaluates this filter for every recorded call; GET calls have no body $body = if ($Body) { $Body | ConvertFrom-Json } else { $null } $Method -eq 'POST' -and $Uri -eq 'v1.0/deviceAppManagement/mobileApps' -and $ContentType -eq 'application/json' -and $body.'@odata.type' -eq '#microsoft.graph.win32LobApp' -and $body.displayName -eq 'MyApp' -and $body.publisher -eq 'Contoso' -and $body.installCommandLine -eq 'msiexec /i "setup.msi" /qn' -and $body.fileName -eq 'MyApp.intunewin' -and $body.setupFilePath -eq 'setup.msi' -and $body.applicableArchitectures -eq 'x64' -and $body.installExperience.runAsAccount -eq 'system' -and $body.installExperience.deviceRestartBehavior -eq 'basedOnReturnCode' -and @($body.rules).Count -eq 1 -and $body.rules[0].productCode -eq '{ABC}' -and (@($body.returnCodes | ForEach-Object { "$($_.returnCode):$($_.type)" }) -join ',') -eq '0:success,1707:success,3010:softReboot,1641:hardReboot,1618:retry' -and $body.notes -eq 'Version: 1.2.3' -and -not ($body.PSObject.Properties.Name -contains 'minimumSupportedWindowsRelease') } } It 'Adds the icon as mimeContent' { $icon = Join-Path -Path $TestDrive -ChildPath 'icon.png' [System.IO.File]::WriteAllBytes($icon, [byte[]](1, 2, 3)) $null = New-IntuneWin32Application @script:Common -IconFilePath $icon -Confirm:$false Should -Invoke -ModuleName tcs.intune.packaging Invoke-MgGraphRequest -Times 1 -Exactly -ParameterFilter { # Pester evaluates this filter for every recorded call; GET calls have no body $body = if ($Body) { $Body | ConvertFrom-Json } else { $null } $Method -eq 'POST' -and $body.largeIcon.type -eq 'image/png' -and $body.largeIcon.value -eq 'AQID' } } It 'Requires a detection rule' { $requirement = @{ '@odata.type' = '#microsoft.graph.win32LobAppRegistryRule'; ruleType = 'requirement' } # Windows PowerShell 5.1 rejects a parameter given both in a splat and explicitly, so replace it in a copy $params = $script:Common.Clone() $params.Rules = $requirement { New-IntuneWin32Application @params -Confirm:$false } | Should -Throw '*detection rule*' Should -Invoke -ModuleName tcs.intune.packaging Invoke-MgGraphRequest -Times 0 -Exactly } It 'Creates nothing with -WhatIf' { $null = New-IntuneWin32Application @script:Common -WhatIf Should -Invoke -ModuleName tcs.intune.packaging Invoke-MgGraphRequest -Times 0 -Exactly Should -Invoke -ModuleName tcs.intune.packaging Publish-IntuneWin32AppContent -Times 0 -Exactly } It 'Reports the created app when the upload fails' { Mock -ModuleName tcs.intune.packaging Publish-IntuneWin32AppContent { throw 'upload broke' } { New-IntuneWin32Application @script:Common -Confirm:$false } | Should -Throw '*app-1*upload broke*' } It 'Throws a clear error when not connected to Microsoft Graph' { Mock -ModuleName tcs.intune.packaging Get-MgContext { } { New-IntuneWin32Application @script:Common -Confirm:$false } | Should -Throw '*Connect-MgGraph*' } It 'Copies the settings of an existing app and applies the parameters on top' { Mock -ModuleName tcs.intune.packaging Invoke-MgGraphRequest { [PSCustomObject]@{ '@odata.type' = '#microsoft.graph.win32LobApp' displayName = 'Old App' description = 'Old description' publisher = 'Old publisher' installCommandLine = 'old-install.cmd' uninstallCommandLine = 'old-uninstall.cmd' rules = @([PSCustomObject]@{ '@odata.type' = '#microsoft.graph.win32LobAppFileSystemRule'; ruleType = 'detection'; path = 'C:\App'; fileOrFolderName = 'app.exe'; operationType = 'exists' }) returnCodes = @([PSCustomObject]@{ returnCode = 0; type = 'success' }) } } -ParameterFilter { $Method -eq 'GET' -and $Uri -eq 'v1.0/deviceAppManagement/mobileApps/old-id' } $null = New-IntuneWin32Application -ExistingPackage 'Old App | old-id' -Name 'New App' -IntuneWinFilePath $script:Package -Confirm:$false Should -Invoke -ModuleName tcs.intune.packaging Invoke-MgGraphRequest -Times 1 -Exactly -ParameterFilter { # Pester evaluates this filter for every recorded call; GET calls have no body $body = if ($Body) { $Body | ConvertFrom-Json } else { $null } $Method -eq 'POST' -and $body.displayName -eq 'New App' -and $body.description -eq 'Old description' -and $body.installCommandLine -eq 'old-install.cmd' -and $body.rules[0].fileOrFolderName -eq 'app.exe' -and @($body.returnCodes).Count -eq 1 } } } |