Skip to content

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

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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-Cmdletsgeneral cmdlet issues

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions