Capstone: The Complete Partner Feed Guardian Testing Strategy

PesterForge · July 2026 · 12 min read

πŸ“¦ Run it yourself β€” the complete companion package is organized under companion/, with source, test layers, Pester configurations, and gate wrappers.

A pile of passing tests is not automatically a production testing strategy.

A strategy connects each test to:

This capstone assembles the Partner Feed Guardian into one complete model. The goal is not to convince you to build this exact system. The goal is to give you a structure you can apply to your own PowerShell automation.

What you will learn

The production workflow

The Partner Feed Guardian receives a partner batch and moves it through:

Intake
  -> Inspection
  -> Eligibility decision
  -> Preflight
  -> Publication
  -> Acknowledgement
  -> Reconciliation
  -> Evidence

Failure can route the batch to:

Quarantine
Retry acknowledgement
Reconcile unknown publication
Suspend partner intake
Rollback module
Incident investigation

The system has enough real complexity to make testing meaningful without relying on the traditional ping, service, disk-space, backup, or website examples.

Repository structure

PartnerFeedGuardian/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ PartnerFeedGuardian.psd1
β”‚   └── PartnerFeedGuardian.psm1
β”œβ”€β”€ tests/
β”‚   β”œβ”€β”€ Unit/
β”‚   β”œβ”€β”€ Integration/
β”‚   β”œβ”€β”€ Preflight/
β”‚   β”œβ”€β”€ Deployment/
β”‚   β”œβ”€β”€ PostDeploy/
β”‚   β”œβ”€β”€ Production/
β”‚   β”œβ”€β”€ Drift/
β”‚   └── Recovery/
β”œβ”€β”€ config/
β”‚   β”œβ”€β”€ pester.unit.ps1
β”‚   β”œβ”€β”€ pester.integration.ps1
β”‚   β”œβ”€β”€ pester.preflight.ps1
β”‚   β”œβ”€β”€ pester.postdeploy.ps1
β”‚   β”œβ”€β”€ pester.production.ps1
β”‚   └── pester.recovery.ps1
β”œβ”€β”€ artifacts/
└── Invoke-Gate.ps1

Each test file must be independently executable. It imports what it needs and does not depend on another test file’s discovery-time state.

The test layers

1. Unit and fast-gate tests

Purpose: Protect decisions, contracts, orchestration rules, and known regressions.

Runs: Developer workstation and clean CI runner.

Touches: In-memory objects and mocks only.

Typical duration: Seconds.

Failure action: Block pull request.

Examples:

This is where the broad decision matrix belongs. Do not repeat every rule through the real provider.

2. Integration tests

Purpose: Prove the real adapter, identity, authentication, serialization, and controlled provider resource work together.

Runs: Dedicated integration environment under a production-like service identity.

Touches: Isolated test partner and disposable test data.

Failure action: Block release; route environment failures to the integration-platform owner.

Examples:

A mock is removed only for the boundary this suite intends to prove.

3. Preflight tests

Purpose: Decide whether one specific production operation may begin.

Runs: Immediately before processing or deployment, under the actual execution identity.

Touches: Production read-only by default.

Failure action: Stop before the first production change.

Examples:

A required preflight rejects failed, skipped, inconclusive, not-run, failed-block, and failed-container counts.

4. Deployment tests

Purpose: Prove the intended artifact landed and resolves correctly.

Runs: First on one canary runner, then each rollout wave.

Touches: Deployed files and module-loading environment; read-only after installation.

Failure action: Stop rollout and investigate or roll back the current wave.

Examples:

These tests import the deployed artifact, not the repository source.

5. Post-deployment tests

Purpose: Prove the deployed module completes controlled behavior under the runner identity.

Runs: After each rollout wave.

Touches: Canary or controlled workload.

Failure action: Stop rollout; preserve evidence; potentially roll back.

Examples:

Deployment and post-deployment failures have different diagnoses, so keep the suites separate.

6. Production synthetic tests

Purpose: Prove the real production path works end to end.

Runs: On demand after deployment and on a carefully chosen schedule.

Touches: Dedicated synthetic production identity, partner, destination, and data.

Failure action: Depends on stage; may open an urgent ticket, suspend only the synthetic partner, stop rollout, or preserve state for reconciliation.

Safeguards:

This suite is intentionally small. Production is not where the full decision matrix belongs.

7. Drift tests

Purpose: Compare approved production configuration with actual production configuration.

Runs: After deployment and on a risk-based schedule.

Touches: Production read-only.

Failure action: Varies by difference type.

Examples:

Pester returns structured differences. A separate operational system retains history and routes alerts.

8. Recovery tests

Purpose: Prove rollback prerequisites, restored artifact, state reconciliation, and incident regression.

Runs: Before release in a controlled exercise and during an actual recovery.

Touches: Disposable or canary state; production only under the incident plan.

Failure action: Block release when recovery evidence is required; escalate during incident recovery.

Examples:

The production test-decision record

Every important suite or test family should answer:

Field Required answer
Risk What production failure is being addressed?
Claim What exact statement does the test evaluate?
Layer Unit, integration, preflight, deployment, production, drift, or recovery
Location Where does it run?
Identity Who runs it?
Timing When does it run?
Access What may it read or change?
Evidence What result and metadata are retained?
Failure action What does failure block or trigger?
Owner Who investigates?
Limitation What does the test not prove?
Recovery What if the test itself leaves state behind?

A sample:

