| Server IP : 85.155.190.233 / Your IP : 216.73.216.103 Web Server : nginx/1.24.0 System : Linux antigravity-cli 6.8.0-31-generic #31-Ubuntu SMP PREEMPT_DYNAMIC Sat Apr 20 00:40:06 UTC 2024 x86_64 User : wp-moonbloom ( 1001) PHP Version : 8.3.6 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /opt/Gemini_Swarm_Core/scripts/ |
Upload File : |
import asyncio
import os
import sys
import argparse
import contextlib
from datetime import datetime, timezone, timedelta
from pathlib import Path
from unittest.mock import AsyncMock, MagicMock, patch
# Load dotenv before importing local modules that require env vars
from dotenv import load_dotenv
load_dotenv()
import pytest
import aiosqlite
# Adjust sys.path to run directly and run pytest
scripts_dir = Path(__file__).resolve().parent
swarm_root = scripts_dir.parent
if str(swarm_root) not in sys.path:
sys.path.insert(0, str(swarm_root))
from tg_bridge import connect_db, DB_PATH, TOKEN, ALLOWED_USERS
from trend_scout import TrendScoutManager
# --- CLI Manual Run Mode ---
async def run_manual():
print("--- Starting Manual Trend Scout Execution ---")
if not TOKEN:
print("ERROR: TELEGRAM_BOT_TOKEN is not set in environment or .env file.")
sys.exit(1)
print(f"Allowed users: {ALLOWED_USERS}")
print(f"Database path: {DB_PATH}")
from aiogram import Bot
bot = Bot(token=TOKEN)
manager = TrendScoutManager(bot=bot, db_path=DB_PATH, allowed_users=ALLOWED_USERS)
# Initialize DB table just in case it's not initialized
from scripts.tg_bridge import init_db
await init_db()
now = datetime.now(timezone.utc)
expected_time = await manager.get_expected_last_run(now)
print(f"Executing scout run. Scheduled target slot: {expected_time.isoformat()}")
success = await manager.run_scout(expected_time)
if success:
print("Scout run finished successfully! Message sent to Telegram.")
else:
print("Scout run failed. Check logs/database for errors.")
await bot.session.close()
# --- Pytest Tests ---
@pytest.mark.asyncio
async def test_get_expected_last_run():
# Setup dummy manager
bot = MagicMock()
manager = TrendScoutManager(bot=bot, db_path=Path("dummy"), allowed_users=[])
# Test case 1: Now is after 01:00 UTC today
now1 = datetime(2026, 6, 11, 10, 30, 0, tzinfo=timezone.utc)
expected1 = await manager.get_expected_last_run(now1)
assert expected1 == datetime(2026, 6, 11, 1, 0, 0, tzinfo=timezone.utc)
# Test case 2: Now is before 01:00 UTC today (which is Thursday), so last expected run is Monday (June 8)
now2 = datetime(2026, 6, 11, 0, 45, 0, tzinfo=timezone.utc)
expected2 = await manager.get_expected_last_run(now2)
assert expected2 == datetime(2026, 6, 8, 1, 0, 0, tzinfo=timezone.utc)
@pytest.mark.asyncio
async def test_is_run_due_no_record():
bot = MagicMock()
# Create an in-memory database for testing
test_db_path = Path("test_scout.db")
if test_db_path.exists():
os.remove(test_db_path)
manager = TrendScoutManager(bot=bot, db_path=test_db_path, allowed_users=[])
with patch("tg_bridge.connect_db") as mock_connect:
# We mock connect_db to point to our test DB file
@contextlib.asynccontextmanager
async def mock_conn_func():
async with aiosqlite.connect(test_db_path) as db:
yield db
mock_connect.side_effect = mock_conn_func
async with aiosqlite.connect(test_db_path) as db:
await db.execute("""
CREATE TABLE IF NOT EXISTS swarm_tasks (
task_id INTEGER PRIMARY KEY AUTOINCREMENT,
task_name TEXT UNIQUE NOT NULL,
repo_path TEXT NOT NULL,
cron_expression TEXT NOT NULL,
active INTEGER DEFAULT 1,
last_run_time TEXT
)
""")
await db.execute("""
INSERT INTO swarm_tasks (task_name, repo_path, cron_expression, active, last_run_time)
VALUES ('trend_scout', 'dummy', '0 1 * * 1,4', 1, NULL)
""")
await db.commit()
due = await manager.is_run_due()
assert due is True
if test_db_path.exists():
os.remove(test_db_path)
@pytest.mark.asyncio
async def test_is_run_due_old_record():
bot = MagicMock()
test_db_path = Path("test_scout_old.db")
if test_db_path.exists():
os.remove(test_db_path)
manager = TrendScoutManager(bot=bot, db_path=test_db_path, allowed_users=[])
with patch("tg_bridge.connect_db") as mock_connect:
@contextlib.asynccontextmanager
async def mock_conn_func():
async with aiosqlite.connect(test_db_path) as db:
yield db
mock_connect.side_effect = mock_conn_func
async with aiosqlite.connect(test_db_path) as db:
await db.execute("""
CREATE TABLE IF NOT EXISTS swarm_tasks (
task_id INTEGER PRIMARY KEY AUTOINCREMENT,
task_name TEXT UNIQUE NOT NULL,
repo_path TEXT NOT NULL,
cron_expression TEXT NOT NULL,
active INTEGER DEFAULT 1,
last_run_time TEXT
)
""")
two_days_ago = (datetime.now(timezone.utc) - timedelta(days=2)).strftime("%Y-%m-%dT%H:%M:%SZ")
await db.execute("""
INSERT INTO swarm_tasks (task_name, repo_path, cron_expression, active, last_run_time)
VALUES ('trend_scout', 'dummy', '0 1 * * 1,4', 1, ?)
""", (two_days_ago,))
await db.commit()
due = await manager.is_run_due()
assert due is True
if test_db_path.exists():
os.remove(test_db_path)
@pytest.mark.asyncio
async def test_is_run_due_already_run():
bot = MagicMock()
test_db_path = Path("test_scout_run.db")
if test_db_path.exists():
os.remove(test_db_path)
manager = TrendScoutManager(bot=bot, db_path=test_db_path, allowed_users=[])
with patch("tg_bridge.connect_db") as mock_connect:
@contextlib.asynccontextmanager
async def mock_conn_func():
async with aiosqlite.connect(test_db_path) as db:
yield db
mock_connect.side_effect = mock_conn_func
async with aiosqlite.connect(test_db_path) as db:
await db.execute("""
CREATE TABLE IF NOT EXISTS swarm_tasks (
task_id INTEGER PRIMARY KEY AUTOINCREMENT,
task_name TEXT UNIQUE NOT NULL,
repo_path TEXT NOT NULL,
cron_expression TEXT NOT NULL,
active INTEGER DEFAULT 1,
last_run_time TEXT
)
""")
now = datetime.now(timezone.utc)
expected_time = await manager.get_expected_last_run(now)
await db.execute("""
INSERT INTO swarm_tasks (task_name, repo_path, cron_expression, active, last_run_time)
VALUES ('trend_scout', 'dummy', '0 1 * * 1,4', 1, ?)
""", (expected_time.strftime("%Y-%m-%dT%H:%M:%SZ"),))
await db.commit()
due = await manager.is_run_due()
assert due is False
if test_db_path.exists():
os.remove(test_db_path)
@pytest.mark.asyncio
async def test_run_scout_success():
bot = MagicMock()
bot.send_message = AsyncMock()
test_db_path = Path("test_scout_success.db")
if test_db_path.exists():
os.remove(test_db_path)
manager = TrendScoutManager(bot=bot, db_path=test_db_path, allowed_users=["12345"])
with patch("tg_bridge.connect_db") as mock_connect, \
patch("asyncio.create_subprocess_exec") as mock_subproc:
@contextlib.asynccontextmanager
async def mock_conn_func():
async with aiosqlite.connect(test_db_path) as db:
yield db
mock_connect.side_effect = mock_conn_func
async with aiosqlite.connect(test_db_path) as db:
await db.execute("""
CREATE TABLE IF NOT EXISTS swarm_tasks (
task_id INTEGER PRIMARY KEY AUTOINCREMENT,
task_name TEXT UNIQUE NOT NULL,
repo_path TEXT NOT NULL,
cron_expression TEXT NOT NULL,
active INTEGER DEFAULT 1,
last_run_time TEXT
)
""")
await db.execute("""
CREATE TABLE IF NOT EXISTS swarm_run_sessions (
session_id TEXT PRIMARY KEY,
task_id INTEGER NOT NULL,
status TEXT NOT NULL,
started_at TEXT NOT NULL,
finished_at TEXT,
log_file_path TEXT,
digest_summary TEXT
)
""")
await db.execute("""
CREATE TABLE IF NOT EXISTS trend_scout_runs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
run_time DATETIME DEFAULT CURRENT_TIMESTAMP,
status TEXT NOT NULL,
summary TEXT,
error_message TEXT
)
""")
await db.execute("""
INSERT INTO swarm_tasks (task_id, task_name, repo_path, cron_expression, active)
VALUES (1, 'trend_scout', 'dummy', '0 1 * * 1,4', 1)
""")
await db.commit()
# Mock subprocess
proc_mock = MagicMock()
proc_mock.communicate = AsyncMock(return_value=(b"Awesome trend scouting result\nWarning: ignored\n", b""))
proc_mock.returncode = 0
mock_subproc.return_value = proc_mock
success = await manager.run_scout(datetime.now(timezone.utc))
assert success is True
# Verify telegram message was sent
bot.send_message.assert_called_once()
# Verify DB entry
async with aiosqlite.connect(test_db_path) as db:
async with db.execute("SELECT status, digest_summary FROM swarm_run_sessions") as cursor:
row = await cursor.fetchone()
assert row is not None
assert row[0] == "SUCCESS"
assert "Awesome trend scouting result" in row[1]
if test_db_path.exists():
os.remove(test_db_path)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Test and run Trend Scout")
parser.add_argument("--run-now", action="store_true", help="Run the scouting process end-to-end immediately")
args = parser.parse_args()
if args.run_now or "--run-now" in sys.argv:
# Load environment variables from .env if present
env_file = swarm_root / ".env"
if env_file.exists():
from dotenv import load_dotenv
load_dotenv(dotenv_path=env_file)
asyncio.run(run_manual())
else:
pytest.main([__file__])