Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/nitinawari/Nest
Browse files Browse the repository at this point in the history
  • Loading branch information
nitinawari committed Jan 5, 2025
2 parents 4271f6c + 80ae681 commit d41cc89
Show file tree
Hide file tree
Showing 14 changed files with 522 additions and 38 deletions.
3 changes: 0 additions & 3 deletions backend/apps/common/constants.py

This file was deleted.

23 changes: 0 additions & 23 deletions backend/apps/common/geocoding.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,11 @@
"""Geocoding utils."""

import logging
import time
from functools import lru_cache

import requests
from geopy.geocoders import Nominatim

from apps.common.utils import get_nest_user_agent

logger = logging.getLogger(__name__)


@lru_cache(maxsize=1024)
def get_ip_coordinates(ip_address):
"""Return IP address geo coordinates."""
if not ip_address:
return None

try:
response = requests.get(f"https://ipapi.co/{ip_address}/latlong/", timeout=5)
if response.status_code == requests.codes.ok:
return response.text.split(",")
except TypeError:
logger.exception(
"Could not get coordinates for the IP address",
extra={"ip_address": ip_address},
)
return None


def get_location_coordinates(query, delay=2):
"""Get location geo coordinates."""
Expand Down
11 changes: 0 additions & 11 deletions backend/apps/common/management/commands/clear_cache.py

This file was deleted.

40 changes: 40 additions & 0 deletions backend/tests/common/geocoding_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from unittest.mock import MagicMock, patch

import pytest

from apps.common.geocoding import get_location_coordinates


class TestGeocoding:
@pytest.mark.parametrize(
("query", "mock_latitude", "mock_longitude"),
[
("San Francisco, CA", 37.7749, -122.4194),
],
)
@patch("apps.common.geocoding.Nominatim.geocode")
def test_get_location_coordinates(
self,
mock_geocode,
query,
mock_latitude,
mock_longitude,
):
mock_geocode.return_value = MagicMock(latitude=mock_latitude, longitude=mock_longitude)
with (
patch("apps.common.geocoding.time.sleep", return_value=None),
patch("apps.common.geocoding.get_nest_user_agent", return_value="test_agent"),
):
result = get_location_coordinates(query)
assert result.latitude == mock_latitude
assert result.longitude == mock_longitude

def test_get_location_coordinates_none(self):
query = "Invalid Location"
with (
patch("apps.common.geocoding.time.sleep", return_value=None),
patch("apps.common.geocoding.Nominatim.geocode", return_value=None),
patch("apps.common.geocoding.get_nest_user_agent", return_value="test_agent"),
):
result = get_location_coordinates(query)
assert result is None
97 changes: 97 additions & 0 deletions backend/tests/common/index_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
from unittest.mock import MagicMock, mock_open, patch

import pytest
from algoliasearch.exceptions import AlgoliaException

from apps.common.index import IndexBase


class TestIndexBase:
@pytest.mark.parametrize(
("app_name", "index_name", "synonyms_data", "expected_synonym"),
[
(
"app1",
"index1",
[{"synonym": "synonym1"}],
[{"objectID": "index1-synonym-1", "synonym": "synonym1", "type": "synonym"}],
),
(
"app2",
"index2",
[{"synonym": "synonym2", "type": "oneWaySynonym"}],
[{"objectID": "index2-synonym-1", "synonym": "synonym2", "type": "oneWaySynonym"}],
),
],
)
@patch("apps.common.index.Path.open", new_callable=mock_open)
@patch("apps.common.index.IndexBase._get_client")
@patch("apps.common.index.settings")
@patch("apps.common.index.json.load")
def test_reindex_synonyms(
self,
mock_json_load,
mock_settings,
mock_get_client,
mock_open,
app_name,
index_name,
synonyms_data,
expected_synonym,
):
mock_settings.ENVIRONMENT.lower.return_value = "testenv"
mock_settings.BASE_DIR = "/base/dir"
mock_json_load.return_value = synonyms_data

mock_index = MagicMock()
mock_get_client.return_value.init_index.return_value = mock_index

expected_file_path = f"/base/dir/apps/{app_name}/index/synonyms/{index_name}.json"

with patch("apps.common.index.logger"):
IndexBase.reindex_synonyms(app_name, index_name)

mock_open.assert_called_once_with(expected_file_path)
mock_index.clear_synonyms.assert_called_once()
mock_index.save_synonyms.assert_called_once_with(
expected_synonym, {"replaceExistingSynonyms": True}
)

@pytest.mark.parametrize(
("index_name", "search_response", "expected_count"),
[
("index1", {"nbHits": 5}, 5),
("index2", AlgoliaException("Error"), 0),
],
)
@patch("apps.common.index.IndexBase._get_client")
@patch("apps.common.index.logger")
@patch("apps.common.index.settings")
def test_get_total_count(
self,
mock_settings,
mock_logger,
mock_get_client,
index_name,
search_response,
expected_count,
):
mock_settings.ENVIRONMENT.lower.return_value = "testenv"

mock_index = MagicMock()
mock_get_client.return_value.init_index.return_value = mock_index

if isinstance(search_response, dict):
mock_index.search.return_value = search_response
else:
mock_index.search.side_effect = search_response

count = IndexBase.get_total_count(index_name)

assert count == expected_count
if isinstance(search_response, dict):
mock_index.search.assert_called_once_with("", {"hitsPerPage": 0, "analytics": False})
else:
mock_logger.exception.assert_called_once_with(
"Error retrieving index count for '%s'", index_name
)
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import json
from io import StringIO
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

