mirror of
https://github.com/nearai/ironclaw.git
synced 2026-09-02 23:56:24 +08:00
* test: add Slack E2E tests, Rust integration tests, and smoke runner Replicate the Telegram test infrastructure for the Slack WASM channel: - Add Slack URL rewriting in wrapper.rs for test API redirection - Create fake_slack_api.py mock server for E2E tests - Add 12 Python E2E tests covering setup, DM, mentions, auth, threads, files - Add 12 Rust integration tests for WASM channel behavior - Add conftest.py fixtures for isolated Slack test instances - Add local smoke test runner for pre-release validation with real Slack Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: wrap env::set_var/remove_var in unsafe blocks for Rust 1.83+ CI uses Rust 1.94 which requires unsafe blocks for std::env::set_var and std::env::remove_var. Wrap the test-only calls in unsafe blocks with safety comments. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR review feedback - Replace fragile time.time()-1 fallback with explicit SmokeError in run_smoke.py attachment case (reviewer finding #1) - Add OnceLock<Mutex> guard around env var mutation in wrapper.rs unit test to prevent parallel test races (reviewer finding #2) - Extract duplicated git-worktree discovery into find_project_file() helper in slack_auth_integration.rs (reviewer finding #3) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test(channels): generalize WASM HTTP test rewrites * fix(channels): gate Slack test URL rewrites from release builds * fix(ci): update wrapper test pairing store ctor --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Slack Local Smoke Test
Exercises the real Slack WASM channel integration against a running IronClaw instance using live Slack API calls.
Prerequisites
- Python 3.11+
- Running IronClaw instance with the Slack channel configured and activated
- Slack App with:
- Bot token (
xoxb-) with scopes:chat:write,channels:history,groups:history,im:history,files:read - User token (
xoxp-) with scopes:chat:write,files:write,channels:history,im:history
- Bot token (
- Test bot added to the DM channel (and optionally a public channel for mention tests)
Setup
# From the repo root
cd tests/e2e
python -m venv .venv
source .venv/bin/activate
pip install -e '.[slack]'
# Configure
cd ../../scripts/slack_smoke
cp config.example.env config.env
# Edit config.env with your tokens and channel IDs
Usage
# Load env vars
set -a && source config.env && set +a
# Run default cases (dm, attachment, thread)
python run_smoke.py
# Run all cases including mention
python run_smoke.py --all
# Run a specific case
python run_smoke.py --case dm
python run_smoke.py --case mention
# List available cases
python run_smoke.py --list-cases
Smoke Cases
| Case | Default | Description |
|---|---|---|
dm |
yes | Send DM via user token, poll for bot reply |
attachment |
yes | Upload file to DM channel, poll for bot reply |
thread |
yes | Send DM, wait for reply, reply in thread, verify bot continues in thread |
mention |
no | Send <@BOT_USER_ID> msg in public channel, poll for threaded reply |
How It Works
Unlike Telegram (which uses a user-client library like Telethon), Slack smoke uses two tokens:
- User token (
xoxp-): Sends messages as a real Slack user, which triggers Slack to send webhook events to IronClaw - Bot token (
xoxb-): Readsconversations.history/conversations.repliesto find the bot's replies
Flow per case:
- Send message via user token
- Slack sends webhook event to IronClaw
- IronClaw processes event and calls
chat.postMessage - Smoke runner polls conversation history with bot token to find the reply
Recommended Release Workflow
- Run the Rust test suite:
cargo test --test slack_auth_integration - Run E2E tests:
cd tests/e2e && pytest scenarios/test_slack_e2e.py -v - Run this smoke test against a staging instance with real Slack
Notes
- The
mentioncase requires bothSLACK_SMOKE_PUBLIC_CHANNELandSLACK_SMOKE_BOT_USER_ID - Use
SLACK_SMOKE_EXPECT_SUBSTRINGwith a mock LLM for deterministic reply matching - Exit codes: 0 = all passed, 1 = failure, 2 = config error