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

gcivil-nyu-org / team2-wed-spring25 / 419

02 Apr 2025 01:44PM UTC coverage: 71.719%. First build
419

push

travis-pro

web-flow
Merge pull request #196 from AlexanderS10/develop2

Fixed wide issue of authentication while retaining backwards compatibility with localStorage code. Updated the tests to match the new views

422 of 431 new or added lines in 5 files covered. (97.91%)

1093 of 1524 relevant lines covered (71.72%)

0.72 hits per line

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

96.91
/backend/nightwalkers/map/tests.py
1
from django.test import TestCase, Client
1✔
2
from django.urls import reverse
1✔
3
from django.contrib.auth import get_user_model
1✔
4
from rest_framework.test import APIClient
1✔
5
from rest_framework import status
1✔
6
from unittest.mock import patch, MagicMock
1✔
7
import requests
1✔
8
import json
1✔
9
from shapely.geometry import Polygon, MultiPolygon
1✔
10

11
from .models import SavedRoute
1✔
12
from .views import (
1✔
13
    process_route_with_crime_data,
14
    get_crime_hotspots,
15
    create_avoid_polygons,
16
    get_safer_ors_route,
17
)
18

19
User = get_user_model()
1✔
20

21

22
class BaseTestCase(TestCase):
1✔
23
    """Base test case with common setup for all test classes"""
24

25
    def setUp(self):
1✔
26
        """Set up test data and clients that are common across test cases"""
27
        # Create test users
28
        self.user1 = User.objects.create_user(
1✔
29
            email="test1@example.com",
30
            password="testpass123",
31
            first_name="Test",
32
            last_name="User1",
33
        )
34

35
        self.user2 = User.objects.create_user(
1✔
36
            email="test2@example.com",
37
            password="testpass123",
38
            first_name="Test",
39
            last_name="User2",
40
        )
41

42
        # Set up API client
43
        self.api_client = APIClient()
1✔
44
        self.client = Client()
1✔
45

46

47
class SavedRouteAPITestCase(BaseTestCase):
1✔
48
    """Test cases for SavedRoute API endpoints"""
49

50
    def setUp(self):
1✔
51
        """Set up test data specific to SavedRoute tests"""
52
        super().setUp()
1✔
53

54
        # Create test routes for user1
55
        self.route1 = SavedRoute.objects.create(
1✔
56
            user=self.user1,
57
            name="Home to Work",
58
            departure_lat=40.7128,
59
            departure_lon=-74.0060,
60
            destination_lat=40.7580,
61
            destination_lon=-73.9855,
62
            favorite=True,
63
        )
64

65
        self.route2 = SavedRoute.objects.create(
1✔
66
            user=self.user1,
67
            name="Home to Gym",
68
            departure_lat=40.7128,
69
            departure_lon=-74.0060,
70
            destination_lat=40.7431,
71
            destination_lon=-73.9712,
72
            favorite=False,
73
        )
74

75
        # Create test route for user2
76
        self.route3 = SavedRoute.objects.create(
1✔
77
            user=self.user2,
78
            name="My Route",
79
            departure_lat=40.6892,
80
            departure_lon=-74.0445,
81
            destination_lat=40.7831,
82
            destination_lon=-73.9712,
83
            favorite=True,
84
        )
85

86
        # URLs for the endpoints - only set the ones that don't need parameters
87
        self.save_route_url = reverse("save-route")
1✔
88
        self.retrieve_routes_url = reverse("retrieve-routes")
1✔
89
        self.update_route_url = reverse("update-route")
1✔
90
        # Note: delete_route_url is not set here because it requires a pk parameter
91

92
    def test_save_route_authenticated(self):
1✔
93
        """Test saving a new route as an authenticated user"""
94
        self.api_client.force_authenticate(user=self.user1)
1✔
95

96
        data = {
1✔
97
            "name": "New Test Route",
98
            "departure_lat": 40.7128,
99
            "departure_lon": -74.0060,
100
            "destination_lat": 40.7580,
101
            "destination_lon": -73.9855,
102
            "favorite": False,
103
        }
104

