Tests/VSAModule.Import.Tests.ps1

BeforeAll {
    $ModuleRoot = Split-Path -Path (Split-Path -Parent $PSCommandPath)
    $ModulePath = Join-Path -Path $ModuleRoot -ChildPath 'VSAModule.psd1'
}

Describe "HTTP transport assembly is loaded at import (Windows PowerShell 5.1 regression)" {

    # Regression guard for a real 5.1 failure: New-VSAConnection threw
    # "Unable to find type [System.Net.Http.HttpClient]"
    # The module transports over HttpClient (F-67). On .NET Core that assembly is part of the shared
    # framework, so the bug is invisible there; on .NET Framework it is a separate assembly that is
    # not loaded by default. It cannot be loaded lazily inside the transport functions: PowerShell
    # compiles a function body on first invocation and resolves every type literal in it at that
    # point, so an Add-Type in the same function always runs too late.

    It "System.Net.Http.HttpClient resolves once the module is imported" {
        Import-Module $ModulePath -Force
        ('System.Net.Http.HttpClient' -as [type]) | Should -Not -BeNullOrEmpty
    }

    It "the assembly is loaded at module load, not lazily inside a transport function" {
        # The source guard is what actually protects 5.1, since on Core the first assertion above
        # would pass even with the bug present.
        $psm1 = Get-Content (Join-Path $ModuleRoot 'VSAModule.psm1') -Raw
        $psm1 | Should -Match 'Add-Type\s+-AssemblyName\s+System\.Net\.Http'

        foreach ($file in Get-ChildItem (Join-Path $ModuleRoot 'private') -Filter '*.ps1') {
            (Get-Content $file.FullName -Raw) |
                Should -Not -Match 'Add-Type\s+-AssemblyName\s+System\.Net\.Http' -Because "$($file.Name) must rely on the module-load Add-Type; a lazy one is too late to satisfy its own type literals"
        }
    }
}

