Skip to content

Commit a2d00c2

Browse files
Update test instructions for per-test-file repository model and self-contained tests
1 parent 6bda396 commit a2d00c2

1 file changed

Lines changed: 44 additions & 26 deletions

File tree

.github/instructions/tests.instructions.md

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
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."
2+
description: "Use when writing, editing, or reviewing Pester test files under the tests/ folder. Covers per-test-file repository setup, self-contained test lifecycle, auth case iteration, naming conventions, and skip patterns for the GitHub module integration tests."
33
applyTo: "tests/**"
44
---
55
# Integration Test Conventions
@@ -54,22 +54,26 @@ Cases 4 (`repository`) and 7 (`enterprise`) skip repo creation. Cases 1 and 3 sh
5454

5555
## Setup and teardown
5656

57-
Shared test infrastructure is provisioned once per workflow run using `BeforeAll.ps1` and torn down using `AfterAll.ps1`.
57+
Test infrastructure is provisioned once per workflow run using `BeforeAll.ps1` and torn down using `AfterAll.ps1`.
5858
For generic guidance on setup/teardown scripts, see the
5959
[Process-PSModule documentation](https://github.com/PSModule/Process-PSModule#setup-and-teardown-scripts).
6060

61+
Each test file gets its own repository, scoped by test name: `{TestName}-{OS}-{TokenType}-{RunID}`. This
62+
eliminates cross-file resource collisions when test files run in parallel across OSes and in sequence
63+
across auth contexts.
64+
6165
### `BeforeAll.ps1` — global setup
6266

6367
Runs once before all parallel test files. For each auth case (except `GITHUB_TOKEN`):
6468

6569
1. Connects using the auth case credentials
6670
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
71+
3. Provisions a per-test-file repository per OS using `Set-GitHubRepository`: `{TestName}-{OS}-{TokenType}-{GITHUB_RUN_ID}`
72+
- Includes `-AddReadme`, `-License 'mit'`, and `-Gitignore 'VisualStudio'` so tests have a default branch with content
6973
- For `user` owners: `Set-GitHubRepository -Name $repoName ...`
7074
- 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
75+
4. For `organization` owners only, provisions extra repositories (`-2`, `-3` suffix) for
76+
test files that need companion repos (e.g., Secrets/Variables `SelectedRepository` tests)
7377

7478
`Set-GitHubRepository` is idempotent — if the repository already exists it updates it in place (issuing a
7579
PATCH), and if it does not exist it creates it. Because the same parameters are passed each time, the
@@ -82,15 +86,20 @@ branching logic.
8286
Runs once after all parallel test files complete. For each auth case (except `GITHUB_TOKEN`):
8387

8488
1. Connects using the auth case credentials
85-
2. Removes the run-scoped repositories by their known names
89+
2. Removes the per-test-file repositories by their deterministic names
90+
91+
## Per-test-file repositories
8692

87-
## Shared test repositories
93+
Each test file that depends on a GitHub repository uses its own repository, scoped by test name:
94+
`{TestName}-{OS}-{TokenType}-{RunID}`. This prevents cross-file resource collisions — test files
95+
run in parallel across OSes and in sequence across auth contexts, so one test file must never
96+
create resources on another test file's repository.
8897

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.
98+
Each test file must ensure its repository exists using `Set-GitHubRepository` in its per-context
99+
`BeforeAll`. `Set-GitHubRepository` is idempotent — if the repository already exists it updates it
100+
in place (PATCH), and if it does not exist it creates it. When the same parameters are passed each
101+
time the end-state is identical. This makes every test file self-sufficient regardless of whether
102+
the global `BeforeAll.ps1` already provisioned the repository.
94103

95104
**Do not** use `Get-GitHubRepository` with a throw guard — that breaks partial reruns.
96105
**Do not** use `New-GitHubRepository` — that fails if the repository already exists.
@@ -103,7 +112,7 @@ Skip provisioning for those owner types and set `$repo = $null` so that repo-dep
103112
be skipped cleanly:
104113

105114
```powershell
106-
$repoPrefix = "Test-$os-$TokenType"
115+
$repoPrefix = "$testName-$os-$TokenType"
107116
$repoName = "$repoPrefix-$id"
108117
if ($OwnerType -in ('repository', 'enterprise')) {
109118
$repo = $null
@@ -148,7 +157,7 @@ Describe 'TestName' {
148157
$context = Connect-GitHubApp @connectAppParams -PassThru -Default -Silent
149158
}
150159
151-
$repoPrefix = "Test-$os-$TokenType"
160+
$repoPrefix = "$testName-$os-$TokenType"
152161
$repoName = "$repoPrefix-$id"
153162
if ($OwnerType -in ('repository', 'enterprise')) {
154163
$repo = $null
@@ -164,9 +173,14 @@ Describe 'TestName' {
164173
'organization' { Set-GitHubRepository @repoParams -Organization $Owner }
165174
}
166175
}
176+
177+
# Clean up stale resources from prior runs (re-runs with same GITHUB_RUN_ID)
178+
# Example: remove leftover releases, environments, etc.
167179
}
168180
169181
AfterAll {
182+
# Remove all test-specific resources created during this context
183+
# (environments, releases, secrets, etc.)
170184
Get-GitHubContext -ListAvailable | Disconnect-GitHubAccount -Silent
171185
}
172186
@@ -179,20 +193,24 @@ Describe 'TestName' {
179193

180194
## Naming conventions
181195

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` |
196+
| Resource | Pattern | Example |
197+
|------------|----------------------------------------------|---------------------------------------|
198+
| Repo | `{TestName}-{OS}-{TokenType}-{RunID}` | `Releases-Linux-USER_FG_PAT-1234` |
199+
| Extra repo | `{TestName}-{OS}-{TokenType}-{RunID}-{N}` | `Secrets-Linux-ORG_FG_PAT-1234-2` |
200+
| Secret | `{TestName}_{OS}_{TokenType}_{RunID}` | `Secrets_Linux_PAT_1234` |
201+
| Variable | `{TestName}_{OS}_{TokenType}_{RunID}` | `Variables_Linux_PAT_1234` |
202+
| Team | `{TestName}_{OS}_{TokenType}_{RunID}_{Name}` | `Teams_Linux_APP_ORG_1234_Pull` |
203+
| Env | `{TestName}-{OS}-{TokenType}-{RunID}` | `Secrets-Linux-PAT-1234` |
190204

191205
## Key rules
192206

193207
- `$id` must always be `$env:GITHUB_RUN_ID` — never `[guid]::NewGuid()` or `Get-Random`.
194208
- Skip repo-dependent tests with `-Skip:($OwnerType -in ('repository', 'enterprise'))`.
195209
- 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.
210+
- Each test file uses its own repository: `{TestName}-{OS}-{TokenType}-{RunID}`. No two test files share a repository.
211+
- Each test file is self-contained and responsible for its own setup and teardown:
212+
- **BeforeAll (per-context):** Ensure the repository exists via `Set-GitHubRepository`. Clean up stale test-specific resources from prior runs (re-runs with the same `GITHUB_RUN_ID`).
213+
- **AfterAll (per-context):** Remove all test-specific resources created during the run (environments, releases, secrets, variables, etc.).
214+
- Any individual test file or auth context can be re-run independently. Tests must not assume clean initial state — they must be idempotent.
215+
- Tests run in parallel across OSes (Linux, macOS, Windows) and in sequence across auth contexts (7 cases). Resource names must include enough dimensions to prevent collisions across all parallel and sequential axes.
216+
- `Repositories.Tests.ps1` is independent — it creates and deletes its own repos because it tests CRUD.

0 commit comments

Comments
 (0)