test(backend): add coverage for services tasks and apis
This commit is contained in:
105
apps/reports/tests/test_api_views.py
Normal file
105
apps/reports/tests/test_api_views.py
Normal file
@@ -0,0 +1,105 @@
|
||||
from datetime import date
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.core.files.base import ContentFile
|
||||
from django.test import TestCase
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from apps.reports.models import ReportExportJob
|
||||
from apps.users.models import User
|
||||
from apps.workspaces.models import Workspace, WorkspaceMembership
|
||||
|
||||
|
||||
class ReportExportApiTests(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.owner = User.objects.create_user(
|
||||
mobile="09126660001",
|
||||
password="secret123",
|
||||
first_name="Owner",
|
||||
)
|
||||
cls.admin = User.objects.create_user(
|
||||
mobile="09126660002",
|
||||
password="secret123",
|
||||
first_name="Admin",
|
||||
)
|
||||
cls.workspace = Workspace.objects.create(name="Exports", owner=cls.owner)
|
||||
WorkspaceMembership.objects.create(
|
||||
workspace=cls.workspace,
|
||||
user=cls.admin,
|
||||
role=WorkspaceMembership.Role.ADMIN,
|
||||
is_active=True,
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
self.client = APIClient()
|
||||
|
||||
def test_create_export_job_enqueues_background_task(self):
|
||||
self.client.force_authenticate(user=self.owner)
|
||||
|
||||
filters = SimpleNamespace(
|
||||
workspace=self.workspace,
|
||||
period="this_month",
|
||||
from_date=date(2026, 4, 1),
|
||||
to_date=date(2026, 4, 30),
|
||||
user_id=None,
|
||||
client_id=None,
|
||||
project_id=None,
|
||||
tag_ids=[],
|
||||
)
|
||||
with patch("apps.reports.api.views.load_report_filters", return_value=filters):
|
||||
with patch("apps.reports.api.views.generate_report_export_task.delay") as delay:
|
||||
response = self.client.post(
|
||||
"/api/reports/exports/",
|
||||
{
|
||||
"workspace": str(self.workspace.id),
|
||||
"period": "this_month",
|
||||
"export_type": "excel",
|
||||
"language": "en",
|
||||
},
|
||||
format="json",
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 202)
|
||||
self.assertEqual(ReportExportJob.objects.count(), 1)
|
||||
delay.assert_called_once()
|
||||
|
||||
def test_list_only_returns_requesting_users_jobs(self):
|
||||
own_job = ReportExportJob.objects.create(
|
||||
requesting_user=self.owner,
|
||||
workspace=self.workspace,
|
||||
export_type=ReportExportJob.ExportType.EXCEL,
|
||||
filters={"workspace": str(self.workspace.id)},
|
||||
)
|
||||
ReportExportJob.objects.create(
|
||||
requesting_user=self.admin,
|
||||
workspace=self.workspace,
|
||||
export_type=ReportExportJob.ExportType.PDF,
|
||||
filters={"workspace": str(self.workspace.id)},
|
||||
)
|
||||
self.client.force_authenticate(user=self.owner)
|
||||
|
||||
response = self.client.get("/api/reports/exports/")
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(response.data), 1)
|
||||
self.assertEqual(response.data[0]["id"], str(own_job.id))
|
||||
|
||||
def test_download_returns_completed_file(self):
|
||||
job = ReportExportJob.objects.create(
|
||||
requesting_user=self.owner,
|
||||
workspace=self.workspace,
|
||||
export_type=ReportExportJob.ExportType.EXCEL,
|
||||
status=ReportExportJob.Status.COMPLETED,
|
||||
filters={"workspace": str(self.workspace.id)},
|
||||
file_name="report.xlsx",
|
||||
)
|
||||
job.file.save("reports/exports/report.xlsx", ContentFile(b"content"), save=False)
|
||||
job.save(update_fields=["file", "updated_at"])
|
||||
self.client.force_authenticate(user=self.owner)
|
||||
|
||||
response = self.client.get(f"/api/reports/exports/{job.id}/download/")
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn("attachment; filename=", response["Content-Disposition"])
|
||||
104
apps/reports/tests/test_exporters.py
Normal file
104
apps/reports/tests/test_exporters.py
Normal file
@@ -0,0 +1,104 @@
|
||||
from io import BytesIO
|
||||
|
||||
from django.test import TestCase
|
||||
from openpyxl import load_workbook
|
||||
|
||||
from apps.reports.services.export_i18n import build_export_locale
|
||||
from apps.reports.services.exporters import build_excel_report, build_pdf_report
|
||||
|
||||
|
||||
def make_report_data(*, user_name="Owner User", mobile="09129990001", hourly_rate=None):
|
||||
return {
|
||||
"scope": {
|
||||
"workspace": {"name": "Exports", "thumbnail_path": None},
|
||||
"period": "this_month",
|
||||
"from_date": "2026-04-01",
|
||||
"to_date": "2026-04-30",
|
||||
"user": {"name": user_name, "mobile": mobile} if user_name else None,
|
||||
},
|
||||
"summary": {
|
||||
"total_duration": "02:00:00",
|
||||
"billable_duration": "02:00:00",
|
||||
"non_billable_duration": "00:00:00",
|
||||
"income_totals": [{"amount": "30.00", "currency": "USD"}],
|
||||
},
|
||||
"days": [
|
||||
{
|
||||
"date": "2026-04-12",
|
||||
"billable_duration": "02:00:00",
|
||||
"non_billable_duration": "00:00:00",
|
||||
"total_duration": "02:00:00",
|
||||
"latest_hourly_rate": hourly_rate,
|
||||
"income_totals": [{"amount": "30.00", "currency": "USD"}],
|
||||
}
|
||||
],
|
||||
"clients": [
|
||||
{
|
||||
"name": "Acme",
|
||||
"billable_duration": "02:00:00",
|
||||
"non_billable_duration": "00:00:00",
|
||||
"total_duration": "02:00:00",
|
||||
"income_totals": [{"amount": "30.00", "currency": "USD"}],
|
||||
}
|
||||
],
|
||||
"projects": [],
|
||||
"tags": [],
|
||||
}
|
||||
|
||||
|
||||
class ReportExporterTests(TestCase):
|
||||
def test_excel_export_adds_per_user_sheets_and_daily_rate_column(self):
|
||||
locale = build_export_locale("en")
|
||||
report_data = make_report_data(
|
||||
hourly_rate={"amount": "15.00", "currency": "USD"},
|
||||
)
|
||||
per_user_reports = [
|
||||
make_report_data(user_name="Owner User", mobile="09129990001"),
|
||||
make_report_data(user_name="Team Mate", mobile="09129990002"),
|
||||
]
|
||||
|
||||
workbook = load_workbook(
|
||||
BytesIO(
|
||||
build_excel_report(
|
||||
report_data=report_data,
|
||||
locale=locale,
|
||||
per_user_reports=per_user_reports,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(workbook.sheetnames[0], "Overall Report")
|
||||
self.assertIn("Owner User", workbook.sheetnames[1])
|
||||
self.assertIn("Team Mate", workbook.sheetnames[2])
|
||||
|
||||
worksheet = workbook.active
|
||||
values = list(worksheet.iter_rows(values_only=True))
|
||||
|
||||
self.assertTrue(any(row[:2] == ("User", "Owner User") for row in values if row))
|
||||
self.assertTrue(any(row[:2] == ("Mobile", "09129990001") for row in values if row))
|
||||
|
||||
daily_header = next(row[:6] for row in values if row and row[0] == "Date")
|
||||
self.assertEqual(
|
||||
daily_header,
|
||||
(
|
||||
"Date",
|
||||
"Billable hours",
|
||||
"Non-billable hours",
|
||||
"Total hours",
|
||||
"Hourly rate",
|
||||
"Income",
|
||||
),
|
||||
)
|
||||
|
||||
daily_row = next(row[:6] for row in values if row and row[0] == "2026/04/12")
|
||||
self.assertEqual(daily_row[4], "15 USD")
|
||||
|
||||
def test_pdf_export_supports_persian_locale(self):
|
||||
locale = build_export_locale("fa")
|
||||
report_data = make_report_data(
|
||||
hourly_rate={"amount": "15.00", "currency": "USD"},
|
||||
)
|
||||
|
||||
content = build_pdf_report(report_data=report_data, locale=locale)
|
||||
|
||||
self.assertEqual(content[:4], b"%PDF")
|
||||
Reference in New Issue
Block a user