functions/crm-ci.Tests.ps1
$here = Split-Path -Parent $MyInvocation.MyCommand.Path $sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.' . "$here\$sut" if (-not $Global:cred) { $Global:cred = Get-Credential -Message "CRM Credentials" -UserName $env:USERNAME } Function Assert-PropertiesMatch { Param ( $Expected, $Actual ) Process { foreach ($prop in ($Expected.psobject.properties.name)) { if ($Expected."$prop".GetType().Name -eq "DateTime") { [string]$Actual."$prop".ToString() | Should -Be $Expected."$prop".ToString() } else { [string]$Actual."$prop" | Should -Be $Expected."$prop" } } } } Describe "Get-CrmConnectionString" { It "Returns either a connection string or just the url depending on the -UrlOnly switch" { [string]$connString = Get-CrmConnectionString -CrmInstance CRMADVISE $connString.StartsWith("AuthType=AD;Url=") | Should -Be $true [string]$urlOnly = Get-CrmConnectionString -CrmInstance CRMADVISE -UrlOnly $urlOnly.StartsWith("https://") | Should -Be $true } It "Returns the correct address based on the crm instance" { [string]$connString = Get-CrmConnectionString -CrmInstance CRMADVISETEST $connString | Should -Be "AuthType=AD;Url=https://advisedevcrm.regent.edu/CRMADVISETEST;" } } Describe "Get-RegentConnection" { It "should call through to Get-CrmConnection" { $regent = Get-RegentConnection -CrmInstance CRMRECRUITTEST $datatTools = (Get-CrmConnection -ConnectionString "AuthType=AD;Url=https://rctrdevcrm.regent.edu/CRMRECRUITTEST;") $regent.GetType() | Should -Be $datatTools.GetType() } } Describe "Export-RegentSolution" { Context "Given a config file specifying repository path TestDrive:/solutions" { New-Item TestDrive:\solutions -ItemType Directory Mock 'Get-Config' { [PSCustomObject]@{ RepositoryPath = "TestDrive:\solutions"; DefaultBranch = "master"; } } It "should download the solution as both managed and unmanged, and export it to the path specified in the config" { $conn = Get-RegentConnection -CrmInstance CRMRECRUITTEST $results = Export-RegentSolution -SolutionName "RecordLocked" -conn $conn -Verbose $results[0].ExportSolutionResponse.GetType().Name | Should -Be "ExportSolutionResponse" $results[1].ExportSolutionResponse.GetType().Name | Should -Be "ExportSolutionResponse" Test-Path "TestDrive:/solutions/RecordLocked/" | Should -Be $true } } } Describe "Get-SolutionImportLog" { Context "Given an ImportJob existing in CRM CRMRECRUITTEST with an ID of 60255876-6aae-e511-80c2-0050569b65f8" { It "Should return the formatted xml results and export them if specified" { $exportPath = "TestDrive:\logs\log.xml" # $exportPath = "C:\repos\ps\crm-ci\log.xml" $id = "60255876-6aae-e511-80c2-0050569b65f8" # $id = "34412c0d-ac41-49ed-a1db-ca8ae5450e1f" $results = Get-SolutionImportLog ` -CrmInstance CRMRECRUITTEST ` -ImportJobId $id ` -ExportPath $exportPath ` -Credential $global:cred $results | Should -Not -BeNullOrEmpty Test-Path $exportPath | Should -Be $true $contents = Get-Content $exportPath $contents | Should -Be $results[1] } } } Describe "Import-RegentSolution" { Context "Given a config file specifying repository path TestDrive:/solutions" { New-Item TestDrive:\solutions -ItemType Directory Mock 'Get-Config' { [PSCustomObject]@{ RepositoryPath = "C:\repos\ps\crm-ci\test-data\unpacked"; DefaultBranch = "master"; } } It "Should Import the solution by default" { Import-RegentSolution ` -SolutionName RecordLocked ` -CrmInstance CRMRECRUITTEST ` -Emails "dhines@regent.edu" ` -Managed $false ` -Credential $global:cred ` -Verbose } It "Should throw any import errors and email the error message" { # In this example, I'm importing a managed version of solution into a CRM that # has the unmanged version intalled, which will cause an import error { Import-RegentSolution ` -SolutionName RecordLocked ` -CrmInstance CRMRECRUITTEST ` -Managed $true ` -Emails "dhines@regent.edu" ` -Credential $global:cred ` -Verbose } | Should -Throw } } } Describe "Invoke-RegentScheduledImports" { Mock -CommandName Import-RegentSolution -Verifiable -MockWith {return} New-Item TestDrive:\solutions -ItemType Directory Mock 'Get-Config' { [PSCustomObject]@{ RepositoryPath = "TestDrive:\solutions"; DefaultBranch = "master"; } } $configPath = "TestDrive:\solutions\solution-imports.json" Context "Given no import jobs" { if (Test-Path $configPath) { Remove-Item $configPath } # On reconsideration, I don't think it should throw an error, since it will be run all the time by the task scheduler, whether or not a config file has been created # It "Should throw an error if the config file doesn't exist" { # { Invoke-RegentScheduledImports } | Should -Throw # } It "Should do nothing if there are no scheduled jobs" { "" > $configPath { Invoke-RegentScheduledImports } | Should -Not -Throw } } Context "Given only one import job that's due on runtime" { BeforeEach { $time = (Get-Date).ToUniversalTime() $testConfig = [pscustomobject] @{ UserName = $env:USERNAME; CrmInstance = "CRMRECRUIT"; PublishCustomizations = $true; Managed = $true; SolutionName = "RegentUniversity"; Emails = "dhines@regent.edu;bryatho@regent.edu;acofer@regent.edu"; Time = $time; Password = $global:cred.Password | ConvertFrom-SecureString } $testConfig | ConvertTo-Json > $configPath } It "Should call Import-RegentSolution, then remove it" { Invoke-RegentScheduledImports Assert-MockCalled -CommandName "Import-RegentSolution" -Scope It -Exactly 1 ` -ParameterFilter { $SolutionName -eq $testConfig.SolutionName ` -and $CrmInstance -eq $testConfig.CrmInstance ` -and $Managed -eq $testConfig.Managed ` -and $PublishCustomizations -eq $testConfig.PublishCustomizations ` -and $Emails -eq $testConfig.Emails ` -and $Credential.UserName -eq $testConfig.UserName } $results = Get-Content $configPath | ConvertFrom-Json $results | Should -Be $null } } Context "Given only one import job that's not due yet" { BeforeEach { $time = (Get-Date).AddHours(1).ToUniversalTime() $testConfig = [pscustomobject] @{ UserName = $env:USERNAME; CrmInstance = "CRMRECRUIT"; PublishCustomizations = $true; Managed = $true; SolutionName = "RegentUniversity"; Emails = "dhines@regent.edu;bryatho@regent.edu;acofer@regent.edu"; Time = $time; Password = $global:cred.Password | ConvertFrom-SecureString } $testConfig | ConvertTo-Json > $configPath } It "Should skip the job if it's not due" { Invoke-RegentScheduledImports Assert-MockCalled -CommandName "Import-RegentSolution" -Scope It -Exactly 0 $results = Get-Content $configPath | ConvertFrom-Json ` | Select-Object -Property * -ExcludeProperty Password $expected = $testConfig | Select-Object -Property * -ExcludeProperty Password Assert-PropertiesMatch -Expected $expected -Actual $results } } Context "Given multiple import jobs" { BeforeEach { $time1 = (Get-Date).ToUniversalTime() $time2 = (Get-Date).AddHours(2).ToUniversalTime() $time3 = (Get-date).AddHours(-1).ToUniversalTime() # Configs 1 and two are identical except for the time, # so asserting Import-RegentSolution is only called once will test the filtering capabilitiy $config1 = [pscustomobject] @{ UserName = $env:USERNAME; CrmInstance = "CRMRECRUIT"; PublishCustomizations = $true; Managed = $true; SolutionName = "RegentUniversity"; Emails = "dhines@regent.edu;bryatho@regent.edu;acofer@regent.edu"; Time = $time1; Password = $global:cred.Password | ConvertFrom-SecureString } $config2 = [pscustomobject] @{ UserName = $env:USERNAME; CrmInstance = "CRMRECRUIT"; PublishCustomizations = $true; Managed = $true; SolutionName = "RegentUniversity"; Emails = "dhines@regent.edu;bryatho@regent.edu;acofer@regent.edu"; Time = $time2; Password = $global:cred.Password | ConvertFrom-SecureString } $config3 = [pscustomobject] @{ UserName = $env:USERNAME; CrmInstance = "CRMADVISE"; PublishCustomizations = $true; Managed = $true; SolutionName = "FooSolution"; Emails = "bam@baz.com"; Time = $time3; Password = $global:cred.Password | ConvertFrom-SecureString } @($config1, $config2, $config3) | ConvertTo-Json > $configPath } It "Should call Import-RegentSolution on each job that is due" { Invoke-RegentScheduledImports Assert-MockCalled -CommandName "Import-RegentSolution" -Scope It -Exactly 1 ` -ParameterFilter { $SolutionName -eq $config1.SolutionName ` -and $CrmInstance -eq $config1.CrmInstance ` -and $Managed -eq $config1.Managed ` -and $PublishCustomizations -eq $config1.PublishCustomizations ` -and $Emails -eq $config1.Emails ` -and $Credential.UserName -eq $config1.UserName } Assert-MockCalled -CommandName "Import-RegentSolution" -Scope It -Exactly 1 ` -ParameterFilter { $SolutionName -eq $config3.SolutionName ` -and $CrmInstance -eq $config3.CrmInstance ` -and $Managed -eq $config3.Managed ` -and $PublishCustomizations -eq $config3.PublishCustomizations ` -and $Emails -eq $config3.Emails ` -and $Credential.UserName -eq $config3.UserName } } It "Should remove a job from the config after a successful import" { Invoke-RegentScheduledImports $results = Get-Content $configPath | ConvertFrom-Json ` | Select-Object -Property * -ExcludeProperty Password $expected = $config2 | Select-Object -Property * -ExcludeProperty Password Assert-PropertiesMatch -Expected $expected -Actual $results } } } Describe "Move-RegentSolution" { Mock -CommandName Import-RegentSolution -Verifiable -MockWith {"Hello World!" > success.txt} Mock -CommandName Export-RegentSolution -Verifiable -MockWith {return} Mock -CommandName Push-RegentSolutionChanges -Verifiable -MockWith {return} $configPath = "$env:APPDATA\solution-imports.json" Context "Given a config file with a repository path TestDrive:/" { BeforeEach { if (Test-Path $configPath) { Remove-Item $configPath -Force } } It "Should export the specified solution, unpack it, and push the changes" { Move-RegentSolution ` -Credential $global:cred ` -SolutionName "RegentUniversity" ` -SourceCrmInstance CRMRECRUIT ` -DestinationCrmInstance CRMRECRUIT ` -Managed $true ` -PublishCustomizations $true ` -Emails "dhines@regent.edu;bryatho@regent.edu;acofer@regent.edu" ` -UpdateVersion "2.5" ` -CommitMessage "Initial Commit" ` -Branch "mybranch" ` -ImportTime (Get-Date).AddSeconds(3) ` -Force Assert-MockCalled -Scope It -CommandName Export-RegentSolution ` -Exactly 1 ` -ParameterFilter { $SolutionName -eq "RegentUniversity" ` -and $UpdateVersion -eq "2.5" } Assert-MockCalled -Scope It -CommandName Push-RegentSolutionChanges ` -Exactly 1 ` -ParameterFilter { $CommitMessage -eq "Initial Commit" ` -and $Branch -eq "mybranch" } } It "Should import the solution immediately if no time is specified" { Move-RegentSolution ` -Credential $global:cred ` -SolutionName "RegentUniversity" ` -SourceCrmInstance CRMRECRUIT ` -DestinationCrmInstance CRMRECRUIT ` -Managed $true ` -PublishCustomizations $true ` -Emails "dhines@regent.edu;bryatho@regent.edu;acofer@regent.edu" ` -UpdateVersion "2.5" ` -CommitMessage "Initial Commit" ` -Branch "mybranch" ` -Force Assert-MockCalled -Scope It -CommandName Import-RegentSolution ` -Exactly 1 ` -ParameterFilter { $SolutionName -eq "RegentUniversity" ` -and $CrmInstance -eq "CRMRECRUIT" ` -and $Managed -eq $true ` -and $PublishCustomizations -eq $true ` -and $Emails -eq "dhines@regent.edu;bryatho@regent.edu;acofer@regent.edu" ` -and $Credential -eq $global:cred } } It "Should schedule the import job for the specified time by adding it to the config" { $time = (Get-Date).AddHours(1).ToUniversalTime() $expectedConfig = [pscustomobject] @{ UserName = $env:USERNAME; CrmInstance = "CRMRECRUIT"; PublishCustomizations = $true; Managed = $true; SolutionName = "RegentUniversity"; Emails = "dhines@regent.edu;bryatho@regent.edu;acofer@regent.edu"; Time = $time; } Move-RegentSolution ` -Credential $global:cred ` -SolutionName $expectedConfig.SolutionName ` -SourceCrmInstance CRMRECRUIT ` -DestinationCrmInstance $expectedConfig.CrmInstance ` -Managed $expectedConfig.Managed ` -PublishCustomizations $expectedConfig.PublishCustomizations ` -Emails $expectedConfig.Emails ` -CommitMessage "Initial Commit" ` -Branch "mybranch" ` -ImportTime $time ` -Force Test-Path $configPath | Should -Be $true #Grab the config, but exclude the password $config = (Get-Content $configPath) | ConvertFrom-Json | Select-Object -Property "*" -ExcludeProperty "Password" Assert-PropertiesMatch -Expected $expectedConfig -Actual $config } It "Should throw an error if the specified solution isn't found" { {Move-RegentSolution ` -Credential $global:cred ` -SolutionName "NonExistingSolution" ` -SourceCrmInstance CRMRECRUIT ` -DestinationCrmInstance CRMRECRUIT ` -Managed $true ` -PublishCustomizations $true ` -Emails "dhines@regent.edu;bryatho@regent.edu;acofer@regent.edu" ` -UpdateVersion "2.5" ` -CommitMessage "Initial Commit" ` -Branch "mybranch" ` -ImportTime (Get-Date).AddSeconds(5) ` -Force} | Should -Throw } It "Should handle existing jobs by appending the new job to the existing file" { $preExistingConfig = [pscustomobject] @{ UserName = $env:USERNAME; CrmInstance = "CRMRECRUIT"; PublishCustomizations = $true; Managed = $true; SolutionName = "RegentUniversity"; Emails = "dhines@regent.edu;bryatho@regent.edu;acofer@regent.edu"; Time = "13:00" Password = $global:cred.Password | ConvertFrom-SecureString } $preExistingConfig | ConvertTo-Json > $configPath $time = (Get-Date).AddHours(1).ToUniversalTime() $expectedConfig = [pscustomobject] @{ UserName = $env:USERNAME; CrmInstance = "CRMRECRUIT"; PublishCustomizations = $true; Managed = $true; SolutionName = "RecordLocked"; Emails = "dhines@regent.edu;"; Time = $time; } Move-RegentSolution ` -Credential $global:cred ` -SolutionName $expectedConfig.SolutionName` -SourceCrmInstance CRMRECRUIT ` -DestinationCrmInstance $expectedConfig.CrmInstance ` -Managed $expectedConfig.Managed ` -PublishCustomizations $expectedConfig.PublishCustomizations ` -Emails $expectedConfig.Emails ` -CommitMessage "Initial Commit" ` -Branch "mybranch" ` -ImportTime $time ` -Force $newConfig = Get-Content $configPath ` | ConvertFrom-Json | Select-Object -Property "*" -ExcludeProperty "Password" Assert-PropertiesMatch -Expected $preExistingConfig -Actual $newConfig.SyncRoot[0] Assert-PropertiesMatch -Expected $expectedConfig -Actual $newConfig.SyncRoot[1] } } } |