Skip to content

Commit

Permalink
feat: Added first version of a CI-focused testing pipeline (#2459)
Browse files Browse the repository at this point in the history
## Description

- Added first version of a CI-focused testing pipeline 
- Runs by default once a week - and for every merge to main if utilities
are changed
- Added test script for resource sorting script

The idea is to build atop this initial setup with future tests and as
such enable regression tests

## Pipeline Reference

<!-- Insert your Pipeline Status Badge below -->

| Pipeline |
| -------- |
| [![.Platform - Run CI
tests](https://github.com/Azure/bicep-registry-modules/actions/workflows/platform.ci-tests.yml/badge.svg?branch=users%2Falsehr%2FpesterCiTests)](https://github.com/Azure/bicep-registry-modules/actions/workflows/platform.ci-tests.yml)
|

## Type of Change

<!-- Use the checkboxes [x] on the options that are relevant. -->

- [x] Update to CI Environment or utilities (Non-module affecting
changes)
- [ ] Azure Verified Module updates:
- [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT
bumped the MAJOR or MINOR version in `version.json`:
- [ ] Someone has opened a bug report issue, and I have included "Closes
#{bug_report_issue_number}" in the PR description.
- [ ] The bug was found by the module author, and no one has opened an
issue to report it yet.
- [ ] Feature update backwards compatible feature updates, and I have
bumped the MINOR version in `version.json`.
- [ ] Breaking changes and I have bumped the MAJOR version in
`version.json`.
  - [ ] Update to documentation

---------

Co-authored-by: ChrisSidebotham-MSFT <[email protected]>
  • Loading branch information
AlexanderSehr and ChrisSidebotham authored Jul 22, 2024
1 parent 38c7769 commit e247d3f
Show file tree
Hide file tree
Showing 4 changed files with 471 additions and 11 deletions.
100 changes: 100 additions & 0 deletions .github/workflows/platform.ci-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
name: ".Platform - Run CI tests"

on:
workflow_dispatch:
inputs:
testFileRegex:
type: string
description: "The regex of the test file(s) to run"
required: false
default: '.*'
push:
branches:
- main
paths:
- ".github/workflows/platform.ci-tests.yml"
- "avm/utilities/**"
- "!*/**/README.md"
schedule:
- cron: "0 0 * * Sun" # Every Sunday

env:
workflowPath: ".github/workflows/platform.ci-tests.yml"


jobs:
###########################
# Initialize pipeline #
###########################
job_initialize_pipeline:
runs-on: ubuntu-latest
name: "Initialize pipeline"
steps:
- name: "Checkout"
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: "Set input parameters to output variables"
id: get-workflow-param
uses: ./.github/actions/templates/avm-getWorkflowInput
with:
workflowPath: "${{ env.workflowPath}}"
outputs:
workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }}

###############
# Removal #
###############
job_run_tests:
runs-on: ubuntu-20.04
name: "Run CI tests"
needs:
- job_initialize_pipeline
steps:
- name: "Checkout"
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set environment
uses: ./.github/actions/templates/avm-setEnvironment

- name: Run CI tests
id: pester_run_step
uses: azure/powershell@v2
with:
inlineScript: |
# Load used functions
. (Join-Path $env:GITHUB_WORKSPACE 'avm' 'utilities' 'tests' 'Test-CI.ps1')
$functionInput = @{
RepoRootPath = $env:GITHUB_WORKSPACE
BranchName = $env:GITHUB_REF
GitHubRepository = $env:GITHUB_REPOSITORY
TestFileRegex = '${{ (fromJson(needs.job_initialize_pipeline.outputs.workflowInput)).testFileRegex }}'
}
Write-Verbose "Invoke task with" -Verbose
Write-Verbose ($functionInput | ConvertTo-Json | Out-String) -Verbose
$outputsFilePath = Test-CI @functionInput
Write-Output ('{0}={1}' -f 'formattedPesterResultsPath', $outputsFilePath) >> $env:GITHUB_OUTPUT
azPSVersion: "latest"

- name: "Output to GitHub job summaries"
if: always()
shell: pwsh
run: |
# Grouping task logs
Write-Output '::group::Output to GitHub job summaries'
$mdPesterOutputFilePath = '${{ steps.pester_run_step.outputs.formattedPesterResultsPath }}'
if (-not (Test-Path $mdPesterOutputFilePath)) {
Write-Warning ('Input file [{0}] not found. Please check if the previous task threw an error and try again.' -f $mdPesterOutputFilePath)
} else {
Get-Content $mdPesterOutputFilePath >> $env:GITHUB_STEP_SUMMARY
Write-Verbose ('Successfully printed out file [{0}] to Job Summaries' -f $mdPesterOutputFilePath) -Verbose
}
Write-Output '::endgroup::'
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ Parse a Pester output containing checks & results and generate formatted markdow
.DESCRIPTION
Parse a Pester output containing checks & results and generate formatted markdown file out of it.
.PARAMETER RepoRootPath
Optional. The path to the root of the repository
.PARAMETER PesterTestResults
Mandatory. The Pester tests results to parse. Can be fetched by running Pester with the `-PassThru` parameter. For example:
Expand Down Expand Up @@ -68,6 +71,9 @@ function Set-PesterGitHubOutput {

[CmdletBinding(SupportsShouldProcess)]
param (
[Parameter(Mandatory = $false)]
[string] $RepoRootPath = (Get-Item $PSScriptRoot).Parent.Parent.Parent.Parent.Parent.FullName,

[Parameter(Mandatory = $true)]
[PSCustomObject] $PesterTestResults,

Expand All @@ -94,6 +100,8 @@ function Set-PesterGitHubOutput {
Write-Verbose ('Formatting [{0}] skipped tests' -f $skippedTests.Count)
Write-Verbose ('Formatting [{0}] tests with explicit warnings' -f $warnings.Count)

$moduleSplitRegex = '[\/|\\]avm[\/|\\](res|ptn)[\/|\\]'

######################
# Set output content #
######################
Expand All @@ -108,7 +116,7 @@ function Set-PesterGitHubOutput {
$fileContent += [System.Collections.ArrayList]@(
'| Total No. of Processed Tests| Passed Tests :white_check_mark: | Failed Tests :x: | Skipped Tests :paperclip: | Tests with warnings :warning: |',
'| :-- | :-- | :-- | :-- | :-- |',
('| {0} | {1} | {2} | {3} | {4} |' -f $PesterTestResults.TotalCount, $passedTests.count , $failedTests.count, $skippedTests.count, $testsWithWarnings.count),
('| {0} | {1} | {2} | {3} | {4} |' -f $PesterTestResults.TotalCount, $passedTests.count , $failedTests.count, $skippedTests.count, $testsWithWarnings.count),
''
)

Expand All @@ -134,9 +142,16 @@ function Set-PesterGitHubOutput {
$intermediateNameElements[-1] = '**{0}**' -f $failedTest.ExpandedName
$testName = ((($intermediateNameElements -join ' / ' | Out-String) -replace '\|', '\|') -replace '_', '\_').Trim()

if ($failedTest.ScriptBlock.File -match $moduleSplitRegex) {
# Module test
$errorFileIdentifier = $failedTest.ErrorRecord.TargetObject.File -split $moduleSplitRegex
$errorTestFile = ('avm/{0}/{1}' -f $errorFileIdentifier[1], $errorFileIdentifier[2]) -replace '\\', '/' # e.g., [avm\res\cognitive-services\account\tests\unit\custom.tests.ps1]
} else {
# None-module test
$testFile = $failedTest.ScriptBlock.File -replace ('{0}[\\|\/]*' -f [regex]::Escape($RepoRootPath))
}

$errorTestLine = $failedTest.ErrorRecord.TargetObject.Line
$errorFileIdentifier = $failedTest.ErrorRecord.TargetObject.File -split '[\/|\\]avm[\/|\\](res|ptn)[\/|\\]'
$errorTestFile = ('avm/{0}/{1}' -f $errorFileIdentifier[1], $errorFileIdentifier[2]) -replace '\\', '/' # e.g., [avm\res\cognitive-services\account\tests\unit\custom.tests.ps1]
$errorMessage = ($failedTest.ErrorRecord.TargetObject.Message.Trim() -replace '_', '\_') -replace '\n', '<br>' # Replace new lines with <br> to enable line breaks in markdown

$testReference = '{0}:{1}' -f (Split-Path $errorTestFile -Leaf), $errorTestLine
Expand Down Expand Up @@ -181,10 +196,16 @@ function Set-PesterGitHubOutput {
$intermediateNameElements[-1] = '**{0}**' -f $passedTest.ExpandedName
$testName = ((($intermediateNameElements -join ' / ' | Out-String) -replace '\|', '\|') -replace '_', '\_').Trim()

$testLine = $passedTest.ScriptBlock.StartPosition.StartLine
$testFileIdentifier = $passedTest.ScriptBlock.File -split '[\/|\\]avm[\/|\\](res|ptn)[\/|\\]'
$testFile = ('avm/{0}/{1}' -f $testFileIdentifier[1], $testFileIdentifier[2]) -replace '\\', '/' # e.g., [avm\res\cognitive-services\account\tests\unit\custom.tests.ps1]
if ($passedTest.ScriptBlock.File -match $moduleSplitRegex) {
# Module test
$testFileIdentifier = $passedTest.ScriptBlock.File -split $moduleSplitRegex
$testFile = ('avm/{0}/{1}' -f $testFileIdentifier[1], $testFileIdentifier[2]) -replace '\\', '/' # e.g., [avm\res\cognitive-services\account\tests\unit\custom.tests.ps1]
} else {
# None-module test
$testFile = $passedTest.ScriptBlock.File -replace ('{0}[\\|\/]*' -f [regex]::Escape($RepoRootPath))
}

$testLine = $passedTest.ScriptBlock.StartPosition.StartLine
$testReference = '{0}:{1}' -f (Split-Path $testFile -Leaf), $testLine
if (-not [String]::IsNullOrEmpty($GitHubRepository) -and -not [String]::IsNullOrEmpty($BranchName)) {
# Creating URL to test file to enable users to 'click' on it
Expand Down Expand Up @@ -229,10 +250,16 @@ function Set-PesterGitHubOutput {

$reason = ('Test {0}' -f $skippedTest.ErrorRecord.Exception.Message -replace '\|', '\|').Trim()

$testLine = $skippedTest.ScriptBlock.StartPosition.StartLine
$testFileIdentifier = $skippedTest.ScriptBlock.File -split '[\/|\\]avm[\/|\\](res|ptn)[\/|\\]'
$testFile = ('avm/{0}/{1}' -f $testFileIdentifier[1], $testFileIdentifier[2]) -replace '\\', '/' # e.g., [avm\res\cognitive-services\account\tests\unit\custom.tests.ps1]
if ($skippedTest.ScriptBlock.File -match $moduleSplitRegex) {
# Module test
$testFileIdentifier = $skippedTest.ScriptBlock.File -split $moduleSplitRegex
$testFile = ('avm/{0}/{1}' -f $testFileIdentifier[1], $testFileIdentifier[2]) -replace '\\', '/' # e.g., [avm\res\cognitive-services\account\tests\unit\custom.tests.ps1]
} else {
# None-module test
$testFile = $skippedTest.ScriptBlock.File -replace ('{0}[\\|\/]*' -f [regex]::Escape($RepoRootPath))
}

$testLine = $skippedTest.ScriptBlock.StartPosition.StartLine
$testReference = '{0}:{1}' -f (Split-Path $testFile -Leaf), $testLine
if (-not [String]::IsNullOrEmpty($GitHubRepository) -and -not [String]::IsNullOrEmpty($BranchName)) {
# Creating URL to test file to enable users to 'click' on it
Expand Down Expand Up @@ -275,8 +302,15 @@ function Set-PesterGitHubOutput {
$intermediateNameElements[-1] = '**{0}**' -f $test.ExpandedName
$testName = ((($intermediateNameElements -join ' / ' | Out-String) -replace '\|', '\|') -replace '_', '\_').Trim()

$testLine = $test.ScriptBlock.StartPosition.StartLine
$testFileIdentifier = $test.ScriptBlock.File -split '[\/|\\]avm[\/|\\](res|ptn)[\/|\\]'
if ($test.ScriptBlock.File -match $moduleSplitRegex) {
# Module test
$testLine = $test.ScriptBlock.StartPosition.StartLine
$testFileIdentifier = $test.ScriptBlock.File -split $moduleSplitRegex
} else {
# None-module test
$testFile = $test.ScriptBlock.File -replace ('{0}[\\|\/]*' -f [regex]::Escape($RepoRootPath))
}

$testFile = ('avm/{0}/{1}' -f $testFileIdentifier[1], $testFileIdentifier[2]) -replace '\\', '/' # e.g., [avm\res\cognitive-services\account\tests\unit\custom.tests.ps1]

$testReference = '{0}:{1}' -f (Split-Path $testFile -Leaf), $testLine
Expand Down
111 changes: 111 additions & 0 deletions avm/utilities/tests/Test-CI.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<#
.SYNOPSIS
This script executes either all CI-focused Pester tests, or a specific test file and generates a markdown with the test results.
.DESCRIPTION
This script executes either all CI-focused Pester tests, or a specific test file and generates a markdown with the test results.
It only considers test files in its current or sub-folders
.PARAMETER RepoRootPath
Optional. The root of the repository. Used to correctly resolve paths.
.PARAMETER BranchName
Optional. The branch to test for.
.PARAMETER GitHubRepository
Optional. The repository containing the test file. If provided it will be used to generate a URL to the exact line of the test.
For example: 'Azure/ResourceModules'
.PARAMETER TestFileRegex
Optional. The regex to use when searching for test files
.PARAMETER SkipOutput
Optional. Set if you don't want the script to generate the results markdown
.EXAMPLE
Test-CI
Invoke all CI-focused Pester tests and generate a markdown file with the test results.
.EXAMPLE
Test-CI -TestFileRegex '.*Ordered.*' -SkipOutput
Invoke all CI-focused Pester tests that match the regex '.*Ordered.*' skip the generation of the markdown file containing the test results.
#>
function Test-CI {

[CmdletBinding()]
param (
[Parameter()]
[string] $RepoRootPath = (Get-Item $PSScriptRoot).Parent.Parent.Parent.FullName,

[Parameter()]
[string] $BranchName = (git branch --show-current),

[Parameter(Mandatory )]
[string] $GitHubRepository,

[Parameter()]
[string] $TestFileRegex = '.*',

[Parameter()]
[switch] $SkipOutput
)

# Load used functions
. (Join-Path $RepoRootPath 'avm' 'utilities' 'pipelines' 'staticValidation' 'compliance' 'Set-PesterGitHubOutput.ps1')

$testFiles = (Get-ChildItem -Path (Join-Path $RepoRootPath 'avm' 'utilities' 'tests') -Recurse -File -Filter '*.tests.ps1').FullName | Where-Object {
$_ -match $TestFileRegex
}

if (-not $testFiles) {
Write-Warning "Skipping test execution as no test files matching the regex [$TestFileRegex] were found."
return
}

# ------------------- #
# Invoke Pester tests #
# ------------------- #
$pesterConfiguration = @{
Run = @{
Container = New-PesterContainer -Path $testFiles -Data @{
RepoRootPath = $RepoRootPath
}
PassThru = $true
}
Output = @{
Verbosity = 'Detailed'
}
}

Write-Verbose 'Invoke test with' -Verbose
Write-Verbose ($pesterConfiguration | ConvertTo-Json -Depth 4 | Out-String) -Verbose

$testResults = Invoke-Pester -Configuration $pesterConfiguration

if (-not $SkipOutput) {
# ----------------------------------------- #
# Create formatted Pester Test Results File #
# ----------------------------------------- #

$functionInput = @{
RepoRootPath = $RepoRootPath
PesterTestResults = $testResults
OutputFilePath = Join-Path $RepoRootPath 'avm' 'utilities' 'tests' 'Pester-output.md'
GitHubRepository = $GitHubRepository
BranchName = $BranchName
}

Write-Verbose 'Invoke Pester formatting function with' -Verbose
Write-Verbose ($functionInput | ConvertTo-Json -Depth 0 | Out-String) -Verbose

Set-PesterGitHubOutput @functionInput -Verbose

return $functionInput.OutputFilePath
}
}




Loading

0 comments on commit e247d3f

Please sign in to comment.