• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gcivil-nyu-org / wed-fall24-team1 / #52

12 Nov 2024 08:25AM UTC coverage: 88.737% (+3.8%) from 84.961%
#52

push

coveralls-python

shub-garg
UI chnages and test cases

313 of 326 new or added lines in 2 files covered. (96.01%)

60 existing lines in 2 files now uncovered.

2718 of 3063 relevant lines covered (88.74%)

0.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.16
/src/services/tests.py
1
# from django.test import TestCase
2
import uuid
1✔
3
from decimal import Decimal
1✔
4
from accounts.models import CustomUser
1✔
5
from django.test import TestCase, Client
1✔
6
from django.urls import reverse
1✔
7
from django.contrib.auth import get_user_model
1✔
8
from public_service_finder.utils.enums.service_status import ServiceStatus
1✔
9

1✔
10
from .forms import ServiceForm, DescriptionFormSet, ReviewResponseForm
1✔
11
from .models import ServiceDTO, ReviewDTO
1✔
12
from .repositories import ReviewRepository, ServiceRepository
1✔
13

1✔
14
from unittest.mock import patch, MagicMock
15

1✔
16

17
class ServiceViewsTestCase(TestCase):
18
    def setUp(self):
1✔
19
        self.client = Client()
1✔
20
        self.service_provider = CustomUser.objects.create_user(
1✔
21
            username="provider",
1✔
22
            email="provider@email.com",
23
            password="testpass123",
24
            user_type="service_provider",
25
        )
26
        self.regular_user = CustomUser.objects.create_user(
27
            username="regular",
1✔
28
            email="regular@email.com",
29
            password="testpass123",
30
            user_type="user",
31
        )
32
        self.service_repo = ServiceRepository()
33

1✔
34
    def tearDown(self):
35
        super().tearDown()
1✔
36
        CustomUser.objects.all().delete()
1✔
37

1✔
38
    def test_service_list_view(self):
39
        self.client.login(username="provider", password="testpass123")
1✔
40
        with patch.object(ServiceRepository, "get_services_by_provider") as mock_get:
1✔
41
            mock_get.return_value = [
1✔
42
                ServiceDTO(
1✔
43
                    id=str(uuid.uuid4()),
44
                    name="Service 1",
45
                    address="Addr1",
46
                    category="Metal Health Center",
47
                    provider_id=str(self.service_provider.id),
48
                    latitude=None,
49
                    longitude=None,
50
                    ratings=None,
51
                    description=None,
52
                    service_created_timestamp="2022-01-01T12:00:00Z",
53
                    service_status=ServiceStatus.APPROVED.value,
54
                    service_approved_timestamp="2022-01-01T12:00:00Z",
55
                    is_active=False,
56
                ),
57
                ServiceDTO(
58
                    id=str(uuid.uuid4()),
59
                    name="Service 2",
60
                    address="Addr2",
61
                    category="Food Pantry",
62
                    provider_id=str(self.service_provider.id),
63
                    ratings=None,
64
                    latitude=None,
65
                    longitude=None,
66
                    description=None,
67
                    service_created_timestamp="2022-01-01T12:00:00Z",
68
                    service_status=ServiceStatus.APPROVED.value,
69
                    service_approved_timestamp="2022-01-01T12:00:00Z",
70
                    is_active=False,
71
                ),
72
            ]
73
            response = self.client.get(reverse("services:list"))
74
            self.assertEqual(response.status_code, 200)
1✔
75
            self.assertTemplateUsed(response, "service_list.html")
1✔
76
            self.assertEqual(len(response.context["services"]), 2)
1✔
77

1✔
78
    def test_service_list_view_permission_denied(self):
79
        self.client.login(username="regular", password="testpass123")
1✔
80
        resp = self.client.get(reverse("services:list"))
1✔
81
        self.assertEqual(resp.status_code, 403)
1✔
82

1✔
83
    def test_service_create_view_get(self):
84
        self.client.login(username="provider", password="testpass123")
1✔
85
        response = self.client.get(reverse("services:create"))
1✔
86
        self.assertEqual(response.status_code, 200)
1✔
87
        self.assertTemplateUsed(response, "service_form.html")
1✔
88
        self.assertIsInstance(response.context["form"], ServiceForm)
1✔
89
        self.assertIsInstance(
1✔
90
            response.context["description_formset"], DescriptionFormSet
1✔
91
        )
92

93
    def test_service_create_view_post_success(self):
94
        self.client.login(username="provider", password="testpass123")
1✔
95
        data = {
1✔
96
            "name": "New Service",
1✔
97
            "address": "123 Test St",
98
            "latitude": "40.7128",
99
            "longitude": "-74.0060",
100
            "category": "Mental Health Center",
101
            "description-0-key": "hours",
102
            "description-0-value": "9-5",
103
            "description-TOTAL_FORMS": "1",
104
            "description-INITIAL_FORMS": "0",
105
            "description-MIN_NUM_FORMS": "0",
106
            "description-MAX_NUM_FORMS": "1000",
107
        }
108
        with patch.object(ServiceRepository, "create_service") as mock_create:
109
            mock_create.return_value = True
1✔
110
            response = self.client.post(reverse("services:create"), data)
1✔
111
            self.assertRedirects(response, reverse("services:list"))
1✔
112

1✔
113
    def test_service_edit_view_get(self):
114
        self.client.login(username="provider", password="testpass123")
1✔
115
        service_id = str(uuid.uuid4())
1✔
116
        with patch.object(ServiceRepository, "get_service") as mock_get:
1✔
117
            mock_get.return_value = ServiceDTO(
1✔
118
                id=service_id,
1✔
119
                name="Test Service",
120
                provider_id=str(self.service_provider.id),
121
                category="MENTAL",
122
                address="123 Test St",
123
                latitude=Decimal(40.71),
124
                longitude=Decimal(-74.32),
125
                ratings=Decimal("4.5"),
126
                description={"hours": "9-5"},
127
                service_created_timestamp="2022-01-01T12:00:00Z",
128
                service_status=ServiceStatus.APPROVED.value,
129
                service_approved_timestamp="2022-01-01T12:00:00Z",
130
                is_active=True,
131
            )
132
            response = self.client.get(reverse("services:edit", args=[service_id]))
133
            self.assertEqual(response.status_code, 200)
1✔
134
            self.assertTemplateUsed(response, "service_form.html")
1✔
135

1✔
136
    def test_service_delete_view_get(self):
137
        self.client.login(username="provider", password="testpass123")
1✔
138
        service_id = str(uuid.uuid4())
1✔
139
        with patch.object(ServiceRepository, "get_service") as mock_get:
1✔
140
            mock_get.return_value = ServiceDTO(
1✔
141
                id=service_id,
1✔
142
                name="Test Service",
143
                address="Test Address",
144
                latitude=None,
145
                longitude=None,
146
                ratings=None,
147
                description=None,
148
                category="MENTAL",
149
                provider_id=str(self.service_provider.id),
150
                service_created_timestamp="2022-01-01T12:00:00Z",
151
                service_status=ServiceStatus.APPROVED.value,
152
                service_approved_timestamp="2022-01-01T12:00:00Z",
153
                is_active=False,
154
            )
155
            response = self.client.post(reverse("services:delete", args=[service_id]))
156
            self.assertEqual(response.status_code, 302)
