Testing applications

Application functions should accept a SessionConfig and create sessions using use_shell_session. Tests can then inject the official mock without introducing a separate application execution path.

import asyncio

from shell_next import (
    Emit,
    MockExpectation,
    MockScenario,
    MockShellSession,
    ProcessCommand,
    SessionConfig,
    use_shell_session,
)


async def read_status(config: SessionConfig) -> bytes:
    async with use_shell_session(config) as shell:
        result = await shell.run(ProcessCommand("git", ("status", "--short")), check=True)
        return result.stdout.tail


async def test_status() -> None:
    command = ProcessCommand("git", ("status", "--short"))
    scenario = MockScenario([MockExpectation(command, (Emit(b" M README.md\n"),))])
    config = SessionConfig(_session_cls=MockShellSession.configured(scenario))
    assert await read_status(config) == b" M README.md\n"
    scenario.assert_called(command)
    scenario.assert_consumed()


asyncio.run(test_status())

Declare the interaction

Scenario step Meaning
Emit(data, stream="stdout") Emit raw output to capture and observers.
Receive(data) Expect bytes across transport chunk boundaries.
Receive(None) Expect business stdin to close.
Advance(seconds) Advance virtual command time without sleeping.
Failure(kind) Simulate input, output, startup, or session failure.

Use MockExpectation.cwd and env for simulated script state changes. The mock does not parse Bash, PowerShell, or cmd syntax. Authentication fields can model private password prompts, cached credentials, or rejected authentication.

Keep tests strict

Unexpected commands raise MockUnexpectedCommandError; mismatched or extra input raises MockUnexpectedInputError. Unused required command expectations raise MockExpectationNotConsumedError on normal context exit. A body exception remains the primary exception.

The mock uses the shared command lifecycle, queue, capture, and input frontend. Ordinary scenarios create no subprocesses, pipes, network connections, capture files, or real sleeps, and never change the parent cwd or environment.

scenario.calls records ordered observable operations. Mark sensitive business input with secret=True to redact it; privilege authentication is always redacted. Use real backend tests when verifying OS-specific syntax or filesystem behavior.