From 2cd166202fad9ec50423ccc1ba2b92ce420acf20 Mon Sep 17 00:00:00 2001 From: Yeming Liu Date: Tue, 19 Aug 2025 17:38:54 +1000 Subject: [PATCH] workflow --- .github/workflows/README-auto-azp-run.md | 51 +++ .github/workflows/auto-azp-run.yml | 85 +++++ requirements.md | 37 ++ .../Check-AzureOrgMembership.ps1 | 125 +++++++ tools/GitHubOrgMember/Demo-ObjectBased.ps1 | 59 ++++ tools/GitHubOrgMember/README.md | 327 ++++++++++++++++++ tools/GitHubOrgMember/Test-ObjectBased.ps1 | 101 ++++++ 7 files changed, 785 insertions(+) create mode 100644 .github/workflows/README-auto-azp-run.md create mode 100644 .github/workflows/auto-azp-run.yml create mode 100644 requirements.md create mode 100644 tools/GitHubOrgMember/Check-AzureOrgMembership.ps1 create mode 100644 tools/GitHubOrgMember/Demo-ObjectBased.ps1 create mode 100644 tools/GitHubOrgMember/README.md create mode 100644 tools/GitHubOrgMember/Test-ObjectBased.ps1 diff --git a/.github/workflows/README-auto-azp-run.md b/.github/workflows/README-auto-azp-run.md new file mode 100644 index 000000000000..7d26c8f1fbce --- /dev/null +++ b/.github/workflows/README-auto-azp-run.md @@ -0,0 +1,51 @@ +# Auto ADO Pipeline Trigger for Azure Members + +This GitHub Action automatically triggers Azure DevOps (ADO) pipelines for pull requests created by Azure organization members. + +## How it works + +1. **Trigger**: The workflow runs when a PR is opened or updated (push to PR branch) +2. **Membership Check**: Uses the existing script in `tools/GitHubOrgMember/Check-AzureOrgMembership.ps1` to verify if the PR author is an Azure organization member +3. **Authentication**: Uses the built-in `GITHUB_TOKEN` secret for authentication +4. **Pipeline Trigger**: Comments "/azp run" on the PR to trigger the ADO pipeline + +## Required Permissions + +The workflow uses the built-in `GITHUB_TOKEN` with the following permissions: +- `pull-requests: write` - To comment on PRs +- `contents: read` - To checkout the repository + +No additional secrets need to be configured. + +## Workflow Behavior + +- โœ… **Azure Members**: ADO pipeline is triggered automatically with "/azp run" comment +- โŒ **Non-Azure Members**: Workflow silently completes without action +- ๐Ÿ”ด **Errors**: Workflow fails with error details in logs + +## Troubleshooting + +### Common Issues + +1. **GitHub CLI Authentication Failed** + - This should not happen as we use the built-in `GITHUB_TOKEN` + - Check if the token permissions are sufficient + +2. **Membership Check Failed** + - GitHub API rate limits may cause temporary failures + - User might have private membership in Azure organization + - Check the GitHub CLI authentication + +3. **Comment Creation Failed** + - Verify the workflow has `pull-requests: write` permission + - Check if the PR number is correct + +### Logs + +Check the GitHub Actions logs for detailed error messages and troubleshooting information. + +## Security Notes + +- Uses `pull_request` trigger (safe for public repositories) +- Only uses built-in `GITHUB_TOKEN` - no external secrets required +- Membership verification happens before any ADO pipeline trigger diff --git a/.github/workflows/auto-azp-run.yml b/.github/workflows/auto-azp-run.yml new file mode 100644 index 000000000000..58e97149ceb4 --- /dev/null +++ b/.github/workflows/auto-azp-run.yml @@ -0,0 +1,85 @@ +name: Auto Trigger ADO Pipeline for Azure Members +run-name: Auto trigger ADO pipeline for PR by Azure member + +on: + pull_request: + types: [opened, synchronize] + +jobs: + check-and-trigger: + runs-on: ubuntu-latest + permissions: + pull-requests: write + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + sparse-checkout: | + tools/GitHubOrgMember + + - name: Set up GitHub CLI + shell: pwsh + run: | + # Install GitHub CLI if not present + if (-not (Get-Command gh -ErrorAction SilentlyContinue)) { + Write-Host "Installing GitHub CLI..." + # GitHub CLI is typically pre-installed on GitHub runners + gh --version + } + + - name: Authenticate with GitHub CLI using token + shell: pwsh + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + Write-Host "Authenticating GitHub CLI..." + echo $env:GH_TOKEN | gh auth login --with-token + gh auth status + + - name: Get PR author and check Azure org membership + shell: pwsh + env: + PR_AUTHOR: ${{ github.event.pull_request.user.login }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + Write-Host "Checking if PR author '$env:PR_AUTHOR' is an Azure organization member..." + + # Use the existing script to check membership + $membershipResult = & "./tools/GitHubOrgMember/Check-AzureOrgMembership.ps1" -Username $env:PR_AUTHOR -Quiet + + Write-Host "Membership check result:" + Write-Host "Username: $($membershipResult.Username)" + Write-Host "Organization: $($membershipResult.Organization)" + Write-Host "IsMember: $($membershipResult.IsMember)" + Write-Host "Status: $($membershipResult.Status)" + + if ($membershipResult.ErrorMessage) { + Write-Host "Error: $($membershipResult.ErrorMessage)" + } + + # Set output for next step + echo "IS_AZURE_MEMBER=$($membershipResult.IsMember)" >> $env:GITHUB_ENV + echo "MEMBERSHIP_STATUS=$($membershipResult.Status)" >> $env:GITHUB_ENV + + - name: Trigger ADO pipeline for Azure member + if: env.IS_AZURE_MEMBER == 'True' + shell: pwsh + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + try { + Write-Host "PR author is an Azure member. Proceeding to trigger ADO pipeline..." + + # Comment "/azp run" on the PR to trigger ADO pipeline + Write-Host "Commenting '/azp run' on PR #$env:PR_NUMBER to trigger ADO pipeline..." + gh pr comment $env:PR_NUMBER --body "/azp run" + + Write-Host "โœ… Successfully triggered ADO pipeline for PR #$env:PR_NUMBER" + } + catch { + Write-Error "โŒ Failed to trigger ADO pipeline: $($_.Exception.Message)" + exit 1 + } diff --git a/requirements.md b/requirements.md new file mode 100644 index 000000000000..23f5a613d195 --- /dev/null +++ b/requirements.md @@ -0,0 +1,37 @@ +# Automatically Trigger ADO Pipelines for PR by Azure Members + +## Background + +The `Azure/azure-powershell` repository is an open-source repository with lots of community contributions, whose quality is ensured through CI - Azure DevOps pipelines. The pipelines are currently not triggered automatically for pull requests (PRs) made by anyone except for repository owners, which can slow down the development process and make it harder to catch issues early. The purpose of this document is to automatically trigger ADO pipelines for PRs made by Azure members. + +## Concepts + +- **Azure member**: An Azure member is a GitHub user who is part of the Azure organization. +- **Azure organization**: The Azure organization is the GitHub organization that contains the Azure-related repositories, as well as GitHub users who are members of the Azure team. + +## Goal & Scope + +Set up a GitHub Action that listens to any change in pull requests and triggers the ADO pipeline for Azure members by commenting "/azp run". + +Scope is all pull requests to the `Azure/azure-powershell` repository. + +## Requirements + +1. **Trigger by push**: The system must trigger the ADO pipelines when a push is made to the PR branch. + - This includes both the initial creation of the PR and any subsequent updates to the PR. +2. **Azure Member Verification**: The system must verify that the PR is **created** by an Azure member. + - The system should not trigger the pipeline for PRs created by non-Azure members. + - Whether it's *updated* by an Azure member doesn't matter because non-members cannot push changes to the PR. +3. **Authenticate with GitHub token**: The system must authenticate using the GitHub token provided by the `GITHUB_TOKEN` secret. + +## Resources + +- There is an existing PowerShell script in `tools/GitHubOrgMember` that can be used to verify Azure organization membership. + - The script depends on `GitHub CLI` for querying GitHub API and authentication. +- Commenting "/azp run" on the PR should trigger the ADO pipeline if the account has the right permission. + +## Notes + +- In case anything unexpected happens, the system should log the error and simply fail the GitHub Action. +- Duplicating "/azp run" comments isn't a problem to the system and can be safely ignored. +- Do not use `pull_request_target` event for this workflow. diff --git a/tools/GitHubOrgMember/Check-AzureOrgMembership.ps1 b/tools/GitHubOrgMember/Check-AzureOrgMembership.ps1 new file mode 100644 index 000000000000..5d5ac334bf86 --- /dev/null +++ b/tools/GitHubOrgMember/Check-AzureOrgMembership.ps1 @@ -0,0 +1,125 @@ +#!/usr/bin/env pwsh +<# +.SYNOPSIS + Check if a GitHub user is a member of the Azure organization. + +.DESCRIPTION + This script uses the GitHub CLI to check if a specified user is a member of the Azure GitHub organization. + Returns a structured PowerShell object with membership details. + +.PARAMETER Username + The GitHub username to check. + +.PARAMETER Organization + The GitHub organization to check membership for. Defaults to "Azure". + +.PARAMETER Quiet + Suppress console output and only return the object. + +.OUTPUTS + PSCustomObject with the following properties: + - Username: The checked username + - Organization: The organization that was checked + - IsMember: Boolean indicating if user is a public member + - Status: Detailed status (PublicMember, NotMember, PrivateMember, UserNotFound, Error) + - ErrorMessage: Error details if Status is Error + - CheckedAt: Timestamp of when the check was performed + +.EXAMPLE + $result = .\Check-AzureOrgMembership.ps1 "octocat" + if ($result.IsMember) { Write-Host "User is a member!" } + +.EXAMPLE + .\Check-AzureOrgMembership.ps1 "octocat" -Quiet | ConvertTo-Json +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory = $true, Position = 0)] + [ValidateNotNullOrEmpty()] + [string]$Username, + + [Parameter()] + [string]$Organization = "Azure", + + [Parameter()] + [switch]$Quiet +) + +function Write-ConditionalOutput { + param([string]$Message, [string]$ForegroundColor = "White") + if (-not $Quiet) { + Write-Host $Message -ForegroundColor $ForegroundColor + } +} + +# Initialize result object +$result = [PSCustomObject]@{ + Username = $Username + Organization = $Organization + IsMember = $false + Status = "Unknown" + ErrorMessage = $null + CheckedAt = Get-Date +} + +try { + # Check if GitHub CLI is available + if (-not (Get-Command gh -ErrorAction SilentlyContinue)) { + $result.Status = "Error" + $result.ErrorMessage = "GitHub CLI (gh) is not installed. Install from: https://cli.github.com/" + Write-ConditionalOutput "โŒ GitHub CLI not found" "Red" + return $result + } + + # Check if authenticated + $null = gh auth status 2>&1 + if ($LASTEXITCODE -ne 0) { + $result.Status = "Error" + $result.ErrorMessage = "GitHub CLI is not authenticated. Run 'gh auth login' first." + Write-ConditionalOutput "โŒ GitHub CLI not authenticated" "Red" + return $result + } + + Write-ConditionalOutput "๐Ÿ” Checking if '$Username' is a member of '$Organization' organization..." "Yellow" + + # Check organization membership using GitHub API + gh api "orgs/$Organization/members/$Username" --silent 2>$null + $apiExitCode = $LASTEXITCODE + + if ($apiExitCode -eq 0) { + # User is a public member + $result.IsMember = $true + $result.Status = "PublicMember" + Write-ConditionalOutput "โœ… $Username is a PUBLIC member of the $Organization organization!" "Green" + } + elseif ($apiExitCode -eq 1) { + # Exit code 1 typically means 404 - could be not a member or private membership + # Check if user exists + gh api "users/$Username" --silent 2>$null + if ($LASTEXITCODE -eq 0) { + $result.IsMember = $false + $result.Status = "NotMemberOrPrivate" + Write-ConditionalOutput "โŒ $Username is either not a member of $Organization organization or has private membership." "Red" + } + else { + $result.Status = "UserNotFound" + $result.ErrorMessage = "User '$Username' was not found on GitHub." + Write-ConditionalOutput "โŒ User '$Username' was not found on GitHub." "Red" + } + } + else { + # Unexpected error + $result.Status = "Error" + $result.ErrorMessage = "Unexpected error occurred (GitHub API exit code: $apiExitCode)" + Write-ConditionalOutput "โŒ Unexpected error checking membership (exit code: $apiExitCode)" "Red" + } +} +catch { + $result.Status = "Error" + $result.ErrorMessage = "Exception occurred: $($_.Exception.Message)" + Write-ConditionalOutput "โŒ Error: $($_.Exception.Message)" "Red" +} + +# Return the result object +return $result \ No newline at end of file diff --git a/tools/GitHubOrgMember/Demo-ObjectBased.ps1 b/tools/GitHubOrgMember/Demo-ObjectBased.ps1 new file mode 100644 index 000000000000..c9f3140342be --- /dev/null +++ b/tools/GitHubOrgMember/Demo-ObjectBased.ps1 @@ -0,0 +1,59 @@ +# Demonstration of Object-Based GitHub Organization Membership Checker + +Write-Host "๐Ÿš€ Object-Based Azure Membership Checker Demo" -ForegroundColor Cyan +Write-Host "=" * 50 -ForegroundColor Cyan + +# Example 1: Simple membership check +Write-Host "`n๐Ÿ“‹ Example 1: Simple Check" -ForegroundColor Yellow +$result = .\Check-AzureOrgMembership.ps1 "vidai-msft" -Quiet 2>$null +Write-Host "User: $($result.Username)" +Write-Host "Is Member: $($result.IsMember)" +Write-Host "Status: $($result.Status)" + +# Example 2: Batch processing with objects +Write-Host "`n๐Ÿ“‹ Example 2: Batch Processing" -ForegroundColor Yellow +$users = @("vidai-msft", "isra-fel", "msJinLei", "NickCandy") +$results = $users | ForEach-Object { + .\Check-AzureOrgMembership.ps1 $_ -Quiet 2>$null +} + +Write-Host "Batch Results:" +$results | Format-Table Username, IsMember, Status -AutoSize + +# Example 3: Filtering and processing +Write-Host "`n๐Ÿ“‹ Example 3: Filter Members Only" -ForegroundColor Yellow +$members = $results | Where-Object { $_.IsMember } +Write-Host "Azure Members Found: $($members.Count)" +$members | ForEach-Object { Write-Host " - $($_.Username)" -ForegroundColor Green } + +# Example 4: JSON export +Write-Host "`n๐Ÿ“‹ Example 4: JSON Export" -ForegroundColor Yellow +$jsonResult = $results | ConvertTo-Json -Depth 2 +Write-Host "Sample JSON (first result):" +$results[0] | ConvertTo-Json | Write-Host + +# Example 5: Error handling +Write-Host "`n๐Ÿ“‹ Example 5: Error Handling" -ForegroundColor Yellow +$errorResult = $results | Where-Object { $_.Status -eq "Error" } +if ($errorResult.Count -gt 0) { + Write-Host "Errors found:" + $errorResult | ForEach-Object { Write-Host " - $($_.Username): $($_.ErrorMessage)" -ForegroundColor Red } +} else { + Write-Host "No errors detected" -ForegroundColor Green +} + +# Example 6: Different organization +Write-Host "`n๐Ÿ“‹ Example 6: Different Organization" -ForegroundColor Yellow +$microsoftResult = .\Check-AzureOrgMembership.ps1 "vidai-msft" -Organization "microsoft" -Quiet 2>$null +Write-Host "Checking Microsoft org membership for vidai-msft:" +Write-Host " Organization: $($microsoftResult.Organization)" +Write-Host " Is Member: $($microsoftResult.IsMember)" +Write-Host " Status: $($microsoftResult.Status)" + +Write-Host "`nโœจ Object-Based Advantages Demonstrated:" -ForegroundColor Cyan +Write-Host " โœ… Type-safe property access" -ForegroundColor Green +Write-Host " โœ… Easy filtering and processing" -ForegroundColor Green +Write-Host " โœ… Pipeline-friendly operations" -ForegroundColor Green +Write-Host " โœ… JSON serialization support" -ForegroundColor Green +Write-Host " โœ… Structured error information" -ForegroundColor Green +Write-Host " โœ… No exit code dependencies" -ForegroundColor Green diff --git a/tools/GitHubOrgMember/README.md b/tools/GitHubOrgMember/README.md new file mode 100644 index 000000000000..1c32267b09e8 --- /dev/null +++ b/tools/GitHubOrgMember/README.md @@ -0,0 +1,327 @@ +# GitHub Azure Organization Membership Checker + +PowerShell script to check if a GitHub user is a member of the Azure GitHub organization using the GitHub CLI. Re```powershell +$result = .\Check-AzureOrgMembership.ps1 "vidai-msft" -Organization "microsoft" -Quietrns structured PowerShell objects for easy processing and integration. + +## Prerequisites + +1. **GitHub CLI**: Install from [https://cli.github.com/](https://cli.github.com/) +2. **Authentication**: Run `gh auth login` to authenticate with GitHub + +## Script + +### Check-AzureOrgMembership.ps1 (Object-Based) + +A PowerShell script that returns structured objects with membership information. + +**Usage:** +```powershell +# Basic usage - returns object +$result = .\Check-AzureOrgMembership.ps1 "username" + +# Quiet mode - suppresses console output +$result = .\Check-AzureOrgMembership.ps1 "username" -Quiet + +# Different organization +$result = .\Check-AzureOrgMembership.ps1 "username" -Organization "microsoft" + +# Check result +if ($result.IsMember) { + Write-Host "$($result.Username) is a member!" +} +``` + +**Features:** +- Returns structured PowerShell objects +- JSON serializable +- Pipeline-friendly +- Comprehensive error handling +- Configurable organization +- Quiet mode support +- Type-safe property access + +## Object Structure + +The script returns a `PSCustomObject` with the following properties: + +```powershell +@{ + Username = "string" # The checked username + Organization = "string" # The organization that was checked + IsMember = $true/$false # Boolean indicating if user is a public member + Status = "string" # Detailed status (see below) + ErrorMessage = "string" # Error details if Status is Error + CheckedAt = [DateTime] # Timestamp of when the check was performed +} +``` + +### Status Values + +- **`PublicMember`**: User is a confirmed public member +- **`NotMemberOrPrivate`**: User is either not a member or has private membership +- **`UserNotFound`**: The specified username doesn't exist on GitHub +- **`Error`**: An error occurred (see ErrorMessage for details) + +## Important Notes + +1. **Privacy**: GitHub organization membership can be set to private. If a user is a private member, the API will return the same response as if they're not a member. + +2. **Authentication**: The GitHub CLI must be authenticated to make API calls. Some organization information may require different permission levels. + +3. **Rate Limiting**: GitHub API has rate limits. For bulk checking, consider implementing delays between requests. + +## Examples + +### Example 1: Basic Object-Based Check + +```powershell +$result = .\Check-AzureOrgMembership.ps1 "vidai-msft" +Write-Host "User: $($result.Username)" +Write-Host "Is Member: $($result.IsMember)" +Write-Host "Status: $($result.Status)" +``` + +**Output:** +``` +๐Ÿ” Checking if 'vidai-msft' is a member of 'Azure' organization... +โœ… vidai-msft is a PUBLIC member of the Azure organization! +User: vidai-msft +Is Member: True +Status: PublicMember +``` + +### Example 2: Quiet Mode Processing + +```powershell +$result = .\Check-AzureOrgMembership.ps1 "vidai-msft" -Quiet +if ($result.IsMember) { + Write-Host "Welcome, Azure team member!" -ForegroundColor Green +} else { + Write-Host "Access denied." -ForegroundColor Red +} +``` + +### Example 3: Batch Processing Multiple Users + +```powershell +$users = @("vidai-msft", "isra-fel", "msJinLei", "NickCandy") +$results = $users | ForEach-Object { + .\Check-AzureOrgMembership.ps1 $_ -Quiet +} + +# Display results in a table +$results | Format-Table Username, IsMember, Status -AutoSize + +# Filter to show only members +$members = $results | Where-Object { $_.IsMember } +Write-Host "Azure Members: $($members.Count)" +``` + +**Sample Output:** +``` +Username IsMember Status +-------- -------- ------ +vidai-msft True PublicMember +isra-fel True PublicMember +msJinLei False NotMemberOrPrivate +NickCandy False NotMemberOrPrivate + +Azure Members: 2 +``` + +### Example 4: JSON Export for APIs + +```powershell +$result = .\Check-AzureOrgMembership.ps1 "vidai-msft" -Quiet +$jsonOutput = $result | ConvertTo-Json +Write-Host $jsonOutput +``` + +**Sample JSON Output:** +```json +{ + "Username": "vidai-msft", + "Organization": "Azure", + "IsMember": true, + "Status": "PublicMember", + "ErrorMessage": null, + "CheckedAt": "2025-08-18T14:05:32.1234567-07:00" +} +``` + +### Example 5: Error Handling + +```powershell +$result = .\Check-AzureOrgMembership.ps1 "non-existent-user" -Quiet +if ($result.Status -eq "Error") { + Write-Warning "Error: $($result.ErrorMessage)" +} elseif ($result.Status -eq "UserNotFound") { + Write-Host "User not found on GitHub" +} +``` + +### Example 6: Different Organization + +```powershell +$result = .\Check-AzureOrgMembership.ps1 "vidai-msft" -Organization "microsoft" -Quiet +Write-Host "$($result.Username) is a member of $($result.Organization): $($result.IsMember)" +``` + +## Troubleshooting + +### GitHub CLI Not Found +``` +Install GitHub CLI from: https://cli.github.com/ +``` + +### Not Authenticated +```powershell +gh auth login +``` + +### Permission Issues +If you get permission errors, ensure your GitHub token has the necessary scopes for reading organization membership. + +## Testing + +This project includes comprehensive testing for the object-based script. + +### Prerequisites for Testing + +1. **Pester Module**: Install the latest version of Pester (optional) + ```powershell + Install-Module -Name Pester -Force -SkipPublisherCheck + ``` + +2. **GitHub CLI**: Must be installed and authenticated (same as runtime requirements) + +### Running Tests + +```powershell +# Run object-based demonstration +.\Demo-ObjectBased.ps1 + +# Run comprehensive object-based tests +.\Test-ObjectBased.ps1 + +# Legacy Pester tests (optional) +Invoke-Pester .\Check-AzureOrgMembership.Tests.ps1 +``` + +### Quick Test + +The object-based approach makes testing much simpler: + +```powershell +# Test multiple users and validate results +$testUsers = @{ + "vidai-msft" = $true # Should be member + "msJinLei" = $false # Should not be member +} + +foreach ($user in $testUsers.Keys) { + $result = .\Check-AzureOrgMembership.ps1 $user -Quiet + $expected = $testUsers[$user] + + if ($result.IsMember -eq $expected) { + Write-Host "โœ… $user test passed" -ForegroundColor Green + } else { + Write-Host "โŒ $user test failed" -ForegroundColor Red + } +} +``` + +## Object-Based Advantages + +### โœ… **Structured Data** +- Type-safe property access +- No string parsing required +- Consistent object structure + +### โœ… **Pipeline Friendly** +```powershell +# Easy filtering and processing +$results | Where-Object { $_.IsMember } | Select-Object Username, Status +``` + +### โœ… **JSON Serializable** +```powershell +# Perfect for APIs and data storage +$results | ConvertTo-Json | Out-File "membership-results.json" +``` + +### โœ… **Error Handling** +```powershell +# Structured error information +if ($result.Status -eq "Error") { + Write-Warning $result.ErrorMessage +} +``` + +### โœ… **No Exit Codes** +- No dependency on `$LASTEXITCODE` +- Works naturally with PowerShell objects +- Easier debugging and testing + +## Important Notes + +1. **Privacy**: GitHub organization membership can be set to private. If a user is a private member, the API will return the same response as if they're not a member. + +2. **Authentication**: The GitHub CLI must be authenticated to make API calls. Some organization information may require different permission levels. + +3. **Rate Limiting**: GitHub API has rate limits. For bulk checking, consider implementing delays between requests. + +### Test Data + +- **Known Azure Members**: `vidai-msft`, `isra-fel`, `wyunchi-ms` +- **Known Non-Members**: `azure-powershell-bot`, `msJinLei`, `NickCandy` +- **Non-Existent User**: Randomly generated username +.\Run-Tests.ps1 + +# Run all Pester tests (alternative) +Invoke-Pester + +# Run tests with detailed output +Invoke-Pester -Output Detailed + +# Run specific test categories +Invoke-Pester -Tag "Performance" + +# Run tests and generate XML report +$config = Import-PowerShellDataFile .\PesterConfiguration.psd1 +Invoke-Pester -Configuration $config +``` + +### Quick Test + +The `Run-Tests.ps1` script provides a comprehensive validation of both scripts and is the recommended way to verify functionality. It tests: + +1. Prerequisites (GitHub CLI installation and authentication) +2. Known Azure member detection (`vidai-msft`) +3. Non-member detection (`azure-powershell-bot`) +4. Consistency between both scripts +5. Parameter validation and help documentation + +### Test Coverage + +The test suite includes: + +- **Prerequisites Tests**: Verify GitHub CLI installation and authentication +- **Parameter Validation**: Test required and optional parameters +- **Known Member Tests**: Test with `vidai-msft` (confirmed Azure member) +- **Non-Member Tests**: Test with `azure-powershell-bot` (confirmed non-member) +- **Error Handling**: Test with non-existent users +- **Integration Tests**: Real GitHub API calls and rate limiting +- **Performance Tests**: Ensure reasonable execution times +- **Consistency Tests**: Compare results between both scripts + +### Test Data + +- **Known Azure Members**: `vidai-msft`, `isra-fel`, `wyunchi-ms` +- **Known Non-Members**: `azure-powershell-bot`, `msJinLei`, `NickCandy` +- **Non-Existent User**: Randomly generated username + +## Exit Codes + +- `0`: User is a confirmed public member +- `1`: User is not a member, has private membership, doesn't exist, or an error occurred diff --git a/tools/GitHubOrgMember/Test-ObjectBased.ps1 b/tools/GitHubOrgMember/Test-ObjectBased.ps1 new file mode 100644 index 000000000000..64e8802d172a --- /dev/null +++ b/tools/GitHubOrgMember/Test-ObjectBased.ps1 @@ -0,0 +1,101 @@ +# Object-Based Test Runner for Azure Organization Membership Checker +# This script demonstrates the object-based approach + +Write-Host "๐Ÿงช Testing Object-Based Azure Organization Membership Checker" -ForegroundColor Cyan +Write-Host "=" * 60 -ForegroundColor Cyan + +# Test data +$AzureMembers = @("vidai-msft", "isra-fel", "wyunchi-ms") +$NonMembers = @("azure-powershell-bot", "msJinLei", "NickCandy") + +# Test 1: Prerequisites +Write-Host "`n๐Ÿ“‹ Test 1: Prerequisites Check" -ForegroundColor Yellow +$prereqResult = .\Check-AzureOrgMembership.ps1 "test-user" -Quiet +if ($prereqResult.Status -eq "Error" -and $prereqResult.ErrorMessage -match "GitHub CLI") { + Write-Host " โŒ $($prereqResult.ErrorMessage)" -ForegroundColor Red + exit 1 +} else { + Write-Host " โœ… GitHub CLI is available and authenticated" -ForegroundColor Green +} + +# Test 2: Azure Members +Write-Host "`n๐Ÿ“‹ Test 2: Azure Members Testing" -ForegroundColor Yellow +$memberResults = @() +foreach ($member in $AzureMembers) { + $result = .\Check-AzureOrgMembership.ps1 $member -Quiet + $memberResults += $result + + if ($result.IsMember -and $result.Status -eq "PublicMember") { + Write-Host " โœ… $member correctly identified as Azure member" -ForegroundColor Green + } else { + Write-Host " โŒ $member test failed - IsMember: $($result.IsMember), Status: $($result.Status)" -ForegroundColor Red + } +} + +# Test 3: Non-Members +Write-Host "`n๐Ÿ“‹ Test 3: Non-Members Testing" -ForegroundColor Yellow +$nonMemberResults = @() +foreach ($nonMember in $NonMembers) { + $result = .\Check-AzureOrgMembership.ps1 $nonMember -Quiet + $nonMemberResults += $result + + if (-not $result.IsMember -and $result.Status -eq "NotMemberOrPrivate") { + Write-Host " โœ… $nonMember correctly identified as non-member" -ForegroundColor Green + } else { + Write-Host " โŒ $nonMember test failed - IsMember: $($result.IsMember), Status: $($result.Status)" -ForegroundColor Red + } +} + +# Test 4: Object Structure Validation +Write-Host "`n๐Ÿ“‹ Test 4: Object Structure Validation" -ForegroundColor Yellow +$sampleResult = $memberResults[0] +$requiredProperties = @("Username", "Organization", "IsMember", "Status", "ErrorMessage", "CheckedAt") +$allPropertiesPresent = $true + +foreach ($prop in $requiredProperties) { + if ($sampleResult.PSObject.Properties.Name -contains $prop) { + Write-Host " โœ… Property present: $prop" -ForegroundColor Green + } else { + Write-Host " โŒ Missing property: $prop" -ForegroundColor Red + $allPropertiesPresent = $false + } +} + +if ($allPropertiesPresent) { + Write-Host " โœ… All required properties present in result objects" -ForegroundColor Green +} else { + Write-Host " โŒ Result object structure validation failed" -ForegroundColor Red +} + +# Test 5: JSON Serialization +Write-Host "`n๐Ÿ“‹ Test 5: JSON Serialization Test" -ForegroundColor Yellow +try { + $jsonOutput = $sampleResult | ConvertTo-Json + $deserializedResult = $jsonOutput | ConvertFrom-Json + + if ($deserializedResult.Username -eq $sampleResult.Username) { + Write-Host " โœ… Objects properly serialize to/from JSON" -ForegroundColor Green + } else { + Write-Host " โŒ JSON serialization test failed" -ForegroundColor Red + } +} catch { + Write-Host " โŒ JSON serialization error: $($_.Exception.Message)" -ForegroundColor Red +} + +# Summary +Write-Host "`n๐ŸŽ‰ Object-Based Test Suite Complete!" -ForegroundColor Green +Write-Host "๐Ÿ“Š Summary:" -ForegroundColor Cyan +Write-Host " - Azure Members: $($AzureMembers.Count) tested" -ForegroundColor White +Write-Host " - Non-Members: $($NonMembers.Count) tested" -ForegroundColor White +Write-Host " - All results returned as structured PowerShell objects" -ForegroundColor White + +# Display sample object +Write-Host "`n๐Ÿ“„ Sample Result Object:" -ForegroundColor Cyan +$sampleResult | Format-List + +Write-Host "โœจ Benefits of Object-Based Approach:" -ForegroundColor Yellow +Write-Host " - No exit code parsing required" -ForegroundColor White +Write-Host " - Structured data easy to process" -ForegroundColor White +Write-Host " - JSON serializable for APIs" -ForegroundColor White +Write-Host " - Type-safe property access" -ForegroundColor White +Write-Host " - Pipeable and filterable" -ForegroundColor White