1✔
157

1✔
158
    def test_service_delete_view_post(self):
159
        self.client.login(username="provider", password="testpass123")
1✔
160
        service_id = str(uuid.uuid4())
1✔
161
        with patch.object(ServiceRepository, "get_service") as mock_get, patch.object(
1✔
162
            ServiceRepository, "delete_service"
1✔
163
        ) as mock_delete:
164
            mock_get.return_value = ServiceDTO(
165
                id=service_id,
1✔
166
                name="Test Service",
167
                address="Test Address",
168
                latitude=None,
169
                longitude=None,
170
                ratings=None,
171
                description=None,
172
                category="MENTAL",
173
                provider_id=str(self.service_provider.id),
174
                service_created_timestamp="2022-01-01T12:00:00Z",
175
                service_status=ServiceStatus.APPROVED.value,
176
                service_approved_timestamp="2022-01-01T12:00:00Z",
177
                is_active=False,
178
            )
179
            mock_delete.return_value = True
180
            response = self.client.post(reverse("services:delete", args=[service_id]))
1✔
181
            self.assertRedirects(response, reverse("services:list"))
1✔
182

1✔
183
    def test_service_delete_view_permission_denied(self):
184
        self.client.login(username="regular", password="testpass123")
1✔
185
        service_id = str(uuid.uuid4())
1✔
186
        resp = self.client.get(reverse("services:delete", args=[service_id]))
1✔
187
        self.assertEqual(resp.status_code, 403)
1✔
188

1✔
189

190
User = get_user_model()
191

1✔
192

193
class ReviewListViewTest(TestCase):
194
    def setUp(self):
1✔
195
        self.client = Client()
1✔
196
        self.user = User.objects.create_user(
1✔
197
            username="testuser", password="testpass", email="testuser@example.com"
1✔
198
        )
199
        self.service_id = "service-id-123"
200
        self.client.login(username="testuser", password="testpass")
1✔
201

1✔
202
        # Sample service and reviews
203
        self.service = ServiceDTO(
204
            id=self.service_id,
1✔
205
            name="Test Service",
206
            provider_id=str(self.user.id),
207
            address="123 Main St",
208
            category="Category",
209
            latitude=0.0,
210
            longitude=0.0,
211
            ratings=5.0,
212
            description="Description",
213
            service_status="Active",
214
            service_created_timestamp="2021-01-01T00:00:00Z",
215
            service_approved_timestamp="2021-01-01T00:00:00Z",
216
            is_active=True,
217
        )
218
        self.review = ReviewDTO(
219
            review_id="review-id-123",
1✔
220
            service_id=self.service_id,
221
            user_id="user-id-123",
222
            username="reviewer",
223
            rating_stars=5,
224
            rating_message="Great service!",
225
            timestamp="2021-01-01T12:00:00Z",
226
        )
227

228
    @patch("services.views.review_repo.get_reviews_for_service")
229
    @patch("services.views.service_repo.get_service")
1✔
230
    def test_review_list_view_authenticated(self, mock_get_service, mock_get_reviews):
1✔
231
        # Mock repository methods
1✔
232
        mock_get_service.return_value = self.service
233
        mock_get_reviews.return_value = [self.review]
1✔
234

1✔
235
        response = self.client.get(
236
            reverse("services:review_list", args=[self.service_id])
1✔
237
        )
238
        self.assertEqual(response.status_code, 200)
239
        self.assertTemplateUsed(response, "review_list.html")
1✔
240
        self.assertEqual(response.context["service"], self.service)
1✔
241
        self.assertEqual(response.context["reviews"], [self.review])
1✔
242

1✔
243
    @patch("services.views.service_repo.get_service")
244
    def test_review_list_view_service_not_found(self, mock_get_service):
1✔
245
        # Mock service not found
1✔
246
        mock_get_service.return_value = None
247

1✔
248
        response = self.client.get(
249
            reverse("services:review_list", args=[self.service_id])
1✔
250
        )
251
        self.assertEqual(response.status_code, 404)
252

1✔
253

254
class RespondToReviewViewTest(TestCase):
255
    def setUp(self):
1✔
256
        self.client = Client()
1✔
257
        self.provider = User.objects.create_user(
1✔
258
            username="provider",
1✔
259
            password="testpass",
260
            email="provider@example.com",
261
            user_type="service_provider",
262
        )
263
        self.regular_user = User.objects.create_user(
264
            username="regular",
1✔
265
            password="testpass",
266
            email="regular@example.com",
267
            user_type="user",
268
        )
269
        self.service_id = "service-id-123"
270
        self.review_id = "review-id-123"
1✔
271

1✔
272
        # Sample service and review data
273
        self.service = ServiceDTO(
274
            id=self.service_id,
1✔
275
            name="Test Service",
276
            provider_id=str(self.provider.id),
277
            address="123 Main St",
278
            category="Category",
279
            latitude=0.0,
280
            longitude=0.0,
281
            ratings=5.0,
282
            description="Description",
283
            service_status="Active",
284
            service_created_timestamp="2021-01-01T00:00:00Z",
285
            service_approved_timestamp="2021-01-01T00:00:00Z",
286
            is_active=True,
287
        )
288
        self.review = ReviewDTO(
289
            review_id=self.review_id,
1✔
290
            service_id=self.service_id,
291
            user_id=str(self.regular_user.id),
292
            username="regular",
293
            rating_stars=5,
294
            rating_message="Excellent service",
295
            timestamp="2021-01-01T12:00:00Z",
296
            responseText="",
297
            responded_at="",
298
        )
299

300
    @patch("services.views.service_repo.get_service")
301
    @patch("services.views.review_repo.get_review")
1✔
302
    def test_respond_to_review_get_provider(self, mock_get_review, mock_get_service):
1✔
303
        # Service provider can access the response form
1✔
304
        self.client.login(username="provider", password="testpass")
305
        mock_get_service.return_value = self.service
1✔
306
        mock_get_review.return_value = self.review
1✔
307

1✔
308
        response = self.client.get(
309
            reverse(
1✔
310
                "services:respond_to_review", args=[self.service_id, self.review_id]
311
            )
312
        )
313

314
        self.assertEqual(response.status_code, 200)
315
        self.assertTemplateUsed(response, "respond_to_review.html")
1✔
316
        self.assertIn("service", response.context)
1✔
317
        self.assertIn("review", response.context)
1✔
318
        self.assertIsInstance(response.context["form"], ReviewResponseForm)
1✔
319

1✔
320
    @patch("services.views.service_repo.get_service")
321
    @patch("services.views.review_repo.get_review")
1✔
322
    @patch("services.views.review_repo.respond_to_review")
1✔
323
    def test_respond_to_review_post_success(
1✔
324
        self, mock_respond_to_review, mock_get_review, mock_get_service
1✔
325
    ):
326
        # Service provider successfully submits a response
327
        self.client.login(username="provider", password="testpass")
328
        mock_get_service.return_value = self.service
1✔
329
        mock_get_review.return_value = self.review
1✔
330
        mock_respond_to_review.return_value = True
1✔
331

1✔
332
        response = self.client.post(
333
            reverse(
1✔
334
                "services:respond_to_review", args=[self.service_id, self.review_id]
335
            ),
336
            {"responseText": "Thank you for your feedback!"},
337
        )
