diff --git a/.azure-pipelines/generate-beta-modules.yml b/.azure-pipelines/generate-beta-modules.yml index e334fc50304..f41459c1093 100644 --- a/.azure-pipelines/generate-beta-modules.yml +++ b/.azure-pipelines/generate-beta-modules.yml @@ -133,7 +133,7 @@ jobs: displayName: 'Generate and Build Graph Resource Modules' inputs: filePath: '$(System.DefaultWorkingDirectory)/tools/GenerateModules.ps1' - arguments: '-ArtifactsLocation $(Build.ArtifactStagingDirectory)\ -Build -EnableSigning' + arguments: '-ArtifactsLocation $(Build.ArtifactStagingDirectory)\ -Build -Test -EnableSigning' pwsh: true - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 diff --git a/.azure-pipelines/generate-modules-template.yml b/.azure-pipelines/generate-modules-template.yml index 4476dc3ba12..d511c73b473 100644 --- a/.azure-pipelines/generate-modules-template.yml +++ b/.azure-pipelines/generate-modules-template.yml @@ -142,7 +142,7 @@ jobs: pwsh: true script: | Write-Host $(BUILDNUMBER) - pwsh $(System.DefaultWorkingDirectory)/tools/GenerateModules.ps1 -ArtifactsLocation $(Build.ArtifactStagingDirectory)\ -Build -EnableSigning -ModulePreviewNumber $(BUILDNUMBER) -UpdateAutoRest -RepositoryName "LocalNugetFeed" + pwsh $(System.DefaultWorkingDirectory)/tools/GenerateModules.ps1 -ArtifactsLocation $(Build.ArtifactStagingDirectory)\ -Build -Test -EnableSigning -ModulePreviewNumber $(BUILDNUMBER) -UpdateAutoRest -RepositoryName "LocalNugetFeed" - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 displayName: 'ESRP DLL Strong Name (Graph Resource Modules)' diff --git a/.azure-pipelines/validate-pr-beta-modules.yml b/.azure-pipelines/validate-pr-beta-modules.yml index abd45602bfc..c89258b16e5 100644 --- a/.azure-pipelines/validate-pr-beta-modules.yml +++ b/.azure-pipelines/validate-pr-beta-modules.yml @@ -48,7 +48,7 @@ jobs: displayName: 'Generate and Build Graph Resource Modules' inputs: filePath: '$(System.DefaultWorkingDirectory)/tools/GenerateModules.ps1' - arguments: '-RepositoryApiKey $(Api_Key) -Build' + arguments: '-RepositoryApiKey $(Api_Key) -Build -Test' pwsh: true - task: YodLabs.O365PostMessage.O365PostMessageBuild.O365PostMessageBuild@0 diff --git a/build.proj b/build.proj index d489085c7dd..b236a72c755 100644 --- a/build.proj +++ b/build.proj @@ -31,7 +31,7 @@ $True pwsh -NonInteractive -NoLogo -NoProfile -Command - $(PowerShellCoreCommandPrefix) " $(RepoTools)/GenerateModules.ps1 -Build -SkipVersionCheck:$(SkipVersionCheck) -EnableSigning:$(EnableSigning) -UpdateAutoRest:$(UpdateAutoRest) " + $(PowerShellCoreCommandPrefix) " $(RepoTools)/GenerateModules.ps1 -Build -Test -SkipVersionCheck:$(SkipVersionCheck) -EnableSigning:$(EnableSigning) -UpdateAutoRest:$(UpdateAutoRest) " $(PowerShellCoreCommandPrefix) " $(RepoTools)/GenerateRollUpModule.ps1 " $(PowerShellCoreCommandPrefix) " $(RepoTools)/GenerateAuthenticationModule.ps1 -Build -BuildWhenEqual:$(BuildWhenEqual) -EnableSigning:$(EnableSigning) " $(PowerShellCoreCommandPrefix) " $(RepoTools)/GenerateProfiles.ps1 " diff --git a/src/Applications/Applications/test/Applications.Tests.ps1 b/src/Applications/Applications/test/Applications.Tests.ps1 new file mode 100644 index 00000000000..414cb8a9379 --- /dev/null +++ b/src/Applications/Applications/test/Applications.Tests.ps1 @@ -0,0 +1,41 @@ +BeforeAll { + $ModuleName = "Microsoft.Graph.Applications" + $ModulePath = Join-Path $PSScriptRoot "..\$ModuleName.psd1" +} + +Describe "Applications Module" { + It "Module should be available when imported" { + $LoadedModule = Get-Module -Name $ModuleName + + $LoadedModule | Should -Not -Be $null + $LoadedModule.ExportedCommands.Count | Should -Not -Be 0 + } + + It "Module import should not write to streams when debug preference is not set" { + $ps = [powershell]::Create() + $ps.AddScript("Import-Module $ModulePath").Invoke() + + $ps.Streams.Information.Count | Should -Be 0 + $ps.Streams.Debug.Count | Should -Be 0 + $ps.Streams.Error.Count | Should -Be 0 + $ps.Streams.Verbose.Count | Should -Be 0 + $ps.Streams.Warning.Count | Should -Be 0 + $ps.Streams.Progress.Count | Should -Be 0 + + $ps.Dispose() + } + + It "Module import should write to streams when debug preference is set" { + $ps = [powershell]::Create() + $ps.AddScript("`$DebugPreference = 'Inquire'; Import-Module $ModulePath").Invoke() + + $ps.Streams.Information.Count | Should -Be 0 + $ps.Streams.Debug.Count | Should -Be 2 + $ps.Streams.Error.Count | Should -Be 0 + $ps.Streams.Verbose.Count | Should -Be 0 + $ps.Streams.Warning.Count | Should -Be 0 + $ps.Streams.Progress.Count | Should -Be 0 + + $ps.Dispose() + } +} \ No newline at end of file diff --git a/src/CloudCommunications/CloudCommunications/test/CloudCommunications.Tests.ps1 b/src/CloudCommunications/CloudCommunications/test/CloudCommunications.Tests.ps1 new file mode 100644 index 00000000000..e21b8b2ed09 --- /dev/null +++ b/src/CloudCommunications/CloudCommunications/test/CloudCommunications.Tests.ps1 @@ -0,0 +1,41 @@ +BeforeAll { + $ModuleName = "Microsoft.Graph.CloudCommunications" + $ModulePath = Join-Path $PSScriptRoot "..\$ModuleName.psd1" +} + +Describe "CloudCommunications Module" { + It "Module should be available when imported" { + $LoadedModule = Get-Module -Name $ModuleName + + $LoadedModule | Should -Not -Be $null + $LoadedModule.ExportedCommands.Count | Should -Not -Be 0 + } + + It "Module import should not write to streams when debug preference is not set" { + $ps = [powershell]::Create() + $ps.AddScript("Import-Module $ModulePath").Invoke() + + $ps.Streams.Information.Count | Should -Be 0 + $ps.Streams.Debug.Count | Should -Be 0 + $ps.Streams.Error.Count | Should -Be 0 + $ps.Streams.Verbose.Count | Should -Be 0 + $ps.Streams.Warning.Count | Should -Be 0 + $ps.Streams.Progress.Count | Should -Be 0 + + $ps.Dispose() + } + + It "Module import should write to streams when debug preference is set" { + $ps = [powershell]::Create() + $ps.AddScript("`$DebugPreference = 'Inquire'; Import-Module $ModulePath").Invoke() + + $ps.Streams.Information.Count | Should -Be 0 + $ps.Streams.Debug.Count | Should -Be 2 + $ps.Streams.Error.Count | Should -Be 0 + $ps.Streams.Verbose.Count | Should -Be 0 + $ps.Streams.Warning.Count | Should -Be 0 + $ps.Streams.Progress.Count | Should -Be 0 + + $ps.Dispose() + } +} \ No newline at end of file diff --git a/src/Users/Users/test/Users.Tests.ps1 b/src/Users/Users/test/Users.Tests.ps1 new file mode 100644 index 00000000000..59cc9561920 --- /dev/null +++ b/src/Users/Users/test/Users.Tests.ps1 @@ -0,0 +1,41 @@ +BeforeAll { + $ModuleName = "Microsoft.Graph.Users" + $ModulePath = Join-Path $PSScriptRoot "..\$ModuleName.psd1" +} + +Describe "Users Module" { + It "Module should be available when imported" { + $LoadedModule = Get-Module -Name $ModuleName + + $LoadedModule | Should -Not -Be $null + $LoadedModule.ExportedCommands.Count | Should -Not -Be 0 + } + + It "Module import should not write to streams when debug preference is not set" { + $ps = [powershell]::Create() + $ps.AddScript("Import-Module $ModulePath").Invoke() + + $ps.Streams.Information.Count | Should -Be 0 + $ps.Streams.Debug.Count | Should -Be 0 + $ps.Streams.Error.Count | Should -Be 0 + $ps.Streams.Verbose.Count | Should -Be 0 + $ps.Streams.Warning.Count | Should -Be 0 + $ps.Streams.Progress.Count | Should -Be 0 + + $ps.Dispose() + } + + It "Module import should write to streams when debug preference is set" { + $ps = [powershell]::Create() + $ps.AddScript("`$DebugPreference = 'Inquire'; Import-Module $ModulePath").Invoke() + + $ps.Streams.Information.Count | Should -Be 0 + $ps.Streams.Debug.Count | Should -Be 2 + $ps.Streams.Error.Count | Should -Be 0 + $ps.Streams.Verbose.Count | Should -Be 0 + $ps.Streams.Warning.Count | Should -Be 0 + $ps.Streams.Progress.Count | Should -Be 0 + + $ps.Dispose() + } +} \ No newline at end of file diff --git a/tools/GenerateModules.ps1 b/tools/GenerateModules.ps1 index 6f28026f013..28b45e1f096 100644 --- a/tools/GenerateModules.ps1 +++ b/tools/GenerateModules.ps1 @@ -7,6 +7,7 @@ Param( [string] $ModuleMappingConfigPath = (Join-Path $PSScriptRoot "..\config\ModulesMapping.jsonc"), [switch] $UpdateAutoRest, [switch] $Build, + [switch] $Test, [switch] $Pack, [switch] $Publish, [switch] $EnableSigning, @@ -34,6 +35,7 @@ $RequiredGraphModules = @() # PS Scripts $ManageGeneratedModulePS1 = Join-Path $PSScriptRoot ".\ManageGeneratedModule.ps1" -Resolve $BuildModulePS1 = Join-Path $PSScriptRoot ".\BuildModule.ps1" -Resolve +$TestModulePS1 = Join-Path $PSScriptRoot ".\TestModule.ps1" -Resolve $PackModulePS1 = Join-Path $PSScriptRoot ".\PackModule.ps1" -Resolve $PublishModulePS1 = Join-Path $PSScriptRoot ".\PublishModule.ps1" -Resolve $ReadModuleReadMePS1 = Join-Path $PSScriptRoot ".\ReadModuleReadMe.ps1" -Resolve @@ -54,8 +56,7 @@ if($ModulePreviewNumber -eq -1) { # Install module locally in order to specify it as a dependency for other modules down the generation pipeline. # https://stackoverflow.com/questions/46216038/how-do-i-define-requiredmodules-in-a-powershell-module-manifest-psd1. $ExistingAuthModule = Find-Module "Microsoft.Graph.Authentication" -Repository $RepositoryName -AllowPrerelease:$AllowPreRelease -Write-Warning "Auth Module: $ExistingAuthModule.Name" -Write-Warning "Auth Module: $ExistingAuthModule.Version" +Write-Host -ForegroundColor Green "Auth Module: $($ExistingAuthModule.Name), $($ExistingAuthModule.Version)" if (!(Get-Module -Name $ExistingAuthModule.Name -ListAvailable)) { Install-Module $ExistingAuthModule.Name -Repository $RepositoryName -Force -AllowClobber -AllowPrerelease:$AllowPreRelease } @@ -83,7 +84,8 @@ $ModuleMapping.Keys | ForEach-Object -ThrottleLimit $ModuleMapping.Keys.Count -P } $ModuleName = $_ - Write-Warning "Generating $ModuleName" + $FullyQualifiedModuleName = "$using:ModulePrefix.$ModuleName" + Write-Host -ForegroundColor Green "Generating '$FullyQualifiedModuleName' module..." $ModuleProjectDir = Join-Path $Using:ModulesOutputDir "$ModuleName\$ModuleName" # Copy AutoRest readme.md config is none exists. @@ -98,37 +100,36 @@ $ModuleMapping.Keys | ForEach-Object -ThrottleLimit $ModuleMapping.Keys.Count -P $ModuleVersion = & $Using:ReadModuleReadMePS1 -ReadMePath $ModuleLevelReadMePath -FieldToRead "module-version" if ($ModuleVersion -eq $null) { # Module version not set in readme.md. - Write-Error "Version number is not set on $Using:ModulePrefix.$ModuleName module. Please set 'module-version' in $ModuleLevelReadMePath." + Write-Error "Version number is not set on $FullyQualifiedModuleName module. Please set 'module-version' in $ModuleLevelReadMePath." } # Validate module version with the one on PSGallery. - [VersionState] $VersionState = & $Using:ValidateUpdatedModuleVersionPS1 -ModuleName "$Using:ModulePrefix.$ModuleName" -NextVersion $ModuleVersion -PSRepository RepositoryName -ModulePreviewNumber $ModulePreviewNumber + [VersionState] $VersionState = & $Using:ValidateUpdatedModuleVersionPS1 -ModuleName "$FullyQualifiedModuleName" -NextVersion $ModuleVersion -PSRepository RepositoryName -ModulePreviewNumber $ModulePreviewNumber if ($VersionState.Equals([VersionState]::Invalid) -and !$Using:SkipVersionCheck) { - Write-Warning "The specified version in $Using:ModulePrefix.$ModuleName module is either higher or lower than what's on $Using:RepositoryName. Update the 'module-version' in $ModuleLevelReadMePath" + Write-Warning "The specified version in $FullyQualifiedModuleName module is either higher or lower than what's on $Using:RepositoryName. Update the 'module-version' in $ModuleLevelReadMePath" } elseif ($VersionState.Equals([VersionState]::EqualToFeed) -and !$SkipVersionCheck) { - Write-Warning "$Using:ModulePrefix.$ModuleName module skipped. Version has not changed and is equal to what's on $Using:RepositoryName." + Write-Warning "$FullyQualifiedModuleName module skipped. Version has not changed and is equal to what's on $Using:RepositoryName." } elseif ($VersionState.Equals([VersionState]::Valid) -or $VersionState.Equals([VersionState]::NotOnFeed) -or $Using:SkipVersionCheck) { # Read release notes from readme. $ModuleReleaseNotes = & $Using:ReadModuleReadMePS1 -ReadMePath $ModuleLevelReadMePath -FieldToRead "release-notes" if ($ModuleReleaseNotes -eq $null) { # Release notes not set in readme.md. - Write-Error "Release notes not set on $Using:ModulePrefix.$ModuleName module. Please set 'release-notes' in $ModuleLevelReadMePath." + Write-Error "Release notes not set on $FullyQualifiedModuleName module. Please set 'release-notes' in $ModuleLevelReadMePath." } try { # Generate PowerShell modules. - Write-Host -ForegroundColor Green "Generating '$Using:ModulePrefix.$ModuleName' module..." & autorest --module-version:$ModuleVersion --service-name:$ModuleName $ModuleLevelReadMePath --verbose if ($LASTEXITCODE) { Write-Error "Failed to generate '$ModuleName' module." } - Write-Host -ForegroundColor Green "AutoRest generated '$Using:ModulePrefix.$ModuleName' successfully." + Write-Host -ForegroundColor Green "AutoRest generated '$FullyQualifiedModuleName' successfully." # Manage generated module. - Write-Host -ForegroundColor Green "Managing '$Using:ModulePrefix.$ModuleName' module..." + Write-Host -ForegroundColor Green "Managing '$FullyQualifiedModuleName' module..." & $Using:ManageGeneratedModulePS1 -Module $ModuleName -ModulePrefix $Using:ModulePrefix if ($Using:Build) { @@ -146,12 +147,12 @@ $ModuleMapping.Keys | ForEach-Object -ThrottleLimit $ModuleMapping.Keys.Count -P $Profiles = Get-ChildItem -Path $ModuleExportsPath -Directory | %{ $_.Name} # Update module manifest wiht profiles. - $ModuleManifestPath = Join-Path $ModuleProjectDir "$Using:ModulePrefix.$ModuleName.psd1" + $ModuleManifestPath = Join-Path $ModuleProjectDir "$FullyQualifiedModuleName.psd1" [HashTable]$PrivateData = @{ Profiles = $Profiles } Update-ModuleManifest -Path $ModuleManifestPath -PrivateData $PrivateData # Update module psm1 with Graph session profile name. - $ModulePsm1 = Join-Path $ModuleProjectDir "/$Using:ModulePrefix.$ModuleName.psm1" + $ModulePsm1 = Join-Path $ModuleProjectDir "/$FullyQualifiedModuleName.psm1" (Get-Content -Path $ModulePsm1) | ForEach-Object{ if ($_ -match '\$instance = \[Microsoft.Graph.PowerShell.Module\]::Instance') { # Update main psm1 with Graph session profile name and module name. @@ -161,13 +162,15 @@ $ModuleMapping.Keys | ForEach-Object -ThrottleLimit $ModuleMapping.Keys.Count -P # Rename all Azure instances in psm1 to `Microsoft Graph`. $updatedLine = $_ -replace 'Azure', 'Microsoft Graph' # Replace all 'instance.Name' declarations with fully qualified module name. - $updatedLine = $updatedLine -replace '\$\(\$instance.Name\)', "$ModulePrefix.$ModuleName" + $updatedLine = $updatedLine -replace '\$\(\$instance.Name\)', "$FullyQualifiedModuleName" + # Replace Write-Information with Write-Debug + $updatedLine = $updatedLine -replace 'Write\-Information', 'Write-Debug' $updatedLine } } | Set-Content $ModulePsm1 # Address AutoREST bug where it looks for exports in the wrong directory. - $InternalModulePsm1 = Join-Path $ModuleProjectDir "/internal/$Using:ModulePrefix.$ModuleName.internal.psm1" + $InternalModulePsm1 = Join-Path $ModuleProjectDir "/internal/$FullyQualifiedModuleName.internal.psm1" (Get-Content -Path $InternalModulePsm1) | ForEach-Object{ $updatedLine = $_ # Address AutoREST bug where it looks for exports in the wrong directory. @@ -188,19 +191,22 @@ $ModuleMapping.Keys | ForEach-Object -ThrottleLimit $ModuleMapping.Keys.Count -P } } + if ($Using:Test) { + & $Using:TestModulePS1 -ModulePath $ModuleProjectDir -ModuleName $FullyQualifiedModuleName + } + if ($Using:Pack) { # Pack generated module. - & $Using:PackModulePS1 -Module $ModuleName -ArtifactsLocation $Using:ArtifactsLocation + . $Using:PackModulePS1 -Module $ModuleName -ArtifactsLocation $Using:ArtifactsLocation } } catch { - Write-Error $_.Exception + throw $_ } - Write-Warning "Generating $ModuleName Completed" + Write-Host -ForeGroundColor Green "Generating $ModuleName Completed" } } -Write-Host -ForeGroundColor Green "Requests: $RequestCount" if ($Publish) { # Publish generated modules. & $PublishModulePS1 -Modules $ModuleMapping.Keys -ModulePrefix $ModulePrefix -ArtifactsLocation $ArtifactsLocation -RepositoryName $RepositoryName -RepositoryApiKey $RepositoryApiKey diff --git a/tools/TestModule.ps1 b/tools/TestModule.ps1 new file mode 100644 index 00000000000..32ee8ee3b6a --- /dev/null +++ b/tools/TestModule.ps1 @@ -0,0 +1,36 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +param([string] $ModulePath, [string] $ModuleName, [switch]$Isolated) +$ErrorActionPreference = 'Stop' + +# Install Pester +if (!(Get-Module -Name Pester -ListAvailable)) { + Install-Module -Name Pester -Force -SkipPublisherCheck +} + +if(-not $Isolated) { + Write-Host -ForegroundColor Green 'Creating isolated process...' + $pwsh = [System.Diagnostics.Process]::GetCurrentProcess().Path + & "$pwsh" -NonInteractive -NoLogo -NoProfile -File $MyInvocation.MyCommand.Path @PSBoundParameters -Isolated + return +} + +$modulePsd1 = Get-Item -Path (Join-Path $ModulePath "./$ModuleName.psd1") + +Import-Module -Name Pester +Import-Module -Name $modulePsd1.FullName + +$testFolder = Join-Path $ModulePath 'test' +$PesterConfiguration = [PesterConfiguration]::Default +$PesterConfiguration.Run.Path = $testFolder +$PesterConfiguration.Run.Exit = $true +$PesterConfiguration.TestResult.OutputPath = (Join-Path $testFolder "$moduleName-TestResults.xml") + +try { + Invoke-Pester -Configuration $PesterConfiguration +} +catch { + throw $_ +} + +Write-Host -ForegroundColor Green '-------------Done-------------' \ No newline at end of file