import os import sys import json from datetime import datetime # Add app to path sys.path.insert(0, os.path.abspath('.')) from fastapi.testclient import TestClient from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from app.main import app from app.core.database import Base, get_db from app.main import limiter # 1. Setup isolated database TEST_DB_PATH = "./data/e2e_test.db" if os.path.exists(TEST_DB_PATH): try: os.remove(TEST_DB_PATH) except: pass TEST_DB = f"sqlite:///{TEST_DB_PATH}" engine = create_engine(TEST_DB, connect_args={"check_same_thread": False}) TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) Base.metadata.create_all(bind=engine) def override_get_db(): db = TestingSessionLocal() try: yield db finally: db.close() from app.core.config import settings # Force fallback generator to bypass rate limits settings.GEMINI_API_KEYS = "" app.dependency_overrides[get_db] = override_get_db limiter._storage.reset() # Reset rate limits client = TestClient(app) print("\n--- Starting HALE E2E Simulation ---\n") try: # 2. Register print("1. Registering new user...") username = f"test_user_{int(datetime.now().timestamp())}" res = client.post("/api/auth/register", json={ "user_id": username, "name": "Bruce Wayne", "password": "Batman_Password1" }) assert res.status_code == 201, res.text token = res.json()["access_token"] headers = {"Authorization": f"Bearer {token}"} print(f"User registered: {username}") # 3. Update Profile print("\n2. Changing Coach Personality to 'drill_sergeant'...") res = client.patch("/api/auth/profile", headers=headers, json={ "coach_personality": "drill_sergeant" }) assert res.status_code == 200, res.text print(f"Profile updated. Coach is now: {res.json()['coach_personality']}") # 4. Create Goal print("\n3. Creating a new goal: 'Learn Neuroscience in 7 days'") print("... Waiting for AI Curriculum Generator (might take a few seconds)...") res = client.post("/api/goals/", headers=headers, json={ "goal": "Learn Neuroscience basics", "category": "education", "duration_days": 10 }) assert res.status_code == 201, res.text goal_data = res.json() print(f"Goal Created! AI generated {len(goal_data['plan']['modules'])} modules.") print(f" Sample module: {goal_data['plan']['modules'][0]['title']}") # 5. Get Initial Daily Plan print("\n4. Fetching today's personalized plan (Normal State)...") res = client.get("/api/plan", headers=headers) assert res.status_code == 200, res.text plan_data = res.json() print(f"Plan fetched:") print(f" Topic: {plan_data['topic']}") print(f" Behavior State: {plan_data['behavior']['action_label']} (Intensity: {plan_data['behavior']['intensity']})") print(" Tasks:") for t in plan_data['tasks']: print(f" - {t}") print(f" Coach Message (Drill Sergeant): \"{plan_data['coach_message']}\"") # 6. Update State (Simulate high fatigue / sickness) print("\n5. Oh no! User is extremely tired and burned out. Updating state...") res = client.post("/api/update-state", headers=headers, json={ "fatigue": 0.9, "mood": 0.2, "stress": 0.8 }) assert res.status_code == 200, res.text print("State updated successfully.") # 7. Get Adapted Daily Plan print("\n6. Fetching daily plan again to check AI adaptation...") res = client.get("/api/plan", headers=headers) assert res.status_code == 200, res.text adapted_plan = res.json() print(f"Adapted Plan fetched:") print(f" Behavior State: {adapted_plan['behavior']['action_label']} (Intensity: {adapted_plan['behavior']['intensity']})") print(" Adapted Tasks:") for t in adapted_plan['tasks']: print(f" - {t}") print(" Notice how the tasks changed dynamically based on fatigue!") # 8. Complete Task print("\n7. Completing the tasks...") today = datetime.today().strftime('%Y-%m-%d') res = client.post("/api/complete-task", headers=headers, json={ "date": today, "task_index": 0, "actual_duration_min": 15, "notes": "Was tired but pushed through a quick review." }) assert res.status_code == 200, res.text print(f"Task Completed! Result: {json.dumps(res.json(), ensure_ascii=True)}") # 9. Check Complete Gamification Stats print("\n8. Checking Gamification Stats...") res = client.get("/api/stats", headers=headers) assert res.status_code == 200, res.text stats = res.json() print("Current Stats:") print(f" Level: {stats['level']} | XP: {stats['xp']}") print(f" Current Streak: {stats['current_streak']}") print(f" Tasks Completed: {stats['total_tasks_completed']}") # 10. Generate Micro-Lesson print("\n9. Asking AI to generate a quick lesson on 'Neurons'...") res = client.get("/api/lesson/Neurons", headers=headers) assert res.status_code == 200, res.text print("Lesson generated successfully!") print(f" Excerpt: {res.json()['content'][:150]}...\n") print("--- E2E Simulation Completed Successfully! ---") except Exception as e: print(f"\nE2E Run Failed! Error:\n{e}") finally: # Cleanup DB try: if os.path.exists("./data/e2e_test.db"): # Ensure engine is disposed to release the file handle engine.dispose() os.remove("./data/e2e_test.db") except Exception as cleanup_err: print(f"Cleanup warning: {cleanup_err}")