338

339
        self.assertEqual(response.status_code, 200)
340
        self.assertJSONEqual(
1✔
341
            response.content, {"status": "success", "message": "Response saved"}
1✔
342
        )
343
        mock_respond_to_review.assert_called_once_with(
344
            self.review_id, "Thank you for your feedback!"
1✔
345
        )
346

347
    @patch("services.views.service_repo.get_service")
348
    @patch("services.views.review_repo.get_review")
1✔
349
    def test_respond_to_review_get_permission_denied(
1✔
350
        self, mock_get_review, mock_get_service
1✔
351
    ):
352
        # Regular user should not be able to access respond to review
353
        self.client.login(username="regular", password="testpass")
354
        mock_get_service.return_value = self.service
1✔
355
        mock_get_review.return_value = self.review
1✔
356

1✔
357
        response = self.client.get(
358
            reverse(
1✔
359
                "services:respond_to_review", args=[self.service_id, self.review_id]
360
            )
361
        )
362

363
        self.assertEqual(response.status_code, 403)
364
        self.assertJSONEqual(
1✔
365
            response.content, {"status": "error", "message": "Permission denied"}
1✔
366
        )
367

368
    @patch("services.views.service_repo.get_service")
369
    @patch("services.views.review_repo.get_review")
1✔
370
    def test_respond_to_review_post_permission_denied(
1✔
371
        self, mock_get_review, mock_get_service
1✔
372
    ):
373
        # Regular user attempts to POST a response
374
        self.client.login(username="regular", password="testpass")
375
        mock_get_service.return_value = self.service
1✔
376
        mock_get_review.return_value = self.review
1✔
377

1✔
378
        response = self.client.post(
379
            reverse(
1✔
380
                "services:respond_to_review", args=[self.service_id, self.review_id]
381
            ),
382
            {"responseText": "Trying to respond"},
383
        )
384

385
        self.assertEqual(response.status_code, 403)
386
        self.assertJSONEqual(
1✔
387
            response.content, {"status": "error", "message": "Permission denied"}
1✔
388
        )
389

390
    @patch("services.views.service_repo.get_service")
391
    @patch("services.views.review_repo.get_review")
1✔
392
    def test_respond_to_review_service_not_found(
1✔
393
        self, mock_get_review, mock_get_service
1✔
394
    ):
395
        # Service does not exist
396
        self.client.login(username="provider", password="testpass")
397
        mock_get_service.return_value = None  # Simulate service not found
1✔
398

1✔
399
        response = self.client.get(
400
            reverse(
1✔
401
                "services:respond_to_review", args=[self.service_id, self.review_id]
402
            )
403
        )
404

405
        self.assertEqual(response.status_code, 404)
406

1✔
407
    @patch("services.views.service_repo.get_service")
408
    @patch("services.views.review_repo.get_review")
1✔
409
    def test_respond_to_review_review_not_found(
1✔
410
        self, mock_get_review, mock_get_service
1✔
411
    ):
412
        # Review does not exist
413
        self.client.login(username="provider", password="testpass")
414
        mock_get_service.return_value = self.service
1✔
415
        mock_get_review.return_value = None  # Simulate review not found
1✔
416

1✔
417
        response = self.client.get(
418
            reverse(
1✔
419
                "services:respond_to_review", args=[self.service_id, self.review_id]
420
            )
421
        )
422

423
        self.assertEqual(response.status_code, 404)
424

1✔
425
    @patch("services.views.service_repo.get_service")
426
    @patch("services.views.review_repo.get_review")
1✔
427
    def test_respond_to_review_invalid_form(self, mock_get_review, mock_get_service):
1✔
428
        # Invalid form data submitted
1✔
429
        self.client.login(username="provider", password="testpass")
430
        mock_get_service.return_value = self.service
1✔
431
        mock_get_review.return_value = self.review
1✔
432

1✔
433
        response = self.client.post(
434
            reverse(
1✔
435
                "services:respond_to_review", args=[self.service_id, self.review_id]
436
            ),
437
            {"responseText": ""},  # Empty responseText is invalid
438
        )
439

440
        self.assertEqual(response.status_code, 400)
441
        self.assertJSONEqual(
1✔
442
            response.content, {"status": "error", "message": "Invalid form data"}
1✔
443
        )
444

445
    def test_respond_to_review_not_authenticated(self):
446
        # Unauthenticated user tries to access the view
1✔
447
        response = self.client.get(
448
            reverse(
1✔
449
                "services:respond_to_review", args=[self.service_id, self.review_id]
450
            )
451
        )
452

453
        # Should redirect to login page
454
        self.assertEqual(response.status_code, 302)
455
        self.assertTrue(response.url.startswith("/accounts/login/"))
1✔
456

1✔
457

458
class ServiceRepositoryTestCase(TestCase):
459
    def setUp(self):
1✔
460
        # Instantiate the ServiceRepository to be tested
1✔
461
        self.service_repo = ServiceRepository()
462

1✔
463
        # Sample service data
464
        self.sample_service = ServiceDTO(
465
            id=str(uuid.uuid4()),
1✔
466
            name="Test Service",
467
            address="123 Test St",
468
            category="Mental Health Center",
469
            provider_id="test_provider_id",
470
            latitude=Decimal("40.7128"),
471
            longitude=Decimal("-74.0060"),
472
            ratings=Decimal("4.5"),
473
            description={"hours": "9-5"},
474
            service_created_timestamp="2022-01-01T12:00:00Z",
475
            service_status=ServiceStatus.APPROVED.value,
476
            service_approved_timestamp="2022-01-01T12:00:00Z",
477
            is_active=True,
478
        )
479

480
    @patch("services.repositories.ServiceRepository.get_services_by_provider")
481
    def test_get_services_by_provider(self, mock_get_services_by_provider):
1✔
482
        # Arrange
1✔
483
        mock_get_services_by_provider.return_value = [self.sample_service]
484

1✔
485
        # Act
486
        result = self.service_repo.get_services_by_provider("test_provider_id")
487

1✔
488
        # Assert
489
        self.assertEqual(len(result), 1)
490
        self.assertEqual(result[0].name, "Test Service")
1✔
491
        mock_get_services_by_provider.assert_called_once_with("test_provider_id")
1✔
492

1✔
493
    @patch("services.repositories.ServiceRepository.get_service")
494
    def test_get_service(self, mock_get_service):
1✔
495
        # Arrange
1✔
496
        mock_get_service.return_value = self.sample_service
497

1✔
498
        # Act
499
        result = self.service_repo.get_service(self.sample_service.id)
500

1✔
501
        # Assert
502
        self.assertEqual(result.name, "Test Service")
503
        mock_get_service.assert_called_once_with(self.sample_service.id)
1✔
504

1✔
505
    @patch("services.repositories.ServiceRepository.create_service")
506
    def test_create_service(self, mock_create_service):
1✔
507
        # Arrange
1✔
508
        mock_create_service.return_value = True
509

1✔
510
        # Act
511
        result = self.service_repo.create_service(self.sample_service)
512

1✔
513
        # Assert
514
        self.assertTrue(result)
515
        mock_create_service.assert_called_once_with(self.sample_service)
1✔
516

1✔
517
    @patch("services.repositories.ServiceRepository.update_service")
