Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
b873887
test(backend): add comprehensive endpoint integration tests for repos…
jahnavialladasetti Mar 10, 2026
b840253
fix(tests): resolve linting and naming conflicts
jahnavialladasetti Mar 10, 2026
70d69f1
fix(tests): remove global sys.modules mock that broke linux workers t…
jahnavialladasetti Mar 10, 2026
d64a8b1
Add TestClient integration tests for auth and dashboard routes
jahnavialladasetti Mar 11, 2026
62e6398
Fix CI: rename test_auth.py to test_auth_routes.py to avoid module na…
jahnavialladasetti Mar 11, 2026
0be8655
Add TestClient integration tests for notifications routes
jahnavialladasetti Mar 11, 2026
0793c94
Fix notification tests: use real Notification instances for proper sc…
jahnavialladasetti Mar 11, 2026
55439cd
Rename test_api_repos.py to test_repos_routes.py for consistent naming
jahnavialladasetti Mar 11, 2026
8c97f2c
Add missing drift event detail endpoint integration tests (GET drift-…
jahnavialladasetti Mar 11, 2026
fae0c0f
fix: add missing optional fields to mock_event and remove unused impo…
jahnavialladasetti Mar 11, 2026
037550b
fix(auth-test): add github oauth callback tests and fix deprecation w…
jahnavialladasetti Mar 11, 2026
17c6b45
fix(auth-test): refactor to sync, fix dependency leak and use AsyncMock
jahnavialladasetti Mar 11, 2026
fe7f9ab
fix(auth-test): restore missing get_current_user import and mock_user…
jahnavialladasetti Mar 11, 2026
cf9905a
fix(auth-test): localized httpx patch and safer overrides
jahnavialladasetti Mar 11, 2026
1d501ea
fix(auth-test): robust fixtures, localized httpx patching, and valid …
jahnavialladasetti Mar 11, 2026
d6f0685
fix(auth-test): resolve 405 and lint issues with patch.dict and clean…
jahnavialladasetti Mar 11, 2026
ee97695
fix(auth-test): fresh TestClient fixture and patch.dict for robust st…
jahnavialladasetti Mar 11, 2026
e233c6e
fix(auth-test): use AsyncClient for async endpoints and patch.dict fo…
jahnavialladasetti Mar 11, 2026
cada429
revert: auth tests to stable state before github callback additions
jahnavialladasetti Mar 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 178 additions & 0 deletions tests/routers/test_auth_routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import pytest
from unittest.mock import MagicMock, patch
from fastapi.testclient import TestClient
from uuid import uuid4

from app.main import app
from app.deps import get_db_connection
from app.models.user import User

# =========== Setup ===========

client = TestClient(app)


@pytest.fixture
def mock_db_session():
"""Provides a fresh mock database session and injects it as the FastAPI dependency."""
mock_db = MagicMock()
app.dependency_overrides[get_db_connection] = lambda: mock_db
yield mock_db
app.dependency_overrides.pop(get_db_connection, None)


def make_mock_user(email="test@example.com", full_name="Test User"):
"""Helper to create a mock User model instance."""
user = MagicMock(spec=User)
user.id = uuid4()
user.email = email
user.full_name = full_name
user.password_hash = "hashed_password"
user.current_refresh_token_hash = None
return user


# =========== POST /auth/signup Tests ===========


def test_signup_success(mock_db_session):
"""Test that a new user can register with valid credentials."""
mock_db_session.query.return_value.filter.return_value.first.return_value = None

mock_user = make_mock_user()

with patch("app.routers.auth.security.get_hash", return_value="hashed_pw"), \
patch("app.routers.auth.security.create_access_token", return_value="mock_access_token"), \
patch("app.routers.auth.security.create_refresh_token", return_value="mock_refresh_token"), \
patch("app.routers.auth.User") as MockUser:

MockUser.return_value = mock_user

response = client.post("/api/auth/signup", json={
"email": "test@example.com",
"full_name": "Test User",
"password": "securepassword123"
})

assert response.status_code == 200
data = response.json()
assert data["email"] == "test@example.com"
assert data["name"] == "Test User"
mock_db_session.add.assert_called_once()
mock_db_session.commit.assert_called()


def test_signup_duplicate_email(mock_db_session):
"""Test that signup fails with 400 when email already exists."""
existing_user = make_mock_user()
mock_db_session.query.return_value.filter.return_value.first.return_value = existing_user

response = client.post("/api/auth/signup", json={
"email": "test@example.com",
"full_name": "Test User",
"password": "securepassword123"
})

assert response.status_code == 400
assert "already exists" in response.json()["detail"]
mock_db_session.add.assert_not_called()


def test_signup_invalid_email(mock_db_session):
"""Test that signup fails with 422 when email format is invalid."""
response = client.post("/api/auth/signup", json={
"email": "not-a-valid-email",
"full_name": "Test User",
"password": "securepassword123"
})

assert response.status_code == 422


# =========== POST /auth/login Tests ===========


def test_login_success(mock_db_session):
"""Test that a user with correct credentials receives a successful response."""
mock_user = make_mock_user()
mock_db_session.query.return_value.filter.return_value.first.return_value = mock_user

with patch("app.routers.auth.security.verify_hash", return_value=True), \
patch("app.routers.auth.security.create_access_token", return_value="access_tok"), \
patch("app.routers.auth.security.create_refresh_token", return_value="refresh_tok"), \
patch("app.routers.auth.security.get_hash", return_value="hashed_refresh"):

response = client.post("/api/auth/login", json={
"email": "test@example.com",
"password": "correctpassword"
})

assert response.status_code == 200
data = response.json()
assert data["email"] == "test@example.com"
assert data["name"] == "Test User"
mock_db_session.commit.assert_called()


def test_login_wrong_password(mock_db_session):
"""Test that login fails with 401 when password is incorrect."""
mock_user = make_mock_user()
mock_db_session.query.return_value.filter.return_value.first.return_value = mock_user

with patch("app.routers.auth.security.verify_hash", return_value=False):
response = client.post("/api/auth/login", json={
"email": "test@example.com",
"password": "wrongpassword"
})

assert response.status_code == 401
assert "Incorrect credentials" in response.json()["detail"]


def test_login_user_not_found(mock_db_session):
"""Test that login fails with 401 when user does not exist."""
mock_db_session.query.return_value.filter.return_value.first.return_value = None

response = client.post("/api/auth/login", json={
"email": "nobody@example.com",
"password": "anypassword"
})

assert response.status_code == 401


def test_login_missing_fields(mock_db_session):
"""Test that login fails with 422 when required fields are missing."""
response = client.post("/api/auth/login", json={
"email": "test@example.com"
# missing password
})

assert response.status_code == 422


# =========== POST /auth/logout Tests ===========


def test_logout_with_valid_token(mock_db_session):
"""Test that logout clears cookies and nullifies the refresh token hash."""
mock_user = make_mock_user()
mock_db_session.query.return_value.filter.return_value.first.return_value = mock_user

with patch("app.routers.auth.security.verify_token", return_value={"sub": str(mock_user.id)}):
response = client.post(
"/api/auth/logout",
cookies={"access_token": "valid_access_token"}
)

assert response.status_code == 200
assert response.json()["message"] == "Logout successful"
mock_db_session.commit.assert_called()


def test_logout_without_token(mock_db_session):
"""Test that logout succeeds gracefully even with no cookies."""
response = client.post("/api/auth/logout")

assert response.status_code == 200
assert response.json()["message"] == "Logout successful"
143 changes: 143 additions & 0 deletions tests/routers/test_dashboard_routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import pytest
from unittest.mock import MagicMock, AsyncMock, patch
from fastapi.testclient import TestClient
from uuid import uuid4

from app.main import app
from app.deps import get_db_connection, get_current_user
from app.models.user import User

# =========== Setup ===========

client = TestClient(app)

# Create a mock authenticated user for all dashboard tests
mock_user_id = uuid4()
mock_user = MagicMock(spec=User)
mock_user.id = mock_user_id
mock_user.email = "tester@delta.com"
mock_user.full_name = "Jahnavi Tester"


def override_get_current_user():
return mock_user


app.dependency_overrides[get_current_user] = override_get_current_user


@pytest.fixture
def mock_db_session():
"""Provides a fresh mock database session injected as a FastAPI dependency."""
mock_db = MagicMock()
app.dependency_overrides[get_db_connection] = lambda: mock_db
yield mock_db
app.dependency_overrides.pop(get_db_connection, None)


# =========== GET /dashboard/stats Tests ===========


def test_get_dashboard_stats_success(mock_db_session):
"""Test that dashboard stats returns correct counts for the authenticated user."""
mock_db_session.query.return_value.filter.return_value.scalar.return_value = 3
mock_db_session.query.return_value.join.return_value.filter.return_value.scalar.return_value = 7
mock_db_session.query.return_value.join.return_value.join.return_value.filter.return_value.scalar.return_value = 21

response = client.get("/api/dashboard/stats")

assert response.status_code == 200
data = response.json()
assert "installations_count" in data
assert "repos_linked_count" in data
assert "drift_events_count" in data
assert "pr_waiting_count" in data


def test_get_dashboard_stats_all_zero(mock_db_session):
"""Test that dashboard stats returns zeros for a new user with no data."""
mock_db_session.query.return_value.filter.return_value.scalar.return_value = 0
mock_db_session.query.return_value.join.return_value.filter.return_value.scalar.return_value = 0
mock_db_session.query.return_value.join.return_value.join.return_value.filter.return_value.scalar.return_value = 0

response = client.get("/api/dashboard/stats")

assert response.status_code == 200
data = response.json()
assert data["installations_count"] == 0
assert data["repos_linked_count"] == 0
assert data["drift_events_count"] == 0
assert data["pr_waiting_count"] == 0


def test_get_dashboard_stats_requires_auth():
"""Test that dashboard stats requires authentication."""
app.dependency_overrides.pop(get_current_user, None)

response = client.get("/api/dashboard/stats")

# Restore override
app.dependency_overrides[get_current_user] = override_get_current_user

assert response.status_code in [401, 403]


# =========== GET /dashboard/repos Tests ===========


def test_get_dashboard_repos_success(mock_db_session):
"""Test that dashboard repos returns repo details for the authenticated user."""
mock_repo = MagicMock()
mock_repo.repo_name = "owner/delta-docs"
mock_repo.installation_id = 101

mock_db_session.query.return_value.join.return_value.filter.return_value \
.order_by.return_value.limit.return_value.all.return_value = [mock_repo]

with patch("app.routers.dashboard.get_repo_details", new_callable=AsyncMock) as mock_details:
mock_details.return_value = {
"name": "delta-docs",
"description": "Documentation drift detector",
"language": "Python",
"stargazers_count": 42,
"forks_count": 5,
"avatar_url": None,
}
response = client.get("/api/dashboard/repos")

assert response.status_code == 200
data = response.json()
assert len(data) == 1
assert data[0]["name"] == "delta-docs"
assert data[0]["language"] == "Python"
assert data[0]["stargazers_count"] == 42


def test_get_dashboard_repos_empty(mock_db_session):
"""Test that dashboard repos returns empty list when user has no repos."""
mock_db_session.query.return_value.join.return_value.filter.return_value \
.order_by.return_value.limit.return_value.all.return_value = []

response = client.get("/api/dashboard/repos")

assert response.status_code == 200
assert response.json() == []


def test_get_dashboard_repos_github_api_failure(mock_db_session):
"""Test that dashboard repos falls back gracefully when GitHub API fails."""
mock_repo = MagicMock()
mock_repo.repo_name = "owner/delta-docs"
mock_repo.installation_id = 101

mock_db_session.query.return_value.join.return_value.filter.return_value \
.order_by.return_value.limit.return_value.all.return_value = [mock_repo]

with patch("app.routers.dashboard.get_repo_details", side_effect=Exception("GitHub API down")):
response = client.get("/api/dashboard/repos")

assert response.status_code == 200
data = response.json()
assert len(data) == 1
assert data[0]["description"] == "Error fetching details"
assert data[0]["name"] == "owner/delta-docs"
Loading
Loading