Tests/Unit/PublishStaging.Tests.ps1

#Requires -Module Pester

<#
New-PublishStaging must never let a key into the package Publish-Module builds. The module folder is
where .env with NUGET_API_KEY lives (Publish-ToGallery.ps1 reads it from there), and every publish
script used to pack that folder, or a full copy of it.
#>


BeforeAll {
    # Publish-ToGallery.ps1 -WhatIf runs this test; the scratch folders must still be created
    $WhatIfPreference = $false
    $PSDefaultParameterValues['*:WhatIf'] = $false
    . (Join-Path $PSScriptRoot '..' '..' 'Build' 'New-PublishStaging.ps1')

    function New-FakeModule {
        param([string]$Root, [switch]$Git)
        New-Item -ItemType Directory -Path (Join-Path $Root 'Public') -Force | Out-Null
        Set-Content (Join-Path $Root 'Fake.psd1') "@{ ModuleVersion = '1.0.0' }"
        Set-Content (Join-Path $Root 'Public/Get-Thing.ps1') 'function Get-Thing { 1 }'
        Set-Content (Join-Path $Root '.env.example') 'NUGET_API_KEY=your-key-here'
        Set-Content (Join-Path $Root '.gitignore') ".env`nTestResults.xml"
        if ($Git) {
            & git -C $Root init --quiet
            & git -C $Root -c user.name=test -c user.email=test@localhost add -A
            & git -C $Root -c user.name=test -c user.email=test@localhost commit --quiet -m init
        }
        # Never committed: what a developer keeps next to the module
        Set-Content (Join-Path $Root '.env') 'NUGET_API_KEY=dummy-not-a-real-key'
        Set-Content (Join-Path $Root '.env.local') 'ANTHROPIC_API_KEY=dummy'
        Set-Content (Join-Path $Root 'Public/secrets.json') '{"k":"dummy"}'
        Set-Content (Join-Path $Root 'TestResults.xml') '<x/>'
    }

    $script:Scratch = Join-Path ([System.IO.Path]::GetTempPath()) "aicr-staging-test-$([guid]::NewGuid().ToString('N'))"
    $script:Staged = @()
}

AfterAll {
    foreach ($path in $script:Staged) { Remove-PublishStaging -StagingPath $path }
    if (Test-Path $script:Scratch) { Remove-Item $script:Scratch -Recurse -Force -ErrorAction SilentlyContinue }
    $PSDefaultParameterValues.Remove('*:WhatIf')
}

Describe 'New-PublishStaging' {
    Context 'module folder in a git checkout' {
        BeforeAll {
            $root = Join-Path $script:Scratch 'git/PSModule'
            New-FakeModule -Root $root -Git
            # A tracked file changed but not committed (e.g. a version bump) must be staged as it is now
            Set-Content (Join-Path $root 'Fake.psd1') "@{ ModuleVersion = '1.0.1' }"
            $script:StagingGit = New-PublishStaging -ModuleRoot $root -ModuleName 'Fake.Module' 6>$null
            $script:Staged += $script:StagingGit
            $script:FilesGit = @(Get-ChildItem $script:StagingGit -Recurse -Force -File | ForEach-Object { $_.FullName.Substring($script:StagingGit.Length + 1) -replace '\\', '/' } | Sort-Object)
        }

        It 'is named after the module' {
            Split-Path $script:StagingGit -Leaf | Should -Be 'Fake.Module'
        }

        It 'contains only the tracked files' {
            $script:FilesGit | Should -Be @('.env.example', '.gitignore', 'Fake.psd1', 'Public/Get-Thing.ps1')
        }

        It 'leaves .env, other secrets and test results out' {
            $script:FilesGit | Should -Not -Contain '.env'
            $script:FilesGit | Should -Not -Contain '.env.local'
            $script:FilesGit | Should -Not -Contain 'Public/secrets.json'
            $script:FilesGit | Should -Not -Contain 'TestResults.xml'
        }

        It 'stages the working-tree content of tracked files' {
            Get-Content (Join-Path $script:StagingGit 'Fake.psd1') | Should -Match "1\.0\.1"
        }

        It 'Remove-PublishStaging deletes the staging root' {
            $extra = New-PublishStaging -ModuleRoot $root -ModuleName 'Fake.Module' 6>$null
            Remove-PublishStaging -StagingPath $extra
            Test-Path (Split-Path $extra -Parent) | Should -BeFalse
        }
    }

    Context 'module folder outside git' {
        It 'copies everything but still removes every secret-named file' {
            $root = Join-Path $script:Scratch 'plain/PSModule'
            New-FakeModule -Root $root
            $staging = New-PublishStaging -ModuleRoot $root -ModuleName 'Fake.Module' 6>$null
            $script:Staged += $staging
            $names = @(Get-ChildItem $staging -Recurse -Force -File).Name
            $names | Should -Contain '.env.example'
            $names | Should -Contain 'Get-Thing.ps1'
            $names | Should -Not -Contain '.env'
            $names | Should -Not -Contain '.env.local'
            $names | Should -Not -Contain 'secrets.json'
        }
    }
}