518
    def test_update_service(self, mock_update_service):
1✔
519
        # Arrange
1✔
520
        mock_update_service.return_value = True
521
        updated_service = self.sample_service
1✔
522
        updated_service.name = "Updated Service Name"
1✔
523

1✔
524
        # Act
525
        result = self.service_repo.update_service(updated_service)
526

1✔
527
        # Assert
528
        self.assertTrue(result)
529
        mock_update_service.assert_called_once_with(updated_service)
1✔
530

1✔
531
    @patch("services.repositories.ServiceRepository.delete_service")
532
    def test_delete_service(self, mock_delete_service):
1✔
533
        # Arrange
1✔
534
        mock_delete_service.return_value = True
535

1✔
536
        # Act
537
        result = self.service_repo.delete_service(self.sample_service.id)
538

1✔
539
        # Assert
540
        self.assertTrue(result)
541
        mock_delete_service.assert_called_once_with(self.sample_service.id)
1✔
542

1✔
543
    def tearDown(self):
544
        # Clean up after tests
1✔
545
        super().tearDown()
546

1✔
547

548
class ServiceViewsAdditionalTests(TestCase):
549
    def setUp(self):
1✔
550
        self.client = Client()
1✔
551
        self.service_provider = CustomUser.objects.create_user(
1✔
552
            username="provider",
1✔
553
            email="provider@email.com",
554
            password="testpass123",
555
            user_type="service_provider",
556
        )
557
        self.regular_user = CustomUser.objects.create_user(
558
            username="regular",
1✔
559
            email="regular@email.com",
560
            password="testpass123",
561
            user_type="user",
562
        )
563
        self.service_repo = ServiceRepository()
564
        self.review_repo = ReviewRepository()
1✔
565

1✔
566
        # Sample service data
567
        self.sample_service_id = str(uuid.uuid4())
568
        self.sample_service = ServiceDTO(
1✔
569
            id=self.sample_service_id,
1✔
570
            name="Test Service",
571
            address="123 Test St",
572
            category="Mental Health Center",
573
            provider_id=str(self.service_provider.id),
574
            latitude=Decimal("40.7128"),
575
            longitude=Decimal("-74.0060"),
576
            ratings=Decimal("4.5"),
577
            description={"hours": "9-5"},
578
            service_created_timestamp="2022-01-01T12:00:00Z",
579
            service_status=ServiceStatus.APPROVED.value,
580
            service_approved_timestamp="2022-01-01T12:00:00Z",
581
            is_active=False,
582
        )
583

584
    def test_service_list_view_permission_denied_regular_user(self):
585
        # Test that a regular user (non-service provider) cannot access the service list
1✔
586
        self.client.login(username="regular", password="testpass123")
587
        response = self.client.get(reverse("services:list"))
1✔
588
        self.assertEqual(response.status_code, 403)
1✔
589

1✔
590
    def test_service_edit_view_invalid_service_id(self):
591
        # Test that an invalid UUID format for service_id raises 404
1✔
592
        self.client.login(username="provider", password="testpass123")
593
        response = self.client.get(reverse("services:edit", args=["invalid-id"]))
1✔
594
        self.assertEqual(response.status_code, 404)
1✔
595

1✔
596
    def test_service_delete_view_invalid_service_id(self):
597
        # Test that an invalid UUID format for service_id in delete view raises 404
1✔
598
        self.client.login(username="provider", password="testpass123")
599
        response = self.client.post(reverse("services:delete", args=["invalid-id"]))
1✔
600
        self.assertEqual(response.status_code, 404)
1✔
601

1✔
602
    def test_service_edit_permission_denied_for_non_owner(self):
603
        # Test that a provider cannot edit another provider's service
1✔
604
        self.client.login(username="provider", password="testpass123")
605
        another_service_id = str(uuid.uuid4())
1✔
606
        with patch.object(ServiceRepository, "get_service") as mock_get:
1✔
607
            mock_get.return_value = ServiceDTO(
1✔
608
                id=another_service_id,
1✔
609
                name="Other Service",
610
                provider_id="different_provider_id",
611
                category="FOOD",
612
                address="Other Address",
613
                latitude=Decimal("40.71"),
614
                longitude=Decimal("-74.32"),
615
                ratings=Decimal("4.5"),
616
                description={"hours": "9-5"},
617
                service_created_timestamp="2022-01-01T12:00:00Z",
618
                service_status=ServiceStatus.APPROVED.value,
619
                service_approved_timestamp="2022-01-01T12:00:00Z",
620
                is_active=True,
621
            )
622
            response = self.client.get(
623
                reverse("services:edit", args=[another_service_id])
1✔
624
            )
625
            self.assertEqual(response.status_code, 403)
626

1✔
627
    def test_service_delete_permission_denied_for_non_owner(self):
628
        # Test that a provider cannot delete another provider's service
1✔
629
        self.client.login(username="provider", password="testpass123")
630
        another_service_id = str(uuid.uuid4())
1✔
631
        with patch.object(ServiceRepository, "get_service") as mock_get:
1✔
632
            mock_get.return_value = ServiceDTO(
1✔
633
                id=another_service_id,
1✔
634
                name="Other Service",
635
                provider_id="different_provider_id",
636
                category="FOOD",
637
                address="Other Address",
638
                latitude=Decimal("40.71"),
639
                longitude=Decimal("-74.32"),
640
                ratings=Decimal("4.5"),
641
                description={"hours": "9-5"},
642
                service_created_timestamp="2022-01-01T12:00:00Z",
643
                service_status=ServiceStatus.APPROVED.value,
644
                service_approved_timestamp="2022-01-01T12:00:00Z",
645
                is_active=True,
646
            )
647
            response = self.client.post(
648
                reverse("services:delete", args=[another_service_id])
1✔
649
            )
650
            self.assertEqual(response.status_code, 403)
651

1✔
652
    # def test_service_details_view_service_not_found(self):
653
    #     # Test service_details view returns 404 if service does not exist
654
    #     self.client.login(username="provider", password="testpass123")
655
    #     with patch.object(ServiceRepository, "get_service", return_value=None):
656
    #         response = self.client.get(reverse("services:service_details", args=[self.sample_service_id]))
657
    #         self.assertEqual(response.status_code, 404)
658

659
    def test_respond_to_review_view_permission_denied(self):
660
        # Test that a provider cannot respond to a review on another provider's service
1✔
661
        self.client.login(username="provider", password="testpass123")
662
        review_id = str(uuid.uuid4())
1✔
663
        another_service_id = str(uuid.uuid4())
1✔
664
        with patch.object(
1✔
665
            ServiceRepository, "get_service"
1✔
666
        ) as mock_get_service, patch.object(
667
            ReviewRepository, "get_review"
668
        ) as mock_get_review:
669

670
            mock_get_service.return_value = ServiceDTO(
671
                id=another_service_id,
1✔
672
                name="Other Service",
673
                provider_id="different_provider_id",
674
                category="FOOD",
675
                address="Other Address",
676
                latitude=Decimal("40.71"),
677
                longitude=Decimal("-74.32"),
678
                ratings=Decimal("4.5"),
679
                description={"hours": "9-5"},
680
                service_created_timestamp="2022-01-01T12:00:00Z",
681
                service_status=ServiceStatus.APPROVED.value,
682
                service_approved_timestamp="2022-01-01T12:00:00Z",
683
                is_active=True,
684
            )
