# Local E2E smoke tests

> Guide: Run deterministic Playwright and ignored Rust smoke tests against the local-only stack and shared seed fixtures.

- Repository: macro-inc/macro
- GitHub: https://github.com/macro-inc/macro
- Human docs: https://grok-wiki.com/public/docs/macro-inc-macro-bb988e1a448e
- Complete Markdown: https://grok-wiki.com/public/docs/macro-inc-macro-bb988e1a448e/llms-full.txt

## Source Files

- `RUNNING_LOCALLY.md`
- `justfile`
- `docker-compose.local-e2e.yml`
- `rust/cloud-storage/seed_cli/README.md`
- `js/app/tests/e2e/fixtures/local-e2e-seed.ts`
- `rust/cloud-storage/integration_tests/local_e2e/README.md`
- `rust/cloud-storage/local_e2e_test_support/README.md`

---

---
title: "Local E2E smoke tests"
description: "Guide: Run deterministic Playwright and ignored Rust smoke tests against the local-only stack and shared seed fixtures."
---

`just local-e2e`, `just local-e2e-rust`, and `just local-e2e-all` start the Macro local stack with `docker-compose.local-e2e.yml` overrides, reset and seed deterministic fixtures from `rust/cloud-storage/seed_cli/seed`, then run Playwright or ignored Rust integration smoke suites against localhost services.

## Prerequisites

Run the normal local setup first:

```bash
just setup
```

The harness expects the same local toolchain used by the repository: Docker, Bun, Rust/Cargo, AWS CLI, SQLx tooling, Pulumi, and a decrypted `.env`. Local Docker resources are intentionally frozen to the `macro` Compose project, so do not run two local stacks from different checkouts at the same time.

<Warning>
The local E2E seed path is destructive for its deterministic fixture ranges. The seed command is guarded by `LOCAL_E2E_SEED=true` and rejects database URLs outside the local Docker database shape `postgres://user:...@(localhost|127.0.0.1|postgres):5432/macrodb`.
</Warning>

## Command reference

| Command | Runs | Notes |
| --- | --- | --- |
| `just local-e2e` | Playwright local smoke tests | Starts LocalStack, starts the local service subset, seeds fixtures, then runs `LOCAL_E2E=true bunx playwright test` from `js/app`. |
| `just local-e2e-ui` | Playwright UI mode | Same setup and seed pass, then opens Playwright UI mode. |
| `just local-e2e-rust` | Ignored Rust integration tests | Runs `SQLX_OFFLINE=true cargo test -p local_e2e_integration_tests -- --ignored --nocapture`. |
| `just local-e2e-all` | Rust tests, then Playwright | Starts and seeds once, runs the ignored Rust suite, then runs Playwright. |
| `just local-e2e-seed` | Seed only | Starts local Postgres/Redis, drops and recreates Macro DB state, initializes DBs, then runs the local E2E seed scenario. |

Forward Playwright file filters or flags through the `just` target:

```bash
just local-e2e tests/e2e/local-smoke.spec.ts
just local-e2e --project=local-chromium
```

For Rust filters, pass the test name after the target:

```bash
just local-e2e-rust channel_message_posts_to_http_and_delivers_to_websocket
```

## Runtime shape

```text
just local-e2e*
  ├─ setup_localstack
  │  ├─ LocalStack on localhost:4566
  │  ├─ SQS queues
  │  ├─ DynamoDB tables
  │  └─ S3 buckets + doc-storage notification wiring
  ├─ COMPOSE_FILE=docker-compose.yml:docker-compose.local-e2e.yml just run_local -d --wait ...
  │  ├─ Postgres localhost:5432
  │  ├─ Redis localhost:6379
  │  └─ Macro service subset on local ports
  ├─ just local-e2e-seed
  └─ Playwright and/or ignored Rust tests
```

The compose override pins application services to local Postgres, Redis, and LocalStack instead of shared dev assets. It also supplies deterministic bucket, queue, table, AWS credential, and region values for the local smoke stack.

The startup service subset is defined in the root `justfile` as:

```text
authentication-service
connection_gateway
contacts_service
document_storage_service
email_service
notification_service
static_file_service
static_file_cdn
sync_service
websocket_service
```

Common local ports used by the tests include:

| Surface | Default local endpoint |
| --- | --- |
| Postgres | `postgres://user:password@localhost:5432/macrodb` |
| Redis | `redis://localhost:6379` |
| LocalStack | `http://localhost:4566` |
| Authentication service | `http://localhost:8080` |
| Connection gateway WebSocket | `ws://localhost:8082/` |
| Document storage service | `http://localhost:8086` |
| Notification service | `http://localhost:8089` |
| Frontend dev server | `http://localhost:${PORT:-3000}/app` |

## Seed fixtures

The shared seed contract lives under `rust/cloud-storage/seed_cli/seed`.

| File | Purpose |
| --- | --- |
| `local_e2e/manifest.json` | Stable smoke aliases: primary user email, project roadmap document, general channel, and canonical welcome message. |
| `local_e2e/users.json` | Local users including `e2e@macro.local`, `bob@example.com`, `charlie@example.com`, `dana@example.com`, and `eve@example.com`. |
| `local_e2e/reset.sql` | Deletes deterministic local E2E channels, channel access rows, mentions, activity, and documents before reseeding. |
| `documents/documents.json` | Seeded document metadata. |
| `channels.json` | Seeded channels. |
| `channel_messages.json` | Seeded channel messages. |

The seed CLI scenario resets fixture ranges, inserts users and verification rows, then seeds documents, channels, and channel messages. A successful seed prints:

```text
Local e2e smoke seed data ready for macro|e2e@macro.local
```

## Playwright local smoke suite

When `LOCAL_E2E=true`, `js/app/playwright.config.ts` switches to a local-only browser project named `local-chromium`. The Playwright web server command sets:

```bash
PORT=${PORT:-3000}
VITE_LOCAL_SERVERS=ALL
VITE_ENABLE_BEARER_TOKEN_AUTH=true
LOCAL_JWT=<generated token>
bun run dev
```

If `LOCAL_JWT` is not already exported, Playwright runs `js/app/scripts/generate-local-e2e-token.ts`. That script calls the Rust `local_e2e_test_support` binary `generate_local_e2e_token`, which loads the shared seed user and signs a Macro API token using `.env` or process environment values. The default local token lifetime is eight hours.

The local Playwright specs skip unless `LOCAL_E2E=true`:

| Spec | Main verification |
| --- | --- |
| `local-smoke.spec.ts` | Documents list shows the seeded Project Roadmap; channels list and channel page show the seeded general channel and welcome message. |
| `local-sidebar.spec.ts` | Sidebar routes open expected list views and show seeded entities where applicable. |
| `local-channel-actions.spec.ts` | Sends a message, reacts with `👍`, and opens the reply composer in the seeded channel. |

Tests import `localE2ESeed` from `js/app/tests/e2e/fixtures/local-e2e-seed.ts`. That fixture loader reads the Rust seed JSON files directly and exposes raw arrays, lookup maps, and `smoke` aliases.

## Rust local E2E suite

The Rust suite lives in `rust/cloud-storage/integration_tests/local_e2e` and is intentionally marked `#[ignore]` so normal workspace test runs do not require Docker services. Run it through:

```bash
just local-e2e-rust
```

The crate uses `local_e2e_test_support` to load the same seed files, generate local JWTs, and resolve local service endpoints. The support crate rejects non-local service URLs for mutating tests.

Supported local overrides include:

| Environment variable | Default | Constraint |
| --- | --- | --- |
| `LOCAL_E2E_DOCUMENT_STORAGE_URL` | `http://localhost:8086` | Must use `http` or `https` and a localhost host. |
| `LOCAL_E2E_CONNECTION_GATEWAY_WS_URL` | `ws://localhost:8082/` | Must use `ws` or `wss` and a localhost host. |
| `LOCAL_E2E_NOTIFICATION_URL` | `http://localhost:8089` | Must use `http` or `https` and a localhost host. |
| `LOCAL_E2E_DATABASE_URL` | `postgres://user:password@localhost:5432/macrodb` | Used by Rust verification queries. |
| `LOCAL_E2E_CHANNELS_BASE_URL` | `${document_storage_url}/channels` | Channel mutation API under test. |
| `LOCAL_E2E_CHANNELS_READ_BASE_URL` | `${document_storage_url}/channels` | Channel read API under test. |

The ignored Rust tests cover channel mutations, persistence checks, notifications/contacts side effects where workers are available, and connection gateway WebSocket delivery.

## Running Playwright directly

Prefer the repository-level harness. If you need a manual loop, start and seed the stack first:

```bash
AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test AWS_DEFAULT_REGION=us-east-1 just setup_localstack
COMPOSE_FILE=docker-compose.yml:docker-compose.local-e2e.yml just run_local -d --wait authentication-service connection_gateway contacts_service document_storage_service email_service notification_service static_file_service static_file_cdn sync_service websocket_service
just local-e2e-seed
cd js/app
LOCAL_E2E=true bunx playwright test
```

You can bypass token generation by exporting `LOCAL_JWT`, but the generated token path is the default and keeps Playwright aligned with the Rust helper.

## Troubleshooting

### `Failed to generate LOCAL_JWT for LOCAL_E2E Playwright`

Ensure `.env` exists from `just get_environment` or `just setup`, then rerun the repo-level harness:

```bash
just local-e2e
```

If running Playwright directly, seed first with `just local-e2e-seed`. As a temporary bypass, export `LOCAL_JWT`.

### Seed refuses to run

Use `just local-e2e-seed` rather than invoking the seed CLI manually. The recipe sets `LOCAL_E2E_SEED=true`, local AWS values, and the expected local database URL.

### Tests refuse a service URL

Rust helpers reject non-local service URLs. Check `LOCAL_E2E_DOCUMENT_STORAGE_URL`, `LOCAL_E2E_CONNECTION_GATEWAY_WS_URL`, and `LOCAL_E2E_NOTIFICATION_URL`; they must point at localhost or `127.0.0.1`.

### Local stack conflicts across checkouts

The Compose project name, networks, and volumes are shared as `macro`. Stop the current stack before starting another checkout:

```bash
just stop-local
just stop-databases
```

## Adding local E2E coverage

1. Put stable shared fixture rows in `rust/cloud-storage/seed_cli/seed`.
2. Add only aliases to `local_e2e/manifest.json`; keep the source data in the fixture files.
3. Use `localE2ESeed` in Playwright and `LocalE2eSeed` in Rust instead of duplicating IDs.
4. Keep Playwright specs gated by `LOCAL_E2E=true`.
5. Keep Rust integration tests marked `#[ignore]`.
6. Use generated unique text or IDs for mutating assertions so repeated smoke runs can coexist with the deterministic base seed.

## Related pages

- `RUNNING_LOCALLY.md`
- `rust/cloud-storage/seed_cli/README.md`
- `rust/cloud-storage/integration_tests/local_e2e/README.md`
- `rust/cloud-storage/local_e2e_test_support/README.md`
