test/Update-PropertiesFromFile.Tests.ps1

BeforeDiscovery {
    $ModuleRootPath = $PSScriptRoot | Split-Path -Parent
    $moduleManifestName = 'azure.datafactory.tools.psd1'
    $moduleManifestPath = Join-Path -Path $ModuleRootPath -ChildPath $moduleManifestName

    Import-Module -Name $moduleManifestPath -Force -Verbose:$false
}

InModuleScope azure.datafactory.tools {
    $testHelperPath = $PSScriptRoot | Join-Path -ChildPath 'TestHelper'
    Import-Module -Name $testHelperPath -Force

    # Variables for use in tests
    $script:SrcFolder = "$PSScriptRoot\BigFactorySample2"
    $script:TmpFolder = (New-TemporaryDirectory).FullName
    $script:RootFolder = Join-Path -Path $script:TmpFolder -ChildPath (Split-Path -Path $script:SrcFolder -Leaf)
    $script:DeploymentFolder = Join-Path -Path $script:RootFolder -ChildPath "deployment"
    $script:ConfigFolder = Join-Path -Path $script:SrcFolder -ChildPath "deployment"

    Copy-Item -Path "$SrcFolder" -Destination "$TmpFolder" -Filter "*.*" -Recurse:$true -Force 
    #Invoke-Expression "explorer.exe '$TmpFolder'"

    Describe 'Update-PropertiesFromFile' -Tag 'Unit','private' {
        It 'Should exist' {
            { Get-Command -Name Update-PropertiesFromFile -ErrorAction Stop } | Should -Not -Throw
        }

        Context 'When called without parameters' {
            It 'Should throw an error' {
                { Update-PropertiesFromFile -Force } | Should -Throw 
            }
        }

        Context 'When called with parameters' {
            $script:adf = Import-AdfFromFolder -FactoryName "xyz" -RootFolder "$RootFolder"
            $script:option = New-AdfPublishOption
            $script:adf.PublishOptions = $option
            It 'and adf param is empty should throw an error ' { {
                Update-PropertiesFromFile -stage "uat" -Force
                } | Should -Throw
            }
            It 'and stage param is empty should throw an error ' { {
                Update-PropertiesFromFile -adf $adf -Force
                } | Should -Throw
            }
            It 'and $adf.Location is empty should throw an error ' { {
                $script:adf.Location = ""
                Update-PropertiesFromFile -adf $adf -stage "uat" -Force
                } | Should -Throw
            }
        }

        Context 'When called with stage as short code but file does not exist' {
            $script:adf = Import-AdfFromFolder -FactoryName "xyz" -RootFolder "$RootFolder"
            $script:option = New-AdfPublishOption
            $script:adf.PublishOptions = $option
            It 'Should throw an error' { {
                Update-PropertiesFromFile -adf $script:adf -stage "FakeStage"
                } | Should -Throw
            }
        }

        Context 'When called' {
            $script:now = Get-Date
            It 'Should return nothing' {
                $script:adf = Import-AdfFromFolder -FactoryName "xyz" -RootFolder "$RootFolder"
                $script:option = New-AdfPublishOption
                $script:adf.PublishOptions = $option
                $result = Update-PropertiesFromFile -adf $script:adf -stage "uat"
                $result | Should -Be $null
            }
            It 'Should not modify any files' {
                Start-Sleep -Milliseconds 100
                $modFiles = (Get-ChildItem -Path $RootFolder -Exclude "~*.*" -Filter "*.json" -Recurse:$true | Where-Object {$_.LastWriteTime -gt $now} )
                $modFiles | Should -Be $null
            }
            It 'Should contains properties replaced and correct types' {
                $t = Get-AdfObjectByName -adf $script:adf -name "TR_AlwaysDisabled" -type "Trigger"
                $t.Body.properties.typeProperties.recurrence.interval | Should -Be 2
                $t.Body.properties.typeProperties.recurrence.interval.GetType().Name | Should -Be 'Int32'
                $t.Body.properties.runtimeState | Should -Be 'Started'
                $t.Body.properties.runtimeState.GetType().Name | Should -Be 'String'
            }

        }

        
        Context 'When called and CSV has wrong format' {
            It 'Should throw' {
                $script:adf = Import-AdfFromFolder -FactoryName "xyz" -RootFolder "$RootFolder"
                $script:option = New-AdfPublishOption
                {
                    $script:adf.PublishOptions = $option
                    Update-PropertiesFromFile -adf $script:adf -stage "badformat"
                } | Should -Throw
            }
            It 'Should contains all properties unchanged' {
                $t = Get-AdfObjectByName -adf $script:adf -name "TR_AlwaysDisabled" -type "Trigger"
                $t.Body.properties.runtimeState | Should -Be 'Stopped'
                $t.Body.properties.typeProperties.recurrence.interval | Should -Be 1
            }
        }

        Context 'When called and CSV contains multiple sub-properties as value' {
            It 'Should complete' {
                $script:adf = Import-AdfFromFolder -FactoryName "xyz" -RootFolder "$RootFolder"
                $script:option = New-AdfPublishOption
                {
                    $script:adf.PublishOptions = $option
                    Update-PropertiesFromFile -adf $script:adf -stage "c002"
                } | Should -Not -Throw
            }
            It 'Should contains properties replaced and correct types' {
                $t = Get-AdfObjectByName -adf $script:adf -name "PL_Wait_Dynamic" -type "pipeline"
                $t.Body.properties.parameters.WaitInSec.type | Should -Be 'int32'
                $t.Body.properties.parameters.WaitInSec.defaultValue | Should -Be 22
            }
        }

        Context 'When called and CSV with commented rows' {
            It 'Should complete' {
                $script:adf = Import-AdfFromFolder -FactoryName "xyz" -RootFolder "$RootFolder"
                $script:option = New-AdfPublishOption
                {
                    $script:adf.PublishOptions = $option
                    Update-PropertiesFromFile -adf $script:adf -stage "commented"
                } | Should -Not -Throw
            }
            It 'Should not apply changes for commented rows' {
                $t = Get-AdfObjectByName -adf $script:adf -name "TR_AlwaysDisabled" -type "trigger"
                $t.Body.properties.runtimeState | Should -Be 'Started'
                $t.Body.properties.typeProperties.recurrence.interval | Should -Be 1
            }
        }

        Context 'When called and CSV contains incorrect path' {
            It 'Should throw' {
                $script:adf = Import-AdfFromFolder -FactoryName "xyz" -RootFolder "$RootFolder"
                $script:option = New-AdfPublishOption
                {
                    $script:adf.PublishOptions = $option
                    Update-PropertiesFromFile -adf $script:adf -stage "c004-wrongpath"
                } | Should -Throw -ExceptionType ([System.Data.DataException])
            }
        }

        Context 'When called and CSV contains incorrect path that can be skipped' {
            It 'Should not throw' {
                $script:adf = Import-AdfFromFolder -FactoryName "xyz" -RootFolder "$RootFolder"
                $script:option = New-AdfPublishOption
                $option.FailsWhenPathNotFound = $false
                $script:adf.PublishOptions = $option
                {
                    Update-PropertiesFromFile -adf $script:adf -stage "c004-wrongpath"
                } | Should -Not -Throw
            }
        }

        Context 'When called and CSV has extra action (add/remove)' {
            It 'Should complete' {
                $script:adf = Import-AdfFromFolder -FactoryName "xyz" -RootFolder "$RootFolder"
                $script:option = New-AdfPublishOption
                $script:adf.PublishOptions = $option
                {
                    Update-PropertiesFromFile -adf $script:adf -stage "c005-extra-action"
                } | Should -Not -Throw
            }
            It 'Should contains 1 updated property' {
                $script:ls = Get-AdfObjectByName -adf $script:adf -name "BlobSampleData" -type "linkedService"
                $script:ls.Body.properties.typeProperties.connectionString | Should -Be "DefaultEndpointsProtocol=https;AccountName=sqlplayer2019;EndpointSuffix=core.windows.net;"
            }
            It 'Should contains 1 new property' {
                $script:ls.Body.properties.typeProperties.accountKey | Should -Be "orefoifakerjgi40passwordrjegjorejgorjeogjoreg=="
            }
            It 'Should lost 1 property (removed)' {
                Get-Member -InputObject $script:ls.Body.properties.typeProperties -name "encryptedCredential" -Membertype "Properties" | Should -Be $null
            }
        }

        Context 'When called and CSV has wildcard in object name column' {
            It 'Should complete' {
                $script:adf = Import-AdfFromFolder -FactoryName "xyz" -RootFolder "$RootFolder"
                $script:option = New-AdfPublishOption
                $script:adf.PublishOptions = $option
                {
                    Update-PropertiesFromFile -adf $script:adf -stage "multiple"
                } | Should -Not -Throw
            }
        }
        Context 'When called and CSV has wildcard in object name column' {
            BeforeEach {
                Mock Update-PropertiesForObject { return 0; }
            }
            It 'Should execute Update-PropertiesForObject 3 times' {
                Update-PropertiesFromFile -adf $script:adf -stage "multiple"
                Assert-MockCalled Update-PropertiesForObject -Times 3
            }
        }

        Context 'When called and CSV has array indexers in object name column' {
            It 'Should complete' {
                # Changing activity names means we cant index into it if another test already ran, so reload the files
                Copy-Item -Path "$SrcFolder" -Destination "$TmpFolder" -Filter "*.*" -Recurse:$true -Force
                $script:adf = Import-AdfFromFolder -FactoryName "xyz" -RootFolder "$RootFolder"
                $script:option = New-AdfPublishOption
                $script:adf.PublishOptions = $option
                {
                    Update-PropertiesFromFile -adf $script:adf -stage "array"
                } | Should -Not -Throw
            }
        }
        Context 'When called and CSV has array indexers in object name column' {
            BeforeEach {
                Mock Update-PropertiesForObject { return 0; }
            }
            It 'Should execute Update-PropertiesForObject 4 times' {
                Update-PropertiesFromFile -adf $script:adf -stage "array"
                Assert-MockCalled Update-PropertiesForObject -Times 4
            }
        }
        Context 'When called and CSV has array indexers in object name column' {
            It 'Should update properties of correct activities' {
                # Changing activity names means we cant index into it if another test already ran, so reload the files
                Copy-Item -Path "$SrcFolder" -Destination "$TmpFolder" -Filter "*.*" -Recurse:$true -Force
                $script:adf = Import-AdfFromFolder -FactoryName "xyz" -RootFolder "$RootFolder"
                $script:option = New-AdfPublishOption
                $script:adf.PublishOptions = $option

                Update-PropertiesFromFile -adf $script:adf -stage "array"
                $t = Get-AdfObjectByName -adf $script:adf -name "Multiple Waits" -type "Pipeline"
                $t.Body.properties.activities[0].name | Should -Be "Wait Number 1"

                # New check for correct type on arrays passed back. Make sure arrays with multiple elements are not boxed up again (Issue #147)
                $t.Body.properties.activities[1].typeProperties.ifTrueActivities.GetType().BaseType.Name | Should -Be "Array"
                $t.Body.properties.activities[1].typeProperties.ifTrueActivities[0].GetType().BaseType.Name | Should -Be "Object"
                $t.Body.properties.activities[1].typeProperties.ifFalseActivities.GetType().BaseType.Name | Should -Be "Array"
                $t.Body.properties.activities[1].typeProperties.ifTrueActivities[0].GetType().BaseType.Name | Should -Be "Object"
                
                $t.Body.properties.activities[1].typeProperties.ifTrueActivities[0].name | Should -Be "Wait Number 2"
                $t.Body.properties.activities[1].typeProperties.ifTrueActivities[0].typeProperties.waitTimeInSeconds | Should -Be 22
                $t.Body.properties.activities[1].typeProperties.ifTrueActivities[1].name | Should -Be "Wait Number 3"
                $t.Body.properties.activities[1].typeProperties.ifTrueActivities[1].typeProperties.waitTimeInSeconds | Should -Be 33

                $t.Body.properties.activities[2].name | Should -Be "Wait Number 4"
            }
        }
    }

    Describe 'Update-PropertiesFromFile' -Tag 'Unit','private' {
        Context 'When called and CSV contains global parameters to be replaced' {
            It 'Should complete' {
                $script:adf = Import-AdfFromFolder -FactoryName "xyz" -RootFolder "$RootFolder"
                $script:option = New-AdfPublishOption
                {
                    $script:adf.PublishOptions = $option
                    Update-PropertiesFromFile -adf $script:adf -stage "globalparam1"
                } | Should -Not -Throw
            }
            It 'Should contains gp values replaced' {
                $script:gp = Get-AdfObjectByName -adf $script:adf -name $script:adf.Factories[0].Name -type "factory"
                $script:gp.Body.properties.globalParameters.'GP-String'.value | Should -Be "This text has been replaced"
                $script:gp.Body.properties.globalParameters.'GP-Int'.value | Should -Be 2020
                $script:gp.Body.properties.globalParameters.'GP-Bool'.value | Should -Be $False
            }
            It 'Should contains new gp value & type added' {
                $script:gp = Get-AdfObjectByName -adf $script:adf -name $script:adf.Factories[0].Name -type "factory"
                $script:gp.Body.properties.globalParameters.'NewGlobalParam'.value | Should -Be 2023
                $script:gp.Body.properties.globalParameters.'envName'.value | Should -Be "POC"
                $script:gp.Body.properties.globalParameters.'NewGlobalParam'.type | Should -Be "int"
                $script:gp.Body.properties.globalParameters.'envName'.type | Should -Be "string"
            }
        }
    } 

    Describe 'Update-PropertiesFromFile with JSON' -Tag 'Unit','private','jsonconfig' {
        
        $testCases =  @( @{ configFile = 'config-c100.csv' }, @{ configFile = 'config-c100.json' } )

        $Env:DatabricksClusterId = "0820-210125-test000"
        $Env:Region = "uks"
        $Env:ProjectName = "adft"
        $Env:Environment = "uat"

        Context 'When called and JSON has extra actions' {
            It 'Should complete and has properties updated, added and removed' -TestCases $testCases {
                $script:adf = Import-AdfFromFolder -FactoryName "xyz" -RootFolder "$RootFolder"
                $script:adf.PublishOptions = New-AdfPublishOption
                {
                    $configFilePath = Join-Path -Path $script:DeploymentFolder -ChildPath "$configFile"
                    Update-PropertiesFromFile -adf $script:adf -stage "$configFilePath"
                } | Should -Not -Throw

                $script:ls = Get-AdfObjectByName -adf $script:adf -name "LS_DataLakeStore" -type "linkedService"
                $script:ls.Body.properties.typeProperties.url | Should -Be "https://datalake$($Env:ProjectName)$($Env:Environment).dfs.core.windows.net/"
                $script:lsdbr = Get-AdfObjectByName -adf $script:adf -name "LS_AzureDatabricks" -type "linkedService"
                $script:lsdbr.Body.properties.typeProperties.existingClusterId | Should -Be "$($Env:DatabricksClusterId)"
                $script:lsdbr.Body.properties.typeProperties.domain | Should -Be "https://$($Env:Region).azuredatabricks.net"
                $script:ls = Get-AdfObjectByName -adf $script:adf -name "LS_AzureKeyVault" -type "linkedService"
                $script:ls.Body.properties.typeProperties.baseUrl | Should -Be "https://keyvault-$($Env:ProjectName)-$($Env:Environment).vault.azure.net/"
                $script:ls = Get-AdfObjectByName -adf $script:adf -name "TR_RunEveryDay" -type "trigger"
                $script:ls.Body.properties.typeProperties.recurrence.startTime | Should -Be "2020-06-01T23:22:11.000Z"

                Get-Member -InputObject $script:lsdbr.Body.properties.typeProperties -name "encryptedCredential" -Membertype "Properties" | Should -Be $null
            }
        }

        Context 'When called and JSON has wildcard in object name column' {
            It 'Should complete' {
                $script:adf = Import-AdfFromFolder -FactoryName "xyz" -RootFolder "$RootFolder"
                $script:option = New-AdfPublishOption
                $script:adf.PublishOptions = $option
                {
                    Update-PropertiesFromFile -adf $script:adf -stage "$($script:ConfigFolder)\config-multiple.json"
                } | Should -Not -Throw
            }
        }
        # Context 'When called and CSV has wildcard in object name column' {
        # BeforeEach {
        # Mock Update-PropertiesForObject { return 0; }
        # }
        # It 'Should execute Update-PropertiesForObject 3 times' {
        # Update-PropertiesFromFile -adf $script:adf -stage "multiple"
        # Assert-MockCalled Update-PropertiesForObject -Times 3
        # }
        # }


    } 


    Describe 'Update-PropertiesFromFile with JSON' -Tag 'Unit','private' {
        Context 'When called with FailsWhenConfigItemNotFound = $true' {
            It 'Should not throw when object exists' {
                {
                    $script:adf = Import-AdfFromFolder -FactoryName "xyz" -RootFolder "$RootFolder"
                    $script:option = New-AdfPublishOption
                    $option.FailsWhenConfigItemNotFound = $true
                    $script:adf.PublishOptions = $option
                    Update-PropertiesFromFile -Adf $adf -stage ( Join-Path -Path $script:ConfigFolder -ChildPath "config-c100.csv" )
                } | Should -Not -Throw
            }
            It 'Should throw when object is missing' {
                {
                    $script:adf = Import-AdfFromFolder -FactoryName "xyz" -RootFolder "$RootFolder"
                    $script:option = New-AdfPublishOption
                    $option.FailsWhenConfigItemNotFound = $true
                    $script:adf.PublishOptions = $option
                    Update-PropertiesFromFile -Adf $adf -stage ( Join-Path -Path $script:ConfigFolder -ChildPath "config-missing.csv" )
                } | Should -Throw
            }
        }

        Context 'When called with FailsWhenConfigItemNotFound = $false' {
            It 'Should not throw when object exists' {
                {
                    $script:adf = Import-AdfFromFolder -FactoryName "xyz" -RootFolder "$RootFolder"
                    $script:option = New-AdfPublishOption
                    $option.FailsWhenConfigItemNotFound = $false
                    $script:adf.PublishOptions = $option
                    Update-PropertiesFromFile -Adf $adf -stage ( Join-Path -Path $script:ConfigFolder -ChildPath "config-c100.csv" )
                } | Should -Not -Throw
            }
             It 'Should not throw when object is missing' {
                {
                    $script:adf = Import-AdfFromFolder -FactoryName "xyz" -RootFolder "$RootFolder"
                    $script:option = New-AdfPublishOption
                    $option.FailsWhenConfigItemNotFound = $false
                    $script:adf.PublishOptions = $option
                    Update-PropertiesFromFile -Adf $adf -stage ( Join-Path -Path $script:ConfigFolder -ChildPath "config-missing.csv" )
                } | Should -Not -Throw
             }
        }



    }

    Describe 'Publish-AdfV2FromJson with DryRun' -Tag 'Unit','private' {

        $cases =  @( @{ configFile = 'config-endpoint.csv' }, @{ configFile = 'config-endpoint2.json' } )

        It 'should update vnet-related objects from <configFile>' -TestCases $cases {
            $DataFactoryName = "BigFactorySample2_vnet"
            $RootFolder = Join-Path -Path $PSScriptRoot -ChildPath $DataFactoryName
            $cfgFile = Join-Path -Path $RootFolder -ChildPath "deployment\$configFile"
            { Publish-AdfV2FromJson -RootFolder $RootFolder -DryRun -Stage $cfgFile -ResourceGroupName 'xxx' -DataFactoryName 'abc' } | Should -Not -Throw
        }
    }

}