Files
FitTrack2/FitnessSync/backend/tests/unit/test_fitbit_auth.py
2026-01-01 07:14:18 -08:00

107 lines
3.4 KiB
Python

import pytest
from unittest.mock import MagicMock, patch
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from datetime import datetime, timedelta
# Import models and app
from src.models import Base, Configuration, APIToken
from main import app
from src.api.setup import get_db
# Setup in-memory DB for tests
SQLALCHEMY_DATABASE_URL = "sqlite:///:memory:"
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
@pytest.fixture(scope="module")
def db_engine():
Base.metadata.create_all(bind=engine)
yield engine
Base.metadata.drop_all(bind=engine)
@pytest.fixture(scope="function")
def db(db_engine):
connection = db_engine.connect()
transaction = connection.begin()
session = TestingSessionLocal(bind=connection)
yield session
session.close()
transaction.rollback()
connection.close()
@pytest.fixture(scope="function")
def client(db):
def override_get_db():
try:
yield db
finally:
pass
app.dependency_overrides[get_db] = override_get_db
yield TestClient(app)
del app.dependency_overrides[get_db]
def test_save_fitbit_credentials(client, db):
"""Test saving Fitbit credentials and generating auth URL."""
payload = {
"client_id": "test_client_id",
"client_secret": "test_client_secret"
}
# Needs to match the Pydantic model we will create
response = client.post("/api/setup/fitbit", json=payload)
assert response.status_code == 200
data = response.json()
assert "auth_url" in data
assert "https://www.fitbit.com/oauth2/authorize" in data["auth_url"]
assert "client_id=test_client_id" in data["auth_url"]
# Verify DB
config = db.query(Configuration).first()
assert config is not None
assert config.fitbit_client_id == "test_client_id"
assert config.fitbit_client_secret == "test_client_secret"
@patch("src.api.setup.FitbitClient")
def test_fitbit_callback_success(mock_fitbit_cls, client, db):
"""Test Fitbit OAuth callback success."""
# Setup initial config
config_entry = Configuration(fitbit_client_id="cid", fitbit_client_secret="csec")
db.add(config_entry)
db.commit()
# Mock FitbitClient instance and method
mock_instance = MagicMock()
mock_fitbit_cls.return_value = mock_instance
mock_instance.exchange_code_for_token.return_value = {
"access_token": "new_at",
"refresh_token": "new_rt",
"expires_at": 3600, # seconds
"user_id": "uid",
"scope": ["weight"]
}
payload = {"code": "auth_code_123"}
response = client.post("/api/setup/fitbit/callback", json=payload)
assert response.status_code == 200
assert response.json()["status"] == "success"
# Verify Token saved
token = db.query(APIToken).filter_by(token_type="fitbit").first()
assert token is not None
assert token.access_token == "new_at"
assert token.refresh_token == "new_rt"
@patch("src.api.setup.FitbitClient")
def test_fitbit_callback_no_config(mock_fitbit_cls, client, db):
"""Test callback fails if no config exists."""
payload = {"code": "auth_code_123"}
response = client.post("/api/setup/fitbit/callback", json=payload)
assert response.status_code == 400
assert "Configuration not found" in response.json()["detail"]