From 4a85e0e145b8f83c17ad14e39018e3d96d204e1d Mon Sep 17 00:00:00 2001 From: deadlydog Date: Fri, 24 Apr 2026 15:19:44 -0600 Subject: [PATCH 01/28] test: Move global test code into BeforeAll block for Pester v5 --- ...owerShellModuleHelper.IntegrationTests.ps1 | 88 ++++++++++--------- 1 file changed, 45 insertions(+), 43 deletions(-) diff --git a/ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 b/ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 index 5d90e4b..11ad803 100644 --- a/ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 +++ b/ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 @@ -10,54 +10,56 @@ param [string] $AzureArtifactsPersonalAccessToken = 'YourPatGoesHereButDoNotCommitItToSourceControl' ) -Set-StrictMode -Version Latest -[string] $RepositoryRootDirectoryPath = Split-Path -Path $PSScriptRoot -Parent -[string] $moduleFilePathToTest = (Join-Path $RepositoryRootDirectoryPath -ChildPath 'src\AzureArtifactsPowerShellModuleHelper\AzureArtifactsPowerShellModuleHelper.psm1') | Resolve-Path -Write-Verbose "Importing the module file '$moduleFilePathToTest' to run tests against it." -Verbose -Import-Module -Name $moduleFilePathToTest -Force -[string] $ModuleNameBeingTested = ((Split-Path -Path $moduleFilePathToTest -Leaf) -split '\.')[0] # Filename without the extension. - -########################################################### -# You will need to update the following variables with info to pull a real package down from a real feed. -########################################################### -# [string] $FeedUrl = 'https://pkgs.dev.azure.com/Organization/_packaging/Feed/nuget/v2' -[string] $FeedUrl = 'https://pkgs.dev.azure.com/iqmetrix/_packaging/iqmetrix/nuget/v2' -[string] $PowerShellModuleName = 'IQ.DataCenter.ServerConfiguration' -[string] $ValidOlderModuleVersionThatExists = '1.0.40' -[string] $InvalidModuleVersionThatDoesNotExist = '1.0.99999' -[string] $ValidModulePrereleaseVersionThatExists = '1.0.66-ci20191121T214736' -[System.Security.SecureString] $SecurePersonalAccessToken = ($AzureArtifactsPersonalAccessToken | ConvertTo-SecureString -AsPlainText -Force) -[PSCredential] $Credential = New-Object System.Management.Automation.PSCredential 'Username@DoesNotMatter.com', $SecurePersonalAccessToken -[System.Version] $MinimumRequiredPowerShellGetModuleVersion = [System.Version]::Parse('2.2.1') - -function Remove-PsRepository([string] $feedUrl) -{ - $repositories = Get-PSRepository - - # PowerShellGet v2 uses SourceLocation and v3 uses Uri for the feed URL of Get-PSRepository, so check both. - if ($repositories -and $repositories[0].PSObject.Properties.Name -contains 'SourceLocation') +BeforeAll { + Set-StrictMode -Version Latest + [string] $RepositoryRootDirectoryPath = Split-Path -Path $PSScriptRoot -Parent + [string] $moduleFilePathToTest = (Join-Path $RepositoryRootDirectoryPath -ChildPath 'src\AzureArtifactsPowerShellModuleHelper\AzureArtifactsPowerShellModuleHelper.psm1') | Resolve-Path + Write-Verbose "Importing the module file '$moduleFilePathToTest' to run tests against it." -Verbose + Import-Module -Name $moduleFilePathToTest -Force + [string] $ModuleNameBeingTested = ((Split-Path -Path $moduleFilePathToTest -Leaf) -split '\.')[0] # Filename without the extension. + + ########################################################### + # You will need to update the following variables with info to pull a real package down from a real feed. + ########################################################### + # [string] $FeedUrl = 'https://pkgs.dev.azure.com/Organization/_packaging/Feed/nuget/v2' + [string] $FeedUrl = 'https://pkgs.dev.azure.com/iqmetrix/_packaging/iqmetrix/nuget/v2' + [string] $PowerShellModuleName = 'IQ.DataCenter.ServerConfiguration' + [string] $ValidOlderModuleVersionThatExists = '1.0.40' + [string] $InvalidModuleVersionThatDoesNotExist = '1.0.99999' + [string] $ValidModulePrereleaseVersionThatExists = '1.0.66-ci20191121T214736' + [System.Security.SecureString] $SecurePersonalAccessToken = ($AzureArtifactsPersonalAccessToken | ConvertTo-SecureString -AsPlainText -Force) + [PSCredential] $Credential = New-Object System.Management.Automation.PSCredential 'Username@DoesNotMatter.com', $SecurePersonalAccessToken + [System.Version] $MinimumRequiredPowerShellGetModuleVersion = [System.Version]::Parse('2.2.1') + + function Remove-PsRepository([string] $feedUrl) { - $repositories | Where-Object { $_.SourceLocation -ieq $feedUrl } | Unregister-PSRepository - Get-PSRepository | Where-Object { $_.SourceLocation -ieq $feedUrl } | Should -BeNullOrEmpty + $repositories = Get-PSRepository + + # PowerShellGet v2 uses SourceLocation and v3 uses Uri for the feed URL of Get-PSRepository, so check both. + if ($repositories -and $repositories[0].PSObject.Properties.Name -contains 'SourceLocation') + { + $repositories | Where-Object { $_.SourceLocation -ieq $feedUrl } | Unregister-PSRepository + Get-PSRepository | Where-Object { $_.SourceLocation -ieq $feedUrl } | Should -BeNullOrEmpty + } + else + { + $repositories | Where-Object { $_.Uri -ieq $feedUrl } | Unregister-PSRepository + Get-PSRepository | Where-Object { $_.Uri -ieq $feedUrl } | Should -BeNullOrEmpty + } } - else + + function Remove-PowerShellModule([string] $powerShellModuleName) { - $repositories | Where-Object { $_.Uri -ieq $feedUrl } | Unregister-PSRepository - Get-PSRepository | Where-Object { $_.Uri -ieq $feedUrl } | Should -BeNullOrEmpty + Remove-Module -Name $powerShellModuleName -Force -ErrorAction SilentlyContinue + Get-Module -Name $powerShellModuleName | Should -BeNullOrEmpty } -} -function Remove-PowerShellModule([string] $powerShellModuleName) -{ - Remove-Module -Name $powerShellModuleName -Force -ErrorAction SilentlyContinue - Get-Module -Name $powerShellModuleName | Should -BeNullOrEmpty -} - -function Uninstall-PowerShellModule([string] $powerShellModuleName) -{ - Remove-PowerShellModule -powerShellModuleName $powerShellModuleName - Uninstall-Module -Name $powerShellModuleName -AllVersions -Force - Get-Module -Name $powerShellModuleName -ListAvailable | Should -BeNullOrEmpty + function Uninstall-PowerShellModule([string] $powerShellModuleName) + { + Remove-PowerShellModule -powerShellModuleName $powerShellModuleName + Uninstall-Module -Name $powerShellModuleName -AllVersions -Force + Get-Module -Name $powerShellModuleName -ListAvailable | Should -BeNullOrEmpty + } } Describe 'Registering an Azure Artifacts PS Repository' { From 8cebfe2ddf20addeacd230b45eb585634c478b68 Mon Sep 17 00:00:00 2001 From: deadlydog Date: Fri, 24 Apr 2026 15:26:06 -0600 Subject: [PATCH 02/28] test: Import module with using syntax Co-authored-by: Copilot --- ...actsPowerShellModuleHelper.IntegrationTests.ps1 | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 b/ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 index 11ad803..078bc28 100644 --- a/ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 +++ b/ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 @@ -4,29 +4,23 @@ # - You need to use a real Azure Artifacts $FeedUrl and a real module to import from it. # Ideally we would mock out any external/infrastructure dependencies; I just haven't had time to yet so for now hit the real dependencies. -param -( - [Parameter(Mandatory = $false, HelpMessage = 'The Personal Access Token to use to connect to the Azure Artifacts feed.')] - [string] $AzureArtifactsPersonalAccessToken = 'YourPatGoesHereButDoNotCommitItToSourceControl' -) +using module '.\..\src\AzureArtifactsPowerShellModuleHelper\AzureArtifactsPowerShellModuleHelper.psm1' BeforeAll { Set-StrictMode -Version Latest - [string] $RepositoryRootDirectoryPath = Split-Path -Path $PSScriptRoot -Parent - [string] $moduleFilePathToTest = (Join-Path $RepositoryRootDirectoryPath -ChildPath 'src\AzureArtifactsPowerShellModuleHelper\AzureArtifactsPowerShellModuleHelper.psm1') | Resolve-Path - Write-Verbose "Importing the module file '$moduleFilePathToTest' to run tests against it." -Verbose - Import-Module -Name $moduleFilePathToTest -Force - [string] $ModuleNameBeingTested = ((Split-Path -Path $moduleFilePathToTest -Leaf) -split '\.')[0] # Filename without the extension. ########################################################### # You will need to update the following variables with info to pull a real package down from a real feed. ########################################################### + [string] $AzureArtifactsPersonalAccessToken = 'YourPatGoesHereButDoNotCommitItToSourceControl' # [string] $FeedUrl = 'https://pkgs.dev.azure.com/Organization/_packaging/Feed/nuget/v2' [string] $FeedUrl = 'https://pkgs.dev.azure.com/iqmetrix/_packaging/iqmetrix/nuget/v2' [string] $PowerShellModuleName = 'IQ.DataCenter.ServerConfiguration' [string] $ValidOlderModuleVersionThatExists = '1.0.40' [string] $InvalidModuleVersionThatDoesNotExist = '1.0.99999' [string] $ValidModulePrereleaseVersionThatExists = '1.0.66-ci20191121T214736' + + [string] $ModuleNameBeingTested = 'AzureArtifactsPowerShellModuleHelper' [System.Security.SecureString] $SecurePersonalAccessToken = ($AzureArtifactsPersonalAccessToken | ConvertTo-SecureString -AsPlainText -Force) [PSCredential] $Credential = New-Object System.Management.Automation.PSCredential 'Username@DoesNotMatter.com', $SecurePersonalAccessToken [System.Version] $MinimumRequiredPowerShellGetModuleVersion = [System.Version]::Parse('2.2.1') From 6e66146167fc32490fd2842dd387e67bfb60a1b5 Mon Sep 17 00:00:00 2001 From: deadlydog Date: Fri, 24 Apr 2026 15:46:05 -0600 Subject: [PATCH 03/28] test: Pull in PAT from environment variable --- ...AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 b/ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 index 078bc28..d8dc2f8 100644 --- a/ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 +++ b/ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 @@ -2,6 +2,9 @@ # This means that these tests will actually reach out to the specified $FeedUrl and connect/authenticate against it. # In order for these tests to run successfully: # - You need to use a real Azure Artifacts $FeedUrl and a real module to import from it. +# - You need to use a real Azure Artifacts Personal Access Token (PAT) with package read permissions, stored in an +# environment variable called AZURE_ARTIFACTS_PAT, or you can hardcode it in the $AzureArtifactsPersonalAccessToken +# variable below, but do not commit that to source control. # Ideally we would mock out any external/infrastructure dependencies; I just haven't had time to yet so for now hit the real dependencies. using module '.\..\src\AzureArtifactsPowerShellModuleHelper\AzureArtifactsPowerShellModuleHelper.psm1' @@ -12,7 +15,7 @@ BeforeAll { ########################################################### # You will need to update the following variables with info to pull a real package down from a real feed. ########################################################### - [string] $AzureArtifactsPersonalAccessToken = 'YourPatGoesHereButDoNotCommitItToSourceControl' + [string] $AzureArtifactsPersonalAccessToken = $Env:AZURE_ARTIFACTS_PAT # [string] $FeedUrl = 'https://pkgs.dev.azure.com/Organization/_packaging/Feed/nuget/v2' [string] $FeedUrl = 'https://pkgs.dev.azure.com/iqmetrix/_packaging/iqmetrix/nuget/v2' [string] $PowerShellModuleName = 'IQ.DataCenter.ServerConfiguration' From 10f2663de252f325b07242f1734ee44a146871b9 Mon Sep 17 00:00:00 2001 From: deadlydog Date: Fri, 24 Apr 2026 16:10:16 -0600 Subject: [PATCH 04/28] test: Add tests to make sure tests are setup properly Co-authored-by: Copilot --- ...actsPowerShellModuleHelper.IntegrationTests.ps1 | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 b/ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 index d8dc2f8..564f968 100644 --- a/ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 +++ b/ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 @@ -59,6 +59,20 @@ BeforeAll { } } +Describe 'Tests setup' { + It 'Should have the Azure Artifacts Personal Access Token available' { + $AzureArtifactsPersonalAccessToken | Should -Not -BeNullOrEmpty + } + + It 'Should have the Feed URL available' { + $FeedUrl | Should -Not -BeNullOrEmpty + } + + It 'Should have the PowerShell module name to test with available' { + $PowerShellModuleName | Should -Not -BeNullOrEmpty + } +} + Describe 'Registering an Azure Artifacts PS Repository' { Context 'When relying on retrieving the Azure Artifacts PAT from the environment variable that exists' { Mock Get-SecurePersonalAccessTokenFromEnvironmentVariable { return $SecurePersonalAccessToken } -ModuleName $ModuleNameBeingTested From 6f1b40c662a78cdd1e06ed24ce9b43a6e77a0731 Mon Sep 17 00:00:00 2001 From: deadlydog Date: Fri, 24 Apr 2026 16:10:33 -0600 Subject: [PATCH 05/28] Add initial dummy module that will be used for testing Co-authored-by: Copilot --- ...actsPowerShellModuleHelper_Tests.Tests.ps1 | 10 ++ ...ArtifactsPowerShellModuleHelper_Tests.psd1 | 127 ++++++++++++++++++ ...ArtifactsPowerShellModuleHelper_Tests.psm1 | 8 ++ 3 files changed, 145 insertions(+) create mode 100644 src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.Tests.ps1 create mode 100644 src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psd1 create mode 100644 src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psm1 diff --git a/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.Tests.ps1 b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.Tests.ps1 new file mode 100644 index 0000000..1f53dda --- /dev/null +++ b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.Tests.ps1 @@ -0,0 +1,10 @@ +using module '.\FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psm1' + +# UPDATE ME: This is just example code. Replace the code below with your module's tests. +Describe 'Get-HelloWorld' { + It 'Should return "Hello, World!"' { + $expected = 'Hello, World!' + $result = Get-HelloWorld + $result | Should -Be $expected + } +} diff --git a/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psd1 b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psd1 new file mode 100644 index 0000000..89f232c --- /dev/null +++ b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psd1 @@ -0,0 +1,127 @@ +# Module manifest docs: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_module_manifests + +@{ + + # Script module or binary module file associated with this manifest. + RootModule = 'FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psm1' + + # Version number of this module. This will be updated automatically by the build and deployment pipelines. + ModuleVersion = '1.0.0' + + # Supported PSEditions + # CompatiblePSEditions = @() + + # ID used to uniquely identify this module + GUID = '26dbeb89-35a3-4aeb-a7cd-2372cac0f246' + + # Author of this module + Author = 'Dan.Schroeder' + + # Company or vendor of this module + CompanyName = 'Dan.Schroeder' + + # Copyright statement for this module + Copyright = '(c) Dan.Schroeder. All rights reserved.' + + # Description of the functionality provided by this module + Description = 'This is a dummy module used for testing that the AzureArtifactsPowerShellModuleHelper module can install packages from an Azure DevOps Artifacts feed.' + + # Minimum version of the PowerShell engine required by this module + # PowerShellVersion = '' + + # Name of the PowerShell host required by this module + # PowerShellHostName = '' + + # Minimum version of the PowerShell host required by this module + # PowerShellHostVersion = '' + + # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. + # DotNetFrameworkVersion = '' + + # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. + # ClrVersion = '' + + # Processor architecture (None, X86, Amd64) required by this module + # ProcessorArchitecture = '' + + # Modules that must be imported into the global environment prior to importing this module + # RequiredModules = @() + + # Assemblies that must be loaded prior to importing this module + # RequiredAssemblies = @() + + # Script files (.ps1) that are run in the caller's environment prior to importing this module. + # ScriptsToProcess = @() + + # Type files (.ps1xml) to be loaded when importing this module + # TypesToProcess = @() + + # Format files (.ps1xml) to be loaded when importing this module + # FormatsToProcess = @() + + # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess + # NestedModules = @() + + # Functions to export from this module. For best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. + FunctionsToExport = @( + 'Get-HelloWorld' + ) + + # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. + CmdletsToExport = @() + + # Variables to export from this module + VariablesToExport = '*' + + # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. + AliasesToExport = @() + + # DSC resources to export from this module + # DscResourcesToExport = @() + + # List of all modules packaged with this module + # ModuleList = @() + + # List of all files packaged with this module + # FileList = @() + + # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. + PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + # Tags = @() + + # A URL to the license for this module. + # LicenseUri = '' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/deadlydog/AzureArtifactsPowerShellModuleHelper' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + # ReleaseNotes = '' + + # Prerelease string of this module + # Prerelease = '' + + # Flag to indicate whether the module requires explicit user acceptance for install/update/save + # RequireLicenseAcceptance = $false + + # External dependent modules of this module + # ExternalModuleDependencies = @() + + } # End of PSData hashtable + + } # End of PrivateData hashtable + + # HelpInfo URI of this module + # HelpInfoURI = '' + + # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. + # DefaultCommandPrefix = '' + +} diff --git a/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psm1 b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psm1 new file mode 100644 index 0000000..af8d223 --- /dev/null +++ b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psm1 @@ -0,0 +1,8 @@ +# UPDATE ME: This is just example code. Replace this file's contents with your module code. + +function Get-HelloWorld { + [CmdletBinding()] + Param () + + Write-Output "Hello, World!" +} From aa2c892add29b5805be7988fbc0606a199957550 Mon Sep 17 00:00:00 2001 From: deadlydog Date: Fri, 24 Apr 2026 16:32:54 -0600 Subject: [PATCH 06/28] chore: Delete fake modules tests --- ...zureArtifactsPowerShellModuleHelper_Tests.Tests.ps1 | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.Tests.ps1 diff --git a/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.Tests.ps1 b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.Tests.ps1 deleted file mode 100644 index 1f53dda..0000000 --- a/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.Tests.ps1 +++ /dev/null @@ -1,10 +0,0 @@ -using module '.\FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psm1' - -# UPDATE ME: This is just example code. Replace the code below with your module's tests. -Describe 'Get-HelloWorld' { - It 'Should return "Hello, World!"' { - $expected = 'Hello, World!' - $result = Get-HelloWorld - $result | Should -Be $expected - } -} From eaad50d1ab18e966315ab07a090f69d91cdd04c0 Mon Sep 17 00:00:00 2001 From: deadlydog Date: Fri, 24 Apr 2026 16:33:16 -0600 Subject: [PATCH 07/28] chore: Add comments to dummy module Co-authored-by: Copilot --- ...eModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psm1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psm1 b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psm1 index af8d223..c6037e8 100644 --- a/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psm1 +++ b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psm1 @@ -1,4 +1,6 @@ -# UPDATE ME: This is just example code. Replace this file's contents with your module code. +# This is a fake dummy module that will be published to an Azure DevOps Artifacts feed. +# This is used when testing the AzureArtifactsPowerShellModuleHelper module to ensure that +# it can successfully install packages from an Azure DevOps Artifacts feed. function Get-HelloWorld { [CmdletBinding()] From be4c15131c694b70c3b91807d296e315d8b02b8e Mon Sep 17 00:00:00 2001 From: deadlydog Date: Fri, 24 Apr 2026 17:23:44 -0600 Subject: [PATCH 08/28] feat: Create script for publishing new fake module versions for testing with Co-authored-by: Copilot --- ...ArtifactsPowerShellModuleHelper_Tests.psd1 | 164 +++++++++--------- .../PublishModuleVersion.ps1 | 32 ++++ 2 files changed, 116 insertions(+), 80 deletions(-) create mode 100644 src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/PublishModuleVersion.ps1 diff --git a/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psd1 b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psd1 index 89f232c..57a9c98 100644 --- a/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psd1 +++ b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psd1 @@ -1,127 +1,131 @@ -# Module manifest docs: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_module_manifests +# +# Module manifest for module 'FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests' +# +# Generated by: Dan.Schroeder +# +# Generated on: 4/24/2026 +# @{ - # Script module or binary module file associated with this manifest. - RootModule = 'FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psm1' +# Script module or binary module file associated with this manifest. +RootModule = 'FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psm1' - # Version number of this module. This will be updated automatically by the build and deployment pipelines. - ModuleVersion = '1.0.0' +# Version number of this module. +ModuleVersion = '0.0.7' - # Supported PSEditions - # CompatiblePSEditions = @() +# Supported PSEditions +# CompatiblePSEditions = @() - # ID used to uniquely identify this module - GUID = '26dbeb89-35a3-4aeb-a7cd-2372cac0f246' +# ID used to uniquely identify this module +GUID = '26dbeb89-35a3-4aeb-a7cd-2372cac0f246' - # Author of this module - Author = 'Dan.Schroeder' +# Author of this module +Author = 'Dan.Schroeder' - # Company or vendor of this module - CompanyName = 'Dan.Schroeder' +# Company or vendor of this module +CompanyName = 'Dan.Schroeder' - # Copyright statement for this module - Copyright = '(c) Dan.Schroeder. All rights reserved.' +# Copyright statement for this module +Copyright = '(c) Dan.Schroeder. All rights reserved.' - # Description of the functionality provided by this module - Description = 'This is a dummy module used for testing that the AzureArtifactsPowerShellModuleHelper module can install packages from an Azure DevOps Artifacts feed.' +# Description of the functionality provided by this module +Description = 'This is a dummy module used for testing that the AzureArtifactsPowerShellModuleHelper module can install packages from an Azure DevOps Artifacts feed.' - # Minimum version of the PowerShell engine required by this module - # PowerShellVersion = '' +# Minimum version of the PowerShell engine required by this module +# PowerShellVersion = '' - # Name of the PowerShell host required by this module - # PowerShellHostName = '' +# Name of the PowerShell host required by this module +# PowerShellHostName = '' - # Minimum version of the PowerShell host required by this module - # PowerShellHostVersion = '' +# Minimum version of the PowerShell host required by this module +# PowerShellHostVersion = '' - # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. - # DotNetFrameworkVersion = '' +# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# DotNetFrameworkVersion = '' - # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. - # ClrVersion = '' +# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# ClrVersion = '' - # Processor architecture (None, X86, Amd64) required by this module - # ProcessorArchitecture = '' +# Processor architecture (None, X86, Amd64) required by this module +# ProcessorArchitecture = '' - # Modules that must be imported into the global environment prior to importing this module - # RequiredModules = @() +# Modules that must be imported into the global environment prior to importing this module +# RequiredModules = @() - # Assemblies that must be loaded prior to importing this module - # RequiredAssemblies = @() +# Assemblies that must be loaded prior to importing this module +# RequiredAssemblies = @() - # Script files (.ps1) that are run in the caller's environment prior to importing this module. - # ScriptsToProcess = @() +# Script files (.ps1) that are run in the caller's environment prior to importing this module. +# ScriptsToProcess = @() - # Type files (.ps1xml) to be loaded when importing this module - # TypesToProcess = @() +# Type files (.ps1xml) to be loaded when importing this module +# TypesToProcess = @() - # Format files (.ps1xml) to be loaded when importing this module - # FormatsToProcess = @() +# Format files (.ps1xml) to be loaded when importing this module +# FormatsToProcess = @() - # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess - # NestedModules = @() +# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess +# NestedModules = @() - # Functions to export from this module. For best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. - FunctionsToExport = @( - 'Get-HelloWorld' - ) +# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. +FunctionsToExport = 'Get-HelloWorld' - # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. - CmdletsToExport = @() +# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. +CmdletsToExport = @() - # Variables to export from this module - VariablesToExport = '*' +# Variables to export from this module +VariablesToExport = '*' - # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. - AliasesToExport = @() +# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. +AliasesToExport = @() - # DSC resources to export from this module - # DscResourcesToExport = @() +# DSC resources to export from this module +# DscResourcesToExport = @() - # List of all modules packaged with this module - # ModuleList = @() +# List of all modules packaged with this module +# ModuleList = @() - # List of all files packaged with this module - # FileList = @() +# List of all files packaged with this module +# FileList = @() - # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. - PrivateData = @{ +# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. +PrivateData = @{ PSData = @{ - # Tags applied to this module. These help with module discovery in online galleries. - # Tags = @() + # Tags applied to this module. These help with module discovery in online galleries. + # Tags = @() - # A URL to the license for this module. - # LicenseUri = '' + # A URL to the license for this module. + # LicenseUri = '' - # A URL to the main website for this project. - ProjectUri = 'https://github.com/deadlydog/AzureArtifactsPowerShellModuleHelper' + # A URL to the main website for this project. + ProjectUri = 'https://github.com/deadlydog/AzureArtifactsPowerShellModuleHelper' - # A URL to an icon representing this module. - # IconUri = '' + # A URL to an icon representing this module. + # IconUri = '' - # ReleaseNotes of this module - # ReleaseNotes = '' + # ReleaseNotes of this module + # ReleaseNotes = '' - # Prerelease string of this module - # Prerelease = '' + # Prerelease string of this module + Prerelease = '' - # Flag to indicate whether the module requires explicit user acceptance for install/update/save - # RequireLicenseAcceptance = $false + # Flag to indicate whether the module requires explicit user acceptance for install/update/save + # RequireLicenseAcceptance = $false - # External dependent modules of this module - # ExternalModuleDependencies = @() + # External dependent modules of this module + # ExternalModuleDependencies = @() } # End of PSData hashtable - } # End of PrivateData hashtable + } # End of PrivateData hashtable - # HelpInfo URI of this module - # HelpInfoURI = '' +# HelpInfo URI of this module +# HelpInfoURI = '' - # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. - # DefaultCommandPrefix = '' +# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. +# DefaultCommandPrefix = '' } diff --git a/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/PublishModuleVersion.ps1 b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/PublishModuleVersion.ps1 new file mode 100644 index 0000000..0a88de8 --- /dev/null +++ b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/PublishModuleVersion.ps1 @@ -0,0 +1,32 @@ +# Use this script to publish versions of the FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests module. + +[string] $versionToPublish = '0.0.7' # Update this value to the version you want to publish. +[string] $prereleaseVersionLabel = '' # Leave blank for no prerelease label. + +[string] $azureArtifactsPersonalAccessToken = $Env:AZURE_ARTIFACTS_TESTING_FEED_PAT +[string] $feedUrl = 'https://pkgs.dev.azure.com/deadlydog/OpenSource/_packaging/OpenSourcePackagesFeed/nuget/v3/index.json' +[string] $moduleName = 'FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests' +[string] $moduleDirectoryPath = $PSScriptRoot +[string] $moduleManifestFilePath = Join-Path -Path $moduleDirectoryPath -ChildPath "$moduleName.psd1" +[PSCredential] $credential = New-Object System.Management.Automation.PSCredential('AzureArtifacts', (ConvertTo-SecureString -String $azureArtifactsPersonalAccessToken -AsPlainText -Force)) + +if (-not $azureArtifactsPersonalAccessToken) { + throw "The environment variable 'AZURE_ARTIFACTS_TESTING_FEED_PAT' is not set. Please set this variable to your Azure Artifacts Personal Access Token and try again." +} + +Write-Output "Updating the module manifest version number to '$versionToPublish' with prerelease label '$prereleaseVersionLabel'." +[string] $manifestContents = Get-Content -Path $moduleManifestFilePath -Raw +$versionReplacedContents = [regex]::Replace($manifestContents, "ModuleVersion = '.*?'", "ModuleVersion = '$versionToPublish'") +$prereleaseLabelReplacedContents = [regex]::Replace($versionReplacedContents, "Prerelease = '.*?'", "Prerelease = '$prereleaseVersionLabel'") +Set-Content -Path $moduleManifestFilePath -Value $prereleaseLabelReplacedContents -Encoding UTF8 +Test-ModuleManifest -Path $moduleManifestFilePath + +# Get the PSRepository if it exists, otherwise register it. +$psRepository = Get-PSResourceRepository | Where-Object { $_.Uri -eq $feedUrl } | Select-Object -First 1 +if (-not $psRepository) { + Write-Output "Registering the PSRepository for the feed URL '$feedUrl'." + $psRepository = Register-PSResourceRepository -Name DeadlydogTestingFeed -Uri $feedUrl -Trusted -PassThru +} + +Write-Output "Publishing new version of the module to '$($psRepository.Uri)'." +Publish-PSResource -Path $moduleDirectoryPath -Repository $psRepository.Name -ApiKey AzureDevOps -Credential $credential From 2d6e55993ccb95111a774bffb793f7897fdd6285 Mon Sep 17 00:00:00 2001 From: deadlydog Date: Fri, 24 Apr 2026 17:27:29 -0600 Subject: [PATCH 09/28] Publish a few more versions --- ...eModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psd1 | 2 +- .../PublishModuleVersion.ps1 | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psd1 b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psd1 index 57a9c98..d88b8a7 100644 --- a/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psd1 +++ b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psd1 @@ -12,7 +12,7 @@ RootModule = 'FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psm1' # Version number of this module. -ModuleVersion = '0.0.7' +ModuleVersion = '1.2.0' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/PublishModuleVersion.ps1 b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/PublishModuleVersion.ps1 index 0a88de8..4b2282a 100644 --- a/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/PublishModuleVersion.ps1 +++ b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/PublishModuleVersion.ps1 @@ -1,6 +1,6 @@ # Use this script to publish versions of the FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests module. -[string] $versionToPublish = '0.0.7' # Update this value to the version you want to publish. +[string] $versionToPublish = '1.2.0' # Update this value to the version you want to publish. [string] $prereleaseVersionLabel = '' # Leave blank for no prerelease label. [string] $azureArtifactsPersonalAccessToken = $Env:AZURE_ARTIFACTS_TESTING_FEED_PAT @@ -18,7 +18,7 @@ Write-Output "Updating the module manifest version number to '$versionToPublish' [string] $manifestContents = Get-Content -Path $moduleManifestFilePath -Raw $versionReplacedContents = [regex]::Replace($manifestContents, "ModuleVersion = '.*?'", "ModuleVersion = '$versionToPublish'") $prereleaseLabelReplacedContents = [regex]::Replace($versionReplacedContents, "Prerelease = '.*?'", "Prerelease = '$prereleaseVersionLabel'") -Set-Content -Path $moduleManifestFilePath -Value $prereleaseLabelReplacedContents -Encoding UTF8 +Set-Content -Path $moduleManifestFilePath -Value $prereleaseLabelReplacedContents -Encoding UTF8 -NoNewline Test-ModuleManifest -Path $moduleManifestFilePath # Get the PSRepository if it exists, otherwise register it. From 50b946889eb544d7ae31138461e9d798abc5ef9f Mon Sep 17 00:00:00 2001 From: deadlydog Date: Fri, 24 Apr 2026 17:47:34 -0600 Subject: [PATCH 10/28] test: Update tests to use dummy open source module and feed --- ...owerShellModuleHelper.IntegrationTests.ps1 | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 b/ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 index 564f968..89ce09d 100644 --- a/ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 +++ b/ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 @@ -3,7 +3,7 @@ # In order for these tests to run successfully: # - You need to use a real Azure Artifacts $FeedUrl and a real module to import from it. # - You need to use a real Azure Artifacts Personal Access Token (PAT) with package read permissions, stored in an -# environment variable called AZURE_ARTIFACTS_PAT, or you can hardcode it in the $AzureArtifactsPersonalAccessToken +# environment variable called AZURE_ARTIFACTS_TESTING_FEED_PAT, or you can hardcode it in the $AzureArtifactsPersonalAccessToken # variable below, but do not commit that to source control. # Ideally we would mock out any external/infrastructure dependencies; I just haven't had time to yet so for now hit the real dependencies. @@ -15,13 +15,12 @@ BeforeAll { ########################################################### # You will need to update the following variables with info to pull a real package down from a real feed. ########################################################### - [string] $AzureArtifactsPersonalAccessToken = $Env:AZURE_ARTIFACTS_PAT - # [string] $FeedUrl = 'https://pkgs.dev.azure.com/Organization/_packaging/Feed/nuget/v2' - [string] $FeedUrl = 'https://pkgs.dev.azure.com/iqmetrix/_packaging/iqmetrix/nuget/v2' - [string] $PowerShellModuleName = 'IQ.DataCenter.ServerConfiguration' - [string] $ValidOlderModuleVersionThatExists = '1.0.40' + [string] $AzureArtifactsPersonalAccessToken = $Env:AZURE_ARTIFACTS_TESTING_FEED_PAT + [string] $FeedUrl = 'https://pkgs.dev.azure.com/deadlydog/OpenSource/_packaging/OpenSourcePackagesFeed/nuget/v2' + [string] $PowerShellModuleName = 'FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests' + [string] $ValidOlderModuleVersionThatExists = '1.0.2' [string] $InvalidModuleVersionThatDoesNotExist = '1.0.99999' - [string] $ValidModulePrereleaseVersionThatExists = '1.0.66-ci20191121T214736' + [string] $ValidModulePrereleaseVersionThatExists = '1.0.1-alpha' [string] $ModuleNameBeingTested = 'AzureArtifactsPowerShellModuleHelper' [System.Security.SecureString] $SecurePersonalAccessToken = ($AzureArtifactsPersonalAccessToken | ConvertTo-SecureString -AsPlainText -Force) @@ -79,7 +78,7 @@ Describe 'Registering an Azure Artifacts PS Repository' { It 'Should register a new PS repository properly when relying in PAT from environmental variable' { # Arrange. - [string] $expectedRepository = 'AzureArtifactsPowerShellFeed' + [string] $expectedRepository = 'TempTestingFeed' Remove-PsRepository -feedUrl $FeedUrl # Act. @@ -92,7 +91,7 @@ Describe 'Registering an Azure Artifacts PS Repository' { It 'Should return an existing PS repository properly when no Repository is specified' { # Arrange. - [string] $expectedRepository = 'AzureArtifactsPowerShellFeed' + [string] $expectedRepository = 'TempTestingFeed' Remove-PsRepository -feedUrl $FeedUrl Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl -Repository $expectedRepository @@ -106,7 +105,7 @@ Describe 'Registering an Azure Artifacts PS Repository' { It 'Should return an existing PS repository properly when a different Repository is specified' { # Arrange. - [string] $expectedRepository = 'AzureArtifactsPowerShellFeed' + [string] $expectedRepository = 'TempTestingFeed' Remove-PsRepository -feedUrl $FeedUrl Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl -Repository $expectedRepository @@ -120,7 +119,7 @@ Describe 'Registering an Azure Artifacts PS Repository' { It 'Should register a new PS repository properly when piping in the Feed URL' { # Arrange. - [string] $expectedRepository = 'AzureArtifactsPowerShellFeed' + [string] $expectedRepository = 'TempTestingFeed' Remove-PsRepository -feedUrl $FeedUrl # Act. @@ -133,7 +132,7 @@ Describe 'Registering an Azure Artifacts PS Repository' { It 'Should register a new PS repository properly when piping in the all of the parameters by property name' { # Arrange. - [string] $expectedRepository = 'AzureArtifactsPowerShellFeed' + [string] $expectedRepository = 'TempTestingFeed' [PSCustomObject] $params = [PSCustomObject]@{ FeedUrl = $FeedUrl Repository = $expectedRepository @@ -197,7 +196,7 @@ Describe 'Registering an Azure Artifacts PS Repository' { It 'Should register a new PS repository properly when passing in a valid Credential' { # Arrange. - [string] $expectedRepository = 'AzureArtifactsPowerShellFeed' + [string] $expectedRepository = 'TempTestingFeed' Remove-PsRepository -feedUrl $FeedUrl # Act. @@ -213,7 +212,7 @@ Describe 'Registering an Azure Artifacts PS Repository' { It 'Should not throw an error when credentials are not found. (Assumes the FeedUrl allows you to register it without a Credential)' { # Arrange. - [string] $expectedRepository = 'AzureArtifactsPowerShellFeed' + [string] $expectedRepository = 'TempTestingFeed' Remove-PsRepository -feedUrl $FeedUrl # Act. From 559cf7d0c874cbefe7a8f53745c4e733d65f8d66 Mon Sep 17 00:00:00 2001 From: deadlydog Date: Fri, 24 Apr 2026 17:50:58 -0600 Subject: [PATCH 11/28] Remove dummy tests file --- .../AzureArtifactsPowerShellModuleHelper.Tests.ps1 | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 diff --git a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 deleted file mode 100644 index ffdd9b0..0000000 --- a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 +++ /dev/null @@ -1,10 +0,0 @@ -using module '.\AzureArtifactsPowerShellModuleHelper.psm1' - -# UPDATE ME: This is just example code. Replace the code below with your module's tests. -# Describe 'Get-HelloWorld' { -# It 'Should return "Hello, World!"' { -# $expected = 'Hello, World!' -# $result = Get-HelloWorld -# $result | Should -Be $expected -# } -# } From a4332c8b2d22a06035695891fa75fec05769fcd3 Mon Sep 17 00:00:00 2001 From: deadlydog Date: Fri, 24 Apr 2026 17:51:49 -0600 Subject: [PATCH 12/28] Move tests file to live beside module --- .../AzureArtifactsPowerShellModuleHelper.Tests.ps1 | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 => src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 (100%) diff --git a/ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 similarity index 100% rename from ManualTests/AzureArtifactsPowerShellModuleHelper.IntegrationTests.ps1 rename to src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 From 5aeee400b27361132d34b524184e05e6d6ddd55a Mon Sep 17 00:00:00 2001 From: deadlydog Date: Fri, 24 Apr 2026 17:52:32 -0600 Subject: [PATCH 13/28] Update comment in tests --- .../AzureArtifactsPowerShellModuleHelper.Tests.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 index 89ce09d..51901ff 100644 --- a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 +++ b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 @@ -1,5 +1,6 @@ # These are Integration tests (not unit tests). # This means that these tests will actually reach out to the specified $FeedUrl and connect/authenticate against it. +# They will also interact with the local machines PowerShell PS Repositories and installed modules. # In order for these tests to run successfully: # - You need to use a real Azure Artifacts $FeedUrl and a real module to import from it. # - You need to use a real Azure Artifacts Personal Access Token (PAT) with package read permissions, stored in an From f7db0c05a851ee9843612f5e5d180bddbabacbf0 Mon Sep 17 00:00:00 2001 From: deadlydog Date: Fri, 24 Apr 2026 18:06:43 -0600 Subject: [PATCH 14/28] chore: Spellcheck ignore --- .cspell.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.cspell.json b/.cspell.json index fe5bc34..7a6da8b 100644 --- a/.cspell.json +++ b/.cspell.json @@ -20,6 +20,7 @@ "unshown" ], "ignoreWords": [ + "Deadlydog", "devcontainer", "pkgs", "pwsh" From 899dfa1d0abd510e670240510ca2019885f6870e Mon Sep 17 00:00:00 2001 From: deadlydog Date: Fri, 24 Apr 2026 18:09:01 -0600 Subject: [PATCH 15/28] test: Fix test file path --- .../AzureArtifactsPowerShellModuleHelper.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 index 51901ff..e7a5dfb 100644 --- a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 +++ b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 @@ -8,7 +8,7 @@ # variable below, but do not commit that to source control. # Ideally we would mock out any external/infrastructure dependencies; I just haven't had time to yet so for now hit the real dependencies. -using module '.\..\src\AzureArtifactsPowerShellModuleHelper\AzureArtifactsPowerShellModuleHelper.psm1' +using module '.\AzureArtifactsPowerShellModuleHelper.psm1' BeforeAll { Set-StrictMode -Version Latest From 7854d0242d40febddaf4f069e8612479298e1b82 Mon Sep 17 00:00:00 2001 From: deadlydog Date: Fri, 24 Apr 2026 18:11:38 -0600 Subject: [PATCH 16/28] ci: Allow tests to access feed Co-authored-by: Copilot --- .github/workflows/build-and-test-powershell-module.yml | 4 ++++ .../workflows/build-test-and-deploy-powershell-module.yml | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/.github/workflows/build-and-test-powershell-module.yml b/.github/workflows/build-and-test-powershell-module.yml index df3ddd1..341b60a 100644 --- a/.github/workflows/build-and-test-powershell-module.yml +++ b/.github/workflows/build-and-test-powershell-module.yml @@ -111,6 +111,8 @@ jobs: - name: Run Pester tests on Windows PowerShell to ensure backward compatibility shell: powershell + env: + AZURE_ARTIFACTS_TESTING_FEED_PAT: ${{ secrets.DEADLYDOG_OPENSOURCEPACKAGESFEED_AZDO_PAT }} run: | Write-Output "Pester version being used:" Import-Module -Name Pester @@ -130,6 +132,8 @@ jobs: - name: Run Pester tests and generate code coverage report shell: pwsh + env: + AZURE_ARTIFACTS_TESTING_FEED_PAT: ${{ secrets.DEADLYDOG_OPENSOURCEPACKAGESFEED_AZDO_PAT }} run: | Write-Output "Pester version being used:" Import-Module -Name Pester diff --git a/.github/workflows/build-test-and-deploy-powershell-module.yml b/.github/workflows/build-test-and-deploy-powershell-module.yml index 2ded4e4..017f5d1 100644 --- a/.github/workflows/build-test-and-deploy-powershell-module.yml +++ b/.github/workflows/build-test-and-deploy-powershell-module.yml @@ -82,6 +82,8 @@ jobs: - name: Run smoke tests shell: pwsh + env: + AZURE_ARTIFACTS_TESTING_FEED_PAT: ${{ secrets.DEADLYDOG_OPENSOURCEPACKAGESFEED_AZDO_PAT }} run: | [string] $smokeTestsScriptPath = "$Env:artifactsDirectoryPath/Invoke-SmokeTests.ps1" @@ -123,6 +125,8 @@ jobs: - name: Run smoke tests shell: powershell + env: + AZURE_ARTIFACTS_TESTING_FEED_PAT: ${{ secrets.DEADLYDOG_OPENSOURCEPACKAGESFEED_AZDO_PAT }} run: | [string] $smokeTestsScriptPath = "$Env:artifactsDirectoryPath/Invoke-SmokeTests.ps1" @@ -189,6 +193,8 @@ jobs: - name: Run smoke tests shell: pwsh + env: + AZURE_ARTIFACTS_TESTING_FEED_PAT: ${{ secrets.DEADLYDOG_OPENSOURCEPACKAGESFEED_AZDO_PAT }} run: | [string] $smokeTestsScriptPath = "$Env:artifactsDirectoryPath/Invoke-SmokeTests.ps1" @@ -230,6 +236,8 @@ jobs: - name: Run smoke tests shell: powershell + env: + AZURE_ARTIFACTS_TESTING_FEED_PAT: ${{ secrets.DEADLYDOG_OPENSOURCEPACKAGESFEED_AZDO_PAT }} run: | [string] $smokeTestsScriptPath = "$Env:artifactsDirectoryPath/Invoke-SmokeTests.ps1" From 28b602de33908a79c038feab1d4949d7ad75be08 Mon Sep 17 00:00:00 2001 From: deadlydog Date: Fri, 24 Apr 2026 18:26:42 -0600 Subject: [PATCH 17/28] chore: Change feed test module is published to so it is private and requires authorization --- .../AzureArtifactsPowerShellModuleHelper.Tests.ps1 | 2 +- ...eModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psd1 | 2 +- .../PublishModuleVersion.ps1 | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 index e7a5dfb..339efba 100644 --- a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 +++ b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 @@ -17,7 +17,7 @@ BeforeAll { # You will need to update the following variables with info to pull a real package down from a real feed. ########################################################### [string] $AzureArtifactsPersonalAccessToken = $Env:AZURE_ARTIFACTS_TESTING_FEED_PAT - [string] $FeedUrl = 'https://pkgs.dev.azure.com/deadlydog/OpenSource/_packaging/OpenSourcePackagesFeed/nuget/v2' + [string] $FeedUrl = 'https://pkgs.dev.azure.com/deadlydog/2fdacc85-2f97-401e-bc68-69090c712dea/_packaging/AzureArtifactsPowerShellModuleHelper-Tests/nuget/v2' [string] $PowerShellModuleName = 'FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests' [string] $ValidOlderModuleVersionThatExists = '1.0.2' [string] $InvalidModuleVersionThatDoesNotExist = '1.0.99999' diff --git a/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psd1 b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psd1 index d88b8a7..89502b6 100644 --- a/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psd1 +++ b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psd1 @@ -12,7 +12,7 @@ RootModule = 'FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests.psm1' # Version number of this module. -ModuleVersion = '1.2.0' +ModuleVersion = '1.3.0' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/PublishModuleVersion.ps1 b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/PublishModuleVersion.ps1 index 4b2282a..46607e2 100644 --- a/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/PublishModuleVersion.ps1 +++ b/src/FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests/PublishModuleVersion.ps1 @@ -1,10 +1,10 @@ # Use this script to publish versions of the FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests module. -[string] $versionToPublish = '1.2.0' # Update this value to the version you want to publish. +[string] $versionToPublish = '1.3.0' # Update this value to the version you want to publish. [string] $prereleaseVersionLabel = '' # Leave blank for no prerelease label. [string] $azureArtifactsPersonalAccessToken = $Env:AZURE_ARTIFACTS_TESTING_FEED_PAT -[string] $feedUrl = 'https://pkgs.dev.azure.com/deadlydog/OpenSource/_packaging/OpenSourcePackagesFeed/nuget/v3/index.json' +[string] $feedUrl = 'https://pkgs.dev.azure.com/deadlydog/2fdacc85-2f97-401e-bc68-69090c712dea/_packaging/AzureArtifactsPowerShellModuleHelper-Tests/nuget/v3/index.json' [string] $moduleName = 'FakeModuleFor_AzureArtifactsPowerShellModuleHelper_Tests' [string] $moduleDirectoryPath = $PSScriptRoot [string] $moduleManifestFilePath = Join-Path -Path $moduleDirectoryPath -ChildPath "$moduleName.psd1" From 2dbdea00bc55159dfc0a25541eafc26264fbd284 Mon Sep 17 00:00:00 2001 From: deadlydog Date: Mon, 27 Apr 2026 10:50:13 -0600 Subject: [PATCH 18/28] test: Use proper Pester v5 syntax for Mocks Co-authored-by: Copilot --- ...eArtifactsPowerShellModuleHelper.Tests.ps1 | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 index 339efba..41625be 100644 --- a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 +++ b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 @@ -75,7 +75,9 @@ Describe 'Tests setup' { Describe 'Registering an Azure Artifacts PS Repository' { Context 'When relying on retrieving the Azure Artifacts PAT from the environment variable that exists' { - Mock Get-SecurePersonalAccessTokenFromEnvironmentVariable { return $SecurePersonalAccessToken } -ModuleName $ModuleNameBeingTested + BeforeEach { + Mock Get-SecurePersonalAccessTokenFromEnvironmentVariable { return $SecurePersonalAccessToken } -ModuleName $ModuleNameBeingTested + } It 'Should register a new PS repository properly when relying in PAT from environmental variable' { # Arrange. @@ -209,7 +211,9 @@ Describe 'Registering an Azure Artifacts PS Repository' { } Context 'When connecting to a feed without using a Credential' { - Mock Get-AzureArtifactsCredential { return $null } -ModuleName $ModuleNameBeingTested + BeforeEach { + Mock Get-AzureArtifactsCredential { return $null } -ModuleName $ModuleNameBeingTested + } It 'Should not throw an error when credentials are not found. (Assumes the FeedUrl allows you to register it without a Credential)' { # Arrange. @@ -228,7 +232,9 @@ Describe 'Registering an Azure Artifacts PS Repository' { Describe 'Finding a PowerShell module from Azure Artifacts' { Context 'When relying on retrieving the Azure Artifacts PAT from the environment variable that exists' { - Mock Get-SecurePersonalAccessTokenFromEnvironmentVariable { return $SecurePersonalAccessToken } -ModuleName $ModuleNameBeingTested + BeforeEach { + Mock Get-SecurePersonalAccessTokenFromEnvironmentVariable { return $SecurePersonalAccessToken } -ModuleName $ModuleNameBeingTested + } It 'Should find the module properly' { # Arrange. @@ -241,7 +247,9 @@ Describe 'Finding a PowerShell module from Azure Artifacts' { } Context 'When connecting to a feed without using a Credential' { - Mock Get-AzureArtifactsCredential { return $null } -ModuleName $ModuleNameBeingTested + BeforeEach { + Mock Get-AzureArtifactsCredential { return $null } -ModuleName $ModuleNameBeingTested + } It 'Should throw an exception' { # Arrange. @@ -267,7 +275,9 @@ Describe 'Finding a PowerShell module from Azure Artifacts' { Describe 'Installing a PowerShell module from Azure Artifacts' { Context 'When relying on retrieving the Azure Artifacts PAT from the environment variable that exists' { - Mock Get-SecurePersonalAccessTokenFromEnvironmentVariable { return $SecurePersonalAccessToken } -ModuleName $ModuleNameBeingTested + BeforeEach { + Mock Get-SecurePersonalAccessTokenFromEnvironmentVariable { return $SecurePersonalAccessToken } -ModuleName $ModuleNameBeingTested + } It 'Should install the module properly' { # Arrange. @@ -284,7 +294,9 @@ Describe 'Installing a PowerShell module from Azure Artifacts' { Describe 'Updating a PowerShell module from Azure Artifacts' { Context 'When relying on retrieving the Azure Artifacts PAT from the environment variable that exists' { - Mock Get-SecurePersonalAccessTokenFromEnvironmentVariable { return $SecurePersonalAccessToken } -ModuleName $ModuleNameBeingTested + BeforeEach { + Mock Get-SecurePersonalAccessTokenFromEnvironmentVariable { return $SecurePersonalAccessToken } -ModuleName $ModuleNameBeingTested + } It 'Should update the module properly when already installed, resulting in 2 versions being installed' { # Arrange. @@ -304,7 +316,9 @@ Describe 'Updating a PowerShell module from Azure Artifacts' { Describe 'Installing-and-Updating a PowerShell module from Azure Artifacts' { Context 'When relying on retrieving the Azure Artifacts PAT from the environment variable that exists' { - Mock Get-SecurePersonalAccessTokenFromEnvironmentVariable { return $SecurePersonalAccessToken } -ModuleName $ModuleNameBeingTested + BeforeEach { + Mock Get-SecurePersonalAccessTokenFromEnvironmentVariable { return $SecurePersonalAccessToken } -ModuleName $ModuleNameBeingTested + } It 'Should install the module properly' { # Arrange. From 882937d3fda920928d22c7faa0d3a4cad6796b0c Mon Sep 17 00:00:00 2001 From: deadlydog Date: Mon, 27 Apr 2026 11:51:19 -0600 Subject: [PATCH 19/28] style: typo --- .../AzureArtifactsPowerShellModuleHelper.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 index 41625be..f2e3b25 100644 --- a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 +++ b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 @@ -79,7 +79,7 @@ Describe 'Registering an Azure Artifacts PS Repository' { Mock Get-SecurePersonalAccessTokenFromEnvironmentVariable { return $SecurePersonalAccessToken } -ModuleName $ModuleNameBeingTested } - It 'Should register a new PS repository properly when relying in PAT from environmental variable' { + It 'Should register a new PS repository properly when relying on PAT from environmental variable' { # Arrange. [string] $expectedRepository = 'TempTestingFeed' Remove-PsRepository -feedUrl $FeedUrl From 7a20201b5a54ca61670d5513fddc2005d5bae289 Mon Sep 17 00:00:00 2001 From: deadlydog Date: Mon, 27 Apr 2026 14:00:12 -0600 Subject: [PATCH 20/28] refactor: Cleanup code a bit Co-authored-by: Copilot --- .../AzureArtifactsPowerShellModuleHelper.psm1 | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.psm1 b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.psm1 index 42b4d6f..71133e6 100644 --- a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.psm1 +++ b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.psm1 @@ -91,16 +91,12 @@ function Register-AzureArtifactsPSRepository $psRepositories = Get-PSRepository # PowerShellGet v2 uses SourceLocation and v3 uses Uri for the feed URL of Get-PSRepository, so check both. - [PSCustomObject] $existingPsRepositoryForFeed = $null - if ($psRepositories -and $psRepositories[0].PSObject.Properties.Name -contains 'SourceLocation') { - $existingPsRepositoryForFeed = $psRepositories | - Where-Object { $_.SourceLocation -ieq $feedUrl } | - Select-Object -First 1 - } else { - $existingPsRepositoryForFeed = $psRepositories | - Where-Object { $_.Uri -ieq $feedUrl } | - Select-Object -First 1 - } + [PSCustomObject] $existingPsRepositoryForFeed = $psRepositories | + Where-Object { + (($_.PSObject.Properties.Name -contains 'SourceLocation') -and ($_.SourceLocation -ieq $feedUrl)) -or + (($_.PSObject.Properties.Name -contains 'Uri') -and ($_.Uri -ieq $feedUrl)) + } | + Select-Object -First 1 [bool] $psRepositoryIsAlreadyRegistered = ($null -ne $existingPsRepositoryForFeed) if ($psRepositoryIsAlreadyRegistered) From d44e6fab37935b4fc317b0d0004d43ebd522167c Mon Sep 17 00:00:00 2001 From: deadlydog Date: Mon, 27 Apr 2026 14:04:30 -0600 Subject: [PATCH 21/28] test: Remove both repositories and package sources for feed --- ...eArtifactsPowerShellModuleHelper.Tests.ps1 | 60 ++++++++++++++++--- 1 file changed, 52 insertions(+), 8 deletions(-) diff --git a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 index f2e3b25..69fcb36 100644 --- a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 +++ b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 @@ -30,19 +30,63 @@ BeforeAll { function Remove-PsRepository([string] $feedUrl) { - $repositories = Get-PSRepository + function Get-RepositoriesForFeed([string] $feedUrlToFind) + { + # PowerShellGet v2 uses SourceLocation and v3 uses Uri for the feed URL of Get-PSRepository, so check both. + @(Get-PSRepository) | Where-Object { + (($_.PSObject.Properties.Name -contains 'SourceLocation') -and ($_.SourceLocation -ieq $feedUrlToFind)) -or + (($_.PSObject.Properties.Name -contains 'Uri') -and ($_.Uri -ieq $feedUrlToFind)) + } + } - # PowerShellGet v2 uses SourceLocation and v3 uses Uri for the feed URL of Get-PSRepository, so check both. - if ($repositories -and $repositories[0].PSObject.Properties.Name -contains 'SourceLocation') + function Get-PackageSourcesForFeed([string] $feedUrlToFind) { - $repositories | Where-Object { $_.SourceLocation -ieq $feedUrl } | Unregister-PSRepository - Get-PSRepository | Where-Object { $_.SourceLocation -ieq $feedUrl } | Should -BeNullOrEmpty + @(Get-PackageSource) | Where-Object { + $_.Location -ieq $feedUrlToFind + } + } + + # Repository registrations can differ across shells/profiles; re-query and remove by name to ensure cleanup. + [int] $maxAttempts = 3 + for ([int] $attempt = 1; $attempt -le $maxAttempts; $attempt++) + { + $repositoriesForFeed = @(Get-RepositoriesForFeed -feedUrlToFind $feedUrl) + if ($repositoriesForFeed.Count -eq 0) + { + break + } + + $repositoryNames = $repositoriesForFeed | + Select-Object -ExpandProperty Name -Unique | + Where-Object { -not [string]::IsNullOrWhiteSpace($_) } + + foreach ($repositoryName in $repositoryNames) + { + Unregister-PSRepository -Name $repositoryName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue + } } - else + + @(Get-RepositoriesForFeed -feedUrlToFind $feedUrl) | Should -BeNullOrEmpty + + for ([int] $attempt = 1; $attempt -le $maxAttempts; $attempt++) { - $repositories | Where-Object { $_.Uri -ieq $feedUrl } | Unregister-PSRepository - Get-PSRepository | Where-Object { $_.Uri -ieq $feedUrl } | Should -BeNullOrEmpty + $packageSourcesForFeed = @(Get-PackageSourcesForFeed -feedUrlToFind $feedUrl) + if ($packageSourcesForFeed.Count -eq 0) + { + break + } + + $packageSourceNames = $packageSourcesForFeed | + Select-Object -ExpandProperty Name -Unique | + Where-Object { -not [string]::IsNullOrWhiteSpace($_) } + + foreach ($packageSourceName in $packageSourceNames) + { + Unregister-PackageSource -Name $packageSourceName -ProviderName NuGet -ErrorAction SilentlyContinue -WarningAction SilentlyContinue > $null + } } + + @(Get-PackageSourcesForFeed -feedUrlToFind $feedUrl) | Should -BeNullOrEmpty } function Remove-PowerShellModule([string] $powerShellModuleName) From 8c9fd075f81e86febe4f5514b28bd67888b57ada Mon Sep 17 00:00:00 2001 From: deadlydog Date: Mon, 27 Apr 2026 14:15:02 -0600 Subject: [PATCH 22/28] test refactor: Rename function to be more explicit Co-authored-by: Copilot --- ...eArtifactsPowerShellModuleHelper.Tests.ps1 | 70 ++++++------------- 1 file changed, 22 insertions(+), 48 deletions(-) diff --git a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 index 69fcb36..bb22848 100644 --- a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 +++ b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 @@ -28,12 +28,12 @@ BeforeAll { [PSCredential] $Credential = New-Object System.Management.Automation.PSCredential 'Username@DoesNotMatter.com', $SecurePersonalAccessToken [System.Version] $MinimumRequiredPowerShellGetModuleVersion = [System.Version]::Parse('2.2.1') - function Remove-PsRepository([string] $feedUrl) + function Remove-PsRepositoryAndPackageSource([string] $feedUrl) { function Get-RepositoriesForFeed([string] $feedUrlToFind) { # PowerShellGet v2 uses SourceLocation and v3 uses Uri for the feed URL of Get-PSRepository, so check both. - @(Get-PSRepository) | Where-Object { + Get-PSRepository | Where-Object { (($_.PSObject.Properties.Name -contains 'SourceLocation') -and ($_.SourceLocation -ieq $feedUrlToFind)) -or (($_.PSObject.Properties.Name -contains 'Uri') -and ($_.Uri -ieq $feedUrlToFind)) } @@ -41,52 +41,26 @@ BeforeAll { function Get-PackageSourcesForFeed([string] $feedUrlToFind) { - @(Get-PackageSource) | Where-Object { - $_.Location -ieq $feedUrlToFind - } + Get-PackageSource | Where-Object { $_.Location -ieq $feedUrlToFind } } - # Repository registrations can differ across shells/profiles; re-query and remove by name to ensure cleanup. - [int] $maxAttempts = 3 - for ([int] $attempt = 1; $attempt -le $maxAttempts; $attempt++) - { - $repositoriesForFeed = @(Get-RepositoriesForFeed -feedUrlToFind $feedUrl) - if ($repositoriesForFeed.Count -eq 0) - { - break - } - - $repositoryNames = $repositoriesForFeed | - Select-Object -ExpandProperty Name -Unique | - Where-Object { -not [string]::IsNullOrWhiteSpace($_) } - - foreach ($repositoryName in $repositoryNames) - { - Unregister-PSRepository -Name $repositoryName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue - } + Get-RepositoriesForFeed -feedUrlToFind $feedUrl | + Select-Object -ExpandProperty Name -Unique | + Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | + ForEach-Object { + Unregister-PSRepository -Name $_ -ErrorAction SilentlyContinue -WarningAction SilentlyContinue } - @(Get-RepositoriesForFeed -feedUrlToFind $feedUrl) | Should -BeNullOrEmpty + Get-RepositoriesForFeed -feedUrlToFind $feedUrl | Should -BeNullOrEmpty - for ([int] $attempt = 1; $attempt -le $maxAttempts; $attempt++) - { - $packageSourcesForFeed = @(Get-PackageSourcesForFeed -feedUrlToFind $feedUrl) - if ($packageSourcesForFeed.Count -eq 0) - { - break + Get-PackageSourcesForFeed -feedUrlToFind $feedUrl | + Select-Object -ExpandProperty Name -Unique | + Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | + ForEach-Object { + Unregister-PackageSource -Name $_ -ProviderName NuGet -ErrorAction SilentlyContinue -WarningAction SilentlyContinue > $null } - $packageSourceNames = $packageSourcesForFeed | - Select-Object -ExpandProperty Name -Unique | - Where-Object { -not [string]::IsNullOrWhiteSpace($_) } - - foreach ($packageSourceName in $packageSourceNames) - { - Unregister-PackageSource -Name $packageSourceName -ProviderName NuGet -ErrorAction SilentlyContinue -WarningAction SilentlyContinue > $null - } - } - - @(Get-PackageSourcesForFeed -feedUrlToFind $feedUrl) | Should -BeNullOrEmpty + Get-PackageSourcesForFeed -feedUrlToFind $feedUrl | Should -BeNullOrEmpty } function Remove-PowerShellModule([string] $powerShellModuleName) @@ -126,7 +100,7 @@ Describe 'Registering an Azure Artifacts PS Repository' { It 'Should register a new PS repository properly when relying on PAT from environmental variable' { # Arrange. [string] $expectedRepository = 'TempTestingFeed' - Remove-PsRepository -feedUrl $FeedUrl + Remove-PsRepositoryAndPackageSource -feedUrl $FeedUrl # Act. [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl -Repository $expectedRepository @@ -139,7 +113,7 @@ Describe 'Registering an Azure Artifacts PS Repository' { It 'Should return an existing PS repository properly when no Repository is specified' { # Arrange. [string] $expectedRepository = 'TempTestingFeed' - Remove-PsRepository -feedUrl $FeedUrl + Remove-PsRepositoryAndPackageSource -feedUrl $FeedUrl Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl -Repository $expectedRepository # Act. @@ -153,7 +127,7 @@ Describe 'Registering an Azure Artifacts PS Repository' { It 'Should return an existing PS repository properly when a different Repository is specified' { # Arrange. [string] $expectedRepository = 'TempTestingFeed' - Remove-PsRepository -feedUrl $FeedUrl + Remove-PsRepositoryAndPackageSource -feedUrl $FeedUrl Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl -Repository $expectedRepository # Act. @@ -167,7 +141,7 @@ Describe 'Registering an Azure Artifacts PS Repository' { It 'Should register a new PS repository properly when piping in the Feed URL' { # Arrange. [string] $expectedRepository = 'TempTestingFeed' - Remove-PsRepository -feedUrl $FeedUrl + Remove-PsRepositoryAndPackageSource -feedUrl $FeedUrl # Act. [string] $repository = ($FeedUrl | Register-AzureArtifactsPSRepository -Repository $expectedRepository) @@ -186,7 +160,7 @@ Describe 'Registering an Azure Artifacts PS Repository' { Credential = $Credential Scope = 'CurrentUser' } - Remove-PsRepository -feedUrl $FeedUrl + Remove-PsRepositoryAndPackageSource -feedUrl $FeedUrl # Act. [string] $repository = ($params | Register-AzureArtifactsPSRepository) @@ -244,7 +218,7 @@ Describe 'Registering an Azure Artifacts PS Repository' { It 'Should register a new PS repository properly when passing in a valid Credential' { # Arrange. [string] $expectedRepository = 'TempTestingFeed' - Remove-PsRepository -feedUrl $FeedUrl + Remove-PsRepositoryAndPackageSource -feedUrl $FeedUrl # Act. [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl -Repository $expectedRepository -Credential $Credential @@ -262,7 +236,7 @@ Describe 'Registering an Azure Artifacts PS Repository' { It 'Should not throw an error when credentials are not found. (Assumes the FeedUrl allows you to register it without a Credential)' { # Arrange. [string] $expectedRepository = 'TempTestingFeed' - Remove-PsRepository -feedUrl $FeedUrl + Remove-PsRepositoryAndPackageSource -feedUrl $FeedUrl # Act. [string] $repository = Register-AzureArtifactsPSRepository -FeedUrl $FeedUrl -Repository $expectedRepository From b20c2d2078be82e87cb4aae031af2b5e92c47739 Mon Sep 17 00:00:00 2001 From: deadlydog Date: Mon, 27 Apr 2026 14:18:42 -0600 Subject: [PATCH 23/28] style: whitespace --- .../AzureArtifactsPowerShellModuleHelper.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 index bb22848..5c3fcfe 100644 --- a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 +++ b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 @@ -49,7 +49,7 @@ BeforeAll { Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | ForEach-Object { Unregister-PSRepository -Name $_ -ErrorAction SilentlyContinue -WarningAction SilentlyContinue - } + } Get-RepositoriesForFeed -feedUrlToFind $feedUrl | Should -BeNullOrEmpty From 6589daf4420063aeb0adeee0b9ca87aec8525e7e Mon Sep 17 00:00:00 2001 From: deadlydog Date: Mon, 27 Apr 2026 16:05:03 -0600 Subject: [PATCH 24/28] chore: Add Dockerfile and VS Code task for running pester tests in a container Co-authored-by: Copilot --- .vscode/tasks.json | 45 +++++++++++++++++++++++++++++++++++++++++++++ Dockerfile | 28 ++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 Dockerfile diff --git a/.vscode/tasks.json b/.vscode/tasks.json index feab2e8..404de33 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -140,6 +140,51 @@ "problemMatcher": [ "$func-powershell-watch" ] + }, + { + "label": "Build Docker image for Pester tests", + "type": "shell", + "options": { + "shell": { + "executable": "pwsh", + "args": [ + "-NoProfile", + "-Command" + ] + } + }, + "command": "docker build -t pester-tests:latest .", + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated", + "clear": true + }, + "problemMatcher": [] + }, + { + "label": "Run Pester tests in Docker", + "type": "shell", + "options": { + "shell": { + "executable": "pwsh", + "args": [ + "-NoProfile", + "-Command" + ] + } + }, + "command": "docker run --rm -e AZURE_ARTIFACTS_TESTING_FEED_PAT=$Env:AZURE_ARTIFACTS_TESTING_FEED_PAT pester-tests:latest", + "dependsOn": [ + "Build Docker image for Pester tests" + ], + "group": "test", + "presentation": { + "reveal": "always", + "panel": "dedicated", + "clear": true + }, + "problemMatcher": [] } ] } diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8f87852 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,28 @@ +# Use .NET 10 runtime as base image +FROM mcr.microsoft.com/dotnet/sdk:10.0 + +# Install PowerShell +RUN apt-get update && \ + apt-get install -y curl gnupg apt-transport-https && \ + curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add - && \ + echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-debian-bullseye-prod bullseye main" > /etc/apt/sources.list.d/microsoft.list && \ + apt-get update && \ + apt-get install -y powershell && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Create working directory +WORKDIR /workspace + +# Copy the module and tests into the container +COPY . . + +# Set PowerShell as the default shell +SHELL ["pwsh", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"] + +# Install required PowerShell modules +RUN Install-Module -Name PowerShellGet -RequiredVersion 2.2.5 -Force && \ + Install-Module -Name Pester -Force + +# Run Pester tests +ENTRYPOINT ["pwsh", "-NoProfile", "-Command", "Invoke-Pester -Configuration (New-PesterConfiguration @{ Output = @{ Verbosity = 'Detailed' }})"] From 1105c3d51694a53809159cc5c1a39938e7797250 Mon Sep 17 00:00:00 2001 From: deadlydog Date: Mon, 27 Apr 2026 16:13:11 -0600 Subject: [PATCH 25/28] test: Update expected error message --- .../AzureArtifactsPowerShellModuleHelper.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 index 5c3fcfe..61d7601 100644 --- a/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 +++ b/src/AzureArtifactsPowerShellModuleHelper/AzureArtifactsPowerShellModuleHelper.Tests.ps1 @@ -275,7 +275,7 @@ Describe 'Finding a PowerShell module from Azure Artifacts' { [scriptblock] $action = { Find-AzureArtifactsModule -Name $PowerShellModuleName -Repository $repository -ErrorAction Stop } # Act and Assert. - $action | Should -Throw "No match was found for the specified search criteria and module name '$PowerShellModuleName'. Try Get-PSRepository to see all available registered module repositories." + $action | Should -Throw "Unable to find repository '$repository'. Use Get-PSRepository to see all available repositories." } } From 68ba80af618fb61a0297f0f7a6cc6943c0370bc8 Mon Sep 17 00:00:00 2001 From: deadlydog Date: Mon, 27 Apr 2026 16:13:51 -0600 Subject: [PATCH 26/28] Run tests in Docker by default --- .vscode/tasks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 404de33..272ddc1 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -20,7 +20,7 @@ "isDefault": true }, "dependsOn": [ - "Run all Pester tests" + "Run Pester tests in Docker" ] }, { From 8303ab9fa72ea3b79f466bd0e16b14aacf0313e0 Mon Sep 17 00:00:00 2001 From: deadlydog Date: Mon, 27 Apr 2026 19:35:27 -0600 Subject: [PATCH 27/28] chore: Add more logging in CI pipeline Co-authored-by: Copilot --- .github/workflows/build-and-test-powershell-module.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/build-and-test-powershell-module.yml b/.github/workflows/build-and-test-powershell-module.yml index 341b60a..207803a 100644 --- a/.github/workflows/build-and-test-powershell-module.yml +++ b/.github/workflows/build-and-test-powershell-module.yml @@ -118,6 +118,12 @@ jobs: Import-Module -Name Pester Get-Module -Name Pester + Write-Output "PowerShell version being used:" + $PSVersionTable + + Write-Output "Host information:" + $host + Write-Output "Running all Pester tests in the repo:" $pesterConfig = New-PesterConfiguration @{ Output = @{ Verbosity = 'Detailed' } From 734281dcaeac16fdd37ece93c0bd381e39bc82a2 Mon Sep 17 00:00:00 2001 From: deadlydog Date: Mon, 27 Apr 2026 19:36:44 -0600 Subject: [PATCH 28/28] chore: Add more CI logging --- .github/workflows/build-and-test-powershell-module.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/build-and-test-powershell-module.yml b/.github/workflows/build-and-test-powershell-module.yml index 207803a..0c1572f 100644 --- a/.github/workflows/build-and-test-powershell-module.yml +++ b/.github/workflows/build-and-test-powershell-module.yml @@ -145,6 +145,12 @@ jobs: Import-Module -Name Pester Get-Module -Name Pester + Write-Output "PowerShell version being used:" + $PSVersionTable + + Write-Output "Host information:" + $host + Write-Output "Running all Pester tests in the repo:" $pesterConfig = New-PesterConfiguration @{ Output = @{ Verbosity = 'Detailed' }