105
        response = self.api_client.post(self.save_route_url, data, format="json")
1✔
106

107
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
1✔
108
        self.assertEqual(SavedRoute.objects.count(), 4)
1✔
109
        self.assertEqual(SavedRoute.objects.filter(user=self.user1).count(), 3)
1✔
110
        self.assertEqual(response.data["name"], "New Test Route")
1✔
111

112
    def test_save_route_duplicate_name(self):
1✔
113
        """Test that a user cannot save two routes with the same name"""
114
        self.api_client.force_authenticate(user=self.user1)
1✔
115

116
        data = {
1✔
117
            "name": "Home to Work",  # This name already exists for user1
118
            "departure_lat": 40.7128,
119
            "departure_lon": -74.0060,
120
            "destination_lat": 40.7580,
121
            "destination_lon": -73.9855,
122
            "favorite": False,
123
        }
124

125
        response = self.api_client.post(self.save_route_url, data, format="json")
1✔
126

127
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
1✔
128
        self.assertEqual(SavedRoute.objects.count(), 3)  # No new route added
1✔
129

130
    def test_save_route_unauthenticated(self):
1✔
131
        """Test that unauthenticated users cannot save routes"""
132
        data = {
1✔
133
            "name": "Unauthenticated Route",
134
            "departure_lat": 40.7128,
135
            "departure_lon": -74.0060,
136
            "destination_lat": 40.7580,
137
            "destination_lon": -73.9855,
138
            "favorite": False,
139
        }
140

141
        response = self.api_client.post(self.save_route_url, data, format="json")
1✔
142

143
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
1✔
144
        self.assertEqual(SavedRoute.objects.count(), 3)  # No new route added
1✔
145

146
    def test_retrieve_saved_routes(self):
1✔
147
        """Test retrieving saved routes for an authenticated user"""
148
        self.api_client.force_authenticate(user=self.user1)
1✔
149

150
        # Make sure we know exactly how many routes should exist for user1
151
        user1_route_count = SavedRoute.objects.filter(user=self.user1).count()
1✔
152

153
        response = self.api_client.get(self.retrieve_routes_url)
1✔
154

155
        self.assertEqual(response.status_code, status.HTTP_200_OK)
1✔
156

157
        # If pagination is in use, we need to check for 'results' key
158
        if "results" in response.data:
1✔
159
            # Check that the first page has the expected routes
160
            results = response.data["results"]
1✔
161
            self.assertGreaterEqual(len(results), 1)
1✔
162

163
            # Check that the route1 (favorite) is before route2 (non-favorite)
164
            # Find the indices of route1 and route2 in the results
165
            route1_index = next(
1✔
166
                (i for i, item in enumerate(results) if item["id"] == self.route1.id),
167
                None,
168
            )
169
            route2_index = next(
1✔
170
                (i for i, item in enumerate(results) if item["id"] == self.route2.id),
171
                None,
172
            )
173

174
            # If both routes are in the first page of results, check their order
175
            if route1_index is not None and route2_index is not None:
1✔
176
                self.assertLess(
1✔
177
                    route1_index,
178
                    route2_index,
179
                    "Favorite route should come before non-favorite route",
180
                )
181
        else:
182
            # No pagination - check the total count matches expected
NEW
183
            self.assertEqual(len(response.data), user1_route_count)
×
184

185
            # Find the indices of route1 and route2 in the results
NEW
186
            route1_index = next(
×
187
                (
188
                    i
189
                    for i, item in enumerate(response.data)
190
                    if item["id"] == self.route1.id
191
                ),
192
                None,
193
            )
NEW
194
            route2_index = next(
×
195
                (
196
                    i
197
                    for i, item in enumerate(response.data)
198
                    if item["id"] == self.route2.id
199
                ),
200
                None,
201
            )
202

203
            # If both routes are in the results, check their order
NEW
204
            if route1_index is not None and route2_index is not None:
×
NEW
205
                self.assertLess(
×
206
                    route1_index,
207
                    route2_index,
208
                    "Favorite route should come before non-favorite route",
209
                )
210

