Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 198 additions & 0 deletions .github/instructions/tests.instructions.md
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

Comment thread
MariusStorhaug marked this conversation as resolved.
## 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.
Comment thread
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.

Comment thread
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.
12 changes: 9 additions & 3 deletions tests/Actions.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,15 @@ Describe 'Actions' {
if ($OwnerType -in ('repository', 'enterprise')) {
$repo = $null
} else {
$repo = Get-GitHubRepository -Owner $Owner -Name $repoName
if (-not $repo) {
throw "Shared test repository '$repoName' was not found for owner '$Owner' (OwnerType: '$OwnerType'). Ensure the repository was provisioned and the repository name is correct."
$repoParams = @{
Name = $repoName
AddReadme = $true
License = 'mit'
Gitignore = 'VisualStudio'
}
$repo = switch ($OwnerType) {
'user' { Set-GitHubRepository @repoParams }
'organization' { Set-GitHubRepository @repoParams -Organization $Owner }
Comment thread
MariusStorhaug marked this conversation as resolved.
}
Comment thread
MariusStorhaug marked this conversation as resolved.
Write-Host ($repo | Select-Object * | Out-String)
}
Expand Down
15 changes: 5 additions & 10 deletions tests/BeforeAll.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -67,28 +67,23 @@ LogGroup 'BeforeAll - Global Test Setup' {
}
}

# Create the primary shared repository (with readme, license, gitignore for release tests).
# Provision the primary shared repository.
$repoParams = @{
Name = $repoName
Comment thread
MariusStorhaug marked this conversation as resolved.
AddReadme = $true
License = 'mit'
Gitignore = 'VisualStudio'
}
switch ($OwnerType) {
'user' {
New-GitHubRepository @repoParams
}
'organization' {
New-GitHubRepository @repoParams -Organization $Owner
}
'user' { Set-GitHubRepository @repoParams }
'organization' { Set-GitHubRepository @repoParams -Organization $Owner }
}
Comment thread
MariusStorhaug marked this conversation as resolved.

# Create extra repositories needed by Secrets/Variables SelectedRepository tests.
# Provision extra repositories needed by Secrets/Variables SelectedRepository tests.
# Only organization owners need them — those tests are skipped for user owners.
if ($OwnerType -eq 'organization') {
foreach ($suffix in 2, 3) {
$extraName = "$repoName-$suffix"
New-GitHubRepository -Organization $Owner -Name $extraName
Set-GitHubRepository -Organization $Owner -Name "$repoName-$suffix"
}
}
}
Expand Down
16 changes: 13 additions & 3 deletions tests/Environments.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,19 @@ Describe 'Environments' {
$environmentName = "$testName-$os-$TokenType-$id"

LogGroup "Using Repository - [$repoName]" {
$repo = Get-GitHubRepository -Owner $Owner -Name $repoName
if (($OwnerType -notin ('repository', 'enterprise')) -and (-not $repo)) {
throw "Shared test repository '$repoName' was not found for owner '$Owner'. Ensure the repository was created before running the environment tests."
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 }
Comment thread
MariusStorhaug marked this conversation as resolved.
Comment thread
MariusStorhaug marked this conversation as resolved.
}
}
Write-Host ($repo | Select-Object * | Out-String)
}
Expand Down
Loading
Loading