Spaces:
Sleeping
Sleeping
| 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}") | |