functions/Invoke-RegentScheduledImports.Tests.ps1

$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
. "$here\$sut"
. "$here\Import-RegentSolution.ps1"
. "$here\TestingUtils.ps1"

Import-Module -Name Microsoft.Xrm.Data.Powershell
if (-not $global:cred) {
  $global:cred = Get-Credential -Message "Enter Credentials to run the tests with" -UserName $env:USERNAME
}

# This var is used in almost all the tests, so it's declared here
Describe "Move-RegentSolution" {
  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
    }
    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
    }
  }
}