-
Notifications
You must be signed in to change notification settings - Fork 8
⚙️ [Maintenance]: Partial test reruns no longer fail when shared infrastructure has been torn down #594
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Marius Storhaug (MariusStorhaug)
merged 12 commits into
main
from
fix/590-self-healing-test-infra
May 2, 2026
Merged
⚙️ [Maintenance]: Partial test reruns no longer fail when shared infrastructure has been torn down #594
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
b1cb3fd
Add SharedTestRepositories helpers for idempotent shared test infra
MariusStorhaug fc43af8
Route global BeforeAll through SharedTestRepositories helpers
MariusStorhaug 40e14a4
Self-heal shared test repositories in leaf-test BeforeAll blocks
MariusStorhaug 99a47f3
Rewrite SharedTestRepositories docs in idiomatic comment-based help
MariusStorhaug 94bd43f
Replace custom helper with idempotent Set-GitHubRepository calls
MariusStorhaug 11262ca
Update tests README to document idempotent Set-GitHubRepository pattern
MariusStorhaug 77ace24
Add Copilot instructions for test file conventions
MariusStorhaug 181615b
Consolidate tests README into Copilot instructions as single source o…
MariusStorhaug a971b94
Align TEMPLATE.ps1 with self-healing repo provisioning pattern
MariusStorhaug f1c850f
Fix documentation accuracy: clarify Set-GitHubRepository issues PATCH…
MariusStorhaug c601697
Add repository/enterprise owner type guard to TEMPLATE and instructio…
MariusStorhaug 0968d3f
Add repository/enterprise owner type guard to Secrets and Variables t…
MariusStorhaug File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,198 @@ | ||
| --- | ||
| description: "Use when writing, editing, or reviewing Pester test files under the tests/ folder. Covers shared repository setup, auth case iteration, naming conventions, and skip patterns for the GitHub module integration tests." | ||
| applyTo: "tests/**" | ||
| --- | ||
| # Integration Test Conventions | ||
|
|
||
| ## Test infrastructure accounts | ||
|
|
||
| ### User | ||
|
|
||
| Login: `psmodule-user` | ||
| Owner of: | ||
|
|
||
| - [psmodule-user](https://github.com/psmodule-user) (standalone org) | ||
| - [psmodule-test-org2](https://github.com/orgs/psmodule-test-org2) (standalone org) | ||
|
|
||
| Secrets: | ||
|
|
||
| - `TEST_USER_PAT` → `psmodule-user` (user) | ||
| - `TEST_USER_USER_FG_PAT` → `psmodule-user` (user) | ||
| - `TEST_USER_ORG_FG_PAT` → `psmodule-test-org2` (org) | ||
|
|
||
| ### APP_ENT — PSModule Enterprise App | ||
|
|
||
| Homed in `MSX`. ClientID: `Iv23lieHcDQDwVV3alK1`. | ||
| Installed on [psmodule-test-org3](https://github.com/orgs/psmodule-test-org3) (enterprise org) with all permissions and push events. | ||
|
|
||
| Secrets: `TEST_APP_ENT_CLIENT_ID`, `TEST_APP_ENT_PRIVATE_KEY` | ||
|
|
||
| ### APP_ORG — PSModule Organization App | ||
|
|
||
| Homed in `PSModule`. ClientID: `Iv23liYDnEbKlS9IVzHf`. | ||
| Installed on [psmodule-test-org](https://github.com/orgs/psmodule-test-org) (standalone org) with all permissions and push events. | ||
|
|
||
| Secrets: `TEST_APP_ORG_CLIENT_ID`, `TEST_APP_ORG_PRIVATE_KEY` | ||
|
|
||
| ## Auth cases | ||
|
|
||
| `AuthCases.ps1` defines 7 auth cases. Each test file iterates over all cases, skipping those | ||
| that don't apply (e.g., `repository` and `enterprise` owner types skip repo-dependent tests). | ||
|
|
||
| | # | AuthType | TokenType | Owner | OwnerType | | ||
| |---|----------|---------------|--------------------|--------------| | ||
| | 1 | PAT | USER_FG_PAT | psmodule-user | user | | ||
| | 2 | PAT | ORG_FG_PAT | psmodule-test-org2 | organization | | ||
| | 3 | PAT | PAT | psmodule-user | user | | ||
| | 4 | IAT | GITHUB_TOKEN | PSModule | repository | | ||
| | 5 | App | APP_ORG | psmodule-test-org | organization | | ||
| | 6 | App | APP_ENT | psmodule-test-org3 | organization | | ||
| | 7 | App | APP_ENT | msx | enterprise | | ||
|
|
||
| Cases 4 (`repository`) and 7 (`enterprise`) skip repo creation. Cases 1 and 3 share the same user owner | ||
| (`psmodule-user`) but have different `$TokenType` values, so repo names are unique. | ||
|
|
||
| ## Setup and teardown | ||
|
|
||
| Shared test infrastructure is provisioned once per workflow run using `BeforeAll.ps1` and torn down using `AfterAll.ps1`. | ||
| For generic guidance on setup/teardown scripts, see the | ||
| [Process-PSModule documentation](https://github.com/PSModule/Process-PSModule#setup-and-teardown-scripts). | ||
|
|
||
| ### `BeforeAll.ps1` — global setup | ||
|
|
||
| Runs once before all parallel test files. For each auth case (except `GITHUB_TOKEN`): | ||
|
|
||
| 1. Connects using the auth case credentials | ||
| 2. Removes any existing repositories for the deterministic names used by the run | ||
| 3. Provisions a primary shared repository per OS using `Set-GitHubRepository`: `Test-{OS}-{TokenType}-{GITHUB_RUN_ID}` | ||
| - Includes `-AddReadme`, `-License 'mit'`, and `-Gitignore 'VisualStudio'` so release tests have a default branch with content | ||
| - For `user` owners: `Set-GitHubRepository -Name $repoName ...` | ||
| - For `organization` owners: `Set-GitHubRepository -Organization $Owner -Name $repoName ...` | ||
| 4. For `organization` owners only, provisions two extra repositories per OS (`-2`, `-3` suffix) for | ||
| Secrets/Variables `SelectedRepository` tests | ||
|
|
||
| `Set-GitHubRepository` is idempotent — if the repository already exists it updates it in place (issuing a | ||
| PATCH), and if it does not exist it creates it. Because the same parameters are passed each time, the | ||
| end-state is identical regardless of how many times the setup runs. The extra PATCH on the happy path is | ||
| a deliberate trade-off for simplicity: one call handles both first-run and partial-rerun scenarios without | ||
| branching logic. | ||
|
|
||
| ### `AfterAll.ps1` — global teardown | ||
|
|
||
| Runs once after all parallel test files complete. For each auth case (except `GITHUB_TOKEN`): | ||
|
|
||
| 1. Connects using the auth case credentials | ||
| 2. Removes the run-scoped repositories by their known names | ||
|
|
||
| ## Shared test repositories | ||
|
|
||
| Each test file that depends on a GitHub repository must ensure it exists using `Set-GitHubRepository` | ||
| in its per-context `BeforeAll`. `Set-GitHubRepository` is idempotent — if the repository already exists | ||
| it updates it in place (PATCH), and if it does not exist it creates it. When the same parameters are | ||
| passed each time the end-state is identical. This makes every test file self-sufficient regardless of | ||
| whether the global `BeforeAll.ps1` already provisioned the repository. | ||
|
MariusStorhaug marked this conversation as resolved.
|
||
|
|
||
| **Do not** use `Get-GitHubRepository` with a throw guard — that breaks partial reruns. | ||
| **Do not** use `New-GitHubRepository` — that fails if the repository already exists. | ||
|
|
||
|
MariusStorhaug marked this conversation as resolved.
|
||
| Primary repositories use `-AddReadme`, `-License 'mit'`, and `-Gitignore 'VisualStudio'` so that | ||
| a default branch with content is available for tests that need commits (e.g., releases, tags). | ||
|
|
||
| Some auth cases (e.g., `repository`, `enterprise`) do not operate on a user- or org-owned repository. | ||
| Skip provisioning for those owner types and set `$repo = $null` so that repo-dependent tests can | ||
| be skipped cleanly: | ||
|
|
||
| ```powershell | ||
| $repoPrefix = "Test-$os-$TokenType" | ||
| $repoName = "$repoPrefix-$id" | ||
| if ($OwnerType -in ('repository', 'enterprise')) { | ||
| $repo = $null | ||
| } else { | ||
| $repoParams = @{ | ||
| Name = $repoName | ||
| AddReadme = $true | ||
| License = 'mit' | ||
| Gitignore = 'VisualStudio' | ||
| } | ||
| $repo = switch ($OwnerType) { | ||
| 'user' { Set-GitHubRepository @repoParams } | ||
| 'organization' { Set-GitHubRepository @repoParams -Organization $Owner } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| For organization-scoped tests that need companion repositories (Secrets/Variables `SelectedRepository`), | ||
| provision `-2` and `-3` variants the same way: | ||
|
|
||
| ```powershell | ||
| $repo2 = Set-GitHubRepository -Organization $Owner -Name "$repoName-2" | ||
| $repo3 = Set-GitHubRepository -Organization $Owner -Name "$repoName-3" | ||
| ``` | ||
|
|
||
| ## Test file structure | ||
|
|
||
| ```powershell | ||
| BeforeAll { | ||
| $testName = 'TestName' | ||
| $os = $env:RUNNER_OS | ||
| $id = $env:GITHUB_RUN_ID | ||
| } | ||
|
|
||
| Describe 'TestName' { | ||
| $authCases = . "$PSScriptRoot/Data/AuthCases.ps1" | ||
|
|
||
| Context 'As <Type> using <Case> on <Target>' -ForEach $authCases { | ||
| BeforeAll { | ||
| $context = Connect-GitHubAccount @connectParams -PassThru -Silent | ||
| if ($AuthType -eq 'APP') { | ||
| $context = Connect-GitHubApp @connectAppParams -PassThru -Default -Silent | ||
| } | ||
|
|
||
| $repoPrefix = "Test-$os-$TokenType" | ||
| $repoName = "$repoPrefix-$id" | ||
| if ($OwnerType -in ('repository', 'enterprise')) { | ||
| $repo = $null | ||
| } else { | ||
| $repoParams = @{ | ||
| Name = $repoName | ||
| AddReadme = $true | ||
| License = 'mit' | ||
| Gitignore = 'VisualStudio' | ||
| } | ||
| $repo = switch ($OwnerType) { | ||
| 'user' { Set-GitHubRepository @repoParams } | ||
| 'organization' { Set-GitHubRepository @repoParams -Organization $Owner } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| AfterAll { | ||
| Get-GitHubContext -ListAvailable | Disconnect-GitHubAccount -Silent | ||
| } | ||
|
|
||
| It 'Should do something' -Skip:($OwnerType -in ('repository', 'enterprise')) { | ||
| # Test logic using $repo, $Owner, $repoName | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Naming conventions | ||
|
|
||
| | Resource | Pattern | Example | | ||
| |------------|----------------------------------------------|----------------------------------| | ||
| | Repo | `Test-{OS}-{TokenType}-{RunID}` | `Test-Linux-USER_FG_PAT-1234` | | ||
| | Extra repo | `Test-{OS}-{TokenType}-{RunID}-{N}` | `Test-Linux-USER_FG_PAT-1234-2` | | ||
| | Secret | `{TestName}_{OS}_{TokenType}_{RunID}` | `Secrets_Linux_PAT_1234` | | ||
| | Variable | `{TestName}_{OS}_{TokenType}_{RunID}` | `Variables_Linux_PAT_1234` | | ||
| | Team | `{TestName}_{OS}_{TokenType}_{RunID}_{Name}` | `Teams_Linux_APP_ORG_1234_Pull` | | ||
| | Env | `{TestName}-{OS}-{TokenType}-{RunID}` | `Secrets-Linux-PAT-1234` | | ||
|
|
||
| ## Key rules | ||
|
|
||
| - `$id` must always be `$env:GITHUB_RUN_ID` — never `[guid]::NewGuid()` or `Get-Random`. | ||
| - Skip repo-dependent tests with `-Skip:($OwnerType -in ('repository', 'enterprise'))`. | ||
| - Disconnect all sessions in `AfterAll`: `Get-GitHubContext -ListAvailable | Disconnect-GitHubAccount -Silent`. | ||
| - Test-specific ephemeral resources (releases, secrets, variables, environments, teams) are created and | ||
| cleaned up within each test file. Only repositories are shared. | ||
| - `Repositories.Tests.ps1` is the exception — it creates and deletes its own repos because it tests CRUD. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.