685
            mock_get_review.return_value = MagicMock(service_id=another_service_id)
686

1✔
687
            response = self.client.post(
688
                reverse(
1✔
689
                    "services:respond_to_review", args=[another_service_id, review_id]
690
                )
691
            )
692
            self.assertEqual(response.status_code, 403)
693

1✔
694
    def test_service_create_view_invalid_form(self):
695
        # Log in as service provider
1✔
696
        self.client.login(username="provider", password="testpass123")
697

1✔
698
        # Submit incomplete data to make form invalid
699
        invalid_data = {
700
            "name": "",  # Required field is empty
1✔
701
            "address": "123 Test St",
702
            "category": "Mental Health Center",
703
            "description-TOTAL_FORMS": "1",
704
            "description-INITIAL_FORMS": "0",
705
            "description-MIN_NUM_FORMS": "0",
706
            "description-MAX_NUM_FORMS": "1000",
707
        }
708

709
        response = self.client.post(reverse("services:create"), data=invalid_data)
710

1✔
711
        # Ensure it re-renders the form with error
712
        self.assertEqual(response.status_code, 200)
713
        self.assertTemplateUsed(response, "service_form.html")
1✔
714
        self.assertFalse(response.context["form"].is_valid())
1✔
715
        self.assertTrue(response.context["description_formset"].is_valid())
1✔
716

1✔
717
class ServiceAnalyticsViewsTest(TestCase):
718
    def setUp(self):
1✔
719
        self.client = Client()
1✔
720
        self.service_provider = User.objects.create_user(
1✔
721
            username="provider_analytics",
1✔
722
            email="provider_analytics@email.com",
723
            password="testpass123",
724
            user_type="service_provider",
725
        )
726
        self.regular_user = User.objects.create_user(
727
            username="regular_analytics",
1✔
728
            email="regular_analytics@email.com",
729
            password="testpass123",
730
            user_type="user",
731
        )
732
        self.service_repo = ServiceRepository()
733
        self.review_repo = ReviewRepository()
1✔
734

1✔
735
        # Sample service data
736
        self.sample_service_id = str(uuid.uuid4())
737
        self.sample_service = ServiceDTO(
1✔
738
            id=self.sample_service_id,
1✔
739
            name="Analytics Service",
740
            address="456 Analytics Ave",
741
            category="MENTAL",
742
            provider_id=str(self.service_provider.id),
743
            latitude=Decimal("40.7128"),
744
            longitude=Decimal("-74.0060"),
745
            ratings=Decimal("4.5"),
746
            description={"hours": "8-6"},
747
            service_created_timestamp="2023-01-01T12:00:00Z",
748
            service_status=ServiceStatus.APPROVED.value,
749
            service_approved_timestamp="2023-01-02T12:00:00Z",
750
            is_active=True,
751
        )
752

753
    def tearDown(self):
754
        super().tearDown()
1✔
755
        User.objects.all().delete()
1✔
756

1✔
757
    # Helper methods
758
    def login_as_provider(self):
759
        self.client.login(username="provider_analytics", password="testpass123")
1✔
760

1✔
761
    def login_as_regular(self):
762
        self.client.login(username="regular_analytics", password="testpass123")
1✔
763

1✔
764
    # 1. Test service_details view
765
    @patch("services.views.service_repo.get_service")
766
    def test_service_details_view_authenticated(self, mock_get_service):
1✔
767
        self.login_as_regular()
1✔
768
        mock_get_service.return_value = self.sample_service
1✔
769

1✔
770
        response = self.client.get(reverse("services:service_details", args=[self.sample_service_id]))
771

1✔
772
        self.assertEqual(response.status_code, 200)
773
        self.assertEqual(response['Content-Type'], 'application/json')
1✔
774
        expected_data = {
1✔
775
            "id": self.sample_service.id,
1✔
776
            "name": self.sample_service.name,
777
            "category": self.sample_service.category,
778
            "address": self.sample_service.address,
779
            "latitude": float(self.sample_service.latitude),
780
            "longitude": float(self.sample_service.longitude),
781
            "description": self.sample_service.description,
782
            "is_active": self.sample_service.is_active,
783
            "reviews": [],  # Assuming no reviews are mocked
784
        }
785
        self.assertJSONEqual(response.content, expected_data)
786
        mock_get_service.assert_called_once_with(self.sample_service_id)
1✔
787

1✔
788
    # @patch("services.views.service_repo.get_service")
789
    # def test_service_details_view_service_not_found(self, mock_get_service):
790
    #     self.login_as_regular()
791
    #     mock_get_service.return_value = None
792

793
    #     response = self.client.get(reverse("services:service_details", args=[self.sample_service_id]))
794

795
    #     self.assertEqual(response.status_code, 404)
796

797
    def test_service_details_view_unauthenticated(self):
798
        response = self.client.get(reverse("services:service_details", args=[self.sample_service_id]))
1✔
799
        self.assertEqual(response.status_code, 302)  # Redirect to login
1✔
800
        self.assertTrue(response.url.startswith("/accounts/login/"))
1✔
801

1✔
802
    # 2. Test dashboard view
803
    def test_dashboard_view_as_service_provider(self):
804
        self.login_as_provider()
1✔
805
        response = self.client.get(reverse("services:dashboard"))
1✔
806
        self.assertEqual(response.status_code, 200)
1✔
807
        self.assertTemplateUsed(response, "dashboard.html")
1✔
808

1✔
809
    def test_dashboard_view_as_regular_user(self):
810
        self.login_as_regular()
1✔
811
        response = self.client.get(reverse("services:dashboard"))
1✔
812
        self.assertEqual(response.status_code, 403)
1✔
813

1✔
814
    def test_dashboard_view_unauthenticated(self):
815
        response = self.client.get(reverse("services:dashboard"))
1✔
816
        self.assertEqual(response.status_code, 302)  # Redirect to login
1✔
817
        self.assertTrue(response.url.startswith("/accounts/login/"))
1✔
818

1✔
819
    # 3. Test bookmarks_over_time view
820
    @patch("services.views.home_repo.get_bookmarks_for_services")
821
    @patch("services.views.service_repo.get_services_by_provider")
1✔
822
    def test_bookmarks_over_time_view_as_provider(self, mock_get_services, mock_get_bookmarks):
1✔
823
        self.login_as_provider()
1✔
824
        mock_get_services.return_value = [self.sample_service]
1✔
825
        mock_get_bookmarks.return_value = [
1✔
826
            {"timestamp": "2024-10-12T12:00:00Z"},
1✔
827
            {"timestamp": "2024-10-13T12:00:00Z"},
828
            # Add more mock bookmarks as needed
829
        ]
830

831
        response = self.client.get(reverse("services:bookmarks_over_time"))
832

1✔
833
        self.assertEqual(response.status_code, 200)
834
        self.assertEqual(response['Content-Type'], 'application/json')
1✔
835
        data = response.json()
1✔
836
        self.assertIn("dates", data)
1✔
837
        self.assertIn("counts", data)
1✔
838
        mock_get_services.assert_called_once_with(self.service_provider.id)
1✔
839
        mock_get_bookmarks.assert_called_once_with([self.sample_service_id])
1✔
840