211
    def test_retrieve_saved_routes_unauthenticated(self):
1✔
212
        """Test that unauthenticated users cannot retrieve routes"""
213
        response = self.api_client.get(self.retrieve_routes_url)
1✔
214

215
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
1✔
216

217
    def test_update_saved_route(self):
1✔
218
        """Test updating a saved route (favorite status)"""
219
        self.api_client.force_authenticate(user=self.user1)
1✔
220

221
        data = {"id": self.route2.id, "favorite": True}
1✔
222

223
        response = self.api_client.put(self.update_route_url, data, format="json")
1✔
224

225
        self.assertEqual(response.status_code, status.HTTP_200_OK)
1✔
226

227
        # Refresh from database
228
        self.route2.refresh_from_db()
1✔
229
        self.assertTrue(self.route2.favorite)
1✔
230

231
    def test_update_route_of_another_user(self):
1✔
232
        """Test that a user cannot update another user's route"""
233
        self.api_client.force_authenticate(user=self.user1)
1✔
234

235
        data = {
1✔
236
            "id": self.route3.id,  # This belongs to user2
237
            "favorite": False,  # Change to False to see if it remains True
238
        }
239

240
        try:
1✔
241
            response = self.api_client.put(self.update_route_url, data, format="json")
1✔
242
            # If we get here, check status code
NEW
243
            self.assertIn(
×
244
                response.status_code,
245
                [status.HTTP_404_NOT_FOUND, status.HTTP_403_FORBIDDEN],
246
            )
247
        except Exception:
1✔
248
            # The test may raise an exception due to the DoesNotExist being raised
249
            # That's okay, as long as the route is unchanged
250
            pass
1✔
251

252
        # Refresh from database - the important thing is that the route is unchanged
253
        self.route3.refresh_from_db()
1✔
254
        self.assertTrue(self.route3.favorite)  # Should remain unchanged
1✔
255

256
    def test_update_route_unauthenticated(self):
1✔
257
        """Test that unauthenticated users cannot update routes"""
258
        data = {"id": self.route1.id, "favorite": False}
1✔
259

260
        response = self.api_client.put(self.update_route_url, data, format="json")
1✔
261

262
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
1✔
263

264
        # Refresh from database
265
        self.route1.refresh_from_db()
1✔
266
        self.assertTrue(self.route1.favorite)  # Unchanged
1✔
267

268
    def test_delete_saved_route(self):
1✔
269
        """Test deleting a saved route"""
270
        self.api_client.force_authenticate(user=self.user1)
1✔
271

272
        # Generate the delete URL with the specific pk
273
        delete_url = reverse("delete-route", kwargs={"pk": self.route2.id})
1✔
274

275
        response = self.api_client.delete(delete_url)
1✔
276

277
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
1✔
278
        self.assertEqual(SavedRoute.objects.count(), 2)
1✔
279
        self.assertFalse(SavedRoute.objects.filter(id=self.route2.id).exists())
1✔
280

281
    def test_delete_route_of_another_user(self):
1✔
282
        """Test that a user cannot delete another user's route"""
283
        self.api_client.force_authenticate(user=self.user1)
1✔
284

285
        # Generate the delete URL with the specific pk
286
        delete_url = reverse("delete-route", kwargs={"pk": self.route3.id})
1✔
287

288
        try:
1✔
289
            response = self.api_client.delete(delete_url)
1✔
290
            # If we get here, check status code
291
            self.assertIn(
1✔
292
                response.status_code,
293
                [status.HTTP_404_NOT_FOUND, status.HTTP_403_FORBIDDEN],
294
            )
NEW
295
        except Exception:
×
296
            # The test may raise an exception due to the DoesNotExist being raised
297
            # That's okay, as long as the route is not deleted
NEW
298
            pass
×
299

300
        # The key assertion is that the route still exists
301
        self.assertEqual(SavedRoute.objects.count(), 3)  # No route deleted
1✔
302
        self.assertTrue(SavedRoute.objects.filter(id=self.route3.id).exists())
1✔
303

304
    def test_delete_route_unauthenticated(self):
