diff --git a/CHANGELOG.md b/CHANGELOG.md index 0667493f25..4954c44452 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- SqlScript + - Fixed logic in `Get-TargetResource` and `Set-TargetResource` to throw an error + when the SQL script file is missing, instead of incorrectly reporting success + or using a null variable. + - Fixed `Test-TargetResource` to return `$false` (instead of throwing) when + the SQL script file is missing, enabling `DependsOn` scenarios where the file + is created at runtime. + ### Changed - SqlScript diff --git a/source/DSCResources/DSC_SqlScript/DSC_SqlScript.psm1 b/source/DSCResources/DSC_SqlScript/DSC_SqlScript.psm1 index c224541152..e1335881ed 100644 --- a/source/DSCResources/DSC_SqlScript/DSC_SqlScript.psm1 +++ b/source/DSCResources/DSC_SqlScript/DSC_SqlScript.psm1 @@ -121,6 +121,13 @@ function Get-TargetResource $serverInstance = ConvertTo-ServerInstanceName -InstanceName $InstanceName -ServerName $ServerName + if (-not (Test-Path -Path $GetFilePath -PathType Leaf)) + { + $errorMessage = $script:localizedData.GetFilePath_FileNotFound -f $GetFilePath + + New-ObjectNotFoundException -Message $errorMessage + } + $invokeParameters = @{ ServerInstance = $serverInstance InputFile = $GetFilePath @@ -278,6 +285,13 @@ function Set-TargetResource $serverInstance = ConvertTo-ServerInstanceName -InstanceName $InstanceName -ServerName $ServerName + if (-not (Test-Path -Path $SetFilePath -PathType Leaf)) + { + $errorMessage = $script:localizedData.SetFilePath_FileNotFound -f $SetFilePath + + New-ObjectNotFoundException -Message $errorMessage + } + Write-Verbose -Message ( $script:localizedData.ExecutingSetScript -f $SetFilePath, $InstanceName, $ServerName ) @@ -419,6 +433,14 @@ function Test-TargetResource $script:localizedData.TestingConfiguration ) + if (-not (Test-Path -Path $TestFilePath -PathType Leaf)) + { + $errorMessage = $script:localizedData.TestFilePath_FileNotFound -f $TestFilePath + Write-Verbose -Message $errorMessage + + return $false + } + $serverInstance = ConvertTo-ServerInstanceName -InstanceName $InstanceName -ServerName $ServerName $invokeParameters = @{ diff --git a/source/DSCResources/DSC_SqlScript/en-US/DSC_SqlScript.strings.psd1 b/source/DSCResources/DSC_SqlScript/en-US/DSC_SqlScript.strings.psd1 index 111b4c5e4d..081b6e946c 100644 --- a/source/DSCResources/DSC_SqlScript/en-US/DSC_SqlScript.strings.psd1 +++ b/source/DSCResources/DSC_SqlScript/en-US/DSC_SqlScript.strings.psd1 @@ -5,4 +5,7 @@ ConvertFrom-StringData @' TestingConfiguration = Determines if the configuration in the Set script is in desired state. InDesiredState = The configuration is in desired state. NotInDesiredState = The configuration is not in desired state. + GetFilePath_FileNotFound = The file specified in GetFilePath ('{0}') does not exist or is not accessible. Cannot determine resource state. + SetFilePath_FileNotFound = The file specified in SetFilePath ('{0}') does not exist or is not accessible. Cannot apply desired state. + TestFilePath_FileNotFound = Test script file '{0}' not found. Assuming resource is not in desired state. '@ diff --git a/tests/Integration/Resources/DSC_SqlScript.config.ps1 b/tests/Integration/Resources/DSC_SqlScript.config.ps1 index 4abadcb07a..7bf314f354 100644 --- a/tests/Integration/Resources/DSC_SqlScript.config.ps1 +++ b/tests/Integration/Resources/DSC_SqlScript.config.ps1 @@ -91,7 +91,12 @@ Configuration DSC_SqlScript_CreateDependencies_Config #> $getScriptResult = & ([ScriptBlock]::Create($GetScript)) - return $getScriptResult.Result -eq $Using:Node.GetSqlScript + if ([System.String]::IsNullOrEmpty($getScriptResult.Result)) + { + return $false + } + + return $true } GetScript = { @@ -102,9 +107,11 @@ Configuration DSC_SqlScript_CreateDependencies_Config $fileContent = Get-Content -Path $Using:Node.GetSqlScriptPath -Raw } - return @{ + $returnValue = @{ Result = $fileContent } + + return $returnValue } } @@ -117,7 +124,12 @@ Configuration DSC_SqlScript_CreateDependencies_Config TestScript = { $getScriptResult = & ([ScriptBlock]::Create($GetScript)) - return $getScriptResult.Result -eq $Using:Node.TestSqlScript + if ([System.String]::IsNullOrEmpty($getScriptResult.Result)) + { + return $false + } + + return $true } GetScript = { @@ -128,9 +140,11 @@ Configuration DSC_SqlScript_CreateDependencies_Config $fileContent = Get-Content -Path $Using:Node.TestSqlScriptPath -Raw } - return @{ + $returnValue = @{ Result = $fileContent } + + return $returnValue } } @@ -143,7 +157,12 @@ Configuration DSC_SqlScript_CreateDependencies_Config TestScript = { $getScriptResult = & ([ScriptBlock]::Create($GetScript)) - return $getScriptResult.Result -eq $Using:Node.SetSqlScript + if ([System.String]::IsNullOrEmpty($getScriptResult.Result)) + { + return $false + } + + return $true } GetScript = { @@ -154,9 +173,11 @@ Configuration DSC_SqlScript_CreateDependencies_Config $fileContent = Get-Content -Path $Using:Node.SetSqlScriptPath -Raw } - return @{ + $returnValue = @{ Result = $fileContent } + + return $returnValue } } @@ -320,7 +341,12 @@ Configuration DSC_SqlScript_RunSqlScriptAsWindowsUserWithDependencies_Config TestScript = { $getScriptResult = & ([ScriptBlock]::Create($GetScript)) - return $getScriptResult.Result -eq $Using:Node.GetSqlScript + if ([System.String]::IsNullOrEmpty($getScriptResult.Result)) + { + return $false + } + + return $true } GetScript = { @@ -331,9 +357,11 @@ Configuration DSC_SqlScript_RunSqlScriptAsWindowsUserWithDependencies_Config $fileContent = Get-Content -Path $Using:Node.GetSqlScriptPath2 -Raw } - return @{ + $returnValue = @{ Result = $fileContent } + + return $returnValue } } @@ -346,7 +374,12 @@ Configuration DSC_SqlScript_RunSqlScriptAsWindowsUserWithDependencies_Config TestScript = { $getScriptResult = & ([ScriptBlock]::Create($GetScript)) - return $getScriptResult.Result -eq $Using:Node.TestSqlScript + if ([System.String]::IsNullOrEmpty($getScriptResult.Result)) + { + return $false + } + + return $true } GetScript = { @@ -357,9 +390,11 @@ Configuration DSC_SqlScript_RunSqlScriptAsWindowsUserWithDependencies_Config $fileContent = Get-Content -Path $Using:Node.TestSqlScriptPath2 -Raw } - return @{ + $returnValue = @{ Result = $fileContent } + + return $returnValue } } @@ -372,7 +407,12 @@ Configuration DSC_SqlScript_RunSqlScriptAsWindowsUserWithDependencies_Config TestScript = { $getScriptResult = & ([ScriptBlock]::Create($GetScript)) - return $getScriptResult.Result -eq $Using:Node.SetSqlScript + if ([System.String]::IsNullOrEmpty($getScriptResult.Result)) + { + return $false + } + + return $true } GetScript = { @@ -383,9 +423,11 @@ Configuration DSC_SqlScript_RunSqlScriptAsWindowsUserWithDependencies_Config $fileContent = Get-Content -Path $Using:Node.SetSqlScriptPath2 -Raw } - return @{ + $returnValue = @{ Result = $fileContent } + + return $returnValue } } diff --git a/tests/Unit/DSC_SqlScript.Tests.ps1 b/tests/Unit/DSC_SqlScript.Tests.ps1 index bc860f18da..9905b2fba3 100644 --- a/tests/Unit/DSC_SqlScript.Tests.ps1 +++ b/tests/Unit/DSC_SqlScript.Tests.ps1 @@ -97,6 +97,7 @@ Describe 'SqlScript\Get-TargetResource' -Tag 'Get' { Context 'When Get-TargetResource returns script results successfully' { BeforeAll { + Mock -CommandName Test-Path -MockWith { return $true } Mock -CommandName Invoke-SqlScript -MockWith { return '' } @@ -120,6 +121,7 @@ Describe 'SqlScript\Get-TargetResource' -Tag 'Get' { Context 'When Get-TargetResource returns script results successfully with query timeout' { BeforeAll { + Mock -CommandName Test-Path -MockWith { return $true } Mock -CommandName Invoke-SqlScript -MockWith { return '' } @@ -145,6 +147,7 @@ Describe 'SqlScript\Get-TargetResource' -Tag 'Get' { Context 'When Get-TargetResource throws an error when running the script in the GetFilePath parameter' { BeforeAll { + Mock -CommandName Test-Path -MockWith { return $true } Mock -CommandName Invoke-SqlScript -MockWith { throw 'Failed to run SQL Script' } @@ -160,6 +163,22 @@ Describe 'SqlScript\Get-TargetResource' -Tag 'Get' { } } } + + Context 'When the GetFilePath file is missing' { + BeforeAll { + Mock -CommandName Test-Path -MockWith { return $false } + } + + It 'Should throw the localized file not found exception' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $expectedError = $script:localizedData.GetScript_FileNotFound -f $script:mockGetTargetResourceParameters.GetFilePath + + { Get-TargetResource @mockGetTargetResourceParameters } | Should -Throw -ExpectedMessage $expectedError + } + } + } } Describe 'SqlScript\Set-TargetResource' -Tag 'Set' { @@ -186,6 +205,7 @@ Describe 'SqlScript\Set-TargetResource' -Tag 'Set' { Context 'When Set-TargetResource runs script without issue' { BeforeAll { + Mock -CommandName Test-Path -MockWith { return $true } Mock -CommandName Invoke-SqlScript -MockWith { return '' } @@ -202,6 +222,7 @@ Describe 'SqlScript\Set-TargetResource' -Tag 'Set' { Context 'When Set-TargetResource runs script without issue using timeout' { BeforeAll { + Mock -CommandName Test-Path -MockWith { return $true } Mock -CommandName Invoke-SqlScript -MockWith { return '' } @@ -220,6 +241,7 @@ Describe 'SqlScript\Set-TargetResource' -Tag 'Set' { Context 'When Set-TargetResource throws an error when running the script in the SetFilePath parameter' { BeforeAll { + Mock -CommandName Test-Path -MockWith { return $true } Mock -CommandName Invoke-SqlScript -MockWith { throw 'Failed to run SQL Script' } @@ -236,6 +258,22 @@ Describe 'SqlScript\Set-TargetResource' -Tag 'Set' { } } } + + Context 'When the SetFilePath file is missing' { + BeforeAll { + Mock -CommandName Test-Path -MockWith { return $false } + } + + It 'Should throw the localized file not found exception' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $expectedError = $script:localizedData.SetScript_FileNotFound -f $script:mockSetTargetResourceParameters.SetFilePath + + { Set-TargetResource @mockSetTargetResourceParameters } | Should -Throw -ExpectedMessage $expectedError + } + } + } } Describe 'SqlScript\Test-TargetResource' { @@ -263,6 +301,7 @@ Describe 'SqlScript\Test-TargetResource' { Context 'When the system is in the desired state' { Context 'When Test-TargetResource runs script without issue' { BeforeAll { + Mock -CommandName Test-Path -MockWith { return $true } Mock -CommandName Invoke-SqlScript } @@ -279,6 +318,7 @@ Describe 'SqlScript\Test-TargetResource' { Context 'When Test-TargetResource runs script without issue with timeout' { BeforeAll { + Mock -CommandName Test-Path -MockWith { return $true } Mock -CommandName Invoke-SqlScript } @@ -299,6 +339,7 @@ Describe 'SqlScript\Test-TargetResource' { Context 'When the system is not in the desired state' { Context 'When Invoke-SqlScript returns an SQL error code from the script that was ran' { BeforeAll { + Mock -CommandName Test-Path -MockWith { return $true } Mock -CommandName Invoke-SqlScript -MockWith { return 1 } @@ -319,6 +360,7 @@ Describe 'SqlScript\Test-TargetResource' { Context 'When Test-TargetResource throws the exception SqlPowerShellSqlExecutionException when running the script in the TestFilePath parameter' { BeforeAll { + Mock -CommandName Test-Path -MockWith { return $true } Mock -CommandName Invoke-SqlScript -MockWith { throw New-Object -TypeName Microsoft.SqlServer.Management.PowerShell.SqlPowerShellSqlExecutionException } @@ -337,6 +379,7 @@ Describe 'SqlScript\Test-TargetResource' { Context 'When Test-TargetResource throws an unexpected error when running the script in the TestFilePath parameter' { BeforeAll { + Mock -CommandName Test-Path -MockWith { return $true } Mock -CommandName Invoke-SqlScript -MockWith { throw 'Failed to run SQL Script' } @@ -353,4 +396,20 @@ Describe 'SqlScript\Test-TargetResource' { } } } + + Context 'When the TestFilePath file is missing' { + BeforeAll { + Mock -CommandName Test-Path -MockWith { return $false } + } + + It 'Should return false' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $result = Test-TargetResource @mockTestTargetResourceParameters + + $result | Should -BeFalse + } + } + } }