internal/functions/Get-DscResourceImplementation.Tests.ps1

Describe 'Get-DscResourceImplementation' -Tag 'Unit' {
  BeforeAll {
    . $PSCommandPath.Replace('.Tests.ps1', '.ps1')

    # The using statement will fail without a correct module path;
    # The scriptblock in Start-Job can't see the Pester TestDrive,
    # so instead we need to point to a real module path.
    $TestModulePath = Get-Module -Name Pester | Select-Object -ExpandProperty Path

    $NonPowerShellDscResources = [Microsoft.PowerShell.DesiredStateConfiguration.DscResourceInfo[]]@(
      @{ Name = 'foo' ; ImplementedAs = 'Binary' }
      @{ Name = 'bar' ; ImplementedAs = 'None' }
    )
    $PowerShellDscResources = [Microsoft.PowerShell.DesiredStateConfiguration.DscResourceInfo[]]@(
      @{ Name = 'classy' ; ImplementedAs = 'PowerShell' ; Path = $TestModulePath }
      @{ Name = 'mofy' ; ImplementedAs = 'PowerShell' ; Path = $TestModulePath }
    )
  }

  Context 'when the Resource is passed explicitly' {
    BeforeEach {
      Mock Get-DscResource {}
    }

    It 'never calls Get-DscResource' {
      $null = $NonPowerShellDscResources | Get-DscResourceImplementation
      Should -Invoke Get-DscResource -Times 0 -Scope It
    }

    It 'processes once for each object in the pipeline' {
      $Result = $NonPowerShellDscResources | Get-DscResourceImplementation
      $Result.Count | Should -Be 2
    }
  }

  Context 'when the Resource is passed by name' {
    BeforeAll {
      Mock Get-DscResource {
        [Microsoft.PowerShell.DesiredStateConfiguration.DscResourceInfo]@{ Name = $Name ; ImplementedAs = 'Binary' }
      }
    }
    It 'calls Get-DscResource by name' {
      $null = Get-DscResourceImplementation -Name 'foo'
      Should -Invoke Get-DscResource -Times 1 -Scope It
      Should -Invoke Get-DscResource -Times 1 -Scope It -ParameterFilter { $Name -eq 'foo' }
    }
  }

  Context 'when the Resource is passed by name and module' {
    BeforeAll {
      Mock Get-DscResource {
        [Microsoft.PowerShell.DesiredStateConfiguration.DscResourceInfo]@{ Name = $Name ; ImplementedAs = 'Binary' }
      }
    }
    It 'calls Get-DscResource by name and module' {
      $null = Get-DscResourceImplementation -Name 'foo' -Module 'bar'
      Should -Invoke Get-DscResource -Times 1 -Scope It
      Should -Invoke Get-DscResource -Times 1 -Scope It -ParameterFilter { $Name -eq 'foo' -and $Module -eq 'bar' }
    }
  }

  Context "when the Resource's ImplementedAs value is not PowerShell" {
    It 'sets the resource implementation to the ImplementedAs value' {
      $Result = $NonPowerShellDscResources | Get-DscResourceImplementation
      $Result.Count | Should -Be 2
      $Result[0] | Should -Be 'Binary'
      $Result[1] | Should -Be 'None'
    }
  }

  Context "when the Resource's ImplementedAs value is PowerShell" {
    It 'starts and waits for a job once per resource' {
      $JobOutput = 'ScriptBlock Output'
      Mock Start-Job {
        [PSCustomObject]@{ID = 0 }
      }
      Mock Wait-Job {
        return @{
          ChildJobs = [System.Collections.ArrayList]@(
            @{ Output = $JobOutput }
          )
        }
      }

      $null = $PowerShellDscResources | Get-DscResourceImplementation
      Should -Invoke -CommandName 'Start-Job' -Times 2 -Scope It -ParameterFilter {
        $ScriptBlock.ToString() -match [Regex]::Escape("using module '$TestModulePath'")
        $ScriptBlock.ToString() -match "('classy'|'mofy') -as \[type\]"
      }
      Should -Invoke -CommandName 'Wait-Job' -Times 2 -Scope It
    }

    Context 'when the Resource is class-based' {
      It "sets the resource implementation to 'Class'" {
        # We actually want to run the scriptblock, so don't mock the *Job functions
        $DscResource = [Microsoft.PowerShell.DesiredStateConfiguration.DscResourceInfo]@{
          # String isn't a DSC Resource, but it works for test purposes
          Name = 'String' ; ImplementedAs = 'PowerShell' ; Path = $TestModulePath
        }
        $DscResource | Get-DscResourceImplementation | Should -Be 'Class'
      }
    }

    Context 'when the Resource is MOF-based' {
      It "sets the resource implementation to 'MOF'" {
        # We actually want to run the scriptblock, so don't mock the *Job functions
        $DscResource = [Microsoft.PowerShell.DesiredStateConfiguration.DscResourceInfo]@{
          Name = 'Mofy' ; ImplementedAs = 'PowerShell' ; Path = $TestModulePath
        }
        $DscResource | Get-DscResourceImplementation | Should -Be 'MOF'
      }
    }
  }

  Context 'when the ModifyResource switch is specified' {
    It 'adds the ResourceImplementation note property to the input resource' {
      $DscResource = [Microsoft.PowerShell.DesiredStateConfiguration.DscResourceInfo]@{
        Name = 'Foo' ; ImplementedAs = 'Binary'
      }
      $null = $DscResource | Get-DscResourceImplementation -ModifyResource
      $DscResource.Name | Should -Be 'Foo'
      $DscResource.ResourceImplementation | Should -Be 'Binary'
    }
  }
}