Skip to content

Commit 1da2ce7

Browse files
Merge pull request #1 from pravin-python/test-structure-generation-14946090886484415323
Generate complete backend testing structure
2 parents 5c1ffd2 + e13cfb8 commit 1da2ce7

25 files changed

+1096
-70
lines changed

.github/workflows/ci.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: WorkSynapse CI
2+
3+
on:
4+
push:
5+
branches: [ "main", "develop" ]
6+
pull_request:
7+
branches: [ "main", "develop" ]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
env:
13+
PROJECT_NAME: "WorkSynapse API Test"
14+
API_V1_STR: "/api/v1"
15+
SECRET_KEY: "test_secret_key_for_ci_pipeline_only"
16+
SERVICE_API_KEY: "test_service_key"
17+
POSTGRES_USER: "test"
18+
POSTGRES_PASSWORD: "test"
19+
POSTGRES_DB: "test_db"
20+
POSTGRES_HOST: "localhost"
21+
POSTGRES_PORT: 5432
22+
DATABASE_URL: "sqlite+aiosqlite:///:memory:"
23+
PYTHONPATH: ${{ github.workspace }}/backend
24+
25+
steps:
26+
- uses: actions/checkout@v3
27+
28+
- name: Set up Python 3.12
29+
uses: actions/setup-python@v4
30+
with:
31+
python-version: "3.12"
32+
33+
- name: Install dependencies
34+
run: |
35+
python -m pip install --upgrade pip
36+
pip install pytest pytest-asyncio pytest-cov httpx faker aiosqlite sqlalchemy
37+
pip install -r backend/requirements.txt
38+
39+
- name: Run Tests
40+
run: |
41+
cd backend
42+
pytest tests/ --cov=app --cov-report=xml
43+
44+
- name: Upload coverage reports to Codecov
45+
uses: codecov/codecov-action@v3
46+
with:
47+
token: ${{ secrets.CODECOV_TOKEN }}

backend/app/api/v1/routers/user_activity.py

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from fastapi import APIRouter, Depends, HTTPException
2-
from sqlalchemy.orm import Session
2+
from sqlalchemy.ext.asyncio import AsyncSession
3+
from sqlalchemy import select
34
from typing import Any, Dict, List
45

56
from app.api import deps
@@ -9,12 +10,13 @@
910
from app.models.task.model import Task
1011
from app.models.note.model import Note
1112
from app.models.worklog.model import WorkLog, ActivityLog
13+
from app.models.agent_chat.model import AgentConversation
1214

1315
router = APIRouter()
1416

1517
@router.get("/", response_model=Dict[str, List[Any]])
16-
def get_user_activity(
17-
db: Session = Depends(deps.get_db),
18+
async def get_user_activity(
19+
db: AsyncSession = Depends(deps.get_db),
1820
current_user: User = Depends(deps.get_current_active_user),
1921
skip: int = 0,
2022
limit: int = 100,
@@ -31,40 +33,44 @@ def format_list(items):
3133
]
3234

3335
# 1. Agents Created
34-
agents = db.query(Agent).filter(Agent.user_id == current_user.id).all()
36+
result = await db.execute(select(Agent).filter(Agent.created_by_user_id == current_user.id))
37+
agents = result.scalars().all()
3538

3639
# 2. Projects Created (Owned)
37-
projects = db.query(Project).filter(Project.owner_id == current_user.id).all()
40+
result = await db.execute(select(Project).filter(Project.owner_id == current_user.id))
41+
projects = result.scalars().all()
3842

3943
# 3. Tasks Created
40-
# Note: Using created_by_user_id based on User model relationship
4144
try:
42-
tasks = db.query(Task).filter(Task.created_by_user_id == current_user.id).limit(limit).all()
45+
result = await db.execute(select(Task).filter(Task.created_by_id == current_user.id).limit(limit))
46+
tasks = result.scalars().all()
4347
except Exception:
44-
# Fallback if field name differs (e.g. might be user_id or creator_id)
45-
# Using a safer generic try/except to avoid crashing the whole endpoint
4648
tasks = []
4749

4850
# 4. Notes Created
4951
try:
50-
notes = db.query(Note).filter(Note.owner_id == current_user.id).limit(limit).all()
52+
result = await db.execute(select(Note).filter(Note.owner_id == current_user.id).limit(limit))
53+
notes = result.scalars().all()
5154
except Exception:
5255
notes = []
5356

54-
# 5. Agent Sessions
57+
# 5. Agent Conversations
5558
try:
56-
sessions = db.query(AgentSession).filter(AgentSession.user_id == current_user.id).limit(limit).all()
59+
result = await db.execute(select(AgentConversation).filter(AgentConversation.user_id == current_user.id).limit(limit))
60+
sessions = result.scalars().all()
5761
except Exception:
5862
sessions = []
5963

6064
# 6. Work Logs
6165
try:
62-
work_logs = db.query(WorkLog).filter(WorkLog.user_id == current_user.id).limit(limit).all()
66+
result = await db.execute(select(WorkLog).filter(WorkLog.user_id == current_user.id).limit(limit))
67+
work_logs = result.scalars().all()
6368
except Exception:
6469
work_logs = []
6570

6671
# 7. Activity Logs
67-
activity_logs = db.query(ActivityLog).filter(ActivityLog.user_id == current_user.id).order_by(ActivityLog.timestamp.desc()).limit(limit).all()
72+
result = await db.execute(select(ActivityLog).filter(ActivityLog.user_id == current_user.id).order_by(ActivityLog.created_at.desc()).limit(limit))
73+
activity_logs = result.scalars().all()
6874

6975
return {
7076
"agents": format_list(agents),
@@ -77,20 +83,23 @@ def format_list(items):
7783
}
7884

7985
@router.get("/logs", response_model=List[Any])
80-
def get_user_activity_logs(
81-
db: Session = Depends(deps.get_db),
86+
async def get_user_activity_logs(
87+
db: AsyncSession = Depends(deps.get_db),
8288
current_user: User = Depends(deps.get_current_active_user),
8389
skip: int = 0,
8490
limit: int = 50,
8591
) -> Any:
8692
"""
8793
Get specific activity logs for the user (audit trail).
8894
"""
89-
logs = db.query(ActivityLog).filter(ActivityLog.user_id == current_user.id)\
90-
.order_by(ActivityLog.timestamp.desc())\
91-
.offset(skip)\
92-
.limit(limit)\
93-
.all()
95+
result = await db.execute(
96+
select(ActivityLog)
97+
.filter(ActivityLog.user_id == current_user.id)
98+
.order_by(ActivityLog.created_at.desc())
99+
.offset(skip)
100+
.limit(limit)
101+
)
102+
logs = result.scalars().all()
94103

95104
return [
96105
{k: v for k, v in log.__dict__.items() if not k.startswith('_')}

backend/app/models/activity/__init__.py

Lines changed: 0 additions & 1 deletion
This file was deleted.

backend/app/models/activity/model.py

Lines changed: 0 additions & 46 deletions
This file was deleted.

backend/app/models/agent_builder/model.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,8 @@ class CustomAgent(Base, AuditMixin):
210210
__tablename__ = "custom_agents"
211211
__table_args__ = (
212212
UniqueConstraint('name', 'created_by_user_id', name='uq_custom_agent_name_user'),
213-
Index('ix_custom_agents_status', 'status'),
214-
Index('ix_custom_agents_creator', 'created_by_user_id'),
213+
# Index('ix_custom_agents_status', 'status'), # Defined in mapped_column
214+
# Index('ix_custom_agents_creator', 'created_by_user_id'), # Defined in AuditMixin
215215
)
216216

217217
id: Mapped[int] = mapped_column(Integer, primary_key=True)

backend/pytest.ini

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[pytest]
2+
asyncio_mode = auto
3+
asyncio_default_fixture_loop_scope = session
4+
python_files = test_*.py
5+
python_classes = Test*
6+
python_functions = test_*
7+
addopts = -v --cov=app --cov-report=term-missing
8+
env_files =
9+
.env

backend/tests/README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# WorkSynapse Testing
2+
3+
This directory contains the comprehensive test suite for the WorkSynapse backend.
4+
5+
## Structure
6+
7+
- `fixtures/`: Reusable test data factories (Users, Notes, Projects, Agents).
8+
- `mocks/`: Mock implementations for external services (OpenAI, Google, Slack).
9+
- `load_test/`: Locust load testing scripts.
10+
- `conftest.py`: Global test configuration, database setup, and fixtures.
11+
12+
## Running Tests
13+
14+
1. **Install Dependencies:**
15+
```bash
16+
pip install -r requirements.txt
17+
pip install pytest pytest-asyncio pytest-cov httpx faker aiosqlite
18+
```
19+
20+
2. **Run All Tests:**
21+
```bash
22+
pytest
23+
```
24+
25+
3. **Run with Coverage:**
26+
```bash
27+
pytest --cov=app --cov-report=html
28+
```
29+
Open `htmlcov/index.html` to view the report.
30+
31+
## Load Testing
32+
33+
1. **Install Locust:**
34+
```bash
35+
pip install locust
36+
```
37+
38+
2. **Run Locust:**
39+
```bash
40+
locust -f tests/load_test/locustfile.py
41+
```
42+
Open http://localhost:8089 in your browser.
43+
44+
## Continuous Integration
45+
46+
The project includes a GitHub Actions workflow `.github/workflows/ci.yml` that automatically runs tests on push/PR.

backend/tests/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)