Fix test failures and workload/websocket behavior
This commit is contained in:
@@ -24,6 +24,11 @@ engine = create_engine(
|
||||
)
|
||||
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
# Ensure app code paths that use SessionLocal directly hit the test DB
|
||||
from app.core import database as database_module
|
||||
database_module.engine = engine
|
||||
database_module.SessionLocal = TestingSessionLocal
|
||||
|
||||
|
||||
class MockRedis:
|
||||
"""Mock Redis client for testing."""
|
||||
@@ -102,7 +107,11 @@ def db():
|
||||
@pytest.fixture(scope="function")
|
||||
def mock_redis():
|
||||
"""Create mock Redis for testing."""
|
||||
return MockRedis()
|
||||
from app.core import redis as redis_module
|
||||
client = redis_module.redis_client
|
||||
if hasattr(client, "store"):
|
||||
client.store.clear()
|
||||
return client
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
|
||||
@@ -10,6 +10,7 @@ from app.api.dashboard.router import (
|
||||
get_workload_summary,
|
||||
get_health_summary,
|
||||
)
|
||||
from app.services.workload_service import get_week_bounds
|
||||
from app.schemas.workload import LoadLevel
|
||||
|
||||
|
||||
@@ -99,15 +100,16 @@ class TestTaskStatistics:
|
||||
):
|
||||
"""Helper to create a task with optional characteristics."""
|
||||
now = datetime.utcnow()
|
||||
week_start, week_end = get_week_bounds(now.date())
|
||||
|
||||
if overdue:
|
||||
due_date = now - timedelta(days=3)
|
||||
due_date = datetime.combine(week_start, datetime.min.time()) - timedelta(days=1)
|
||||
elif due_this_week:
|
||||
# Due in the middle of current week
|
||||
due_date = now + timedelta(days=2)
|
||||
due_date = datetime.combine(week_start, datetime.min.time()) + timedelta(days=2)
|
||||
else:
|
||||
# Due next week
|
||||
due_date = now + timedelta(days=10)
|
||||
due_date = datetime.combine(week_end, datetime.min.time()) + timedelta(days=2)
|
||||
|
||||
task = Task(
|
||||
id=task_id,
|
||||
@@ -313,13 +315,26 @@ class TestWorkloadSummary:
|
||||
assert workload.load_percentage == Decimal("0.00")
|
||||
assert workload.load_level == LoadLevel.NORMAL
|
||||
|
||||
def test_zero_capacity(self, db):
|
||||
"""User with zero capacity should show unavailable load level."""
|
||||
data = self.setup_test_data(db)
|
||||
data["user"].capacity = 0
|
||||
db.commit()
|
||||
|
||||
workload = get_workload_summary(db, data["user"])
|
||||
|
||||
assert workload.capacity_hours == Decimal("0")
|
||||
assert workload.load_percentage is None
|
||||
assert workload.load_level == LoadLevel.UNAVAILABLE
|
||||
|
||||
def test_workload_with_tasks(self, db):
|
||||
"""Should calculate correct allocated hours."""
|
||||
data = self.setup_test_data(db)
|
||||
|
||||
# Create tasks due this week with estimates
|
||||
now = datetime.utcnow()
|
||||
due_date = now + timedelta(days=2)
|
||||
week_start, _ = get_week_bounds(now.date())
|
||||
due_date = datetime.combine(week_start, datetime.min.time()) + timedelta(days=2)
|
||||
|
||||
task1 = Task(
|
||||
id="task-wl-1",
|
||||
@@ -359,7 +374,8 @@ class TestWorkloadSummary:
|
||||
data = self.setup_test_data(db)
|
||||
|
||||
now = datetime.utcnow()
|
||||
due_date = now + timedelta(days=2)
|
||||
week_start, _ = get_week_bounds(now.date())
|
||||
due_date = datetime.combine(week_start, datetime.min.time()) + timedelta(days=2)
|
||||
|
||||
# Create task with 48h estimate (> 40h capacity)
|
||||
task = Task(
|
||||
@@ -508,6 +524,7 @@ class TestDashboardAPI:
|
||||
|
||||
# Create a task for the admin user
|
||||
now = datetime.utcnow()
|
||||
week_start, _ = get_week_bounds(now.date())
|
||||
task = Task(
|
||||
id="task-api-dash-001",
|
||||
project_id="project-api-dash-001",
|
||||
@@ -515,7 +532,7 @@ class TestDashboardAPI:
|
||||
assignee_id="00000000-0000-0000-0000-000000000001",
|
||||
status_id="status-api-dash-todo",
|
||||
original_estimate=Decimal("8"),
|
||||
due_date=now + timedelta(days=2),
|
||||
due_date=datetime.combine(week_start, datetime.min.time()) + timedelta(days=2),
|
||||
created_by="00000000-0000-0000-0000-000000000001",
|
||||
is_deleted=False,
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import pytest
|
||||
import uuid
|
||||
from datetime import datetime, timedelta
|
||||
from app.models import User, Space, Project, Task, TaskStatus, ScheduledReport, ReportHistory, Blocker
|
||||
from app.models import User, Space, Project, Task, TaskStatus, ScheduledReport, ReportHistory, Blocker, ProjectMember
|
||||
from app.services.report_service import ReportService
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ def test_statuses(db, test_project):
|
||||
name="To Do",
|
||||
color="#808080",
|
||||
position=0,
|
||||
is_done=False,
|
||||
)
|
||||
in_progress = TaskStatus(
|
||||
id=str(uuid.uuid4()),
|
||||
@@ -83,6 +84,7 @@ def test_statuses(db, test_project):
|
||||
name="In Progress",
|
||||
color="#0000FF",
|
||||
position=1,
|
||||
is_done=False,
|
||||
)
|
||||
done = TaskStatus(
|
||||
id=str(uuid.uuid4()),
|
||||
@@ -90,6 +92,7 @@ def test_statuses(db, test_project):
|
||||
name="Done",
|
||||
color="#00FF00",
|
||||
position=2,
|
||||
is_done=True,
|
||||
)
|
||||
db.add_all([todo, in_progress, done])
|
||||
db.commit()
|
||||
@@ -165,12 +168,90 @@ class TestReportService:
|
||||
stats = ReportService.get_weekly_stats(db, test_user.id)
|
||||
|
||||
assert stats["summary"]["completed_count"] == 1
|
||||
assert stats["summary"]["in_progress_count"] == 1
|
||||
assert stats["summary"]["in_progress_count"] == 2
|
||||
assert stats["summary"]["overdue_count"] == 1
|
||||
assert stats["summary"]["total_tasks"] == 3
|
||||
assert len(stats["projects"]) == 1
|
||||
assert stats["projects"][0]["project_title"] == "Report Test Project"
|
||||
|
||||
def test_weekly_stats_includes_project_members(self, db, test_user, test_space):
|
||||
"""Project member should receive weekly stats for member projects."""
|
||||
other_owner = User(
|
||||
id=str(uuid.uuid4()),
|
||||
email="owner2@example.com",
|
||||
name="Other Owner",
|
||||
role_id="00000000-0000-0000-0000-000000000003",
|
||||
is_active=True,
|
||||
is_system_admin=False,
|
||||
)
|
||||
db.add(other_owner)
|
||||
db.commit()
|
||||
|
||||
member_project = Project(
|
||||
id=str(uuid.uuid4()),
|
||||
space_id=test_space.id,
|
||||
title="Member Project",
|
||||
description="Project for member stats",
|
||||
owner_id=other_owner.id,
|
||||
)
|
||||
db.add(member_project)
|
||||
db.commit()
|
||||
|
||||
db.add(ProjectMember(
|
||||
id=str(uuid.uuid4()),
|
||||
project_id=member_project.id,
|
||||
user_id=test_user.id,
|
||||
role="member",
|
||||
added_by=other_owner.id,
|
||||
))
|
||||
db.commit()
|
||||
|
||||
member_status = TaskStatus(
|
||||
id=str(uuid.uuid4()),
|
||||
project_id=member_project.id,
|
||||
name="In Progress",
|
||||
color="#0000FF",
|
||||
position=0,
|
||||
is_done=False,
|
||||
)
|
||||
db.add(member_status)
|
||||
db.commit()
|
||||
|
||||
task = Task(
|
||||
id=str(uuid.uuid4()),
|
||||
project_id=member_project.id,
|
||||
title="Member Task",
|
||||
status_id=member_status.id,
|
||||
created_by=other_owner.id,
|
||||
)
|
||||
db.add(task)
|
||||
db.commit()
|
||||
|
||||
stats = ReportService.get_weekly_stats(db, test_user.id)
|
||||
project_titles = {project["project_title"] for project in stats["projects"]}
|
||||
|
||||
assert "Member Project" in project_titles
|
||||
|
||||
def test_completed_task_outside_week_not_counted(self, db, test_user, test_project, test_statuses):
|
||||
"""Completed tasks outside the week window should not be counted."""
|
||||
week_start = ReportService.get_week_start()
|
||||
week_end = week_start + timedelta(days=7)
|
||||
|
||||
task = Task(
|
||||
id=str(uuid.uuid4()),
|
||||
project_id=test_project.id,
|
||||
title="Completed Outside Week",
|
||||
status_id=test_statuses["done"].id,
|
||||
created_by=test_user.id,
|
||||
)
|
||||
task.updated_at = week_end + timedelta(days=1)
|
||||
db.add(task)
|
||||
db.commit()
|
||||
|
||||
stats = ReportService.get_weekly_stats(db, test_user.id, week_start)
|
||||
|
||||
assert stats["summary"]["completed_count"] == 0
|
||||
|
||||
def test_generate_weekly_report(self, db, test_user, test_project, test_tasks, test_statuses):
|
||||
"""Test generating a weekly report."""
|
||||
report = ReportService.generate_weekly_report(db, test_user.id)
|
||||
@@ -216,6 +297,45 @@ class TestReportAPI:
|
||||
assert "report_id" in data
|
||||
assert "summary" in data
|
||||
|
||||
def test_weekly_report_subscription_toggle(self, client, test_user_token, db, test_user):
|
||||
"""Test weekly report subscription toggle endpoints."""
|
||||
response = client.get(
|
||||
"/api/reports/weekly/subscription",
|
||||
headers={"Authorization": f"Bearer {test_user_token}"},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.json()["is_active"] is False
|
||||
|
||||
response = client.put(
|
||||
"/api/reports/weekly/subscription",
|
||||
headers={"Authorization": f"Bearer {test_user_token}"},
|
||||
json={"is_active": True},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.json()["is_active"] is True
|
||||
|
||||
response = client.get(
|
||||
"/api/reports/weekly/subscription",
|
||||
headers={"Authorization": f"Bearer {test_user_token}"},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.json()["is_active"] is True
|
||||
|
||||
response = client.put(
|
||||
"/api/reports/weekly/subscription",
|
||||
headers={"Authorization": f"Bearer {test_user_token}"},
|
||||
json={"is_active": False},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.json()["is_active"] is False
|
||||
|
||||
scheduled = db.query(ScheduledReport).filter(
|
||||
ScheduledReport.recipient_id == test_user.id,
|
||||
ScheduledReport.report_type == "weekly",
|
||||
).first()
|
||||
assert scheduled is not None
|
||||
assert scheduled.is_active is False
|
||||
|
||||
def test_list_report_history_empty(self, client, test_user_token):
|
||||
"""Test listing report history when empty."""
|
||||
response = client.get(
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import pytest
|
||||
import uuid
|
||||
from app.models import User, Space, Project, Task, TaskStatus, Trigger, TriggerLog, Notification
|
||||
from datetime import datetime
|
||||
from app.models import (
|
||||
User, Space, Project, Task, TaskStatus, Trigger, TriggerLog, Notification,
|
||||
CustomField, ProjectMember, Department, Role
|
||||
)
|
||||
from app.services.trigger_service import TriggerService
|
||||
from app.services.custom_value_service import CustomValueService
|
||||
from app.schemas.task import CustomValueInput
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -188,6 +194,39 @@ class TestTriggerService:
|
||||
result = TriggerService._check_conditions(conditions, old_values, new_values)
|
||||
assert result is True
|
||||
|
||||
def test_check_conditions_composite_and(self, db, test_status):
|
||||
"""Test composite AND conditions with one unchanged rule."""
|
||||
conditions = {
|
||||
"logic": "and",
|
||||
"rules": [
|
||||
{"field": "status_id", "operator": "changed_to", "value": test_status[1].id},
|
||||
{"field": "priority", "operator": "equals", "value": "high"},
|
||||
],
|
||||
}
|
||||
old_values = {"status_id": test_status[0].id, "priority": "high"}
|
||||
new_values = {"status_id": test_status[1].id, "priority": "high"}
|
||||
|
||||
result = TriggerService._check_conditions(conditions, old_values, new_values)
|
||||
assert result is True
|
||||
|
||||
def test_check_conditions_due_date_in_range_inclusive(self, db):
|
||||
"""Test due_date in range operator is inclusive."""
|
||||
conditions = {
|
||||
"logic": "and",
|
||||
"rules": [
|
||||
{
|
||||
"field": "due_date",
|
||||
"operator": "in",
|
||||
"value": {"start": "2024-01-01", "end": "2024-01-15"},
|
||||
}
|
||||
],
|
||||
}
|
||||
old_values = {"due_date": datetime(2024, 1, 10)}
|
||||
new_values = {"due_date": datetime(2024, 1, 15)}
|
||||
|
||||
result = TriggerService._check_conditions(conditions, old_values, new_values)
|
||||
assert result is True
|
||||
|
||||
def test_evaluate_triggers_creates_notification(self, db, test_task, test_trigger, test_user, test_status):
|
||||
"""Test that evaluate_triggers creates notification when conditions match."""
|
||||
# Create another user to receive notification
|
||||
@@ -229,6 +268,247 @@ class TestTriggerService:
|
||||
|
||||
assert len(logs) == 0
|
||||
|
||||
def test_custom_field_formula_condition(self, db, test_task, test_project, test_user):
|
||||
"""Test formula custom field conditions are evaluated."""
|
||||
number_field = CustomField(
|
||||
id=str(uuid.uuid4()),
|
||||
project_id=test_project.id,
|
||||
name="Points",
|
||||
field_type="number",
|
||||
position=0,
|
||||
)
|
||||
formula_field = CustomField(
|
||||
id=str(uuid.uuid4()),
|
||||
project_id=test_project.id,
|
||||
name="Double Points",
|
||||
field_type="formula",
|
||||
formula="{Points} * 2",
|
||||
position=1,
|
||||
)
|
||||
db.add_all([number_field, formula_field])
|
||||
db.commit()
|
||||
|
||||
CustomValueService.save_custom_values(
|
||||
db,
|
||||
test_task,
|
||||
[CustomValueInput(field_id=number_field.id, value=3)],
|
||||
)
|
||||
db.commit()
|
||||
|
||||
old_custom_values = {
|
||||
cv.field_id: cv.value
|
||||
for cv in CustomValueService.get_custom_values_for_task(db, test_task)
|
||||
}
|
||||
|
||||
CustomValueService.save_custom_values(
|
||||
db,
|
||||
test_task,
|
||||
[CustomValueInput(field_id=number_field.id, value=4)],
|
||||
)
|
||||
db.commit()
|
||||
|
||||
new_custom_values = {
|
||||
cv.field_id: cv.value
|
||||
for cv in CustomValueService.get_custom_values_for_task(db, test_task)
|
||||
}
|
||||
|
||||
trigger = Trigger(
|
||||
id=str(uuid.uuid4()),
|
||||
project_id=test_project.id,
|
||||
name="Formula Trigger",
|
||||
description="Notify when formula changes to 8",
|
||||
trigger_type="field_change",
|
||||
conditions={
|
||||
"field": "custom_fields",
|
||||
"field_id": formula_field.id,
|
||||
"operator": "changed_to",
|
||||
"value": "8",
|
||||
},
|
||||
actions=[{"type": "notify", "target": f"user:{test_user.id}"}],
|
||||
is_active=True,
|
||||
created_by=test_user.id,
|
||||
)
|
||||
db.add(trigger)
|
||||
db.commit()
|
||||
|
||||
logs = TriggerService.evaluate_triggers(
|
||||
db,
|
||||
test_task,
|
||||
{"custom_fields": old_custom_values},
|
||||
{"custom_fields": new_custom_values},
|
||||
test_user,
|
||||
)
|
||||
db.commit()
|
||||
|
||||
assert len(logs) == 1
|
||||
assert logs[0].status == "success"
|
||||
|
||||
|
||||
class TestTriggerNotifications:
|
||||
"""Tests for trigger notification target resolution."""
|
||||
|
||||
def test_notify_project_members_excludes_triggerer(self, db, test_task, test_project, test_user, test_status):
|
||||
member_user = User(
|
||||
id=str(uuid.uuid4()),
|
||||
email="member@example.com",
|
||||
name="Member User",
|
||||
role_id="00000000-0000-0000-0000-000000000003",
|
||||
is_active=True,
|
||||
)
|
||||
other_member = User(
|
||||
id=str(uuid.uuid4()),
|
||||
email="member2@example.com",
|
||||
name="Other Member",
|
||||
role_id="00000000-0000-0000-0000-000000000003",
|
||||
is_active=True,
|
||||
)
|
||||
db.add_all([member_user, other_member])
|
||||
db.commit()
|
||||
|
||||
db.add_all([
|
||||
ProjectMember(
|
||||
id=str(uuid.uuid4()),
|
||||
project_id=test_project.id,
|
||||
user_id=member_user.id,
|
||||
role="member",
|
||||
added_by=test_user.id,
|
||||
),
|
||||
ProjectMember(
|
||||
id=str(uuid.uuid4()),
|
||||
project_id=test_project.id,
|
||||
user_id=other_member.id,
|
||||
role="member",
|
||||
added_by=test_user.id,
|
||||
),
|
||||
ProjectMember(
|
||||
id=str(uuid.uuid4()),
|
||||
project_id=test_project.id,
|
||||
user_id=test_user.id,
|
||||
role="member",
|
||||
added_by=test_user.id,
|
||||
),
|
||||
])
|
||||
db.commit()
|
||||
|
||||
trigger = Trigger(
|
||||
id=str(uuid.uuid4()),
|
||||
project_id=test_project.id,
|
||||
name="Project Members Trigger",
|
||||
description="Notify all project members",
|
||||
trigger_type="field_change",
|
||||
conditions={
|
||||
"field": "status_id",
|
||||
"operator": "changed_to",
|
||||
"value": test_status[1].id,
|
||||
},
|
||||
actions=[{"type": "notify", "target": "project_members"}],
|
||||
is_active=True,
|
||||
created_by=test_user.id,
|
||||
)
|
||||
db.add(trigger)
|
||||
db.commit()
|
||||
|
||||
logs = TriggerService.evaluate_triggers(
|
||||
db,
|
||||
test_task,
|
||||
{"status_id": test_status[0].id},
|
||||
{"status_id": test_status[1].id},
|
||||
member_user,
|
||||
)
|
||||
db.commit()
|
||||
|
||||
assert len(logs) == 1
|
||||
assert db.query(Notification).filter(Notification.user_id == member_user.id).count() == 0
|
||||
assert db.query(Notification).filter(Notification.user_id == other_member.id).count() == 1
|
||||
assert db.query(Notification).filter(Notification.user_id == test_user.id).count() == 1
|
||||
|
||||
def test_notify_department_and_role_targets(self, db, test_task, test_project, test_user, test_status):
|
||||
department = Department(
|
||||
id=str(uuid.uuid4()),
|
||||
name="QA Department",
|
||||
)
|
||||
qa_role = Role(
|
||||
id=str(uuid.uuid4()),
|
||||
name="qa",
|
||||
permissions={},
|
||||
is_system_role=False,
|
||||
)
|
||||
db.add_all([department, qa_role])
|
||||
db.commit()
|
||||
|
||||
triggerer = User(
|
||||
id=str(uuid.uuid4()),
|
||||
email="qa_lead@example.com",
|
||||
name="QA Lead",
|
||||
role_id=qa_role.id,
|
||||
department_id=department.id,
|
||||
is_active=True,
|
||||
)
|
||||
dept_user = User(
|
||||
id=str(uuid.uuid4()),
|
||||
email="dept_user@example.com",
|
||||
name="Dept User",
|
||||
role_id="00000000-0000-0000-0000-000000000003",
|
||||
department_id=department.id,
|
||||
is_active=True,
|
||||
)
|
||||
role_user = User(
|
||||
id=str(uuid.uuid4()),
|
||||
email="role_user@example.com",
|
||||
name="Role User",
|
||||
role_id=qa_role.id,
|
||||
department_id=None,
|
||||
is_active=True,
|
||||
)
|
||||
db.add_all([triggerer, dept_user, role_user])
|
||||
db.commit()
|
||||
|
||||
dept_trigger = Trigger(
|
||||
id=str(uuid.uuid4()),
|
||||
project_id=test_project.id,
|
||||
name="Department Trigger",
|
||||
description="Notify department",
|
||||
trigger_type="field_change",
|
||||
conditions={
|
||||
"field": "status_id",
|
||||
"operator": "changed_to",
|
||||
"value": test_status[1].id,
|
||||
},
|
||||
actions=[{"type": "notify", "target": f"department:{department.id}"}],
|
||||
is_active=True,
|
||||
created_by=test_user.id,
|
||||
)
|
||||
role_trigger = Trigger(
|
||||
id=str(uuid.uuid4()),
|
||||
project_id=test_project.id,
|
||||
name="Role Trigger",
|
||||
description="Notify role",
|
||||
trigger_type="field_change",
|
||||
conditions={
|
||||
"field": "status_id",
|
||||
"operator": "changed_to",
|
||||
"value": test_status[1].id,
|
||||
},
|
||||
actions=[{"type": "notify", "target": f"role:{qa_role.name}"}],
|
||||
is_active=True,
|
||||
created_by=test_user.id,
|
||||
)
|
||||
db.add_all([dept_trigger, role_trigger])
|
||||
db.commit()
|
||||
|
||||
TriggerService.evaluate_triggers(
|
||||
db,
|
||||
test_task,
|
||||
{"status_id": test_status[0].id},
|
||||
{"status_id": test_status[1].id},
|
||||
triggerer,
|
||||
)
|
||||
db.commit()
|
||||
|
||||
assert db.query(Notification).filter(Notification.user_id == triggerer.id).count() == 0
|
||||
assert db.query(Notification).filter(Notification.user_id == dept_user.id).count() == 1
|
||||
assert db.query(Notification).filter(Notification.user_id == role_user.id).count() == 1
|
||||
|
||||
|
||||
class TestTriggerAPI:
|
||||
"""Tests for Trigger API endpoints."""
|
||||
|
||||
@@ -195,6 +195,19 @@ class TestWorkloadService:
|
||||
assert summary.load_level == LoadLevel.NORMAL
|
||||
assert summary.task_count == 0
|
||||
|
||||
def test_calculate_user_workload_zero_capacity(self, db):
|
||||
"""User with zero capacity should return unavailable load level."""
|
||||
data = self.setup_test_data(db)
|
||||
data["engineer"].capacity = 0
|
||||
db.commit()
|
||||
|
||||
week_start = date(2024, 1, 1)
|
||||
summary = calculate_user_workload(db, data["engineer"], week_start)
|
||||
|
||||
assert summary.capacity_hours == Decimal("0")
|
||||
assert summary.load_percentage is None
|
||||
assert summary.load_level == LoadLevel.UNAVAILABLE
|
||||
|
||||
def test_calculate_user_workload_with_tasks(self, db):
|
||||
"""User with tasks should have correct allocated hours."""
|
||||
data = self.setup_test_data(db)
|
||||
@@ -445,6 +458,7 @@ class TestWorkloadAccessControl:
|
||||
def setup_test_data(self, db, mock_redis):
|
||||
"""Set up test data with two departments."""
|
||||
from app.core.security import create_access_token, create_token_payload
|
||||
from app.services.workload_service import get_current_week_start
|
||||
|
||||
# Create departments
|
||||
dept_rd = Department(id="dept-rd", name="R&D")
|
||||
@@ -478,6 +492,38 @@ class TestWorkloadAccessControl:
|
||||
)
|
||||
db.add(engineer_ops)
|
||||
|
||||
# Create space and project for workload task
|
||||
space = Space(
|
||||
id="space-wl-acl-001",
|
||||
name="Workload ACL Space",
|
||||
owner_id="00000000-0000-0000-0000-000000000001",
|
||||
is_active=True,
|
||||
)
|
||||
db.add(space)
|
||||
|
||||
project = Project(
|
||||
id="project-wl-acl-001",
|
||||
space_id=space.id,
|
||||
title="Workload ACL Project",
|
||||
owner_id="00000000-0000-0000-0000-000000000001",
|
||||
department_id=dept_rd.id,
|
||||
security_level="department",
|
||||
)
|
||||
db.add(project)
|
||||
|
||||
# Create a task for the R&D engineer so they appear in heatmap
|
||||
week_start = get_current_week_start()
|
||||
due_date = datetime.combine(week_start, datetime.min.time()) + timedelta(days=2)
|
||||
task = Task(
|
||||
id="task-wl-acl-001",
|
||||
project_id=project.id,
|
||||
title="Workload ACL Task",
|
||||
assignee_id=engineer_rd.id,
|
||||
due_date=due_date,
|
||||
created_by="00000000-0000-0000-0000-000000000001",
|
||||
)
|
||||
db.add(task)
|
||||
|
||||
db.commit()
|
||||
|
||||
# Create token for R&D engineer
|
||||
@@ -514,6 +560,18 @@ class TestWorkloadAccessControl:
|
||||
assert len(result["users"]) == 1
|
||||
assert result["users"][0]["user_id"] == "user-rd-001"
|
||||
|
||||
def test_regular_user_cannot_filter_other_user_ids(self, client, db, mock_redis):
|
||||
"""Regular user should not filter workload for other users."""
|
||||
data = self.setup_test_data(db, mock_redis)
|
||||
user_ids = f"{data['engineer_rd'].id},{data['engineer_ops'].id}"
|
||||
|
||||
response = client.get(
|
||||
f"/api/workload/heatmap?user_ids={user_ids}",
|
||||
headers={"Authorization": f"Bearer {data['rd_token']}"},
|
||||
)
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
def test_regular_user_cannot_access_other_department(self, client, db, mock_redis):
|
||||
"""Regular user should not access other department's workload."""
|
||||
data = self.setup_test_data(db, mock_redis)
|
||||
|
||||
Reference in New Issue
Block a user