public/Invoke-MyCorp.ps1

<#
.SYNOPSIS
    This is the main MyCorp command that runs the tests and generates a report of the results.
 
.DESCRIPTION
    Invoke-MyCorp runs Pester tests and generates reports in HTML, Markdown, JSON, CSV and Excel formats.
    It is a production-ready rebrand of the original MyCorp commandset with minimal breaking changes.
 
    NOTES
    - Keeps existing Mt-* helper functions to avoid breaking existing tests and integrations (Get-Mt*, Set-Mt*, ConvertTo-Mt*, Get-MtHtmlReport, etc.).
    - Renamed runtime/session artifacts per the hybrid rule:
        - Top-level function: Invoke-MyCorp
        - $__MyCorpSession replaces $__MtSession for session state
        - MyCorpConfig -> MyCorpConfig (session property)
        - MyCorpResult -> MyCorpResult (local result variable name)
        - Initialize-MtSession -> Initialize-MyCorpSession (explicitly renamed)
 
    - Removed all original vendor signature blocks and references to the MyCorp domain.
#>


function Invoke-MyCorp {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '', Justification = 'Colors are helpful for UX')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', Justification = 'False positives for ExportCsv/ExportExcel')]
    [CmdletBinding()]
    param (
        [Parameter(Position = 0)]
        [string] $Path,

        [string[]] $Tag,

        [string[]] $ExcludeTag,

        [string] $OutputHtmlFile,

        [string] $OutputMarkdownFile,

        [string] $OutputJsonFile,

        [string] $OutputFolder,

        [string] $OutputFolderFileName,

        [PesterConfiguration] $PesterConfiguration,

        [ValidateSet('None','Normal','Detailed','Diagnostic')]
        [string] $Verbosity = 'None',

        [switch] $NonInteractive,
        [switch] $PassThru,
        [string[]] $MailRecipient,
        [string] $MailTestResultsUri,
        [string] $MailUserId,
        [string] $TeamId,
        [string] $TeamChannelId,
        [string] $TeamChannelWebhookUri,
        [switch] $SkipGraphConnect,
        [switch] $DisableTelemetry,
        [switch] $SkipVersionCheck,
        [switch] $ExportCsv,
        [switch] $ExportExcel,
        [switch] $NoLogo
    )

    # --- Session and helpers ---
    if (-not (Test-Path variable:\__MyCorpSession)) {
        New-Variable -Name __MyCorpSession -Value ([PSCustomObject]@{ Connections=@(); MyCorpConfig=$null }) -Scope Script -Force | Out-Null
    }

    function GetDefaultFileName() {
        $timestamp = Get-Date -Format "yyyy-MM-dd-HHmmss"
        return "TestResults-$timestamp.html"
    }

    function ValidateAndSetOutputFiles($out) {
        $result = $null
        if (![string]::IsNullOrEmpty($out.OutputHtmlFile)) {
            if ($out.OutputHtmlFile -notlike '*.html') { $result = 'The OutputHtmlFile parameter must have an .html extension.' }
        }
        if (![string]::IsNullOrEmpty($out.OutputMarkdownFile)) {
            if ($out.OutputMarkdownFile -notlike '*.md') { $result = 'The OutputMarkdownFile parameter must have an .md extension.' }
        }
        if (![string]::IsNullOrEmpty($out.OutputJsonFile)) {
            if ($out.OutputJsonFile -notlike '*.json') { $result = 'The OutputJsonFile parameter must have a .json extension.' }
        }

        $someOutputFileHasValue = ![string]::IsNullOrEmpty($out.OutputHtmlFile) -or `
            ![string]::IsNullOrEmpty($out.OutputMarkdownFile) -or ![string]::IsNullOrEmpty($out.OutputJsonFile)

        if ([string]::IsNullOrEmpty($out.OutputFolder) -and -not $someOutputFileHasValue) {
            $out.OutputFolder = './test-results'
        }

        if (![string]::IsNullOrEmpty($out.OutputFolder)) {
            New-Item -Path $out.OutputFolder -ItemType Directory -Force | Out-Null
            if ([string]::IsNullOrEmpty($out.OutputFolderFileName)) {
                $out.OutputFolderFileName = "TestResults-$(Get-Date -Format 'yyyy-MM-dd-HHmmss')"
            }

            $out.OutputHtmlFile = Join-Path $out.OutputFolder "$($out.OutputFolderFileName).html"
            $out.OutputMarkdownFile = Join-Path $out.OutputFolder "$($out.OutputFolderFileName).md"
            $out.OutputJsonFile = Join-Path $out.OutputFolder "$($out.OutputFolderFileName).json"

            if ($ExportCsv.IsPresent) { $out.OutputCsvFile = Join-Path $out.OutputFolder "$($out.OutputFolderFileName).csv" }
            if ($ExportExcel.IsPresent) { $out.OutputExcelFile = Join-Path $out.OutputFolder "$($out.OutputFolderFileName).xlsx" }
        }
        return $result
    }

    function GetPesterConfiguration($Path, $Tag, $ExcludeTag, $PesterConfiguration) {
        if (-not $PesterConfiguration) { $PesterConfiguration = New-PesterConfiguration }
        $PesterConfiguration.Run.PassThru = $true
        $PesterConfiguration.Output.Verbosity = $Verbosity
        if ($Path) { $PesterConfiguration.Run.Path = $Path } else {
            if (Test-Path -Path "./powershell/tests/pester.ps1") { $PesterConfiguration.Run.Path = './tests' }
        }
        if ($Tag) { $PesterConfiguration.Filter.Tag = $Tag }
        if ($ExcludeTag) { $PesterConfiguration.Filter.ExcludeTag = $ExcludeTag }
        return $PesterConfiguration
    }

    # --- Version and logo ---
    $version = Get-MtModuleVersion

    if ($NonInteractive.IsPresent -or $NoLogo.IsPresent) {
        Write-Verbose "Running MyCorp v$version"
    } else {
        $banner = @"
$$\ $$\ $$$$$$\
$$$\ $$$ | $$ __$$\
$$$$\ $$$$ |$$\ $$\ $$ / \__| $$$$$$\ $$$$$$\ $$$$$$\
$$\$$\$$ $$ |$$ | $$ |$$ | $$ __$$\ $$ __$$\ $$ __$$\
$$ \$$$ $$ |$$ | $$ |$$ | $$ / $$ |$$ | \__|$$ / $$ |
$$ |\$ /$$ |$$ | $$ |$$ | $$\ $$ | $$ |$$ | $$ | $$ |
$$ | \_/ $$ |\$$$$$$$ |\$$$$$$ |\$$$$$$ |$$ | $$$$$$$ |
\__| \__| \____$$ | \______/ \______/ \__| $$ ____/
             $$\ $$ | $$ |
             \$$$$$$ | $$ |
              \______/ \__|
 
MyCorp Test Runner v$version
"@

        Write-Host $banner -ForegroundColor Red
    }

    # Telemetry
    if (-not $DisableTelemetry) { Write-Telemetry -EventName InvokeMyCorp -ErrorAction SilentlyContinue }

    $isMail = $null -ne $MailRecipient
    $isTeamsChannelMessage = -not ([String]::IsNullOrEmpty($TeamId) -or [String]::IsNullOrEmpty($TeamChannelId))
    $isWebUri = -not ([String]::IsNullOrEmpty($TeamChannelWebhookUri))

    if ($SkipGraphConnect) { Write-Host "Skipping graph connection check" -ForegroundColor Yellow } else {
        if (!(Test-MtContext -SendMail:$isMail -SendTeamsMessage:$isTeamsChannelMessage)) { return }
    }

    # Initialize session (renamed as requested). This function is expected to exist in the environment.
    try {
        if (Get-Command -Name Initialize-MyCorpSession -ErrorAction SilentlyContinue) {
            Initialize-MyCorpSession
        } elseif (Get-Command -Name Initialize-MtSession -ErrorAction SilentlyContinue) {
            # fallback to original if renamed initialization function not available
            Initialize-MtSession
        }
    } catch {
        Write-Verbose "Warning: session initialization failed or was not found: $_"
    }

    if ($isWebUri) {
        $urlPattern = '^(https)://[^\s/$.?#].[^\s]*$'
        if (-not ($TeamChannelWebhookUri -match $urlPattern)) { Write-Output "Invalid Webhook URL: $TeamChannelWebhookUri"; return }
    }

    $out = [PSCustomObject]@{
        OutputFolder = $OutputFolder
        OutputFolderFileName = $OutputFolderFileName
        OutputHtmlFile = $OutputHtmlFile
        OutputMarkdownFile = $OutputMarkdownFile
        OutputJsonFile = $OutputJsonFile
        OutputCsvFile = $null
        OutputExcelFile = $null
    }

    $result = ValidateAndSetOutputFiles $out
    if ($result) { Write-Error -Message $result; return }

    # Tag defaults
    if ("CAWhatIf" -notin $Tag) { $ExcludeTag += 'CAWhatIf' }
    if (-not $Tag) { $ExcludeTag += 'Full' } elseif ('Full' -in $Tag) { $Tag += 'All' }

    $pesterConfig = GetPesterConfiguration -Path $Path -Tag $Tag -ExcludeTag $ExcludeTag -PesterConfiguration $PesterConfiguration
    $Path = $pesterConfig.Run.Path.value
    Write-Verbose "Merged configuration: $($pesterConfig | ConvertTo-Json -Depth 5 -Compress)"

    if (Test-Path -Path $Path -PathType Leaf) {
        Write-Host "The path '$Path' is a file. Please provide a folder path." -ForegroundColor Red
        Write-Host "→ Update-MyCorpTests" -ForegroundColor Green
        return
    }

    if (-not (Test-Path -Path $Path -PathType Container)) {
        Write-Host "The path '$Path' does not exist." -ForegroundColor Red
        Write-Host "→ Update-MyCorpTests" -ForegroundColor Green
        return
    }

    if (-not (Get-ChildItem -Path "$Path\*.Tests.ps1" -Recurse -ErrorAction SilentlyContinue)) {
        Write-Host "No test files found in the path '$Path'." -ForegroundColor Red
        Write-Host "→ Update-MyCorpTests" -ForegroundColor Green
        return
    }

    # Discover and run tests
    Set-MtProgressView
    Write-MtProgress -Activity "Starting MyCorp" -Status "Reading MyCorp config..." -Force

    try {
        # keep underlying helper Get-MtMyCorpConfig but map into MyCorp session property
        $__MyCorpSession.MyCorpConfig = Get-MtMyCorpConfig -Path $Path
    } catch {
        Write-Verbose "Failed to read configuration via Get-MtMyCorpConfig: $_"
    }

    Write-MtProgress -Activity "Starting MyCorp" -Status "Discovering tests to run..." -Force
    $pesterResults = Invoke-Pester -Configuration $pesterConfig

    if ($pesterResults) {
        Write-MtProgress -Activity "Processing test results" -Status "$($pesterResults.TotalCount) test(s)" -Force

        # Convert results (keep ConvertTo-MtMyCorpResult for compatibility)
        $mycorpResults = ConvertTo-MtMyCorpResult $pesterResults

        if (![string]::IsNullOrEmpty($out.OutputJsonFile)) {
            $mycorpResults | ConvertTo-Json -Depth 5 -WarningAction SilentlyContinue | Out-File -FilePath $out.OutputJsonFile -Encoding UTF8
        }

        if (![string]::IsNullOrEmpty($out.OutputMarkdownFile)) {
            Write-MtProgress -Activity "Creating markdown report"
            $output = Get-MtMarkdownReport -MyCorpResults $mycorpResults
            $output | Out-File -FilePath $out.OutputMarkdownFile -Encoding UTF8
        }

        if (![string]::IsNullOrEmpty($out.OutputCsvFile)) {
            Write-MtProgress -Activity "Creating CSV"
            Convert-MtResultsToFlatObject -InputObject $mycorpResults -CsvFilePath $out.OutputCsvFile
        }

        if (![string]::IsNullOrEmpty($out.OutputExcelFile)) {
            Write-MtProgress -Activity "Creating Excel workbook"
            Convert-MtResultsToFlatObject -InputObject $mycorpResults -ExcelFilePath $out.OutputExcelFile
        }

        if (![string]::IsNullOrEmpty($out.OutputHtmlFile)) {
            Write-MtProgress -Activity "Creating html report"
            $output = Get-MtHtmlReport -MyCorpResults $mycorpResults
            $output | Out-File -FilePath $out.OutputHtmlFile -Encoding UTF8
            Write-Host "MyCorp test report generated at $($out.OutputHtmlFile)" -ForegroundColor Green

            if ((Get-MtUserInteractive) -and (-not $NonInteractive)) { Invoke-Item $out.OutputHtmlFile | Out-Null }
        }

        if ($MailRecipient) {
            Write-MtProgress -Activity "Sending mail"
            Send-MtMail -MyCorpResults $mycorpResults -Recipient $MailRecipient -TestResultsUri $MailTestResultsUri -UserId $MailUserId
        }

        if ($TeamId -and $TeamChannelId) {
            Write-MtProgress -Activity "Sending Teams message"
            Send-MtTeamsMessage -MyCorpResults $mycorpResults -TeamId $TeamId -TeamChannelId $TeamChannelId -TestResultsUri $MailTestResultsUri
        }

        if ($TeamChannelWebhookUri) {
            Write-MtProgress -Activity "Sending Teams message"
            Send-MtTeamsMessage -MyCorpResults $mycorpResults -TeamChannelWebhookUri $TeamChannelWebhookUri -TestResultsUri $MailTestResultsUri
        }

        if ($Verbosity -eq 'None') {
            Write-Host "`nTests Passed: $($pesterResults.PassedCount), Failed: $($pesterResults.FailedCount), Skipped: $($pesterResults.SkippedCount)`n"
        }

        if (-not $SkipVersionCheck -and 'Next' -ne $version) { Get-IsNewMyCorpVersionAvailable | Out-Null }

        Write-MtProgress -Activity "Completed tests" -Status "Total $($pesterResults.TotalCount)" -Completed -Force
    }

    Reset-MtProgressView

    if ($PassThru) { return $mycorpResults }
}

# End of Invoke-MyCorp