|
| 1 | +--- |
| 2 | +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." |
| 3 | +applyTo: "tests/**" |
| 4 | +--- |
| 5 | +# Integration Test Conventions |
| 6 | + |
| 7 | +## Test infrastructure accounts |
| 8 | + |
| 9 | +### User |
| 10 | + |
| 11 | +Login: `psmodule-user` |
| 12 | +Owner of: |
| 13 | + |
| 14 | +- [psmodule-user](https://github.com/psmodule-user) (standalone org) |
| 15 | +- [psmodule-test-org2](https://github.com/orgs/psmodule-test-org2) (standalone org) |
| 16 | + |
| 17 | +Secrets: |
| 18 | + |
| 19 | +- `TEST_USER_PAT` → `psmodule-user` (user) |
| 20 | +- `TEST_USER_USER_FG_PAT` → `psmodule-user` (user) |
| 21 | +- `TEST_USER_ORG_FG_PAT` → `psmodule-test-org2` (org) |
| 22 | + |
| 23 | +### APP_ENT — PSModule Enterprise App |
| 24 | + |
| 25 | +Homed in `MSX`. ClientID: `Iv23lieHcDQDwVV3alK1`. |
| 26 | +Installed on [psmodule-test-org3](https://github.com/orgs/psmodule-test-org3) (enterprise org) with all permissions and push events. |
| 27 | + |
| 28 | +Secrets: `TEST_APP_ENT_CLIENT_ID`, `TEST_APP_ENT_PRIVATE_KEY` |
| 29 | + |
| 30 | +### APP_ORG — PSModule Organization App |
| 31 | + |
| 32 | +Homed in `PSModule`. ClientID: `Iv23liYDnEbKlS9IVzHf`. |
| 33 | +Installed on [psmodule-test-org](https://github.com/orgs/psmodule-test-org) (standalone org) with all permissions and push events. |
| 34 | + |
| 35 | +Secrets: `TEST_APP_ORG_CLIENT_ID`, `TEST_APP_ORG_PRIVATE_KEY` |
| 36 | + |
| 37 | +## Auth cases |
| 38 | + |
| 39 | +`AuthCases.ps1` defines 7 auth cases. Each test file iterates over all cases, skipping those |
| 40 | +that don't apply (e.g., `repository` and `enterprise` owner types skip repo-dependent tests). |
| 41 | + |
| 42 | +| # | AuthType | TokenType | Owner | OwnerType | |
| 43 | +|---|----------|---------------|--------------------|--------------| |
| 44 | +| 1 | PAT | USER_FG_PAT | psmodule-user | user | |
| 45 | +| 2 | PAT | ORG_FG_PAT | psmodule-test-org2 | organization | |
| 46 | +| 3 | PAT | PAT | psmodule-user | user | |
| 47 | +| 4 | IAT | GITHUB_TOKEN | PSModule | repository | |
| 48 | +| 5 | App | APP_ORG | psmodule-test-org | organization | |
| 49 | +| 6 | App | APP_ENT | psmodule-test-org3 | organization | |
| 50 | +| 7 | App | APP_ENT | msx | enterprise | |
| 51 | + |
| 52 | +Cases 4 (`repository`) and 7 (`enterprise`) skip repo creation. Cases 1 and 3 share the same user owner |
| 53 | +(`psmodule-user`) but have different `$TokenType` values, so repo names are unique. |
| 54 | + |
| 55 | +## Setup and teardown |
| 56 | + |
| 57 | +Shared test infrastructure is provisioned once per workflow run using `BeforeAll.ps1` and torn down using `AfterAll.ps1`. |
| 58 | +For generic guidance on setup/teardown scripts, see the |
| 59 | +[Process-PSModule documentation](https://github.com/PSModule/Process-PSModule#setup-and-teardown-scripts). |
| 60 | + |
| 61 | +### `BeforeAll.ps1` — global setup |
| 62 | + |
| 63 | +Runs once before all parallel test files. For each auth case (except `GITHUB_TOKEN`): |
| 64 | + |
| 65 | +1. Connects using the auth case credentials |
| 66 | +2. Removes any existing repositories for the deterministic names used by the run |
| 67 | +3. Provisions a primary shared repository per OS using `Set-GitHubRepository`: `Test-{OS}-{TokenType}-{GITHUB_RUN_ID}` |
| 68 | + - Includes `-AddReadme`, `-License 'mit'`, and `-Gitignore 'VisualStudio'` so release tests have a default branch with content |
| 69 | + - For `user` owners: `Set-GitHubRepository -Name $repoName ...` |
| 70 | + - For `organization` owners: `Set-GitHubRepository -Organization $Owner -Name $repoName ...` |
| 71 | +4. For `organization` owners only, provisions two extra repositories per OS (`-2`, `-3` suffix) for |
| 72 | + Secrets/Variables `SelectedRepository` tests |
| 73 | + |
| 74 | +`Set-GitHubRepository` is idempotent — if the repository already exists it updates it in place (issuing a |
| 75 | +PATCH), and if it does not exist it creates it. Because the same parameters are passed each time, the |
| 76 | +end-state is identical regardless of how many times the setup runs. The extra PATCH on the happy path is |
| 77 | +a deliberate trade-off for simplicity: one call handles both first-run and partial-rerun scenarios without |
| 78 | +branching logic. |
| 79 | + |
| 80 | +### `AfterAll.ps1` — global teardown |
| 81 | + |
| 82 | +Runs once after all parallel test files complete. For each auth case (except `GITHUB_TOKEN`): |
| 83 | + |
| 84 | +1. Connects using the auth case credentials |
| 85 | +2. Removes the run-scoped repositories by their known names |
| 86 | + |
| 87 | +## Shared test repositories |
| 88 | + |
| 89 | +Each test file that depends on a GitHub repository must ensure it exists using `Set-GitHubRepository` |
| 90 | +in its per-context `BeforeAll`. `Set-GitHubRepository` is idempotent — if the repository already exists |
| 91 | +it updates it in place (PATCH), and if it does not exist it creates it. When the same parameters are |
| 92 | +passed each time the end-state is identical. This makes every test file self-sufficient regardless of |
| 93 | +whether the global `BeforeAll.ps1` already provisioned the repository. |
| 94 | + |
| 95 | +**Do not** use `Get-GitHubRepository` with a throw guard — that breaks partial reruns. |
| 96 | +**Do not** use `New-GitHubRepository` — that fails if the repository already exists. |
| 97 | + |
| 98 | +Primary repositories use `-AddReadme`, `-License 'mit'`, and `-Gitignore 'VisualStudio'` so that |
| 99 | +a default branch with content is available for tests that need commits (e.g., releases, tags). |
| 100 | + |
| 101 | +Some auth cases (e.g., `repository`, `enterprise`) do not operate on a user- or org-owned repository. |
| 102 | +Skip provisioning for those owner types and set `$repo = $null` so that repo-dependent tests can |
| 103 | +be skipped cleanly: |
| 104 | + |
| 105 | +```powershell |
| 106 | +$repoPrefix = "Test-$os-$TokenType" |
| 107 | +$repoName = "$repoPrefix-$id" |
| 108 | +if ($OwnerType -in ('repository', 'enterprise')) { |
| 109 | + $repo = $null |
| 110 | +} else { |
| 111 | + $repoParams = @{ |
| 112 | + Name = $repoName |
| 113 | + AddReadme = $true |
| 114 | + License = 'mit' |
| 115 | + Gitignore = 'VisualStudio' |
| 116 | + } |
| 117 | + $repo = switch ($OwnerType) { |
| 118 | + 'user' { Set-GitHubRepository @repoParams } |
| 119 | + 'organization' { Set-GitHubRepository @repoParams -Organization $Owner } |
| 120 | + } |
| 121 | +} |
| 122 | +``` |
| 123 | + |
| 124 | +For organization-scoped tests that need companion repositories (Secrets/Variables `SelectedRepository`), |
| 125 | +provision `-2` and `-3` variants the same way: |
| 126 | + |
| 127 | +```powershell |
| 128 | +$repo2 = Set-GitHubRepository -Organization $Owner -Name "$repoName-2" |
| 129 | +$repo3 = Set-GitHubRepository -Organization $Owner -Name "$repoName-3" |
| 130 | +``` |
| 131 | + |
| 132 | +## Test file structure |
| 133 | + |
| 134 | +```powershell |
| 135 | +BeforeAll { |
| 136 | + $testName = 'TestName' |
| 137 | + $os = $env:RUNNER_OS |
| 138 | + $id = $env:GITHUB_RUN_ID |
| 139 | +} |
| 140 | +
|
| 141 | +Describe 'TestName' { |
| 142 | + $authCases = . "$PSScriptRoot/Data/AuthCases.ps1" |
| 143 | +
|
| 144 | + Context 'As <Type> using <Case> on <Target>' -ForEach $authCases { |
| 145 | + BeforeAll { |
| 146 | + $context = Connect-GitHubAccount @connectParams -PassThru -Silent |
| 147 | + if ($AuthType -eq 'APP') { |
| 148 | + $context = Connect-GitHubApp @connectAppParams -PassThru -Default -Silent |
| 149 | + } |
| 150 | +
|
| 151 | + $repoPrefix = "Test-$os-$TokenType" |
| 152 | + $repoName = "$repoPrefix-$id" |
| 153 | + if ($OwnerType -in ('repository', 'enterprise')) { |
| 154 | + $repo = $null |
| 155 | + } else { |
| 156 | + $repoParams = @{ |
| 157 | + Name = $repoName |
| 158 | + AddReadme = $true |
| 159 | + License = 'mit' |
| 160 | + Gitignore = 'VisualStudio' |
| 161 | + } |
| 162 | + $repo = switch ($OwnerType) { |
| 163 | + 'user' { Set-GitHubRepository @repoParams } |
| 164 | + 'organization' { Set-GitHubRepository @repoParams -Organization $Owner } |
| 165 | + } |
| 166 | + } |
| 167 | + } |
| 168 | +
|
| 169 | + AfterAll { |
| 170 | + Get-GitHubContext -ListAvailable | Disconnect-GitHubAccount -Silent |
| 171 | + } |
| 172 | +
|
| 173 | + It 'Should do something' -Skip:($OwnerType -in ('repository', 'enterprise')) { |
| 174 | + # Test logic using $repo, $Owner, $repoName |
| 175 | + } |
| 176 | + } |
| 177 | +} |
| 178 | +``` |
| 179 | + |
| 180 | +## Naming conventions |
| 181 | + |
| 182 | +| Resource | Pattern | Example | |
| 183 | +|------------|----------------------------------------------|----------------------------------| |
| 184 | +| Repo | `Test-{OS}-{TokenType}-{RunID}` | `Test-Linux-USER_FG_PAT-1234` | |
| 185 | +| Extra repo | `Test-{OS}-{TokenType}-{RunID}-{N}` | `Test-Linux-USER_FG_PAT-1234-2` | |
| 186 | +| Secret | `{TestName}_{OS}_{TokenType}_{RunID}` | `Secrets_Linux_PAT_1234` | |
| 187 | +| Variable | `{TestName}_{OS}_{TokenType}_{RunID}` | `Variables_Linux_PAT_1234` | |
| 188 | +| Team | `{TestName}_{OS}_{TokenType}_{RunID}_{Name}` | `Teams_Linux_APP_ORG_1234_Pull` | |
| 189 | +| Env | `{TestName}-{OS}-{TokenType}-{RunID}` | `Secrets-Linux-PAT-1234` | |
| 190 | + |
| 191 | +## Key rules |
| 192 | + |
| 193 | +- `$id` must always be `$env:GITHUB_RUN_ID` — never `[guid]::NewGuid()` or `Get-Random`. |
| 194 | +- Skip repo-dependent tests with `-Skip:($OwnerType -in ('repository', 'enterprise'))`. |
| 195 | +- Disconnect all sessions in `AfterAll`: `Get-GitHubContext -ListAvailable | Disconnect-GitHubAccount -Silent`. |
| 196 | +- Test-specific ephemeral resources (releases, secrets, variables, environments, teams) are created and |
| 197 | + cleaned up within each test file. Only repositories are shared. |
| 198 | +- `Repositories.Tests.ps1` is the exception — it creates and deletes its own repos because it tests CRUD. |
0 commit comments