feat(test): added test for models
This commit is contained in:
1 parent
63d63d5e5c
commit
54df9af013
4 files changed
+259
-32
No files matched your search
@@ -0,0 +1,17 @@
|
||||
# Generated by Django 6.0 on 2025-12-11 15:57
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("src", "0003_notification"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="invoice",
|
||||
name="due_date",
|
||||
field=models.DateField(),
|
||||
),
|
||||
]
|
||||
@@ -23,7 +23,7 @@ class Invoice(TimeStampedModel):
|
||||
invoice_number = models.CharField(max_length=20, unique=True, editable=False)
|
||||
amount = models.DecimalField(max_digits=10, decimal_places=2)
|
||||
date_issued = models.DateField(auto_now_add=True)
|
||||
due_date = models.DateField(auto_now=True)
|
||||
due_date = models.DateField()
|
||||
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default="DRAFT")
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
from datetime import timedelta
|
||||
from decimal import Decimal
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.test import TestCase
|
||||
from django.utils import timezone
|
||||
|
||||
from src.models.clients import Client
|
||||
from src.models.finance import Invoice
|
||||
from src.models.productivity import TimeEntry
|
||||
from src.models.projects import Milestone, Project, Task
|
||||
from src.models.services import Service
|
||||
|
||||
|
||||
class ClientModelTest(TestCase):
|
||||
def test_short_code_generation(self):
|
||||
client1 = Client.objects.create(name="Sankalp Corp", email="s@example.com")
|
||||
self.assertEqual(client1.short_code, "SAN001")
|
||||
|
||||
client2 = Client.objects.create(name="Sankalp Inc", email="s2@example.com")
|
||||
self.assertEqual(client2.short_code, "SAN002")
|
||||
|
||||
client3 = Client.objects.create(name="Other Corp", email="o@example.com")
|
||||
self.assertEqual(client3.short_code, "OTH001")
|
||||
|
||||
|
||||
class ProjectModelTest(TestCase):
|
||||
def setUp(self):
|
||||
self.client = Client.objects.create(name="Test Client", email="c@example.com")
|
||||
|
||||
def test_deadline_validation(self):
|
||||
start_date = timezone.localdate()
|
||||
deadline = start_date - timedelta(days=1) # Deadline before start
|
||||
|
||||
project = Project(
|
||||
name="Bad Project",
|
||||
client=self.client,
|
||||
start_date=start_date,
|
||||
deadline=deadline,
|
||||
)
|
||||
with self.assertRaises(ValidationError):
|
||||
project.clean()
|
||||
|
||||
def test_milestone_str(self):
|
||||
project = Project.objects.create(name="P1", client=self.client)
|
||||
milestone = Milestone.objects.create(project=project, title="M1")
|
||||
self.assertIn("✗", str(milestone))
|
||||
|
||||
milestone.is_completed = True
|
||||
milestone.save()
|
||||
self.assertIn("✓", str(milestone))
|
||||
|
||||
def test_task_due_date_validation(self):
|
||||
yesterday = timezone.localdate() - timedelta(days=1)
|
||||
task = Task(
|
||||
project=Project.objects.create(name="P1", client=self.client),
|
||||
title="Old Task",
|
||||
due_date=yesterday,
|
||||
)
|
||||
with self.assertRaises(ValidationError):
|
||||
task.clean()
|
||||
|
||||
|
||||
class ServiceModelTest(TestCase):
|
||||
def setUp(self):
|
||||
self.client = Client.objects.create(name="Test Client", email="c@example.com")
|
||||
|
||||
def test_expiry_date_validation(self):
|
||||
start_date = timezone.localdate()
|
||||
expiry_date = start_date - timedelta(days=1)
|
||||
|
||||
service = Service(
|
||||
client=self.client,
|
||||
name="Bad Service",
|
||||
service_type="DOMAIN",
|
||||
start_date=start_date,
|
||||
expiry_date=expiry_date,
|
||||
)
|
||||
with self.assertRaises(ValidationError):
|
||||
service.clean()
|
||||
|
||||
|
||||
class FinanceModelTest(TestCase):
|
||||
def setUp(self):
|
||||
self.client = Client.objects.create(name="Test Client", email="c@example.com")
|
||||
self.project = Project.objects.create(name="P1", client=self.client)
|
||||
|
||||
def test_invoice_number_generation(self):
|
||||
# Client short code is TES001
|
||||
inv1 = Invoice.objects.create(
|
||||
client=self.client,
|
||||
project=self.project,
|
||||
amount=Decimal("100.00"),
|
||||
due_date=timezone.localdate(),
|
||||
)
|
||||
self.assertEqual(inv1.invoice_number, "TES001-0001")
|
||||
|
||||
inv2 = Invoice.objects.create(
|
||||
client=self.client,
|
||||
project=self.project,
|
||||
amount=Decimal("200.00"),
|
||||
due_date=timezone.localdate(),
|
||||
)
|
||||
self.assertEqual(inv2.invoice_number, "TES001-0002")
|
||||
|
||||
|
||||
class ProductivityModelTest(TestCase):
|
||||
def setUp(self):
|
||||
self.client = Client.objects.create(name="Test Client", email="c@example.com")
|
||||
self.project = Project.objects.create(name="P1", client=self.client)
|
||||
|
||||
def test_duration_calculation(self):
|
||||
start = timezone.now()
|
||||
end = start + timedelta(hours=2, minutes=30)
|
||||
|
||||
entry = TimeEntry.objects.create(
|
||||
project=self.project, description="Coding", start_time=start, end_time=end
|
||||
)
|
||||
|
||||
# Refresh to get DB value if needed, but save() sets it on instance
|
||||
self.assertEqual(entry.duration, timedelta(hours=2, minutes=30))
|
||||
|
||||
def test_clean_validation(self):
|
||||
start = timezone.now()
|
||||
end = start - timedelta(hours=1)
|
||||
|
||||
entry = TimeEntry(
|
||||
project=self.project,
|
||||
description="Time Travel",
|
||||
start_time=start,
|
||||
end_time=end,
|
||||
)
|
||||
with self.assertRaises(ValidationError):
|
||||
entry.clean()
|
||||
+107
-31
@@ -1,50 +1,126 @@
|
||||
from datetime import timedelta
|
||||
from decimal import Decimal
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.core import mail
|
||||
from django.test import TestCase
|
||||
from django.utils import timezone
|
||||
|
||||
from src.models.clients import Client
|
||||
from src.models.finance import Invoice
|
||||
from src.models.notifications import Notification, check_and_send_notifications
|
||||
from src.models.projects import Project
|
||||
from src.models.productivity import TimeEntry
|
||||
from src.models.projects import Milestone, Project, Task
|
||||
from src.models.services import Service
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class NotificationTests(TestCase):
|
||||
def test_send_deadline_notifications(self):
|
||||
# Setup
|
||||
User.objects.create_superuser("admin", "admin@example.com", "password")
|
||||
client = Client.objects.create(name="Test Client", email="client@example.com")
|
||||
def setUp(self):
|
||||
self.admin = User.objects.create_superuser(
|
||||
"admin", "admin@example.com", "password"
|
||||
)
|
||||
self.client = Client.objects.create(
|
||||
name="Test Client", email="client@example.com"
|
||||
)
|
||||
self.tomorrow = timezone.localdate() + timedelta(days=1)
|
||||
|
||||
# Project due tomorrow
|
||||
tomorrow = timezone.localdate() + timezone.timedelta(days=1)
|
||||
Project.objects.create(name="Test Project", client=client, deadline=tomorrow)
|
||||
def test_project_deadline_notification(self):
|
||||
with self.settings(ADMINS=[("Admin", "admin@example.com")]):
|
||||
Project.objects.create(
|
||||
name="Test Project", client=self.client, deadline=self.tomorrow
|
||||
)
|
||||
check_and_send_notifications()
|
||||
|
||||
# Run notification check
|
||||
# 1 to client, 1 to admin
|
||||
self.assertEqual(len(mail.outbox), 2)
|
||||
self.assertTrue(
|
||||
Notification.objects.filter(
|
||||
subject__contains="Project Deadline"
|
||||
).exists()
|
||||
)
|
||||
|
||||
def test_milestone_due_notification(self):
|
||||
with self.settings(ADMINS=[("Admin", "admin@example.com")]):
|
||||
project = Project.objects.create(name="Test Project", client=self.client)
|
||||
Milestone.objects.create(
|
||||
project=project, title="Test Milestone", due_date=self.tomorrow
|
||||
)
|
||||
check_and_send_notifications()
|
||||
|
||||
# 1 to client, 1 to admin
|
||||
self.assertEqual(len(mail.outbox), 2)
|
||||
self.assertTrue(
|
||||
Notification.objects.filter(subject__contains="Milestone Due").exists()
|
||||
)
|
||||
|
||||
def test_task_due_notification(self):
|
||||
with self.settings(ADMINS=[("Admin", "admin@example.com")]):
|
||||
project = Project.objects.create(name="Test Project", client=self.client)
|
||||
Task.objects.create(
|
||||
project=project, title="Test Task", due_date=self.tomorrow
|
||||
)
|
||||
check_and_send_notifications()
|
||||
|
||||
# 1 to admin only
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
self.assertEqual(mail.outbox[0].to, ["admin@example.com"])
|
||||
self.assertTrue(
|
||||
Notification.objects.filter(subject__contains="Task Due").exists()
|
||||
)
|
||||
|
||||
def test_service_expiry_notification(self):
|
||||
Service.objects.create(
|
||||
client=self.client,
|
||||
name="Test Service",
|
||||
service_type="DOMAIN",
|
||||
expiry_date=self.tomorrow,
|
||||
)
|
||||
check_and_send_notifications()
|
||||
|
||||
# Verify emails sent
|
||||
# Expecting 2 emails: 1 to client, 1 to admin
|
||||
self.assertEqual(len(mail.outbox), 2)
|
||||
|
||||
# Verify Notification objects created
|
||||
self.assertEqual(Notification.objects.count(), 2)
|
||||
|
||||
client_notif = Notification.objects.get(recipient="client@example.com")
|
||||
self.assertIn("Project Deadline Reminder", client_notif.subject)
|
||||
|
||||
admin_notif = Notification.objects.get(recipient="admin@example.com")
|
||||
self.assertIn("Admin Alert", admin_notif.subject)
|
||||
|
||||
def test_no_notifications_if_not_due(self):
|
||||
# Setup
|
||||
client = Client.objects.create(name="Test Client", email="client@example.com")
|
||||
|
||||
# Project due day after tomorrow
|
||||
day_after = timezone.localdate() + timezone.timedelta(days=2)
|
||||
Project.objects.create(name="Future Project", client=client, deadline=day_after)
|
||||
# 1 to client only
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
self.assertEqual(mail.outbox[0].to, ["client@example.com"])
|
||||
self.assertTrue(
|
||||
Notification.objects.filter(subject__contains="Service Expiry").exists()
|
||||
)
|
||||
|
||||
def test_invoice_due_notification(self):
|
||||
project = Project.objects.create(name="Test Project", client=self.client)
|
||||
Invoice.objects.create(
|
||||
client=self.client,
|
||||
project=project,
|
||||
amount=Decimal("100.00"),
|
||||
due_date=self.tomorrow,
|
||||
status="SENT",
|
||||
)
|
||||
check_and_send_notifications()
|
||||
|
||||
self.assertEqual(len(mail.outbox), 0)
|
||||
self.assertEqual(Notification.objects.count(), 0)
|
||||
# 1 to client
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
self.assertEqual(mail.outbox[0].to, ["client@example.com"])
|
||||
self.assertTrue(
|
||||
Notification.objects.filter(subject__contains="Invoice Due").exists()
|
||||
)
|
||||
|
||||
def test_time_entry_notification(self):
|
||||
with self.settings(ADMINS=[("Admin", "admin@example.com")]):
|
||||
project = Project.objects.create(name="Test Project", client=self.client)
|
||||
now = timezone.now()
|
||||
TimeEntry.objects.create(
|
||||
project=project,
|
||||
description="Work done",
|
||||
start_time=now - timedelta(hours=2),
|
||||
end_time=now - timedelta(hours=1),
|
||||
)
|
||||
check_and_send_notifications()
|
||||
|
||||
# 1 to admin
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
self.assertEqual(mail.outbox[0].to, ["admin@example.com"])
|
||||
self.assertTrue(
|
||||
Notification.objects.filter(
|
||||
subject__contains="Time Entry Logged"
|
||||
).exists()
|
||||
)
|
||||
Reference in new issue
Block a user