245 lines
8.1 KiB
Python
245 lines
8.1 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Aetheel Slack Adapter — Integration Test
|
|
==========================================
|
|
Tests the Slack adapter by:
|
|
1. Connecting to Slack via Socket Mode
|
|
2. Sending a test message to a specified channel
|
|
3. Verifying the bot can send and receive
|
|
|
|
Usage:
|
|
python test_slack.py # Interactive — prompts for channel
|
|
python test_slack.py --channel C0123456789 # Send to a specific channel
|
|
python test_slack.py --dm U0123456789 # Send a DM to a user
|
|
python test_slack.py --send-only # Just send, don't listen
|
|
|
|
Requirements:
|
|
- SLACK_BOT_TOKEN and SLACK_APP_TOKEN set in .env
|
|
- Bot must be invited to the target channel
|
|
"""
|
|
|
|
import argparse
|
|
import logging
|
|
import os
|
|
import sys
|
|
import time
|
|
import threading
|
|
|
|
from dotenv import load_dotenv
|
|
|
|
load_dotenv()
|
|
|
|
from adapters.slack_adapter import SlackAdapter, SlackMessage
|
|
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format="%(asctime)s [%(name)s] %(levelname)s: %(message)s",
|
|
datefmt="%Y-%m-%d %H:%M:%S",
|
|
)
|
|
logger = logging.getLogger("aetheel.test")
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Test 1: Send a message
|
|
# ---------------------------------------------------------------------------
|
|
def test_send_message(adapter: SlackAdapter, target: str) -> bool:
|
|
"""Test sending a message to a channel or user."""
|
|
print("\n" + "=" * 60)
|
|
print(" TEST 1: Send Message")
|
|
print("=" * 60)
|
|
|
|
try:
|
|
result = adapter.send_message(
|
|
channel=target,
|
|
text=(
|
|
"🧪 *Aetheel Slack Test*\n\n"
|
|
"If you can see this message, the Slack adapter is working!\n\n"
|
|
f"• Bot ID: `{adapter._bot_user_id}`\n"
|
|
f"• Bot Name: `@{adapter._bot_user_name}`\n"
|
|
f"• Timestamp: `{time.strftime('%Y-%m-%d %H:%M:%S')}`\n"
|
|
f"• Mode: Socket Mode\n\n"
|
|
"_Reply to this message to test receiving._"
|
|
),
|
|
)
|
|
print(f" ✅ Message sent successfully!")
|
|
print(f" Channel: {result.channel_id}")
|
|
print(f" Message ID: {result.message_id}")
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f" ❌ Failed to send: {e}")
|
|
return False
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Test 2: Send a threaded reply
|
|
# ---------------------------------------------------------------------------
|
|
def test_threaded_reply(adapter: SlackAdapter, target: str) -> bool:
|
|
"""Test sending a message and then replying in a thread."""
|
|
print("\n" + "=" * 60)
|
|
print(" TEST 2: Threaded Reply")
|
|
print("=" * 60)
|
|
|
|
try:
|
|
# Send parent message
|
|
parent = adapter.send_message(
|
|
channel=target,
|
|
text="🧵 *Thread Test* — This is the parent message.",
|
|
)
|
|
print(f" ✅ Parent message sent (ts={parent.message_id})")
|
|
|
|
time.sleep(1)
|
|
|
|
# Send threaded reply
|
|
reply = adapter.send_message(
|
|
channel=target,
|
|
text="↳ This is a threaded reply! Thread isolation is working.",
|
|
thread_ts=parent.message_id,
|
|
)
|
|
print(f" ✅ Thread reply sent (ts={reply.message_id})")
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f" ❌ Failed: {e}")
|
|
return False
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Test 3: Long message chunking
|
|
# ---------------------------------------------------------------------------
|
|
def test_long_message(adapter: SlackAdapter, target: str) -> bool:
|
|
"""Test that long messages are properly chunked."""
|
|
print("\n" + "=" * 60)
|
|
print(" TEST 3: Long Message Chunking")
|
|
print("=" * 60)
|
|
|
|
try:
|
|
# Create a message that exceeds 4000 chars
|
|
long_text = "📜 *Long Message Test*\n\n"
|
|
for i in range(1, 101):
|
|
long_text += f"{i}. This is line number {i} of the long message test. " \
|
|
f"It contains enough text to test the chunking behavior.\n"
|
|
|
|
result = adapter.send_message(channel=target, text=long_text)
|
|
print(f" ✅ Long message sent (length={len(long_text)}, id={result.message_id})")
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f" ❌ Failed: {e}")
|
|
return False
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Test 4: Receive messages (interactive)
|
|
# ---------------------------------------------------------------------------
|
|
def test_receive_messages(adapter: SlackAdapter, duration: int = 30) -> bool:
|
|
"""
|
|
Test receiving messages by listening for a specified duration.
|
|
The bot will echo back any messages it receives.
|
|
"""
|
|
print("\n" + "=" * 60)
|
|
print(" TEST 4: Receive Messages (Interactive)")
|
|
print("=" * 60)
|
|
print(f" Listening for {duration} seconds...")
|
|
print(f" Send a message to @{adapter._bot_user_name} to test receiving.")
|
|
print(f" Press Ctrl+C to stop early.\n")
|
|
|
|
received = []
|
|
|
|
def test_handler(msg: SlackMessage) -> str:
|
|
received.append(msg)
|
|
print(f" 📨 Received: '{msg.text}' from @{msg.user_name}")
|
|
return f"✅ Got it! You said: _{msg.text}_"
|
|
|
|
adapter.on_message(test_handler)
|
|
|
|
try:
|
|
adapter.start_async()
|
|
time.sleep(duration)
|
|
except KeyboardInterrupt:
|
|
print("\n Stopped by user.")
|
|
finally:
|
|
adapter.stop()
|
|
|
|
print(f"\n Messages received: {len(received)}")
|
|
if received:
|
|
print(" ✅ Receive test PASSED")
|
|
return True
|
|
else:
|
|
print(" ⚠️ No messages received (send a message to the bot to test)")
|
|
return True # Not a failure — just no one sent a message
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Main
|
|
# ---------------------------------------------------------------------------
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Test the Aetheel Slack Adapter")
|
|
group = parser.add_mutually_exclusive_group()
|
|
group.add_argument("--channel", help="Channel ID to send test messages to (C...)")
|
|
group.add_argument("--dm", help="User ID to DM for testing (U...)")
|
|
parser.add_argument(
|
|
"--send-only",
|
|
action="store_true",
|
|
help="Only run send tests (don't listen for messages)",
|
|
)
|
|
parser.add_argument(
|
|
"--duration",
|
|
type=int,
|
|
default=30,
|
|
help="How long to listen for messages in seconds (default: 30)",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
# Validate tokens
|
|
if not os.environ.get("SLACK_BOT_TOKEN") or not os.environ.get("SLACK_APP_TOKEN"):
|
|
print("❌ Missing SLACK_BOT_TOKEN or SLACK_APP_TOKEN in environment.")
|
|
print(" Copy .env.example to .env and fill in your tokens.")
|
|
sys.exit(1)
|
|
|
|
# Get target
|
|
target = args.channel or args.dm
|
|
if not target:
|
|
print("You need to specify a target for send tests.")
|
|
print(" --channel C0123456789 (channel ID)")
|
|
print(" --dm U0123456789 (user ID for DM)")
|
|
target = input("\nEnter a channel or user ID (or press Enter to skip send tests): ").strip()
|
|
|
|
# Create adapter
|
|
adapter = SlackAdapter(log_level="INFO")
|
|
|
|
# Resolve identity first
|
|
adapter._resolve_identity()
|
|
|
|
# Run tests
|
|
results = {}
|
|
|
|
if target:
|
|
results["send"] = test_send_message(adapter, target)
|
|
results["thread"] = test_threaded_reply(adapter, target)
|
|
results["chunking"] = test_long_message(adapter, target)
|
|
else:
|
|
print("\n⏭️ Skipping send tests (no target specified)")
|
|
|
|
if not args.send_only:
|
|
results["receive"] = test_receive_messages(adapter, duration=args.duration)
|
|
|
|
# Summary
|
|
print("\n" + "=" * 60)
|
|
print(" TEST RESULTS")
|
|
print("=" * 60)
|
|
for test_name, passed in results.items():
|
|
icon = "✅" if passed else "❌"
|
|
print(f" {icon} {test_name}")
|
|
|
|
total = len(results)
|
|
passed = sum(1 for v in results.values() if v)
|
|
print(f"\n {passed}/{total} tests passed")
|
|
print("=" * 60)
|
|
|
|
return 0 if all(results.values()) else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|