1✔
305
        """Test that unauthenticated users cannot delete routes"""
306
        # Generate the delete URL with the specific pk
307
        delete_url = reverse("delete-route", kwargs={"pk": self.route1.id})
1✔
308

309
        response = self.api_client.delete(delete_url)
1✔
310

311
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
1✔
312
        self.assertEqual(SavedRoute.objects.count(), 3)  # No route deleted
1✔
313
        self.assertTrue(SavedRoute.objects.filter(id=self.route1.id).exists())
1✔
314

315

316
class RouteViewAPITestCase(BaseTestCase):
1✔
317
    """Test cases for the RouteViewAPI endpoint"""
318

319
    def setUp(self):
1✔
320
        """Set up test data specific to RouteViewAPI tests"""
321
        super().setUp()
1✔
322

323
        # Create test route
324
        self.saved_route = SavedRoute.objects.create(
1✔
325
            user=self.user1,
326
            name="Existing Route",
327
            departure_lat=40.7128,
328
            departure_lon=-74.0060,
329
            destination_lat=40.7580,
330
            destination_lon=-73.9855,
331
            favorite=True,
332
        )
333

334
        # URL for API endpoint
335
        self.url = reverse("get-route")
1✔
336

337
        # Sample valid data for requests
338
        self.valid_data = {
1✔
339
            "departure": [40.7128, -74.0060],
340
            "destination": [34.0522, -118.2437],
341
            "saved_route": False,
342
        }
343

344
        # Set up successful mock response from OpenRouteService
345
        self.mock_ors_response = {
1✔
346
            "routes": [
347
                {
348
                    "geometry": "some_encoded_polyline",
349
                    "legs": [],
350
                    "summary": {"distance": 3941.2, "duration": 3146.6},
351
                }
352
            ],
353
            "type": "FeatureCollection",
354
            "features": [
355
                {
356
                    "type": "Feature",
357
                    "properties": {
358
                        "segments": [{"distance": 3941.2, "duration": 3146.6}],
359
                        "summary": {"distance": 3941.2, "duration": 3146.6},
360
                    },
361
                    "geometry": {
362
                        "coordinates": [[-74.0060, 40.7128], [-118.2437, 34.0522]],
363
                        "type": "LineString",
364
                    },
365
                }
366
            ],
367
        }
368

369
        # Mock safer route response
370
        self.mock_safer_route = {
1✔
371
            "routes": [
372
                {
373
                    "geometry": "another_encoded_polyline",
374
                    "legs": [],
375
                    "summary": {"distance": 4100.5, "duration": 3300.2},
376
                }
377
            ],
378
            "type": "FeatureCollection",
379
            "features": [
380
                {
381
                    "type": "Feature",
382
                    "properties": {
383
                        "segments": [{"distance": 4100.5, "duration": 3300.2}],
384
                        "summary": {"distance": 4100.5, "duration": 3300.2},
385
                    },
386
                    "geometry": {
387
                        "coordinates": [[-74.0060, 40.7128], [-118.2437, 34.0522]],
388
                        "type": "LineString",
389
                    },
390
                }
391
            ],
392
        }
393

394
    @patch("requests.post")
1✔
395
    def test_unauthenticated_access_denied(self, mock_post):
1✔
396
        """Test that unauthenticated users cannot access the endpoint"""
397
        response = self.api_client.post(self.url, self.valid_data, format="json")
1✔
398
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
1✔
399

400
    @patch("requests.post")
1✔
401
    def test_authenticated_access_allowed(self, mock_post):
1✔
402
        """Test that authenticated users can access the endpoint"""
403
        # Setup mock for OpenRouteService
404
        mock_response = MagicMock()
1✔
405
        mock_response.json.return_value = self.mock_ors_response
1✔
406
        mock_response.raise_for_status.return_value = None
1✔
407
        mock_post.return_value = mock_response
1✔
408

409
        # Authenticate user
410
        self.api_client.force_authenticate(user=self.user1)
1✔
411

412
        response = self.api_client.post(self.url, self.valid_data, format="json")
1✔
413
        self.assertEqual(response.status_code, status.HTTP_200_OK)
