Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .cargo/audit.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[advisories]
ignore = [
# extism transitively pins wasmtime 37.x today.
"RUSTSEC-2026-0006",
# transitive via wasmtime profiling stack
"RUSTSEC-2025-0057",
# transitive via async-nats / rustls-native-certs
"RUSTSEC-2025-0134",
# transitive in current leptos dependency graph
"RUSTSEC-2024-0436",
]

7 changes: 6 additions & 1 deletion .github/workflows/claude-code-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ on:
# - "src/**/*.js"
# - "src/**/*.jsx"

concurrency:
group: claude-review-${{ github.repository }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
claude-review:
# Optional: Filter by PR author
Expand All @@ -18,7 +22,9 @@ jobs:
# github.event.pull_request.user.login == 'new-developer' ||
# github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'

if: ${{ !github.event.pull_request.draft }}
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read
pull-requests: read
Expand All @@ -41,4 +47,3 @@ jobs:
prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}'
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
# or https://code.claude.com/docs/en/cli-reference for available options

6 changes: 5 additions & 1 deletion .github/workflows/claude.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ on:
pull_request_review:
types: [submitted]

concurrency:
group: claude-code-${{ github.repository }}-${{ github.event.pull_request.number || github.event.issue.number || github.ref }}
cancel-in-progress: true

jobs:
claude:
if: |
Expand All @@ -18,6 +22,7 @@ jobs:
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read
pull-requests: read
Expand Down Expand Up @@ -47,4 +52,3 @@ jobs:
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
# or https://code.claude.com/docs/en/cli-reference for available options
# claude_args: '--allowed-tools Bash(gh pr:*)'

113 changes: 113 additions & 0 deletions .github/workflows/test-suite.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
name: Test Suite

on:
pull_request:
push:
branches:
- main

jobs:
rust-tests:
name: Rust + Coverage
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Rust
uses: dtolnay/rust-toolchain@stable

- name: Install Rust test tooling
uses: taiki-e/install-action@v2
with:
tool: cargo-nextest,cargo-llvm-cov

- name: Install llvm-tools
run: rustup component add llvm-tools-preview

- name: Run backend tests and coverage gate
run: ./scripts/test-rust.sh

security:
name: Dependency Security
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Rust
uses: dtolnay/rust-toolchain@stable

- name: Install security tooling
uses: taiki-e/install-action@v2
with:
tool: cargo-audit,cargo-deny

- name: Run cargo-audit
run: cargo audit --deny warnings

- name: Run cargo-deny
run: cargo deny check advisories bans sources

wasm-ui-tests:
name: WASM UI Tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Rust (wasm target)
uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown

- name: Install wasm-pack
uses: taiki-e/install-action@v2
with:
tool: wasm-pack

- name: Run WASM UI tests
run: ./scripts/test-wasm-ui.sh

e2e-tests:
name: Playwright E2E
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm

- name: Install JavaScript dependencies
run: npm install

- name: Install Playwright browser
run: npx playwright install --with-deps chromium

- name: Set up Rust (wasm target)
uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown

- name: Install trunk
uses: taiki-e/install-action@v2
with:
tool: trunk

- name: Run Playwright E2E tests
env:
EXOCLAW_E2E_PORT: "7210"
EXOCLAW_E2E_TOKEN: "e2e-test-token"
run: npm run test:e2e

- name: Upload Playwright artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-artifacts
path: output/playwright
if-no-files-found: ignore
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ Thumbs.db
*.tmp
*.swp
.vscode/
node_modules/
output/playwright/
64 changes: 64 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,19 @@ cargo run -- plugin load ./plugins/telegram.wasm

The gateway binds to `127.0.0.1:7200` by default. When binding to a non-loopback address, an auth token is required (via `--token` or `EXOCLAW_TOKEN` env var).

## Testing

See `TESTING.md` for the full red/green workflow and CI layout.

Quick commands:

```bash
./scripts/test-rust.sh # backend + coverage gate
./scripts/test-wasm-ui.sh # wasm-bindgen-test via wasm-pack
./scripts/test-e2e.sh # Playwright browser flows
./scripts/test-all.sh # full stack
```

## Project status

**Early development. Not production ready.**
Expand Down
62 changes: 62 additions & 0 deletions TESTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Testing and TDD

This repository uses a layered test stack:

- Backend/unit/integration: `cargo-nextest`
- Coverage gate: `cargo-llvm-cov`
- UI Rust-to-WASM tests: `wasm-pack` + `wasm-bindgen-test`
- Browser E2E: Playwright
- Dependency security: `cargo-audit` + `cargo-deny`

## Prerequisites

```bash
rustup target add wasm32-unknown-unknown
cargo install cargo-nextest cargo-llvm-cov wasm-pack trunk
npm install
npx playwright install chromium
```

## Test Commands

```bash
# Backend tests + line coverage gate (default: 70%)
./scripts/test-rust.sh

# WASM UI tests (runs ui/tests/*.rs in Node)
./scripts/test-wasm-ui.sh

# Browser E2E tests
./scripts/test-e2e.sh

# Full suite
./scripts/test-all.sh
```

Set a stricter coverage gate locally:

```bash
COVERAGE_MIN_LINES=75 ./scripts/test-rust.sh
```

## Red/Green Workflow

1. Write a failing test in the right layer first:
- backend behavior: `tests/*.rs` or module `#[cfg(test)]`
- UI parser/transform logic: `ui/tests/*.rs` with `#[wasm_bindgen_test]`
- full user flow: `e2e/*.spec.ts`
2. Run the smallest relevant command.
3. Implement the behavior.
4. Re-run targeted tests.
5. Run `./scripts/test-all.sh` before merging.

## CI

GitHub Actions workflow: `.github/workflows/test-suite.yml`

Jobs:

- `rust-tests`: nextest + coverage gate
- `security`: cargo-audit + cargo-deny
- `wasm-ui-tests`: wasm-pack tests
- `e2e-tests`: Playwright E2E against the real gateway + embedded UI
23 changes: 23 additions & 0 deletions deny.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[graph]
all-features = true

[advisories]
ignore = [
# extism transitively pins wasmtime 37.x today.
# Track upgrade path to a patched wasmtime line.
"RUSTSEC-2026-0006",
# transitive via wasmtime profiling stack
"RUSTSEC-2025-0057",
# transitive via async-nats / rustls-native-certs
"RUSTSEC-2025-0134",
]

[bans]
multiple-versions = "warn"
wildcards = "allow"
highlight = "all"

[sources]
unknown-registry = "deny"
unknown-git = "deny"
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
Loading
Loading