from apps.common.management.commands.add_project_custom_tags import Command, Project


class TestAddProjectCustomTags:
@pytest.mark.parametrize(
("file_exists", "file_content", "expected_output"),
[
(False, None, "File not found: /mocked/path/data/project-custom-tags/test-file.json"),
(
True,
json.dumps({"projects": [], "tags": []}),
"No projects or tags found in the file.",
),
(
True,
json.dumps({"projects": ["proj1"], "tags": ["tag1"]}),
"Project proj1 does not exists.",
),
],
)
@patch("apps.common.management.commands.add_project_custom_tags.Project.objects.get")
@patch("apps.common.management.commands.add_project_custom_tags.Path.open")
@patch("apps.common.management.commands.add_project_custom_tags.Path.exists")
@patch(
"apps.common.management.commands.add_project_custom_tags.settings.BASE_DIR",
new=Path("/mocked/path"),
)
def test_handle(
self,
mock_exists,
mock_open_func,
mock_get,
file_exists,
file_content,
expected_output,
capsys,
):
mock_exists.return_value = file_exists
mock_open_func.side_effect = (
lambda *_args, **__kwargs: StringIO(file_content) if file_content else None
)

def mock_get_side_effect(key):
if key == "proj1":
raise Project.DoesNotExist
mock_project = MagicMock()
mock_project.custom_tags = []
return mock_project

mock_get.side_effect = mock_get_side_effect
command = Command()

command.handle(**{"file-name": "test-file.json"})

captured = capsys.readouterr()
assert expected_output in captured.err

@pytest.mark.parametrize(
("file_content", "projects", "expected_saved_tags"),
[
(
json.dumps({"projects": ["proj1"], "tags": ["tag1"]}),
{"proj1": ["tag2"]},
{"proj1": ["tag1", "tag2"]},
),
],
)
@patch("apps.common.management.commands.add_project_custom_tags.Project.objects.get")
@patch("apps.common.management.commands.add_project_custom_tags.Path.open")
@patch("apps.common.management.commands.add_project_custom_tags.Path.exists")
@patch(
"apps.common.management.commands.add_project_custom_tags.settings.BASE_DIR",
new=Path("/mocked/path"),
)
def test_handle_with_projects_and_tags(
self,
mock_exists,
mock_open_func,
mock_get,
file_content,
projects,
expected_saved_tags,
):
mock_exists.return_value = True
mock_open_func.side_effect = lambda *_args, **__kwargs: StringIO(file_content)

class MockProject:
def __init__(self, key):
self.key = key
self.custom_tags = projects[key]

def save(self, update_fields=None):
projects[self.key] = self.custom_tags

mock_get.side_effect = lambda key: MockProject(key)
command = Command()

command.handle(**{"file-name": "test-file.json"})

for key, expected_tags in expected_saved_tags.items():
assert sorted(projects[key]) == sorted(expected_tags)
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from unittest.mock import MagicMock, patch

import pytest

from apps.common.management.commands.algolia_update_synonyms import Command


@pytest.mark.parametrize(
"indexes",
[["IssueIndex", "ProjectIndex"]],
)
class TestAlgoliaUpdateSynonyms:
@patch("builtins.print")
@patch("apps.common.management.commands.algolia_update_synonyms.ProjectIndex")
@patch("apps.common.management.commands.algolia_update_synonyms.IssueIndex")
def test_handle(self, mock_issue_index, mock_project_index, mock_print, indexes):
mock_indexes = {
"IssueIndex": mock_issue_index,
"ProjectIndex": mock_project_index,
}
for index_name, index_instance in mock_indexes.items():
index_instance.update_synonyms = MagicMock()
index_instance.index_name = index_name

command = Command()
command.handle()

for index_name in indexes:
index_instance = mock_indexes[index_name]
index_instance.update_synonyms.assert_called_once()
mock_print.assert_any_call(f"Updated {index_name.capitalize()} synonyms")
44 changes: 44 additions & 0 deletions backend/tests/common/management/commands/load_data_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from unittest.mock import MagicMock, patch

import pytest

from apps.common.management.commands.load_data import Command


@pytest.mark.parametrize("nest_apps", [("github", "owasp")])
class TestLoadDataCommand:
@patch("apps.common.management.commands.load_data.apps.get_app_config")
@patch("apps.common.management.commands.load_data.unregister")
@patch("apps.common.management.commands.load_data.register")
@patch("apps.common.management.commands.load_data.call_command")
@patch("apps.common.management.commands.load_data.transaction.atomic")
def test_handle(
self,
mock_atomic,
mock_call_command,
mock_register,
mock_unregister,
mock_get_app_config,
nest_apps,
):
mock_model = MagicMock()
mock_app_config = MagicMock()
mock_app_config.get_models.return_value = [mock_model]
mock_get_app_config.return_value = mock_app_config

mock_atomic.return_value.__enter__ = MagicMock()
mock_atomic.return_value.__exit__ = MagicMock()

mock_unregister.return_value = None
mock_register.return_value = None

command = Command()
command.handle()

for app in nest_apps:
mock_get_app_config.assert_any_call(app)
mock_app_config.get_models.assert_any_call()

mock_call_command.assert_called_once_with("loaddata", "data/nest.json.gz", "-v", "3")

mock_register.assert_called_with(mock_model)
Loading

0 comments on commit d41cc89

Please sign in to comment.