diff --git a/resources/GitDsc/GitDsc.psd1 b/resources/GitDsc/GitDsc.psd1 index fff1be9a..15177152 100644 --- a/resources/GitDsc/GitDsc.psd1 +++ b/resources/GitDsc/GitDsc.psd1 @@ -85,8 +85,7 @@ 'GitClone', 'GitRemote', 'GitConfigUserName', - 'GitConfigUserEmail', - 'GitConfigFile' + 'GitConfigUserEmail' ) # List of all modules packaged with this module @@ -101,7 +100,12 @@ PSData = @{ # Tags applied to this module. These help with module discovery in online galleries. - Tags = @('PSDscResource_GitClone', 'PSDscResource_GitRemote', 'PSDscResource_GitConfigUserName', 'PSDscResource_GitConfigUserEmail', 'PSDscResource_GitConfigFile') + Tags = @( + 'PSDscResource_GitClone', + 'PSDscResource_GitRemote', + 'PSDscResource_GitConfigUserName', + 'PSDscResource_GitConfigUserEmail' + ) # A URL to the license for this module. # LicenseUri = '' diff --git a/resources/GitDsc/GitDsc.psm1 b/resources/GitDsc/GitDsc.psm1 index e64d816b..ff80deaa 100644 --- a/resources/GitDsc/GitDsc.psm1 +++ b/resources/GitDsc/GitDsc.psm1 @@ -57,8 +57,7 @@ class GitClone { if ($gitRemoteValue -like $this.HttpsUrl) { $currentState.Ensure = [Ensure]::Present } - } - catch { + } catch { # Failed to execute `git remote`. Ensure state is `absent` } } @@ -114,8 +113,7 @@ class GitRemote { try { $gitRemoteValue = Invoke-GitRemote("get-url $($this.RemoteName)") $currentState.Ensure = ($gitRemoteValue -like $this.RemoteUrl) ? [Ensure]::Present : [Ensure]::Absent - } - catch { + } catch { $currentState.Ensure = [Ensure]::Absent } @@ -133,16 +131,13 @@ class GitRemote { if ($this.Ensure -eq [Ensure]::Present) { try { Invoke-GitRemote("add $($this.RemoteName) $($this.RemoteUrl)") - } - catch { + } catch { throw 'Failed to add remote repository.' } - } - else { + } else { try { Invoke-GitRemote("remove $($this.RemoteName)") - } - catch { + } catch { throw 'Failed to remove remote repository.' } } @@ -174,12 +169,10 @@ class GitConfigUserName { if ($this.ProjectDirectory) { if (Test-Path -Path $this.ProjectDirectory) { Set-Location $this.ProjectDirectory - } - else { + } else { throw 'Project directory does not exist.' } - } - else { + } else { throw 'Project directory parameter must be specified for non-system and non-global configurations.' } } @@ -206,8 +199,7 @@ class GitConfigUserName { if ($this.Ensure -eq [Ensure]::Present) { $configArgs = ConstructGitConfigUserArguments -Arguments "user.name '$($this.UserName)'" -ConfigLocation $this.ConfigLocation - } - else { + } else { $configArgs = ConstructGitConfigUserArguments -Arguments '--unset user.name' -ConfigLocation $this.ConfigLocation } @@ -240,12 +232,10 @@ class GitConfigUserEmail { if ($this.ProjectDirectory) { if (Test-Path -Path $this.ProjectDirectory) { Set-Location $this.ProjectDirectory - } - else { + } else { throw 'Project directory does not exist.' } - } - else { + } else { throw 'Project directory parameter must be specified for non-system and non-global configurations.' } } @@ -272,8 +262,7 @@ class GitConfigUserEmail { if ($this.Ensure -eq [Ensure]::Present) { $configArgs = ConstructGitConfigUserArguments -Arguments "user.email $($this.UserEmail)" -ConfigLocation $this.ConfigLocation - } - else { + } else { $configArgs = ConstructGitConfigUserArguments -Arguments '--unset user.email' -ConfigLocation $this.ConfigLocation } @@ -290,8 +279,7 @@ function Assert-Git { try { Invoke-Git -Command 'help' return - } - catch { + } catch { throw 'Git is not installed' } } diff --git a/tests/GitDsc/GitDsc.Tests.ps1 b/tests/GitDsc/GitDsc.Tests.ps1 new file mode 100644 index 00000000..a7b69578 --- /dev/null +++ b/tests/GitDsc/GitDsc.Tests.ps1 @@ -0,0 +1,102 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +using module GitDsc + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version Latest + +<# +.Synopsis + Pester tests related to the GitDsc PowerShell module. +#> + +BeforeAll { + if ($null -eq (Get-Module -ListAvailable -Name PSDesiredStateConfiguration)) { + Install-Module -Name PSDesiredStateConfiguration -Force -SkipPublisherCheck + } + + # Because the module asserts that Git is installed before it will load, the function needs to be mocked + # This ensures that the tests can run even on a machine that does not have Git installed + Mock Assert-Git -ModuleName GitDsc { return $true } + Import-Module GitDsc + +} + +Describe 'List available DSC resources' { + It 'Shows DSC Resources' { + $expectedDSCResources = 'GitClone', 'GitRemote', 'GitConfigUserName', 'GitConfigUserEmail' + $availableDSCResources = (Get-DscResource -Module GitDsc).Name + $availableDSCResources.count | Should -Be 4 + $availableDSCResources | Where-Object { $expectedDSCResources -notcontains $_ } | Should -BeNullOrEmpty -ErrorAction Stop + } +} + +InModuleScope -ModuleName GitDsc { + Describe 'GitClone' { + + BeforeAll { + Mock Invoke-GitClone -Verifiable + $global:HttpsUrl = 'https://github.com/microsoft/winget-dsc.git' + $global:TestGitRoot = Join-Path -Path $env:TEMP -ChildPath $(New-Guid) + } + + $script:gitCloneResource = [GitClone]::new() + Write-Output $gitCloneResource + + It 'New folder starts without cloned repo' { + $gitCloneResource.HttpsUrl = $global:HttpsUrl + $gitCloneResource.RootDirectory = $global:TestGitRoot + $initialState = $gitCloneResource.Get() + $initialState.Ensure | Should -Be 'Absent' + } + + It 'Set throws when ensuring absent' { + $gitCloneResource.Ensure = [Ensure]::Absent + { $gitCloneResource.Set() } | Should -Throw + } + + It 'Calls Invoke-GitClone when ensuring present' { + $gitCloneResource.HttpsUrl = $global:HttpsUrl + $gitCloneResource.RootDirectory = $global:TestGitRoot + $gitCloneResource.Ensure = [Ensure]::Present + # Run the setter + { $gitCloneResource.Set() } | Should -Not -Throw + # The setter should create the root directory if it doesn't exist + Test-Path $global:TestGitRoot | Should -Be $true + # Git clone should have been called once + Assert-MockCalled Invoke-GitClone -Exactly 1 + } + + It 'Test should fail when remote does not match' { + Mock Invoke-GitRemote { return 'https://github.com/Trenly/winget-dsc.git' } + + $gitCloneResource.HttpsUrl = $global:HttpsUrl + $gitCloneResource.RootDirectory = $global:TestGitRoot + $gitCloneResource.Ensure = [Ensure]::Present + + $gitCloneResource.Test() | Should -Be $false + } + + It 'Test should succeed when remote matches' { + # The folder has to be created here so that the DSC resource can attempt to fetch the remote from within it + New-Item -ItemType Directory -Path $(Join-Path -Path $global:TestGitRoot -ChildPath 'winget-dsc') -Force + + Mock Invoke-GitRemote -Verifiable { return 'https://github.com/microsoft/winget-dsc.git' } + + $gitCloneResource.HttpsUrl = $global:HttpsUrl + $gitCloneResource.RootDirectory = $global:TestGitRoot + $gitCloneResource.Ensure = [Ensure]::Present + + $gitCloneResource.Test() | Should -Be $true + Assert-MockCalled Invoke-GitRemote -Exactly 1 + } + + AfterAll { + # Clean up cloned folder + Remove-Item -Recurse -Force $global:TestGitRoot -ErrorAction 'SilentlyContinue' + } + } +} + +AfterAll { +}