> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vers.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Parallel scenario testing

> Navigate to a stateful point in a real webapp, branch, then run N divergent test paths in parallel against isolated copies of the live state.

The expensive part of end-to-end testing isn't the test — it's getting to the point where the test can run. You sign in, navigate four pages deep, seed some data, and then you're ready to exercise one assertion. Do that serially for every scenario and your suite spends its life re-setting-up.

Vers lets you set up **once**, branch at the decision point, and run every scenario in parallel against its own live copy of that state.

## What you'll build

* A small Flask shopping-cart app running inside a Vers VM
* A **"pre-checkout" state** with a user signed in and items in the cart
* Three branches exercising different checkout scenarios (valid card / expired card / empty cart edge case) — all in parallel, against live isolated state
* **Time:** \~20 minutes

## Prerequisites

* Vers CLI installed and authenticated
* Basic familiarity with HTTP and the command line

## The idea

Traditionally, each test re-creates its setup. Scenario A does login → browse → add-to-cart → test A. Scenario B does login → browse → add-to-cart → test B. The shared prefix gets paid over and over.

With Vers you do login → browse → add-to-cart **once**, branch the running VM at that exact moment (memory, sockets, in-flight session — all of it), then run scenario A on one branch, scenario B on another, scenario C on a third. Every branch is live and independent. The setup prefix is paid once and inherited for free.

## Step 1: Initialize the project

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
mkdir parallel-scenario-testing && cd parallel-scenario-testing
vers init
```

Edit `vers.toml` — the Flask app is tiny, so default resources are fine:

```toml theme={"theme":{"light":"min-light","dark":"min-dark"}}
[machine]
mem_size_mib = 1024
vcpu_count = 1
fs_size_mib = 2048

[rootfs]
name = "default"

[kernel]
name = "default.bin"
```

## Step 2: Boot the VM and install the app

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
vers run --vm-alias root
vers connect
```

Inside the VM:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
apt-get update -qq && apt-get install -y -qq python3 python3-flask curl jq
mkdir -p /app && cd /app
```

Write a minimal shopping-cart service (`/app/server.py`) — two endpoints, in-memory state:

```python theme={"theme":{"light":"min-light","dark":"min-dark"}}
cat > /app/server.py << 'PYEOF'
from flask import Flask, request, jsonify
app = Flask(__name__)

state = {"user": None, "cart": [], "inventory": {"book": 12.00, "mug": 8.00}}

@app.post("/login")
def login():
    state["user"] = request.json["user"]
    return {"ok": True, "user": state["user"]}

@app.post("/cart/add")
def add():
    item = request.json["item"]
    if item not in state["inventory"]:
        return {"ok": False, "error": "unknown_item"}, 400
    state["cart"].append(item)
    return {"ok": True, "cart": state["cart"]}

@app.post("/checkout")
def checkout():
    if not state["user"]:
        return {"ok": False, "error": "not_logged_in"}, 401
    if not state["cart"]:
        return {"ok": False, "error": "empty_cart"}, 400
    card = request.json.get("card", {})
    if card.get("exp", "") < "2026-01":
        return {"ok": False, "error": "card_expired"}, 402
    total = sum(state["inventory"][i] for i in state["cart"])
    return {"ok": True, "total": total, "items": state["cart"]}

app.run(host="0.0.0.0", port=8080)
PYEOF

# Run it in the background
nohup python3 /app/server.py > /app/server.log 2>&1 &
sleep 1
curl -s http://localhost:8080/login -X POST -H 'content-type: application/json' -d '{}' && echo
```

The last line confirms the server boots. You'll see `{"ok":true,"user":null}`.

## Step 3: Navigate to the pre-checkout state

Still inside the VM, drive the app into a meaningful state — a signed-in user with two items in the cart:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
curl -s -X POST http://localhost:8080/login \
  -H 'content-type: application/json' \
  -d '{"user": "alice"}' | jq

curl -s -X POST http://localhost:8080/cart/add \
  -H 'content-type: application/json' \
  -d '{"item": "book"}' | jq

curl -s -X POST http://localhost:8080/cart/add \
  -H 'content-type: application/json' \
  -d '{"item": "mug"}' | jq
```

