From 4ad643502d06b6bf179d1b588ff43b6da7def646 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 1 Jan 2023 12:43:28 +0100 Subject: [PATCH] `Assert-SetupActionProperties`: Use commands from DscResource.Common --- .vscode/settings.json | 4 +- CHANGELOG.md | 19 +++- .../Assert-RequiredCommandParameter.ps1 | 93 --------------- .../Private/Assert-SetupActionProperties.ps1 | 26 ++--- .../Test-ServiceAccountRequirePassword.ps1 | 58 ---------- source/en-US/SqlServerDsc.strings.psd1 | 4 - .../Assert-RequiredCommandParameter.Tests.ps1 | 107 ------------------ .../Assert-SetupActionProperties.Tests.ps1 | 2 +- ...st-ServiceAccountRequirePassword.Tests.ps1 | 85 -------------- 9 files changed, 32 insertions(+), 366 deletions(-) delete mode 100644 source/Private/Assert-RequiredCommandParameter.ps1 delete mode 100644 source/Private/Test-ServiceAccountRequirePassword.ps1 delete mode 100644 tests/Unit/Private/Assert-RequiredCommandParameter.Tests.ps1 delete mode 100644 tests/Unit/Private/Test-ServiceAccountRequirePassword.Tests.ps1 diff --git a/.vscode/settings.json b/.vscode/settings.json index ef03f8461..c8deeda3c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -98,9 +98,11 @@ }, "powershell.pester.useLegacyCodeLens": false, "pester.testFilePath": [ + "[tT]ests/[qQ][aA]/*.[tT]ests.[pP][sS]1", + "[tT]ests/[uU]nit/**/*.[tT]ests.[pP][sS]1", "[tT]ests/[uU]nit/*.[tT]ests.[pP][sS]1" ], - "pester.runTestsInNewProcess": false, + "pester.runTestsInNewProcess": true, "pester.pesterModulePath": "./output/RequiredModules/Pester", "powershell.pester.codeLens": true, "pester.suppressCodeLensNotice": true diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b03bd301..4207c76ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,16 +9,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - SqlServerDsc - Removed `Assert-ElevatedUser` from private functions - [Issue #1797](https://github.com/dsccommunity/SqlServerDsc/issues/1797) - - `Assert-ElevatedUser` added to _DscResource.Common_ public functions - [Issue #82](https://github.com/dsccommunity/DscResource.Common/issues/82) - - Removed `Test-IsNumericType` from private functions - [Issue #1795](https://github.com/dsccommunity/SqlServerDsc/issues/1795) - - `Test-IsNumericType` added to _DscResource.Common_ public functions - [Issue #87](https://github.com/dsccommunity/DscResource.Common/issues/87) + - `Assert-ElevatedUser` added to _DscResource.Common_ public functions + ([issue #82](https://github.com/dsccommunity/DscResource.Common/issues/82)). + - Removed `Test-IsNumericType` from private functions ([issue #1795](https://github.com/dsccommunity/SqlServerDsc/issues/1795)). + - `Test-IsNumericType` added to _DscResource.Common_ public functions + ([issue #87](https://github.com/dsccommunity/DscResource.Common/issues/87)). + - Removed `Test-ServiceAccountRequirePassword` from private functions ([issue #1794](https://github.com/dsccommunity/SqlServerDsc/issues/1794) + - Replaced by `Test-AccountRequirePassword` that was added to _DscResource.Common_ + public functions ([issue #93](https://github.com/dsccommunity/DscResource.Common/issues/93)). + - Removed `Assert-RequiredCommandParameter` from private functions ([issue #1796](https://github.com/dsccommunity/SqlServerDsc/issues/1796)). + - Replaced by `Assert-BoundParameter` (part of _DscResource.Common_) + that had a new parameter set added ([issue #92](https://github.com/dsccommunity/DscResource.Common/issues/92)). ### Added - SqlServerDsc - The following private functions were added to the module (see comment-based help for more information): - - `Assert-RequiredCommandParameter` - `Assert-SetupActionProperties` - `Invoke-SetupAction` - The following public functions were added to the module (see comment-based @@ -58,6 +65,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Update to build module in separate folder under `output`. - Moved the build step of the pipeline to a Windows build worker when running in Azure DevOps. + - Settings for the _Visual Studio Code_ extension _Pester Tests_ was changed + to be able to run all unit tests, and all tests run by the extension + are now run in a separate process to be able to handle changes in + class-based resources. - `Install-SqlServerDsc` - No longer throws an exception when parameter `AgtSvcAccount` is not specified. - SqlAgReplica diff --git a/source/Private/Assert-RequiredCommandParameter.ps1 b/source/Private/Assert-RequiredCommandParameter.ps1 deleted file mode 100644 index 95c11d966..000000000 --- a/source/Private/Assert-RequiredCommandParameter.ps1 +++ /dev/null @@ -1,93 +0,0 @@ -<# - .SYNOPSIS - Assert that required parameters has been specified. - - .DESCRIPTION - Assert that required parameters has been specified, and throws an exception if not. - - .PARAMETER BoundParameter - A hashtable containing the parameters to evaluate. Normally this is set to - $PSBoundParameters. - - .PARAMETER RequiredParameter - One or more parameter names that is required to have been specified. - - .PARAMETER IfParameterPresent - One or more parameter names that if specified will trigger the evaluation. - If neither of the parameter names has been specified the evaluation of required - parameters are not made. - - .EXAMPLE - Assert-RequiredCommandParameter -BoundParameter $PSBoundParameters -RequiredParameter @('PBStartPortRange', 'PBEndPortRange') - - Throws an exception if either of the two parameters are not specified. - - .EXAMPLE - Assert-RequiredCommandParameter -BoundParameter $PSBoundParameters -RequiredParameter @('Property2', 'Property3') -IfParameterPresent @('Property1') - - Throws an exception if the parameter 'Property1' is specified and one of the required parameters are not. - - .OUTPUTS - None. - - .NOTES - This command should probably be a parmeter set of the command Assert-BoundParameter - in the module DscResource.Common, instead of being a separate command. -#> -function Assert-RequiredCommandParameter -{ - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $true)] - [System.Collections.Hashtable] - $BoundParameter, - - [Parameter(Mandatory = $true)] - [System.String[]] - $RequiredParameter, - - [Parameter()] - [System.String[]] - $IfParameterPresent - ) - - $evaluateRequiredParameter = $true - - if ($PSBoundParameters.ContainsKey('IfParameterPresent')) - { - $hasIfParameterPresent = $BoundParameter.Keys.Where( { $_ -in $IfParameterPresent } ) - - if (-not $hasIfParameterPresent) - { - $evaluateRequiredParameter = $false - } - } - - if ($evaluateRequiredParameter) - { - foreach ($parameter in $RequiredParameter) - { - if ($parameter -notin $BoundParameter.Keys) - { - $errorMessage = if ($PSBoundParameters.ContainsKey('IfParameterPresent')) - { - $script:localizedData.RequiredCommandParameter_SpecificParametersMustAllBeSetWhenParameterExist -f ($RequiredParameter -join ''', '''), ($IfParameterPresent -join ''', ''') - } - else - { - $script:localizedData.RequiredCommandParameter_SpecificParametersMustAllBeSet -f ($RequiredParameter -join ''', ''') - } - - $PSCmdlet.ThrowTerminatingError( - [System.Management.Automation.ErrorRecord]::new( - $errorMessage, - 'ARCP0001', # cspell: disable-line - [System.Management.Automation.ErrorCategory]::InvalidOperation, - 'Command parameters' - ) - ) - } - } - } -} diff --git a/source/Private/Assert-SetupActionProperties.ps1 b/source/Private/Assert-SetupActionProperties.ps1 index c11475a12..ebed3c61a 100644 --- a/source/Private/Assert-SetupActionProperties.ps1 +++ b/source/Private/Assert-SetupActionProperties.ps1 @@ -42,12 +42,12 @@ function Assert-SetupActionProperties $assertParameters = @('PBStartPortRange', 'PBEndPortRange') $assertRequiredCommandParameterParameters = @{ - BoundParameter = $Property + BoundParameterList = $Property RequiredParameter = $assertParameters IfParameterPresent = $assertParameters } - Assert-RequiredCommandParameter @assertRequiredCommandParameterParameters + Assert-BoundParameter @assertRequiredCommandParameterParameters # The parameter UseSqlRecommendedMemoryLimits is mutually exclusive to SqlMinMemory and SqlMaxMemory. Assert-BoundParameter -BoundParameterList $Property -MutuallyExclusiveList1 @( @@ -60,7 +60,7 @@ function Assert-SetupActionProperties # If Role is set to SPI_AS_NewFarm then the specific parameters are required. if ($Property.ContainsKey('Role') -and $Property.Role -eq 'SPI_AS_NewFarm') { - Assert-RequiredCommandParameter -BoundParameter $Property -RequiredParameter @( + Assert-BoundParameter -BoundParameterList $Property -RequiredParameter @( 'FarmAccount' 'FarmPassword' 'Passphrase' @@ -71,13 +71,13 @@ function Assert-SetupActionProperties # If the parameter SecurityMode is set to 'SQL' then the parameter SAPwd is required. if ($Property.ContainsKey('SecurityMode') -and $Property.SecurityMode -eq 'SQL') { - Assert-RequiredCommandParameter -BoundParameter $Property -RequiredParameter @('SAPwd') + Assert-BoundParameter -BoundParameterList $Property -RequiredParameter @('SAPwd') } # If the parameter FileStreamLevel is set and is greater or equal to 2 then the parameter FileStreamShareName is required. if ($Property.ContainsKey('FileStreamLevel') -and $Property.FileStreamLevel -ge 2) { - Assert-RequiredCommandParameter -BoundParameter $Property -RequiredParameter @('FileStreamShareName') + Assert-BoundParameter -BoundParameterList $Property -RequiredParameter @('FileStreamShareName') } # If a *SvcAccount is specified then the accompanying *SvcPassword must be set unless it is a (global) managed service account, virtual account, or a built-in account. @@ -97,11 +97,11 @@ function Assert-SetupActionProperties if ($currentAccountProperty -in $Property.Keys) { # If not (global) managed service account, virtual account, or a built-in account. - if ((Test-ServiceAccountRequirePassword -Name $Property.$currentAccountProperty)) + if ((Test-AccountRequirePassword -Name $Property.$currentAccountProperty)) { $assertPropertyName = $currentAccountProperty -replace 'Account', 'Password' - Assert-RequiredCommandParameter -BoundParameter $Property -RequiredParameter $assertPropertyName + Assert-BoundParameter -BoundParameterList $Property -RequiredParameter $assertPropertyName } } } @@ -109,7 +109,7 @@ function Assert-SetupActionProperties # If feature AzureExtension is specified then the all the Azure* parameters must be set (except AzureArcProxy). if ($Property.ContainsKey('Features') -and $Property.Features -contains 'AZUREEXTENSION') # cSpell: disable-line { - Assert-RequiredCommandParameter -BoundParameter $Property -RequiredParameter @( + Assert-BoundParameter -BoundParameterList $Property -RequiredParameter @( 'AzureSubscriptionId' 'AzureResourceGroup' 'AzureRegion' @@ -125,7 +125,7 @@ function Assert-SetupActionProperties { if ($Property.ContainsKey('Features') -and $Property.Features -contains 'SQLENGINE') { - Assert-RequiredCommandParameter -BoundParameter $Property -RequiredParameter @('AgtSvcAccount') + Assert-BoundParameter -BoundParameterList $Property -RequiredParameter @('AgtSvcAccount') } } @@ -134,24 +134,24 @@ function Assert-SetupActionProperties # The parameter ASSvcAccount is mandatory if feature AS is installed and setup action is InstallFailoverCluster, PrepareFailoverCluster, or AddNode. if ($Property.ContainsKey('Features') -and $Property.Features -contains 'AS') { - Assert-RequiredCommandParameter -BoundParameter $Property -RequiredParameter @('ASSvcAccount') + Assert-BoundParameter -BoundParameterList $Property -RequiredParameter @('ASSvcAccount') } # The parameter SqlSvcAccount is mandatory if feature SQLENGINE is installed and setup action is InstallFailoverCluster, PrepareFailoverCluster, or AddNode. if ($Property.ContainsKey('Features') -and $Property.Features -contains 'SQLENGINE') { - Assert-RequiredCommandParameter -BoundParameter $Property -RequiredParameter @('SqlSvcAccount') + Assert-BoundParameter -BoundParameterList $Property -RequiredParameter @('SqlSvcAccount') } # The parameter ISSvcAccount is mandatory if feature IS is installed and setup action is InstallFailoverCluster, PrepareFailoverCluster, or AddNode. if ($Property.ContainsKey('Features') -and $Property.Features -contains 'IS') { - Assert-RequiredCommandParameter -BoundParameter $Property -RequiredParameter @('ISSvcAccount') + Assert-BoundParameter -BoundParameterList $Property -RequiredParameter @('ISSvcAccount') } if ($Property.ContainsKey('Features') -and $Property.Features -contains 'RS') { - Assert-RequiredCommandParameter -BoundParameter $Property -RequiredParameter @('RSSvcAccount') + Assert-BoundParameter -BoundParameterList $Property -RequiredParameter @('RSSvcAccount') } } diff --git a/source/Private/Test-ServiceAccountRequirePassword.ps1 b/source/Private/Test-ServiceAccountRequirePassword.ps1 deleted file mode 100644 index 1f605474f..000000000 --- a/source/Private/Test-ServiceAccountRequirePassword.ps1 +++ /dev/null @@ -1,58 +0,0 @@ -<# - .SYNOPSIS - Returns wether the specified service account require password to be provided. - - .SYNOPSIS - Returns wether the specified service account require password to be provided. - If the account is a (global) managed service account, virtual account, or a - built-in account then there is no need to provide a password. - - .PARAMETER Name - Credential name for the service account. - - .OUTPUTS - [System.Boolean] -#> -function Test-ServiceAccountRequirePassword -{ - [CmdletBinding()] - [OutputType([System.Boolean])] - param - ( - [Parameter(Mandatory = $true)] - [System.String] - $Name - ) - - # Assume local or domain service account. - $requirePassword = $true - - switch -Regex ($Name.ToUpper()) - { - # Built-in account. - '^(?:NT ?AUTHORITY\\)?(SYSTEM|LOCALSERVICE|LOCAL SERVICE|NETWORKSERVICE|NETWORK SERVICE)$' # CSpell: disable-line - { - $requirePassword = $false - - break - } - - # Virtual account. - '^(?:NT SERVICE\\)(.*)$' - { - $requirePassword = $false - - break - } - - # (Global) Managed Service Account. - '\$$' - { - $requirePassword = $false - - break - } - } - - return $requirePassword -} diff --git a/source/en-US/SqlServerDsc.strings.psd1 b/source/en-US/SqlServerDsc.strings.psd1 index d5f11b31e..3090a6451 100644 --- a/source/en-US/SqlServerDsc.strings.psd1 +++ b/source/en-US/SqlServerDsc.strings.psd1 @@ -91,10 +91,6 @@ ConvertFrom-StringData @' Server_MediaPathNotFound = The specified media path does not exist or does not contain 'setup.exe'. Server_ConfigurationFileNotFound = The specified configuration file was not found. - ## Assert-RequiredCommandParameter - RequiredCommandParameter_SpecificParametersMustAllBeSet = The parameters '{0}' must all be specified. - RequiredCommandParameter_SpecificParametersMustAllBeSetWhenParameterExist = The parameters '{0}' must all be specified if either parameter '{1}' is specified. - ## Assert-SetupActionProperties InstallSqlServerProperties_ASServerModeInvalidValue = The value for ASServerMode is not valid for the setup action {0}. InstallSqlServerProperties_RsInstallModeInvalidValue = The only valid value for RsInstallMode is 'FilesOnlyMode' when using setup action {0}. diff --git a/tests/Unit/Private/Assert-RequiredCommandParameter.Tests.ps1 b/tests/Unit/Private/Assert-RequiredCommandParameter.Tests.ps1 deleted file mode 100644 index 2805bb04e..000000000 --- a/tests/Unit/Private/Assert-RequiredCommandParameter.Tests.ps1 +++ /dev/null @@ -1,107 +0,0 @@ -[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] -param () - -BeforeDiscovery { - try - { - if (-not (Get-Module -Name 'DscResource.Test')) - { - # Assumes dependencies has been resolved, so if this module is not available, run 'noop' task. - if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) - { - # Redirect all streams to $null, except the error stream (stream 2) - & "$PSScriptRoot/../../build.ps1" -Tasks 'noop' 2>&1 4>&1 5>&1 6>&1 > $null - } - - # If the dependencies has not been resolved, this will throw an error. - Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' - } - } - catch [System.IO.FileNotFoundException] - { - throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' - } -} - -BeforeAll { - $script:dscModuleName = 'SqlServerDsc' - - Import-Module -Name $script:dscModuleName - - $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName - $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscModuleName - $PSDefaultParameterValues['Should:ModuleName'] = $script:dscModuleName -} - -AfterAll { - $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') - $PSDefaultParameterValues.Remove('Mock:ModuleName') - $PSDefaultParameterValues.Remove('Should:ModuleName') - - # Unload the module being tested so that it doesn't impact any other tests. - Get-Module -Name $script:dscModuleName -All | Remove-Module -Force -} - -Describe 'Assert-RequiredCommandParameter' -Tag 'Private' { - Context 'When required parameter is missing' { - It 'Should throw the correct error' { - InModuleScope -ScriptBlock { - $mockErrorMessage = $script:localizedData.RequiredCommandParameter_SpecificParametersMustAllBeSet -f 'Parameter1' - - { Assert-RequiredCommandParameter -BoundParameter @{} -RequiredParameter 'Parameter1' } | - Should -Throw -ExpectedMessage $mockErrorMessage - } - } - } - - Context 'When the parameter in IfParameterPresent is not present' { - It 'Should not throw an error' { - InModuleScope -ScriptBlock { - { Assert-RequiredCommandParameter -BoundParameter @{} -RequiredParameter 'Parameter1' -IfParameterPresent 'Parameter2' } | - Should -Not -Throw - } - } - } - - Context 'When the parameter in IfParameterPresent is not present' { - It 'Should throw the correct error' { - InModuleScope -ScriptBlock { - $mockErrorMessage = $script:localizedData.RequiredCommandParameter_SpecificParametersMustAllBeSetWhenParameterExist -f 'Parameter1', 'Parameter2' - - { - Assert-RequiredCommandParameter -BoundParameter @{ - Parameter2 = 'Value2' - } -RequiredParameter 'Parameter1' -IfParameterPresent 'Parameter2' - } | Should -Throw -ExpectedMessage $mockErrorMessage - } - } - } - - Context 'When the parameters in IfParameterPresent is present and the required parameters are not present' { - It 'Should throw the correct error' { - InModuleScope -ScriptBlock { - $mockErrorMessage = $script:localizedData.RequiredCommandParameter_SpecificParametersMustAllBeSetWhenParameterExist -f "Parameter3', 'Parameter4", "Parameter1', 'Parameter2" - - { - Assert-RequiredCommandParameter -BoundParameter @{ - Parameter1 = 'Value1' - Parameter2 = 'Value2' - } -RequiredParameter @('Parameter3', 'Parameter4') -IfParameterPresent @('Parameter1', 'Parameter2') - } | Should -Throw -ExpectedMessage $mockErrorMessage - } - } - } - - Context 'When the parameters in IfParameterPresent is present and required parameters are present' { - It 'Should throw the correct error' { - InModuleScope -ScriptBlock { - { - Assert-RequiredCommandParameter -BoundParameter @{ - Parameter1 = 'Value1' - Parameter2 = 'Value2' - } -RequiredParameter @('Parameter1', 'Parameter2') -IfParameterPresent @('Parameter1', 'Parameter2') - } | Should -Not -Throw - } - } - } -} diff --git a/tests/Unit/Private/Assert-SetupActionProperties.Tests.ps1 b/tests/Unit/Private/Assert-SetupActionProperties.Tests.ps1 index 13117fd44..b04523b27 100644 --- a/tests/Unit/Private/Assert-SetupActionProperties.Tests.ps1 +++ b/tests/Unit/Private/Assert-SetupActionProperties.Tests.ps1 @@ -310,7 +310,7 @@ Describe 'Assert-SetupActionProperties' -Tag 'Private' { } ) { BeforeAll { - Mock -CommandName Test-ServiceAccountRequirePassword -MockWith { + Mock -CommandName Test-AccountRequirePassword -MockWith { return $false } } diff --git a/tests/Unit/Private/Test-ServiceAccountRequirePassword.Tests.ps1 b/tests/Unit/Private/Test-ServiceAccountRequirePassword.Tests.ps1 deleted file mode 100644 index 0d40bedaf..000000000 --- a/tests/Unit/Private/Test-ServiceAccountRequirePassword.Tests.ps1 +++ /dev/null @@ -1,85 +0,0 @@ -[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] -param () - -BeforeDiscovery { - try - { - if (-not (Get-Module -Name 'DscResource.Test')) - { - # Assumes dependencies has been resolved, so if this module is not available, run 'noop' task. - if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) - { - # Redirect all streams to $null, except the error stream (stream 2) - & "$PSScriptRoot/../../build.ps1" -Tasks 'noop' 2>&1 4>&1 5>&1 6>&1 > $null - } - - # If the dependencies has not been resolved, this will throw an error. - Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' - } - } - catch [System.IO.FileNotFoundException] - { - throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' - } -} - -BeforeAll { - $script:dscModuleName = 'SqlServerDsc' - - Import-Module -Name $script:dscModuleName - - $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName - $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscModuleName - $PSDefaultParameterValues['Should:ModuleName'] = $script:dscModuleName -} - -AfterAll { - $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') - $PSDefaultParameterValues.Remove('Mock:ModuleName') - $PSDefaultParameterValues.Remove('Should:ModuleName') - - # Unload the module being tested so that it doesn't impact any other tests. - Get-Module -Name $script:dscModuleName -All | Remove-Module -Force -} - -Describe 'Test-ServiceAccountRequirePassword' -Tag 'Private' { - Context 'When service account is a built-in account' { - It 'Should return $false' { - InModuleScope -ScriptBlock { - Test-ServiceAccountRequirePassword -Name 'NT Authority\NETWORK SERVICE' | Should -BeFalse - } - } - } - - Context 'When service account is a virtual account' { - It 'Should return $false' { - InModuleScope -ScriptBlock { - Test-ServiceAccountRequirePassword -Name 'NT SERVICE\MSSQL$PAYROLL' | Should -BeFalse - } - } - } - - Context 'When service account is a (global) managed service account' { - It 'Should return $false' { - InModuleScope -ScriptBlock { - Test-ServiceAccountRequirePassword -Name 'DOMAIN\MyMSA$' | Should -BeFalse - } - } - } - - Context 'When service account is a local user account' { - It 'Should return $true' { - InModuleScope -ScriptBlock { - Test-ServiceAccountRequirePassword -Name 'MySqlUser' | Should -BeTrue - } - } - } - - Context 'When service account is a domain user account' { - It 'Should return $true' { - InModuleScope -ScriptBlock { - Test-ServiceAccountRequirePassword -Name 'DOMAIN\MySqlUser' | Should -BeTrue - } - } - } -}