tests/CodeCompass.GitHub.Tests.ps1
|
#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } BeforeAll { $modulePath = Join-Path $PSScriptRoot '..' 'CodeCompass.GitHub.psd1' Import-Module $modulePath -Force } AfterAll { Remove-Module CodeCompass.GitHub -Force -ErrorAction SilentlyContinue } # Mock helper to simulate GitHub API responses function New-MockRepoInfo { param( [string]$DefaultBranch = 'main', [bool]$HasIssues = $true, [bool]$HasWiki = $false, [bool]$DeleteOnMerge = $true, [bool]$AllowSquash = $true, [string]$Description = 'A test repository', [string[]]$Topics = @('test', 'powershell') ) return @{ default_branch = $DefaultBranch has_issues = $HasIssues has_wiki = $HasWiki delete_branch_on_merge = $DeleteOnMerge allow_squash_merge = $AllowSquash description = $Description topics = $Topics visibility = 'public' security_and_analysis = @{ secret_scanning = @{ status = 'enabled' } secret_scanning_push_protection = @{ status = 'enabled' } dependabot_security_updates = @{ status = 'enabled' } } } } function New-MockProtection { param( [int]$Reviewers = 1, [bool]$DismissStale = $true, [bool]$StatusChecks = $true, [bool]$Strict = $true, [bool]$EnforceAdmins = $false, [bool]$AllowForcePush = $false, [bool]$AllowDelete = $false ) return @{ required_pull_request_reviews = @{ required_approving_review_count = $Reviewers dismiss_stale_reviews = $DismissStale } required_status_checks = if ($StatusChecks) { @{ strict = $Strict; contexts = @('ci') } } else { $null } enforce_admins = @{ enabled = $EnforceAdmins } allow_force_pushes = @{ enabled = $AllowForcePush } allow_deletions = @{ enabled = $AllowDelete } } } Describe 'Module loads correctly' { It 'Exports expected functions' { $commands = Get-Command -Module CodeCompass.GitHub $commands.Name | Should -Contain 'Test-CCGitHub' $commands.Name | Should -Contain 'Test-CCBranchProtection' $commands.Name | Should -Contain 'Test-CCSecurityConfig' $commands.Name | Should -Contain 'Test-CCRepoSettings' $commands.Name | Should -Contain 'Test-CCCollaboration' $commands.Name | Should -Contain 'Repair-CCGitHub' $commands.Name | Should -Contain 'Get-CCGitHubReport' } } Describe 'Standard configuration' { It 'Loads core standard' { InModuleScope CodeCompass.GitHub { $config = Get-CCStandardConfig -Standard 'core' $config | Should -Not -BeNullOrEmpty $config.branch_protection.require | Should -BeTrue $config.branch_protection.min_reviewers | Should -Be 1 } } It 'Loads active standard with relaxed settings' { InModuleScope CodeCompass.GitHub { $config = Get-CCStandardConfig -Standard 'active' $config.branch_protection.dismiss_stale_reviews | Should -BeFalse $config.branch_protection.require_status_checks | Should -BeFalse } } It 'Loads minimal standard with most checks disabled' { InModuleScope CodeCompass.GitHub { $config = Get-CCStandardConfig -Standard 'minimal' $config.branch_protection.require | Should -BeFalse $config.security.secret_scanning | Should -BeFalse } } It 'Throws on unknown standard' { InModuleScope CodeCompass.GitHub { { Get-CCStandardConfig -Standard 'nonexistent' } | Should -Throw '*Unknown standard*' } } } Describe 'Token resolution' { It 'Uses explicit token parameter' { InModuleScope CodeCompass.GitHub { Resolve-CCToken -Token 'explicit-token' | Should -Be 'explicit-token' } } It 'Falls back to GITHUB_TOKEN env var' { $env:GITHUB_TOKEN = 'env-token' try { InModuleScope CodeCompass.GitHub { Resolve-CCToken | Should -Be 'env-token' } } finally { Remove-Item env:GITHUB_TOKEN -ErrorAction SilentlyContinue } } It 'Returns null when no token available' { $origGH = $env:GITHUB_TOKEN $origGHT = $env:GH_TOKEN Remove-Item env:GITHUB_TOKEN -ErrorAction SilentlyContinue Remove-Item env:GH_TOKEN -ErrorAction SilentlyContinue try { InModuleScope CodeCompass.GitHub { # Mock gh CLI to fail Mock gh { throw "not found" } Resolve-CCToken | Should -BeNullOrEmpty } } finally { if ($origGH) { $env:GITHUB_TOKEN = $origGH } if ($origGHT) { $env:GH_TOKEN = $origGHT } } } } Describe 'Check result objects' { It 'Creates properly structured result' { InModuleScope CodeCompass.GitHub { $result = New-CCCheckResult -CheckId 'GH-TEST-001' -Category 'Test' -Item 'thing' ` -Status 'Pass' -Severity 'Info' -Message 'All good' $result.CheckId | Should -Be 'GH-TEST-001' $result.Category | Should -Be 'Test' $result.Status | Should -Be 'Pass' $result.FixAvailable | Should -BeFalse } } It 'Includes fix metadata when available' { InModuleScope CodeCompass.GitHub { $result = New-CCCheckResult -CheckId 'GH-TEST-002' -Category 'Test' -Item 'thing' ` -Status 'Fail' -Severity 'Error' -Message 'Broken' ` -FixAvailable $true -FixId 'FixSomething' -Current 'bad' -Expected 'good' $result.FixAvailable | Should -BeTrue $result.FixId | Should -Be 'FixSomething' $result.Current | Should -Be 'bad' $result.Expected | Should -Be 'good' } } } Describe 'YAML config parser' { It 'Parses simple key-value pairs' { InModuleScope CodeCompass.GitHub { $yaml = @" github: branch_protection: min_reviewers: 2 require: true settings: wiki_disabled: false "@ $result = ConvertFrom-CCYaml -Content $yaml $result.github.branch_protection.min_reviewers | Should -Be 2 $result.github.branch_protection.require | Should -BeTrue $result.github.settings.wiki_disabled | Should -BeFalse } } It 'Ignores comments and blank lines' { InModuleScope CodeCompass.GitHub { $yaml = @" # This is a comment key: value # Another comment nested: sub: 42 "@ $result = ConvertFrom-CCYaml -Content $yaml $result.key | Should -Be 'value' $result.nested.sub | Should -Be 42 } } } Describe 'Test-CCBranchProtection (unit - with mocked API)' { It 'Throws when no token is available' { InModuleScope CodeCompass.GitHub { # Directly test that Resolve-CCToken returns null when no sources available Mock gh { $global:LASTEXITCODE = 1; return $null } $origGH = $env:GITHUB_TOKEN; $origGHT = $env:GH_TOKEN Remove-Item env:GITHUB_TOKEN -ErrorAction SilentlyContinue Remove-Item env:GH_TOKEN -ErrorAction SilentlyContinue try { $result = Resolve-CCToken $result | Should -BeNullOrEmpty } finally { if ($origGH) { $env:GITHUB_TOKEN = $origGH } if ($origGHT) { $env:GH_TOKEN = $origGHT } } } } It 'Requires -Repository or git remote' { { Test-CCBranchProtection -Token 'fake-token' -Repository '' } | Should -Throw } } Describe 'Test-CCRepoSettings (unit - with mocked API)' { It 'Requires -Repository or git remote' { { Test-CCRepoSettings -Token 'fake-token' -Repository '' } | Should -Throw } } Describe 'Config merge' { It 'Override values replace base values' { InModuleScope CodeCompass.GitHub { $base = @{ branch_protection = @{ min_reviewers = 1; require = $true } } $override = @{ branch_protection = @{ min_reviewers = 2 } } $result = Merge-CCConfig -Base $base -Override $override $result.branch_protection.min_reviewers | Should -Be 2 $result.branch_protection.require | Should -BeTrue } } It 'Override adds new keys' { InModuleScope CodeCompass.GitHub { $base = @{ settings = @{ issues = $true } } $override = @{ settings = @{ wiki = $false }; extra = 'new' } $result = Merge-CCConfig -Base $base -Override $override $result.settings.issues | Should -BeTrue $result.settings.wiki | Should -BeFalse $result.extra | Should -Be 'new' } } } |