Files
firat.sertgoz f2b5813a32 test(channels): add Slack E2E tests, integration tests, and smoke runner (#2042)
* 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>
2026-04-08 17:03:09 +09:00
..

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
  • 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:

  1. User token (xoxp-): Sends messages as a real Slack user, which triggers Slack to send webhook events to IronClaw
  2. Bot token (xoxb-): Reads conversations.history / conversations.replies to find the bot's replies

Flow per case:

  1. Send message via user token
  2. Slack sends webhook event to IronClaw
  3. IronClaw processes event and calls chat.postMessage
  4. Smoke runner polls conversation history with bot token to find the reply
  1. Run the Rust test suite: cargo test --test slack_auth_integration
  2. Run E2E tests: cd tests/e2e && pytest scenarios/test_slack_e2e.py -v
  3. Run this smoke test against a staging instance with real Slack

Notes

  • The mention case requires both SLACK_SMOKE_PUBLIC_CHANNEL and SLACK_SMOKE_BOT_USER_ID
  • Use SLACK_SMOKE_EXPECT_SUBSTRING with a mock LLM for deterministic reply matching
  • Exit codes: 0 = all passed, 1 = failure, 2 = config error