1✔
841
    def test_bookmarks_over_time_view_as_regular_user(self):
842
        self.login_as_regular()
1✔
843
        response = self.client.get(reverse("services:bookmarks_over_time"))
1✔
844
        self.assertEqual(response.status_code, 403)
1✔
845

1✔
846
    def test_bookmarks_over_time_view_unauthenticated(self):
847
        response = self.client.get(reverse("services:bookmarks_over_time"))
1✔
848
        self.assertEqual(response.status_code, 302)
1✔
849
        self.assertTrue(response.url.startswith("/accounts/login/"))
1✔
850

1✔
851
    # 4. Test bookmarks_over_time_view_no_bookmarks
852
    @patch("services.views.home_repo.get_bookmarks_for_services")
853
    @patch("services.views.service_repo.get_services_by_provider")
1✔
854
    def test_bookmarks_over_time_view_no_bookmarks(self, mock_get_services, mock_get_bookmarks):
1✔
855
        self.login_as_provider()
1✔
NEW
856
        mock_get_services.return_value = [self.sample_service]
×
NEW
857
        mock_get_bookmarks.return_value = []  # No bookmarks
×
NEW
858

×
859
        response = self.client.get(reverse("services:bookmarks_over_time"))
NEW
860

×
861
        self.assertEqual(response.status_code, 200)
NEW
862
        data = response.json()
×
NEW
863
        self.assertIn("dates", data)
×
NEW
864
        self.assertIn("counts", data)
×
NEW
865
        self.assertEqual(len(data["dates"]), 30)
×
NEW
866
        self.assertEqual(len(data["counts"]), 30)
×
NEW
867
        self.assertTrue(all(count == 0 for count in data["counts"]))
×
NEW
868

×
869
        mock_get_services.assert_called_once_with(self.service_provider.id)
NEW
870
        mock_get_bookmarks.assert_called_once_with([self.sample_service_id])
×
NEW
871

×
872
    # 5. Test reviews_over_time view
873
    @patch("services.views.home_repo.get_reviews_for_services")
874
    @patch("services.views.service_repo.get_services_by_provider")
1✔
875
    def test_reviews_over_time_view_as_provider(self, mock_get_services, mock_get_reviews):
1✔
876
        self.login_as_provider()
1✔
877
        mock_get_services.return_value = [self.sample_service]
1✔
878
        mock_get_reviews.return_value = [
1✔
879
            {"Timestamp": "2024-10-12T12:00:00Z"},
1✔
880
            {"Timestamp": "2024-10-13T12:00:00Z"},
881
            # Add more mock reviews as needed
882
        ]
883

884
        response = self.client.get(reverse("services:reviews_over_time"))
885

1✔
886
        self.assertEqual(response.status_code, 200)
887
        self.assertEqual(response['Content-Type'], 'application/json')
1✔
888
        data = response.json()
1✔
889
        self.assertIn("dates", data)
1✔
890
        self.assertIn("counts", data)
1✔
891
        mock_get_services.assert_called_once_with(self.service_provider.id)
1✔
892
        mock_get_reviews.assert_called_once_with([self.sample_service_id])
1✔
893

1✔
894
    def test_reviews_over_time_view_as_regular_user(self):
895
        self.login_as_regular()
1✔
896
        response = self.client.get(reverse("services:reviews_over_time"))
1✔
897
        self.assertEqual(response.status_code, 403)
1✔
898

1✔
899
    def test_reviews_over_time_view_unauthenticated(self):
900
        response = self.client.get(reverse("services:reviews_over_time"))
1✔
901
        self.assertEqual(response.status_code, 302)
1✔
902
        self.assertTrue(response.url.startswith("/accounts/login/"))
1✔
903

1✔
904
    # 6. Test average_rating_over_time view
905
    @patch("services.views.home_repo.get_reviews_for_services")
906
    @patch("services.views.service_repo.get_services_by_provider")
1✔
907
    def test_average_rating_over_time_view_as_provider(self, mock_get_services, mock_get_reviews):
1✔
908
        self.login_as_provider()
1✔
909
        mock_get_services.return_value = [self.sample_service]
1✔
910
        mock_get_reviews.return_value = [
1✔
911
            {"Timestamp": "2024-10-12T12:00:00Z", "RatingStars": "5"},
1✔
912
            {"Timestamp": "2024-10-12T13:00:00Z", "RatingStars": "4"},
913
            {"Timestamp": "2024-10-13T12:00:00Z", "RatingStars": "3"},
914
            # Add more mock reviews as needed
915
        ]
916

917
        response = self.client.get(reverse("services:average_rating_over_time"))
918

1✔
919
        self.assertEqual(response.status_code, 200)
920
        self.assertEqual(response['Content-Type'], 'application/json')
1✔
921
        data = response.json()
1✔
922
        self.assertIn("dates", data)
1✔
923
        self.assertIn("avg_ratings", data)
1✔
924
        mock_get_services.assert_called_once_with(self.service_provider.id)
1✔
925
        mock_get_reviews.assert_called_once_with([self.sample_service_id])
1✔
926

1✔
927
    def test_average_rating_over_time_view_as_regular_user(self):
928
        self.login_as_regular()
1✔
929
        response = self.client.get(reverse("services:average_rating_over_time"))
1✔
930
        self.assertEqual(response.status_code, 403)
1✔
931

1✔
932
    def test_average_rating_over_time_view_unauthenticated(self):
933
        response = self.client.get(reverse("services:average_rating_over_time"))
1✔
934
        self.assertEqual(response.status_code, 302)
1✔
935
        self.assertTrue(response.url.startswith("/accounts/login/"))
1✔
936

1✔
937
    # 7. Test rating_distribution view
938
    @patch("services.views.home_repo.get_reviews_for_services")
939
    @patch("services.views.service_repo.get_services_by_provider")
1✔
940
    def test_rating_distribution_view_as_provider(self, mock_get_services, mock_get_reviews):
1✔
941
        self.login_as_provider()
1✔
942
        mock_get_services.return_value = [self.sample_service]
1✔
943
        mock_get_reviews.return_value = [
1✔
944
            {"RatingStars": "5"},
1✔
945
            {"RatingStars": "4"},
946
            {"RatingStars": "5"},
947
            {"RatingStars": "3"},
948
            # Add more mock reviews as needed
949
        ]
950

951
        response = self.client.get(reverse("services:rating_distribution"))
952

1✔
953
        self.assertEqual(response.status_code, 200)
954
        self.assertEqual(response['Content-Type'], 'application/json')
1✔
955
        data = response.json()
1✔
956
        self.assertIn("ratings", data)
1✔
957
        self.assertIn("counts", data)
1✔
958
        mock_get_services.assert_called_once_with(self.service_provider.id)
1✔
959
        mock_get_reviews.assert_called_once_with([self.sample_service_id])
1✔
960

1✔
961
    def test_rating_distribution_view_as_regular_user(self):
962
        self.login_as_regular()
1✔
963
        response = self.client.get(reverse("services:rating_distribution"))
1✔
964
        self.assertEqual(response.status_code, 403)
1✔
965

1✔
966
    def test_rating_distribution_view_unauthenticated(self):
967
        response = self.client.get(reverse("services:rating_distribution"))
1✔
968
        self.assertEqual(response.status_code, 302)