1✔
414
        self.assertIn("initial_route", response.data)
1✔
415

416
    @patch("requests.post")
1✔
417
    def test_route_with_coordinates(self, mock_post):
1✔
418
        """Test getting a route using coordinates"""
419
        # Setup mock for OpenRouteService
420
        mock_response = MagicMock()
1✔
421
        mock_response.json.return_value = self.mock_ors_response
1✔
422
        mock_response.raise_for_status.return_value = None
1✔
423
        mock_post.return_value = mock_response
1✔
424

425
        # Authenticate user
426
        self.api_client.force_authenticate(user=self.user1)
1✔
427

428
        response = self.api_client.post(self.url, self.valid_data, format="json")
1✔
429
        self.assertEqual(response.status_code, status.HTTP_200_OK)
1✔
430
        self.assertEqual(response.data["initial_route"], self.mock_ors_response)
1✔
431
        self.assertIsNone(response.data["safer_route"])
1✔
432

433
    @patch("requests.post")
1✔
434
    @patch("polyline.decode")
1✔
435
    @patch("django.db.connection.cursor")
1✔
436
    def test_safer_route_generation_failure(
1✔
437
        self, mock_cursor, mock_polyline_decode, mock_post
438
    ):
439
        """Test fallback when safer route generation fails"""
440
        # Setup mock for OpenRouteService
441
        mock_response = MagicMock()
1✔
442
        mock_response.json.return_value = self.mock_ors_response
1✔
443
        mock_post.return_value = mock_response
1✔
444

445
        # Mock polyline decoding to raise an exception during safer route generation
446
        mock_polyline_decode.side_effect = Exception("Polyline decode error")
1✔
447

448
        # Authenticate user
449
        self.api_client.force_authenticate(user=self.user1)
1✔
450

451
        response = self.api_client.post(self.url, self.valid_data, format="json")
1✔
452

453
        self.assertEqual(response.status_code, status.HTTP_200_OK)
1✔
454
        self.assertEqual(response.data["initial_route"], self.mock_ors_response)
1✔
455
        self.assertIsNone(response.data["safer_route"])
1✔
456
        self.assertIn("message", response.data)
1✔
457

458
    @patch("requests.post")
1✔
459
    def test_openrouteservice_error(self, mock_post):
1✔
460
        """Test handling of errors from OpenRouteService"""
461
        # Setup mock for OpenRouteService error
462
        mock_post.side_effect = requests.exceptions.RequestException("API Error")
1✔
463

464
        # Authenticate user
465
        self.api_client.force_authenticate(user=self.user1)
1✔
466

467
        response = self.api_client.post(self.url, self.valid_data, format="json")
1✔
468
        self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR)
1✔
469
        self.assertIn("error", response.data)
1✔
470

471
    @patch("requests.post")
1✔
472
    def test_save_route_unauthenticated(self, mock_post):
1✔
473
        """Test attempting to save a route while unauthenticated"""
474
        # Data for saving a new route
475
        data = {
1✔
476
            "departure": [40.7128, -74.0060],
477
            "destination": [34.0522, -118.2437],
478
            "saved_route": True,
479
            "name": "New Saved Route",
480
        }
481

482
        response = self.api_client.post(self.url, data, format="json")
1✔
483
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
1✔
484

485

486
class HeatmapDataTestCase(BaseTestCase):
1✔
487
    """Test cases for the heatmap_data function"""
488

489
    def setUp(self):
1✔
490
        super().setUp()
1✔
491
        self.url = reverse("heatmap-data")  # URL for heatmap data endpoint
1✔
492

493
        # Sample data to be returned by the cursor
494
        self.mock_data = [
1✔
495
            (40.7128, -74.0060, "5"),
496
            (40.7580, -73.9855, "10"),
497
            (40.7431, -73.9712, None),
498
        ]
499

500
    @patch("django.db.connection.cursor")
1✔
501
    def test_heatmap_data_success(self, mock_cursor):
1✔
502
        """Test successful retrieval of heatmap data"""
503
        # Mock the cursor's fetchall method to return our sample data
