#!/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())