name: Synthetic partner publication
risk: Production intake path is deployed but cannot complete a real transaction
claim: A controlled synthetic batch publishes, acknowledges, and reconciles through production
layer: production-synthetic
location: production
authority: svc-pfg-synthetic
timing: after canary deployment and daily
access: state-changing, synthetic partner only
failureAction: stop rollout when post-deployment; otherwise open urgent partner-platform ticket
owner: Partner Platform
limitations:
  - Does not prove every real partner works
  - Does not test production load
  - Does not validate every schema variation
recovery: reconcile by correlation id; stale synthetic cleanup after 24 hours

Gate orchestration

The companion wrapper separates evaluation from orchestration:

[CmdletBinding()]
param(
    [Parameter(Mandatory)]
    [ValidateSet(
        'unit',
        'integration',
        'preflight',
        'postdeploy',
        'production',
        'recovery'
    )]
    [string]$Name
)

Import-Module "$PSScriptRoot/src/PartnerFeedGuardian.psd1" -Force
$config = & "$PSScriptRoot/config/pester.$Name.ps1"
$result = Invoke-Pester -Configuration $config
Assert-PfgGateResult -Result $result -GateName $Name

The deployment pipeline can call:

./Invoke-Gate.ps1 -Name unit
./Invoke-Gate.ps1 -Name integration
./Invoke-Gate.ps1 -Name preflight

After a canary deployment:

./Invoke-Gate.ps1 -Name postdeploy

Pester does not deploy the next wave from inside the test. The pipeline continues only after the gate returns successfully.

Strict gate policy

The helper rejects incomplete evidence:

$problems = [ordered]@{
    FailedTests       = $Result.FailedCount
    SkippedTests      = $Result.SkippedCount
    InconclusiveTests = $Result.InconclusiveCount
    NotRunTests       = $Result.NotRunCount
    FailedBlocks      = $Result.FailedBlocksCount
    FailedContainers  = $Result.FailedContainersCount
}

if ($problems.Values | Where-Object { $_ -gt 0 }) {
    throw "$GateName did not complete successfully."
}

If a suite contains optional tests, put them in an advisory profile. Do not make a required gate ambiguous.

Tagging standard

Use tags for distinct dimensions:

Layer.Unit
Layer.Integration
Gate.PullRequest
Gate.Preflight
Gate.Deployment
Gate.PostDeploy
Gate.Production
Gate.Drift
Gate.Recovery
Environment.Lab
Environment.Canary
Environment.Production
Risk.ReadOnly
Risk.StateChanging
Regression
INC-2417

A test can carry more than one:

Describe 'Synthetic partner batch' -Tag @(
    'Gate.Production'
    'Environment.Production'
    'Risk.StateChanging'
) {
    # ...
}

Avoid tags that mix meaning, such as ProductionIntegrationCriticalFast.

Version policy

The capstone pins:

Configurations set:

$config.Should.DisableV5 = $true

This keeps new series code consistently on the Pester 6 assertion family.

Do not enable experimental parallel execution for stateful or production-facing suites by default.

Mock policy

Use mocks to:

Do not use mocks to claim:

For dangerous dependencies, use an explicit default mock that throws when an unexpected call occurs.

ShouldProcess policy

Every state-changing public command should:

-WhatIf is a safety preview. It is not production-path evidence.

State and retry policy

The operation state must distinguish at least:

NotStarted
Publishing
PublishedPendingAcknowledgement
Completed
Failed

A timeout during Publishing requires reconciliation before retry.

A retry from PublishedPendingAcknowledgement must not publish again.

Every state-changing request uses a correlation ID and durable evidence.

Cleanup policy

Test-created state must have:

AfterAll is useful but not sufficient.

Monitoring boundary

Pester evaluates:

Other systems manage:

Do not let the test runner quietly become an unmaintainable monitoring platform.

Avoid duplicated evidence

The same behavior should not be tested at every layer merely to increase the number of tests.

Example: unsupported schema rejection.

Each layer proves the part that only that layer can prove.

Readiness checklist

Before calling the strategy production-ready, confirm:

Code design

Fast gate

Integration

Preflight

Deployment

Production tests

Operations

Recovery

What the complete strategy proves

It provides layered evidence that the code makes the intended decisions, the real dependencies work under controlled conditions, the target is ready, the deployed artifact is correct, selected production paths operate, configuration remains approved, and recovery procedures have been exercised.

What it does not prove

It cannot prove production will never fail.

It cannot model every provider behavior, data shape, timing condition, human decision, or future change.

A good test strategy reduces unknowns and makes failures safer to detect, stop, diagnose, and recover from. It does not grant immortality to the system.

Try it yourself

Take one production PowerShell project and create the directory structure from this capstone.

Do not fill every folder immediately.

Start with one test family in each necessary layer:

  1. One high-risk decision test
  2. One real-dependency integration test
  3. One preflight gate
  4. One deployment-verification test
  5. One controlled post-deployment workload
  6. One read-only drift check
  7. One practiced recovery test

For every test, complete the decision record before scheduling or gating it.

Common mistakes

Measuring maturity by test count. Measure the important risks and decisions covered.

Repeating the full decision matrix in every layer. Each layer should prove what only it can prove.

Using a successful Pester summary as the entire gate policy. Inspect incomplete and container-level failures.

Leaving owners and failure actions outside the test strategy. Unowned evidence becomes noise.

Treating production tests as the final layer only. Most production protection happens before production.

Final recap

The central lesson of this series is not a new assertion command or a clever mock.

It is this:

A production-facing test should prove one necessary claim, at the safest useful layer, and control a clearly defined decision.

Ask every time:

That is how Pester moves from β€œtests around a script” to a system that genuinely helps protect production.

← All posts