504
        mock_cursor_instance = MagicMock()
1✔
505
        mock_cursor_instance.fetchall.return_value = self.mock_data
1✔
506
        mock_cursor.return_value.__enter__.return_value = mock_cursor_instance
1✔
507

508
        response = self.client.get(self.url)
1✔
509

510
        self.assertEqual(response.status_code, 200)
1✔
511
        data = json.loads(response.content)
1✔
512
        self.assertEqual(len(data), 3)
1✔
513

514
        # Verify the data is formatted correctly
515
        self.assertEqual(data[0]["latitude"], 40.7128)
1✔
516
        self.assertEqual(data[0]["longitude"], -74.0060)
1✔
517
        self.assertEqual(data[0]["intensity"], 5.0)
1✔
518

519
        # Verify that None is handled properly
520
        self.assertEqual(data[2]["intensity"], 0.0)
1✔
521

522
    @patch("django.db.connection.cursor")
1✔
523
    def test_heatmap_data_db_error(self, mock_cursor):
1✔
524
        """Test handling of database errors"""
525
        # Mock the cursor to raise an exception
526
        mock_cursor.return_value.__enter__.side_effect = Exception("Database error")
1✔
527

528
        response = self.client.get(self.url)
1✔
529

530
        self.assertEqual(response.status_code, 200)  # Even on error, returns 200
1✔
531
        data = json.loads(response.content)
1✔
532
        self.assertEqual(data, [])  # Empty list on error
1✔
533

534

535
class RouteSafetyFunctionsTestCase(BaseTestCase):
1✔
536
    """Test cases for the route safety processing functions"""
537

538
    def setUp(self):
1✔
539
        super().setUp()
1✔
540
        self.linestring = "LINESTRING(-74.0060 40.7128, -118.2437 34.0522)"
1✔
541

542
        self.mock_hotspots = [
1✔
543
            {
544
                "latitude": 40.7200,
545
                "longitude": -74.0100,
546
                "complaints": 15,
547
                "distance": 0.01,
548
            },
549
            {
550
                "latitude": 40.7300,
551
                "longitude": -74.0200,
552
                "complaints": 20,
553
                "distance": 0.02,
554
            },
555
        ]
556

557
        self.mock_initial_route = {
1✔
558
            "routes": [
559
                {
560
                    "geometry": "some_encoded_polyline",
561
                    "summary": {"distance": 3941.2, "duration": 3146.6},
562
                }
563
            ]
564
        }
565

566
        self.departure = [-74.0060, 40.7128]
1✔
567
        self.destination = [-118.2437, 34.0522]
1✔
568

569
    @patch("django.db.connection.cursor")
1✔
570
    def test_get_crime_hotspots(self, mock_cursor):
1✔
571
        """Test the get_crime_hotspots function"""
572
        # Mock the cursor response
573
        mock_cursor_instance = MagicMock()
1✔
574
        mock_cursor_instance.fetchall.return_value = [
1✔
575
            (40.7200, -74.0100, 15, 0.01),
576
            (40.7300, -74.0200, 20, 0.02),
577
        ]
578
        mock_cursor.return_value.__enter__.return_value = mock_cursor_instance
1✔
579

580
        result = get_crime_hotspots(self.linestring)
1✔
581

582
        self.assertEqual(len(result), 2)
1✔
583
        self.assertEqual(result[0]["latitude"], 40.7200)
1✔
584
        self.assertEqual(result[0]["longitude"], -74.0100)
1✔
585
        self.assertEqual(result[0]["complaints"], 15)
1✔
586
        self.assertEqual(result[0]["distance"], 0.01)
1✔
587

588
    @patch("django.db.connection.cursor")
1✔
589
    def test_get_crime_hotspots_db_error(self, mock_cursor):
1✔
590
        """Test get_crime_hotspots handling of database errors"""
591
        # Mock the cursor to raise an exception
592
        mock_cursor.return_value.__enter__.side_effect = Exception("Database error")
1✔
593

594
        result = get_crime_hotspots(self.linestring)
1✔
595