1✔
969
        self.assertTrue(response.url.startswith("/accounts/login/"))
1✔
970

1✔
971
    # 8. Test recent_reviews view
972
    @patch("services.views.home_repo.get_reviews_for_services")
973
    @patch("services.views.service_repo.get_services_by_provider")
1✔
974
    def test_recent_reviews_view_as_provider(self, mock_get_services, mock_get_reviews):
1✔
975
        self.login_as_provider()
1✔
976
        mock_get_services.return_value = [self.sample_service]
1✔
977
        mock_get_reviews.return_value = [
1✔
978
            {"ReviewId": "r1", "Timestamp": "2024-10-15T12:00:00Z"},
1✔
979
            {"ReviewId": "r2", "Timestamp": "2024-10-14T12:00:00Z"},
980
            {"ReviewId": "r3", "Timestamp": "2024-10-13T12:00:00Z"},
981
            {"ReviewId": "r4", "Timestamp": "2024-10-12T12:00:00Z"},
982
            {"ReviewId": "r5", "Timestamp": "2024-10-11T12:00:00Z"},
983
            {"ReviewId": "r6", "Timestamp": "2024-10-10T12:00:00Z"},
984
        ]
985

986
        response = self.client.get(reverse("services:recent_reviews"))
987

1✔
988
        self.assertEqual(response.status_code, 200)
989
        self.assertEqual(response['Content-Type'], 'application/json')
1✔
990
        data = response.json()
1✔
991
        self.assertIn("reviews", data)
1✔
992
        self.assertEqual(len(data["reviews"]), 5)  # Top 5 recent reviews
1✔
993
        mock_get_services.assert_called_once_with(self.service_provider.id)
1✔
994
        mock_get_reviews.assert_called_once_with([self.sample_service_id])
1✔
995

1✔
996
    def test_recent_reviews_view_as_regular_user(self):
997
        self.login_as_regular()
1✔
998
        response = self.client.get(reverse("services:recent_reviews"))
1✔
999
        self.assertEqual(response.status_code, 403)
1✔
1000

1✔
1001
    def test_recent_reviews_view_unauthenticated(self):
1002
        response = self.client.get(reverse("services:recent_reviews"))
1✔
1003
        self.assertEqual(response.status_code, 302)
1✔
1004
        self.assertTrue(response.url.startswith("/accounts/login/"))
1✔
1005

1✔
1006
    # 9. Test response_rate view
1007
    @patch("services.views.home_repo.get_reviews_for_services")
1008
    @patch("services.views.service_repo.get_services_by_provider")
1✔
1009
    def test_response_rate_view_as_provider(self, mock_get_services, mock_get_reviews):
1✔
1010
        self.login_as_provider()
1✔
1011
        mock_get_services.return_value = [self.sample_service]
1✔
1012
        mock_get_reviews.return_value = [
1✔
1013
            {"ResponseText": "Thanks!"},
1✔
1014
            {"ResponseText": ""},
1015
            {"ResponseText": "We appreciate your feedback."},
1016
            {"ResponseText": ""},
1017
        ]
1018

1019
        response = self.client.get(reverse("services:response_rate"))
1020

1✔
1021
        self.assertEqual(response.status_code, 200)
1022
        self.assertEqual(response['Content-Type'], 'application/json')
1✔
1023
        data = response.json()
1✔
1024
        self.assertIn("total_reviews", data)
1✔
1025
        self.assertIn("responded_reviews", data)
1✔
1026
        self.assertIn("response_rate", data)
1✔
1027
        self.assertEqual(data["total_reviews"], 4)
1✔
1028
        self.assertEqual(data["responded_reviews"], 2)
1✔
1029
        self.assertEqual(data["response_rate"], 50.0)
1✔
1030
        mock_get_services.assert_called_once_with(self.service_provider.id)
1✔
1031
        mock_get_reviews.assert_called_once_with([self.sample_service_id])
1✔
1032

1✔
1033
    def test_response_rate_view_as_regular_user(self):
1034
        self.login_as_regular()
1✔
1035
        response = self.client.get(reverse("services:response_rate"))
1✔
1036
        self.assertEqual(response.status_code, 403)
1✔
1037

1✔
1038
    def test_response_rate_view_unauthenticated(self):
1039
        response = self.client.get(reverse("services:response_rate"))
1✔
1040
        self.assertEqual(response.status_code, 302)
1✔
1041
        self.assertTrue(response.url.startswith("/accounts/login/"))
1✔
1042

1✔
1043
    # 10. Test review_word_cloud view
1044
    @patch("services.views.home_repo.get_reviews_for_services")
1045
    @patch("services.views.service_repo.get_services_by_provider")
1✔
1046
    def test_review_word_cloud_view_as_provider(self, mock_get_services, mock_get_reviews):
1✔
1047
        self.login_as_provider()
1✔
1048
        mock_get_services.return_value = [self.sample_service]
1✔
1049
        mock_get_reviews.return_value = [
1✔
1050
            {"RatingMessage": "Great service and friendly staff."},
1✔
1051
            {"RatingMessage": "Excellent support and helpful."},
1052
            {"RatingMessage": "Good experience overall."},
1053
        ]
1054

1055
        response = self.client.get(reverse("services:review_word_cloud"))
1056

1✔
1057
        self.assertEqual(response.status_code, 200)
1058
        self.assertEqual(response['Content-Type'], 'application/json')
1✔
1059
        data = response.json()
1✔
1060
        self.assertIn("words", data)
1✔
1061
        self.assertIsInstance(data["words"], list)
1✔
1062
        self.assertTrue(len(data["words"]) <= 50)
1✔
1063
        mock_get_services.assert_called_once_with(self.service_provider.id)
1✔
1064
        mock_get_reviews.assert_called_once_with([self.sample_service_id])
1✔
1065

1✔
1066
    def test_review_word_cloud_view_as_regular_user(self):
1067
        self.login_as_regular()
1✔
1068
        response = self.client.get(reverse("services:review_word_cloud"))
1✔
1069
        self.assertEqual(response.status_code, 403)
1✔
1070

1✔
1071
    def test_review_word_cloud_view_unauthenticated(self):
1072
        response = self.client.get(reverse("services:review_word_cloud"))
1✔
1073
        self.assertEqual(response.status_code, 302)
1✔
1074
        self.assertTrue(response.url.startswith("/accounts/login/"))
1✔
1075

1✔
1076
    # 11. Test service_category_distribution view
1077
    @patch("services.views.service_repo.get_services_by_provider")
1078
    def test_service_category_distribution_view_as_provider(self, mock_get_services):
1✔
1079
        self.login_as_provider()
1✔
1080
        mock_get_services.return_value = [
1✔
1081
            self.sample_service,
1✔
1082
            ServiceDTO(
1083
                id=str(uuid.uuid4()),
1084
                name="Another Service",
1085
                address="789 Another St",
1086
                category="FOOD",
1087
                provider_id=str(self.service_provider.id),
1088
                latitude=Decimal("40.7128"),
1089
                longitude=Decimal("-74.0060"),
1090
                ratings=Decimal("4.0"),
1091
                description={"hours": "10-4"},
1092
                service_created_timestamp="2023-02-01T12:00:00Z",
1093
                service_status=ServiceStatus.APPROVED.value,
1094
                service_approved_timestamp="2023-02-02T12:00:00Z",
1095
                is_active=True,
1096
            ),
1097
        ]
