"Run All Tests" hangs on *Running*

"Run All Tests" hangs on *Running*

avatar

# Bug Report — "Run All Tests" hangs on *Running*; `ObjectDisposedException` in `CollectTestResultsAsync`

## Summary
Running tests from the admin console (**Automation > Tests > Run All Tests**, or a single test at
`/admin/settings/.../tests/{id}`) never completes. The UI shows the run stuck on **"Running"**
indefinitely. The Pester tests themselves execute and produce results (rows appear in `TestCases`
/`TestSuites`), but the server throws an `ObjectDisposedException` while finalizing the run, so the
`TestRuns` row is never marked complete (`Status` stays `Running`, `EndTime = -infinity`).

## Environment
| | |
|---|---|
| PowerShell Universal | **2026.1.7** (win-x64) |
| Host | Windows, `Universal.Server.exe` run on the host (developer license) — not containerized |
| Database backend | PostgreSQL 16 (Docker container), `Plugins: ["PostgreSQL"]` |
| Pester | 5.7.1 (bundled, resolved in the integrated runspace) |
| Repo mode | file-based repository at `.\Repository`; Git sync = PushOnly / Manual |
| Test files | 3 × `*.tests.ps1` at repo root, registered in `TestFiles` (Id/Path) |

## Steps to reproduce
1. Place one or more `*.tests.ps1` (Pester 5) files in the repository and register them under
Automation > Tests (rows in the `TestFiles` table).
2. Open the admin console → **Automation > Tests**.
3. Click **Run All Tests** (also reproduces with a single test run).
4. Observe the run shows **Running** and never transitions to Passed/Failed.

## Expected
The run completes; `TestRuns.Status` becomes a terminal state and per-test results show in the UI.

## Actual
- `TestRuns` row: `Status = Running (1)`, `EndTime = -infinity`, `Total = 0` — never finalized.
- `TestCases` / `TestSuites` rows **are** written (the tests ran), confirming execution succeeds and
only the *result collection / finalization* step fails.
- The server logs an unhandled `ObjectDisposedException`.

## Error / stack trace
```
[ERR][PowerShellUniversal.Testing.TestService] Error executing test run.
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'IServiceProvider'.
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ThrowHelper.ThrowObjectDisposedException()
at PowerShellUniversal.DatabaseBase.GetTable[T]() in .../src/PowerShellUniversal/Services/DatabaseBase.cs:line 95
at PowerShellUniversal.Testing.TestService.CollectTestResultsAsync(TestRun testRun) in .../src/PowerShellUniversal.Testing/TestService.cs:line 119
at host.TestExecutionService.ExecuteTestAsync(TestRun testRun) in .../src/Host/TestExecutionService.cs:line 136
```

## Analysis
`CollectTestResultsAsync` runs after the Pester host process finishes and tries to persist results via
`DatabaseBase.GetTable[T]()`. By then the scoped `IServiceProvider` (and its `DbContext`) has already
been disposed, so the DB access throws. Because the exception is unhandled inside the test-execution
continuation, the `TestRuns` record is left in the `Running` state forever (the UI polls it and shows
the spinner indefinitely).

This looks like a **DI scope lifetime bug**: the result-collection continuation outlives the scope it
captured. It reproduces consistently on this build regardless of which/how many test files are run, and
is independent of the tests' content (they pass when run directly via `Invoke-Pester`).

## Impact
The built-in Test runner is effectively unusable — every run hangs and leaves an orphaned `Running`
record that must be cleared manually.

## Workarounds
1. **Run tests via a Script instead of the Test runner.** A `Invoke-Tests.ps1` script that calls
`Invoke-Pester` over the same `*.tests.ps1` files completes reliably (27/27 in ~5s) and reports a
pass/fail summary in the job output — it never touches `CollectTestResultsAsync`.
2. **Clear the stuck run** so the UI unsticks:
```sql
DELETE FROM "TestCases"; DELETE FROM "TestSuites"; DELETE FROM "TestRuns";
-- keep "TestFiles" (registration)
```
then restart the server.

## Suggested fix (for Devolutions)
Resolve/scope the `DbContext` (or capture an `IServiceScopeFactory` and create a fresh scope) inside
`CollectTestResultsAsync` rather than relying on the ambient scope captured when the run was started,
and wrap the finalization so any failure still writes a terminal `TestRuns.Status` (e.g. `Failed`)
instead of leaving it `Running`.

All Comments (1)

avatar

@alexrgreenwood We've got a fix coming for this in 2026.2.0. It was, in fact, a DI scope issue. Thanks for the report!

Adam Driscoll
PowerShell Expert and Developer at Devolutions