596
        self.assertEqual(result, [])  # Should return empty list on error
1✔
597

598
    def test_create_avoid_polygons(self):
1✔
599
        """Test the create_avoid_polygons function"""
600
        result = create_avoid_polygons(self.mock_hotspots, radius=0.1)
1✔
601

602
        self.assertIsInstance(result, MultiPolygon)
1✔
603
        self.assertEqual(len(result.geoms), 2)
1✔
604

605
        # Each polygon should have 9 coordinates (8 points + closing point)
606
        for polygon in result.geoms:
1✔
607
            self.assertEqual(len(polygon.exterior.coords), 9)
1✔
608

609
    def test_create_avoid_polygons_empty(self):
1✔
610
        """Test create_avoid_polygons with empty hotspots"""
611
        result = create_avoid_polygons([])
1✔
612

613
        self.assertIsNone(result)
1✔
614

615
    @patch("requests.post")
1✔
616
    def test_get_safer_ors_route(self, mock_post):
1✔
617
        """Test the get_safer_ors_route function"""
618
        # Create a simple avoid polygon
619
        avoid_polygons = MultiPolygon(
1✔
620
            [
621
                Polygon(
622
                    [
623
                        (-74.02, 40.72),
624
                        (-74.01, 40.72),
625
                        (-74.01, 40.73),
626
                        (-74.02, 40.73),
627
                        (-74.02, 40.72),
628
                    ]
629
                )
630
            ]
631
        )
632

633
        # Mock ORS response
634
        mock_response = MagicMock()
1✔
635
        mock_response.json.return_value = {
1✔
636
            "routes": [{"geometry": "safer_route_polyline"}]
637
        }
638
        mock_post.return_value = mock_response
1✔
639

640
        result = get_safer_ors_route(self.departure, self.destination, avoid_polygons)
1✔
641

642
        self.assertEqual(result["routes"][0]["geometry"], "safer_route_polyline")
1✔
643

644
        # Verify that avoid_polygons was included in the request
645
        args, kwargs = mock_post.call_args
1✔
646
        self.assertIn("json", kwargs)
1✔
647
        self.assertIn("options", kwargs["json"])
1✔
648
        self.assertIn("avoid_polygons", kwargs["json"]["options"])
1✔
649

650
    @patch("requests.post")
1✔
651
    def test_get_safer_ors_route_error(self, mock_post):
1✔
652
        """Test get_safer_ors_route handling of API errors"""
653
        # Mock ORS response to raise an exception
654
        mock_post.side_effect = requests.exceptions.RequestException("API error")
1✔
655

656
        result = get_safer_ors_route(self.departure, self.destination, None)
1✔
657

658
        self.assertIn("error", result)
1✔
659

660
    @patch("polyline.decode")
1✔
661
    @patch("django.db.connection.cursor")
1✔
662
    @patch("requests.post")
1✔
663
    def test_process_route_with_crime_data(
1✔
664
        self, mock_post, mock_cursor, mock_polyline_decode
665
    ):
666
        """Test the process_route_with_crime_data function"""
667
        # Mock polyline decode
668
        mock_polyline_decode.return_value = [[-74.0060, 40.7128], [-118.2437, 34.0522]]
1✔
669

670
        # Mock cursor for crime data
671
        mock_cursor_instance = MagicMock()
1✔
672
        mock_cursor_instance.fetchall.return_value = [
1✔
673
            (40.7200, -74.0100, 15, 0.01),
674
            (40.7300, -74.0200, 20, 0.02),
675
        ]
676
        mock_cursor.return_value.__enter__.return_value = mock_cursor_instance
1✔
677

678
        # Mock ORS response for safer route
679
        mock_response = MagicMock()
1✔
680
        mock_response.json.return_value = {
1✔
681
            "routes": [{"geometry": "safer_route_polyline"}]
682
        }
683
        mock_post.return_value = mock_response
1✔
684

685
        result = process_route_with_crime_data(self.mock_initial_route)
1✔
686

687
        self.assertEqual(result["routes"][0]["geometry"], "safer_route_polyline")
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

© 2025 Coveralls, Inc