You're now at the state every checkout scenario wants to start from: alice logged in, cart has `["book", "mug"]`, server process is running and holding that state in memory.

Exit back to your host shell:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
exit
```

## Step 4: Branch the live state

Branch the running VM. Each child inherits the Flask process, the in-memory cart, and alice's session — live.

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
vers branch --alias happy-path
vers branch --alias expired-card
vers branch --alias double-checkout
```

Three forks from the same moment. In a traditional setup this would have been "run the login/add-to-cart prefix three more times." Here it's one `branch` call each.

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
vers status
```

You'll see four VMs running — the root plus three branches. All are at *exactly* the state you left the cart in.

## Step 5: Run divergent scenarios in parallel

### Scenario A — happy path (valid card)

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
vers execute happy-path "curl -s -X POST http://localhost:8080/checkout \
  -H 'content-type: application/json' \
  -d '{\"card\": {\"exp\": \"2027-05\"}}' | jq"
```

Expected: `{"ok": true, "total": 20.0, "items": ["book", "mug"]}`

### Scenario B — expired card

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
vers execute expired-card "curl -s -X POST http://localhost:8080/checkout \
  -H 'content-type: application/json' \
  -d '{\"card\": {\"exp\": \"2024-01\"}}' | jq"
```

Expected: `{"ok": false, "error": "card_expired"}`

### Scenario C — double checkout edge case

This one exercises a more interesting property of branching: the scenario mutates state and asserts a second call fails cleanly.

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
vers execute double-checkout "curl -s -X POST http://localhost:8080/checkout \
  -H 'content-type: application/json' \
  -d '{\"card\": {\"exp\": \"2027-05\"}}' && \
  curl -s -X POST http://localhost:8080/checkout \
    -H 'content-type: application/json' \
    -d '{\"card\": {\"exp\": \"2027-05\"}}' | jq"
```

Because this branch has its own cart state, the mutation doesn't touch the other two branches. Run them again and they'll still see the original cart.

### True parallelism across terminals

For visible parallelism, open three terminals and run the three scenarios simultaneously. They'll finish in roughly the time of the slowest single scenario — not the sum.

## Step 6: Verify isolation

Re-run scenario A after scenario C has mutated state in its branch. It still succeeds because each branch has its own memory:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
vers execute happy-path "curl -s -X POST http://localhost:8080/checkout \
  -H 'content-type: application/json' \
  -d '{\"card\": {\"exp\": \"2027-05\"}}' | jq"
```

Still `{"ok": true, ...}`. This is the key property: branches aren't shared mutable state. They're siblings.

## Step 7: Clean up

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
vers checkout root
vers kill root -r
```

The `-r` tears down the root and all descendants.

## The pattern

**Set up expensive state once, branch at the decision point, run every scenario in parallel.** This works anywhere the setup cost is asymmetric with the test cost:

* End-to-end tests that require login + navigation + seeding
* Database migrations where you want to try three approaches against a populated dataset
* Fuzzing — branch a running server, throw divergent inputs at each branch
* Reproducing stateful bugs — branch at the suspicious moment, explore from there without losing the setup

Once you stop paying the setup cost per scenario, the shape of your test suite changes. You start writing scenarios you wouldn't have written before because the cost was too high.

## What's next

<CardGroup cols={2}>
  <Card title="Database state testing" icon="database" href="/tutorials/database-state-testing">
    Same pattern, applied to testing parallel schema migrations against a seeded database.
  </Card>

  <Card title="Agent swarms" icon="robot" href="/tutorials/agent-swarms">
    Branch the same golden state into N parallel agents.
  </Card>

  <Card title="vers branch" icon="code-branch" href="/cli-reference/branch">
    Every flag on the branch primitive.
  </Card>

  <Card title="Architecture" icon="sitemap" href="/architecture">
    How branches inherit live memory without paying for a full copy.
  </Card>
</CardGroup>