1098

1099
        response = self.client.get(reverse("services:service_category_distribution"))
1100

1✔
1101
        self.assertEqual(response.status_code, 200)
1102
        self.assertEqual(response['Content-Type'], 'application/json')
1✔
1103
        data = response.json()
1✔
1104
        self.assertIn("categories", data)
1✔
1105
        self.assertIn("counts", data)
1✔
1106
        self.assertEqual(data["categories"], ["MENTAL", "FOOD"])
1✔
1107
        self.assertEqual(data["counts"], [1, 1])
1✔
1108
        mock_get_services.assert_called_once_with(self.service_provider.id)
1✔
1109

1✔
1110
    def test_service_category_distribution_view_as_regular_user(self):
1111
        self.login_as_regular()
1✔
1112
        response = self.client.get(reverse("services:service_category_distribution"))
1✔
1113
        self.assertEqual(response.status_code, 403)
1✔
1114

1✔
1115
    def test_service_category_distribution_view_unauthenticated(self):
1116
        response = self.client.get(reverse("services:service_category_distribution"))
1✔
1117
        self.assertEqual(response.status_code, 302)
1✔
1118
        self.assertTrue(response.url.startswith("/accounts/login/"))
1✔
1119

1✔
1120
    # 12. Test user_analytics view
1121
    @patch("services.views.home_repo.compute_user_metrics")
1122
    def test_user_analytics_view_as_provider(self, mock_compute_metrics):
1✔
1123
        self.login_as_provider()
1✔
1124
        mock_compute_metrics.return_value = {
1✔
1125
            "total_services": 5,
1✔
1126
            "total_reviews": 20,
1127
            "average_rating": 4.2,
1128
            # Add more metrics as needed
1129
        }
1130

1131
        response = self.client.get(reverse("services:user_analytics"))
1132

1✔
1133
        self.assertEqual(response.status_code, 200)
1134
        self.assertEqual(response['Content-Type'], 'application/json')
1✔
1135
        data = response.json()
1✔
1136
        self.assertIn("user_metrics", data)
1✔
1137
        self.assertEqual(data["user_metrics"], mock_compute_metrics.return_value)
1✔
1138
        mock_compute_metrics.assert_called_once_with(str(self.service_provider.id))
1✔
1139

1✔
1140
    def test_user_analytics_view_as_regular_user(self):
1141
        self.login_as_regular()
1✔
1142
        response = self.client.get(reverse("services:user_analytics"))
1✔
1143
        self.assertEqual(response.status_code, 403)
1✔
1144

1✔
1145
    def test_user_analytics_view_unauthenticated(self):
1146
        response = self.client.get(reverse("services:user_analytics"))
1✔
1147
        self.assertEqual(response.status_code, 302)
1✔
1148
        self.assertTrue(response.url.startswith("/accounts/login/"))
1✔
1149

1✔
1150
    # 13. Additional Edge Case Tests
1151

1152
    @patch("services.views.home_repo.get_bookmarks_for_services")
1153
    @patch("services.views.service_repo.get_services_by_provider")
1✔
1154
    def test_bookmarks_over_time_view_no_bookmarks(self, mock_get_services, mock_get_bookmarks):
1✔
1155
        self.login_as_provider()
1✔
1156
        mock_get_services.return_value = [self.sample_service]
1✔
1157
        mock_get_bookmarks.return_value = []  # No bookmarks
1✔
1158

1✔
1159
        response = self.client.get(reverse("services:bookmarks_over_time"))
1160

1✔
1161
        self.assertEqual(response.status_code, 200)
1162
        data = response.json()
1✔
1163
        self.assertIn("dates", data)
1✔
1164
        self.assertIn("counts", data)
1✔
1165
        self.assertEqual(len(data["dates"]), 30)
1✔
1166
        self.assertEqual(len(data["counts"]), 30)
1✔
1167
        self.assertTrue(all(count == 0 for count in data["counts"]))
1✔
1168

1✔
1169
        mock_get_services.assert_called_once_with(self.service_provider.id)
1170
        mock_get_bookmarks.assert_called_once_with([self.sample_service_id])
1✔
1171

1✔
1172
    @patch("services.views.home_repo.get_reviews_for_services")
1173
    @patch("services.views.service_repo.get_services_by_provider")
1✔
1174
    def test_average_rating_over_time_view_no_reviews(self, mock_get_services, mock_get_reviews):
1✔
1175
        self.login_as_provider()
1✔
1176
        mock_get_services.return_value = [self.sample_service]
1✔
1177
        mock_get_reviews.return_value = []  # No reviews
1✔
1178

1✔
1179
        response = self.client.get(reverse("services:average_rating_over_time"))
1180

1✔
1181
        self.assertEqual(response.status_code, 200)
1182
        data = response.json()
1✔
1183
        self.assertIn("avg_ratings", data)
1✔
1184
        self.assertEqual(len(data["avg_ratings"]), 30)
1✔
1185
        self.assertTrue(all(rating is None for rating in data["avg_ratings"]))
1✔
1186

1✔
1187
    @patch("services.views.home_repo.get_reviews_for_services")
1188
    @patch("services.views.service_repo.get_services_by_provider")
1✔
1189
    def test_response_rate_view_no_reviews(self, mock_get_services, mock_get_reviews):
1✔
1190
        self.login_as_provider()
1✔
1191
        mock_get_services.return_value = [self.sample_service]
1✔
1192
        mock_get_reviews.return_value = []  # No reviews
1✔
1193

1✔
1194
        response = self.client.get(reverse("services:response_rate"))
1195

1✔
1196
        self.assertEqual(response.status_code, 200)
1197
        data = response.json()
1✔
1198
        self.assertEqual(data["total_reviews"], 0)
1✔
1199
        self.assertEqual(data["responded_reviews"], 0)
1✔
1200
        self.assertEqual(data["response_rate"], 0)
1✔
1201
    
1✔
1202
    @patch("services.views.service_repo.get_services_by_provider")
1203
    def test_service_category_distribution_view_no_services(self, mock_get_services):
1✔
1204
        self.login_as_provider()
1✔
1205
        mock_get_services.return_value = []  # No services
1✔
1206

1✔
1207
        response = self.client.get(reverse("services:service_category_distribution"))
1208

1✔
1209
        self.assertEqual(response.status_code, 200)
1210
        data = response.json()
1✔
1211
        self.assertEqual(data["categories"], [])
1✔
1212
        self.assertEqual(data["counts"], [])
1✔
1213

1✔
1214
    @patch("services.views.home_repo.compute_user_metrics")
1215
    def test_user_analytics_view_no_metrics(self, mock_compute_metrics):
1✔
1216
        self.login_as_provider()
1✔
1217
        mock_compute_metrics.return_value = {}  # No metrics
1✔
1218

1✔
1219
        response = self.client.get(reverse("services:user_analytics"))
1220

1✔
1221
        self.assertEqual(response.status_code, 200)
1222
        data = response.json()
1✔
1223
        self.assertIn("user_metrics", data)
1✔
1224
        self.assertEqual(data["user_metrics"], mock_compute_metrics.return_value)
1✔
1225
        mock_compute_metrics.assert_called_once_with(str(self.service_provider.id))
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc