feat(project): user_id isolation in create_project, list_projects and _to_dict

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ubuntu 2026-05-16 09:20:13 +00:00
parent fe357668b4
commit 815400f3b6
2 changed files with 67 additions and 3 deletions

View File

@ -21,10 +21,10 @@ class ProjectManager:
"""Gestiona projectes: metadades a BD, fitxers a StorageService."""
@classmethod
def create_project(cls, name: str = "Unnamed Project", storage=None) -> Dict[str, Any]:
def create_project(cls, name: str = "Unnamed Project", storage=None, user_id: str = None) -> Dict[str, Any]:
project_id = str(uuid.uuid4())
with get_session() as db:
proj = ProjectModel(id=project_id, name=name, status="created")
proj = ProjectModel(id=project_id, name=name, status="created", user_id=user_id)
db.add(proj)
db.commit()
db.refresh(proj)
@ -59,10 +59,12 @@ class ProjectManager:
db.commit()
@classmethod
def list_projects(cls, limit: int = 50) -> List[Dict[str, Any]]:
def list_projects(cls, limit: int = 50, user_id: str = None) -> List[Dict[str, Any]]:
from sqlalchemy import select, desc
with get_session() as db:
stmt = select(ProjectModel).order_by(desc(ProjectModel.created_at)).limit(limit)
if user_id is not None:
stmt = stmt.where(ProjectModel.user_id == user_id)
projects = db.execute(stmt).scalars().all()
for p in projects:
db.expunge(p)
@ -302,6 +304,7 @@ class ProjectManager:
return {
"id": proj.id,
"project_id": proj.id,
"user_id": proj.user_id,
"name": proj.name,
"status": proj.status,
"analysis_summary": proj.analysis_summary,

View File

@ -0,0 +1,61 @@
"""Tests d'aïllament de projectes per user_id."""
import pytest
@pytest.fixture(autouse=True)
def _db(in_memory_db):
pass
def _make_user(email, role='user'):
from backend.app.models.db_models import UserModel
from backend.app.db import get_session
with get_session() as db:
user = UserModel(email=email, name=email, role=role, status='active')
db.add(user)
db.commit()
db.refresh(user)
return user.id
def test_list_projects_filtered_by_user():
from backend.app.models.project import ProjectManager
uid1 = _make_user('u1@test.com')
uid2 = _make_user('u2@test.com')
ProjectManager.create_project(name="U1-A", user_id=uid1)
ProjectManager.create_project(name="U1-B", user_id=uid1)
ProjectManager.create_project(name="U2-A", user_id=uid2)
u1_projects = ProjectManager.list_projects(user_id=uid1)
assert len(u1_projects) == 2
assert all(p['user_id'] == uid1 for p in u1_projects)
u2_projects = ProjectManager.list_projects(user_id=uid2)
assert len(u2_projects) == 1
assert u2_projects[0]['name'] == 'U2-A'
def test_list_projects_no_filter_returns_all():
from backend.app.models.project import ProjectManager
uid1 = _make_user('all1@test.com')
uid2 = _make_user('all2@test.com')
ProjectManager.create_project(name="P1", user_id=uid1)
ProjectManager.create_project(name="P2", user_id=uid2)
all_projects = ProjectManager.list_projects(user_id=None)
assert len(all_projects) >= 2
def test_create_project_assigns_user_id():
from backend.app.models.project import ProjectManager
uid = _make_user('owner@test.com')
proj = ProjectManager.create_project(name="Owned", user_id=uid)
assert proj['user_id'] == uid
def test_to_dict_includes_user_id():
from backend.app.models.project import ProjectManager
uid = _make_user('dict@test.com')
proj = ProjectManager.create_project(name="DictTest", user_id=uid)
assert 'user_id' in proj
assert proj['user_id'] == uid