Describe "VSAModule Import and Function Availability" {

    Context "Module Import" {
        It "Module imports without errors" {
            { Import-Module $ModulePath -Force -ErrorAction Stop } | Should -Not -Throw
        }

        It "Module is loaded after import" {
            $Module = Get-Module -Name VSAModule
            $Module | Should -Not -BeNullOrEmpty
        }

        It "Module type is Script" {
            $Module = Get-Module -Name VSAModule
            $Module.ModuleType | Should -Be "Script"
        }
    }

    Context "Exported Functions Availability" {
        BeforeEach {
            Import-Module $ModulePath -Force
            $ManifestData = Import-PowerShellDataFile -Path $ModulePath
            $ExportedFunctions = $ManifestData.FunctionsToExport
        }

        It "Module exports more than 100 functions" {
            $ExportedFunctions.Count | Should -BeGreaterThan 100
        }

        It "New-VSAConnection function is available" {
            Get-Command -Name "New-VSAConnection" -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty
        }

        It "New-VSAConnection is a function" {
            (Get-Command -Name "New-VSAConnection").CommandType | Should -Be "Function"
        }

        # F-20: these dispatch wrappers must be reachable from the caller's scope, else their
        # aliases (~89 commands) cannot resolve. Get-RequestData stays private (no alias targets it).
        It "Dispatch wrapper Get-VSAItem is accessible (its aliases resolve to it)" {
            { Get-Command -Name "Get-VSAItem" -CommandType Function -ErrorAction Stop } | Should -Not -Throw
        }

        It "Dispatch wrapper Get-VSAItemById is accessible" {
            { Get-Command -Name "Get-VSAItemById" -CommandType Function -ErrorAction Stop } | Should -Not -Throw
        }

        It "Dispatch wrapper Remove-VSAItem is accessible" {
            { Get-Command -Name "Remove-VSAItem" -CommandType Function -ErrorAction Stop } | Should -Not -Throw
        }

        It "Genuinely private helper Get-RequestData is NOT accessible" {
            { Get-Command -Name "Get-RequestData" -CommandType Function -ErrorAction Stop } | Should -Throw
        }

        # Regression guard for F-20: an exported alias must resolve to a real command on a NORMAL
        # import (not InModuleScope). This is the coverage whose absence let the D-3 break ship.
        It "Alias-dispatched commands resolve to a command on a normal import" {
            foreach ($name in 'Get-VSATenant','Get-VSAAgentPackage','Get-VSAAgentSettings','Remove-VSAAsset') {
                $cmd = Get-Command -Name $name -ErrorAction Stop
                $cmd.ResolvedCommand | Should -Not -BeNullOrEmpty
            }
        }
    }

    Context "Exported Aliases Availability" {
        BeforeEach {
            Import-Module $ModulePath -Force
            $ManifestData = Import-PowerShellDataFile -Path $ModulePath
            $ExportedAliases = $ManifestData.AliasesToExport
        }

        It "Module exports more than 75 aliases" {
            $ExportedAliases.Count | Should -BeGreaterThan 75
        }

        It "Get-VSAActivityType alias is available" {
            Get-Alias -Name "Get-VSAActivityType" -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty
        }

        It "Get-VSATenants alias is available" {
            Get-Alias -Name "Get-VSATenants" -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty
        }

        It "Get-VSAAgentNote alias is available" {
            Get-Alias -Name "Get-VSAAgentNote" -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty
        }

        It "Remove-VSAAsset alias is available" {
            Get-Alias -Name "Remove-VSAAsset" -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty
        }

        It "Remove-VSAOrganization alias is available" {
            Get-Alias -Name "Remove-VSAOrganization" -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty
        }
    }

    Context "Module Classes" {
        BeforeEach {
            Import-Module $ModulePath -Force
        }

        It "VSAConnection class exists" {
            { [VSAConnection] } | Should -Not -Throw
        }

        It "Can create VSAConnection instance" {
            { $conn = New-Object VSAConnection } | Should -Not -Throw
        }

        It "TrustAllCertsPolicy class is removed (F-26)" {
            ('TrustAllCertsPolicy' -as [type]) | Should -BeNullOrEmpty
        }
    }

    Context "Help System Integration" {
        BeforeEach {
            Import-Module $ModulePath -Force
        }

        It "Comment-based help is available for exported functions" {
            $Help = Get-Help -Name "Get-VSAAgent" -ErrorAction SilentlyContinue
            $Help.Synopsis | Should -Not -BeNullOrEmpty
        }

        It "Can get help for New-VSAConnection" {
            $Help = Get-Help -Name "New-VSAConnection" -ErrorAction SilentlyContinue
            $Help | Should -Not -BeNullOrEmpty
        }
    }

    Context "Function File Organization" {
        It "Public folder contains function files" {
            $PublicFunctions = Get-ChildItem -Path (Join-Path $ModuleRoot 'public') -Filter "*.ps1" -ErrorAction SilentlyContinue
            $PublicFunctions | Should -Not -BeNullOrEmpty
        }

        It "Private folder contains helper functions" {
            $PrivateFunctions = Get-ChildItem -Path (Join-Path $ModuleRoot 'private') -Filter "*.ps1" -ErrorAction SilentlyContinue
            $PrivateFunctions | Should -Not -BeNullOrEmpty
        }

        It "Public functions include Get functions" {
            $GetFunctions = @(Get-ChildItem -Path (Join-Path $ModuleRoot 'public') -Filter "Get-*.ps1" -ErrorAction SilentlyContinue)
            $GetFunctions.Count | Should -BeGreaterThan 0
        }

        It "Public functions include New functions" {
            $NewFunctions = @(Get-ChildItem -Path (Join-Path $ModuleRoot 'public') -Filter "New-*.ps1" -ErrorAction SilentlyContinue)
            $NewFunctions.Count | Should -BeGreaterThan 0
        }

        It "Public functions include Update functions" {
            $UpdateFunctions = @(Get-ChildItem -Path (Join-Path $ModuleRoot 'public') -Filter "Update-*.ps1" -ErrorAction SilentlyContinue)
            $UpdateFunctions.Count | Should -BeGreaterThan 0
        }

        It "Public functions include Remove functions" {
            $RemoveFunctions = @(Get-ChildItem -Path (Join-Path $ModuleRoot 'public') -Filter "Remove-*.ps1" -ErrorAction SilentlyContinue)
            $RemoveFunctions.Count | Should -BeGreaterThan 0
        }
    }

    Context "Module Metadata" {
        BeforeEach {
            Import-Module $ModulePath -Force
            $Module = Get-Module -Name VSAModule
        }

        It "Module has version" {
            $Module.Version | Should -Not -BeNullOrEmpty
        }

        It "Module version is semantic" {
            $Module.Version.ToString() -match '^\d+\.\d+\.\d+' | Should -Be $true
        }

        It "Module has author" {
            $Module.Author | Should -Not -BeNullOrEmpty
        }
    }
}