Skip to content

Get-Module [-ListAvailable] -FullyQualifiedName with path ignores other constraints #8262

@rjmholt

Description

@rjmholt

From #8218.

Get-Module using a path in a fully qualified name will skip other constraints in the fully qualified name and be too permissive.

Steps to reproduce

Describe 'Get-Module -ListAvailable with path' {
    BeforeAll {
        $moduleName = 'Banana'
        $modulePath = Join-Path $TestDrive $moduleName
        $v1 = '1.2.3'
        $v2 = '4.8.3'
        $v1DirPath = Join-Path $modulePath $v1
        $v2DirPath = Join-Path $modulePath $v2
        $manifestV1Path = Join-Path $v1DirPath "$moduleName.psd1"
        $manifestV2Path = Join-Path $v2DirPath "$moduleName.psd1"

        New-Item -ItemType Directory $modulePath
        New-Item -ItemType Directory -Path $v1DirPath
        New-Item -ItemType Directory -Path $v2DirPath
        New-ModuleManifest -Path $manifestV1Path -ModuleVersion $v1
        New-ModuleManifest -Path $manifestV2Path -ModuleVersion $v2
    }

    It "Gets all versions by path" {
        $modules = Get-Module -ListAvailable $modulePath | Sort-Object -Property Version

        $modules | Should -HaveCount 2
        $modules[0].Name | Should -BeExactly $moduleName
        $modules[0].Path | Should -BeExactly $manifestV1Path
        $modules[0].Version | Should -Be $v1
        $modules[1].Name | Should -BeExactly $moduleName
        $modules[1].Path | Should -BeExactly $manifestV2Path
        $modules[1].Version | Should -Be $v2
    }

    It "Gets all versions by FullyQualifiedName with path with lower version" {
        $modules = Get-Module -ListAvailable -FullyQualifiedName @{ ModuleName = $modulePath; ModuleVersion = '0.0' } | Sort-Object -Property Version

        $modules | Should -HaveCount 2
        $modules[0].Name | Should -BeExactly $moduleName
        $modules[0].Path | Should -BeExactly $manifestV1Path
        $modules[0].Version | Should -Be $v1
        $modules[1].Name | Should -BeExactly $moduleName
        $modules[1].Path | Should -BeExactly $manifestV2Path
        $modules[1].Version | Should -Be $v2
    }

    It "Gets high version by FullyQualifiedName with path with high version" {
        $modules = Get-Module -ListAvailable -FullyQualifiedName @{ ModuleName = $modulePath; ModuleVersion = '2.0' } | Sort-Object -Property Version

        $modules | Should -HaveCount 1
        $modules[0].Name | Should -BeExactly $moduleName
        $modules[0].Path | Should -BeExactly $manifestV2Path
        $modules[0].Version | Should -Be $v2
    }

    It "Gets low version by FullyQualifiedName with path with low maximum version" {
        $modules = Get-Module -ListAvailable -FullyQualifiedName @{ ModuleName = $modulePath; MaximumVersion = '2.0' } | Sort-Object -Property Version

        $modules | Should -HaveCount 1
        $modules[0].Name | Should -BeExactly $moduleName
        $modules[0].Path | Should -BeExactly $manifestV1Path
        $modules[0].Version | Should -Be $v1
    }

    It "Gets low version by FullyQualifiedName with path with low maximum version and version" {
        $modules = Get-Module -ListAvailable -FullyQualifiedName @{ ModuleName = $modulePath; MaximumVersion = '2.0'; ModuleVersion = '1.0' } | Sort-Object -Property Version

        $modules | Should -HaveCount 1
        $modules[0].Name | Should -BeExactly $moduleName
        $modules[0].Path | Should -BeExactly $manifestV1Path
        $modules[0].Version | Should -Be $v1
    }

    It "Gets correct version by FullyQualifiedName with path with required version" -TestCases @(
        @{ Version = $v1 }
        @{ Version = $v2 }
    ) {
        param([version]$Version)

        switch ($Version)
        {
            $v1
            {
                $expectedPath = $manifestV1Path
                break
            }

            $v2
            {
                $expectedPath = $manifestV2Path
            }
        }

        $modules = Get-Module -ListAvailable -FullyQualifiedName @{ ModuleName = $modulePath; RequiredVersion = $Version }

        $modules | Should -HaveCount 1
        $modules[0].Name | Should -BeExactly $moduleName
        $modules[0].Path | Should -BeExactly $expectedPath
        $modules[0].Version | Should -Be $Version
    }
}

Expected behavior

All tests pass

Actual behavior

Tests with additional version checks in fully qualified names have their checks ignored.
This is of the following code:

// TODO:
// moduleSpecification.Name may be a path and will not match module.Name when they refer to the same module.
// This actually causes the module to be returned always, so other specification checks are skipped erroneously.
// Instead we need to be able to look up or match modules by path as well (e.g. a new comparer for PSModuleInfo).
// No table entry means we return the module
if (!moduleSpecificationTable.TryGetValue(module.Name, out ModuleSpecification moduleSpecification))
{
yield return module;
continue;
}

When a path is specified, the lookup fails and we (perversely) return the module.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue-Enhancementthe issue is more of a feature request than a bugResolution-No ActivityIssue has had no activity for 6 months or moreWG-Cmdlets-Corecmdlets in the Microsoft.PowerShell.Core module

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions