Tests/Unit/Psm1Structure.Tests.ps1
|
BeforeAll { $script:Psm1Content = Get-Content (Join-Path $PSScriptRoot '..' '..' 'Devolutions.CIEM.psm1') -Raw $script:Psm1Ast = [System.Management.Automation.Language.Parser]::ParseInput($script:Psm1Content, [ref]$null, [ref]$null) } Describe 'Devolutions.CIEM.psm1 Structure' { Context 'Sub-module root variables' { It 'Contains $script:AzureDiscoveryRoot assignment' { $script:Psm1Content | Should -Match '\$script:AzureDiscoveryRoot\s*=' } It 'Does NOT contain $script:AzurePermissionsRoot' { $script:Psm1Content | Should -Not -Match '\$script:AzurePermissionsRoot' } It 'Does NOT contain $script:IdentitiesRoot' { $script:Psm1Content | Should -Not -Match '\$script:IdentitiesRoot' } } Context 'Class loading' { It 'Loads CIEMAuthenticationContext and CIEMProvider base classes' { $script:Psm1Content | Should -Match "'CIEMAuthenticationContext'" $script:Psm1Content | Should -Match "'CIEMProvider'" } It 'Does NOT load CIEMIdentity or CIEMResourceType' { $script:Psm1Content | Should -Not -Match "'CIEMIdentity'" $script:Psm1Content | Should -Not -Match "'CIEMResourceType'" } It 'Does NOT contain Identity classes loading block' { $script:Psm1Content | Should -Not -Match 'CIEMIdentityNodes' $script:Psm1Content | Should -Not -Match 'CIEMRBACNodes' $script:Psm1Content | Should -Not -Match 'CIEMIdentityResourceAccess' } } Context 'Schema application' { It 'Contains discovery_schema.sql in schema loop' { $script:Psm1Content | Should -Match 'discovery_schema\.sql' } It 'Contains AzureDiscovery label' { $script:Psm1Content | Should -Match "Label\s*=\s*'AzureDiscovery'" } } Context 'Dead cache keys removed' { It 'Does NOT contain GraphLatestCacheKey' { $script:Psm1Content | Should -Not -Match 'GraphLatestCacheKey' } It 'Does NOT contain GraphAzureCacheKey' { $script:Psm1Content | Should -Not -Match 'GraphAzureCacheKey' } } Context 'App registration references' { BeforeAll { $script:AppContent = Get-Content (Join-Path $PSScriptRoot '..' '..' 'modules' 'Devolutions.CIEM.PSU' 'Public' 'New-DevolutionsCIEMApp.ps1') -Raw } It 'Does NOT reference New-CIEMGraphPage (dead function)' { $script:AppContent | Should -Not -Match 'New-CIEMGraphPage' } It 'Does NOT reference New-CIEMIdentityRiskPage' { $script:AppContent | Should -Not -Match 'New-CIEMIdentityRiskPage' } It 'References New-CIEMIdentitiesPage' { $script:AppContent | Should -Match 'New-CIEMIdentitiesPage' } It 'References New-CIEMAttackPathsPage' { $script:AppContent | Should -Match 'New-CIEMAttackPathsPage' } } Context 'No empty catch blocks' { It 'Does not contain catch {}' { $script:Psm1Content | Should -Not -Match 'catch\s*\{\s*\}' } It 'throws after logging module initialization failures' { $violations = @($script:Psm1Ast.FindAll( { param($node) $node -is [System.Management.Automation.Language.CatchClauseAst] }, $true ) | Where-Object { $_.Body.Extent.Text -match 'FAILED to load|schema failed|Database initialization failed' -and -not $_.Body.Find({ param($node) $node -is [System.Management.Automation.Language.ThrowStatementAst] }, $true) }) $violations | Should -BeNullOrEmpty } } Context 'Schema application fail-fast behavior' { It 'Throws when an expected provider schema path is missing' { $script:Psm1Content | Should -Match 'Schema file not found' } It 'Throws when the module database path is unavailable before schema application' { $script:Psm1Content | Should -Match 'Database path not resolved' } } Context 'Sub-module roots array' { It '$subModuleRoots contains $script:AzureDiscoveryRoot' { $script:Psm1Content | Should -Match '\$subModuleRoots\s*=\s*@\([^)]*\$script:AzureDiscoveryRoot' } It '$subModuleRoots does NOT contain $script:AzurePermissionsRoot' { # Extract the $subModuleRoots block and check it doesn't reference the old roots $script:Psm1Content | Should -Not -Match '\$subModuleRoots\s*=\s*@\([^)]*AzurePermissionsRoot' } It '$subModuleRoots does NOT contain $script:IdentitiesRoot' { $script:Psm1Content | Should -Not -Match '\$subModuleRoots\s*=\s*@\([^)]*IdentitiesRoot' } } } |