Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unit tests #4

Merged
merged 17 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added src/__init__.py
Empty file.
14 changes: 10 additions & 4 deletions src/geocode.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@ def geocode(address) -> str:
location = geolocator.geocode(address)
try:
latitude, longitude = location.latitude, location.longitude
except ValueError:
raise ValueError(
'geocode unable to find latitude & longitude for {address}'.format(address=address))
except AttributeError as e:
if "'NoneType' object has no attribute 'latitude'" in str(e):
raise AttributeError(
'geocode unable to find latitude & longitude for {address}'.format(address=address))
if "'NoneType' object has no attribute 'longitude'" in str(e):
raise AttributeError(
'geocode unable to find latitude & longitude for {address}'.format(address=address))
else:
raise # Re-raise the original AttributeError if the message doesn't match
return latitude, longitude


Expand All @@ -30,7 +36,7 @@ def find_timezone(gps) -> tuple:
try:
timezone_name = tf_init.timezone_at(lat=latitude, lng=longitude)
except ValueError:
raise ValueError('The coordinates were out of bounds {latitude}:{longitude}'.format(
raise ValueError('The coordinates were out of bounds {lat}:{lng}'.format(
lat=latitude, lng=longitude))
if timezone_name is None:
raise ValueError('GPS coordinates did not match a time_zone')
Expand Down
31 changes: 18 additions & 13 deletions src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,22 @@


# Convert CSV file to JSON object.
def csv_to_json(file_path):
def convert_csv_to_json(file_path):
csv_rows = []
with open(file_path) as csvfile:
reader = csv.DictReader(csvfile, skipinitialspace=True, quotechar='"')
title = reader.fieldnames

for row in reader:
csv_rows.extend([{title[i]: row[title[i]]
for i in range(len(title))}])
for i in range(len(title))}])

if csv_rows == None or csv_rows == []:
raise ValueError('Failed to convert CSV file to JSON. Exiting script.')
return csv_rows


if __name__ == '__main__':

csv_file_path = os.getcwd() + '/../test_data/sites_with_clients.csv'

# Convert CSV to valid JSON
data = csv_to_json(csv_file_path)
if data == None or data == []:
raise ValueError('Failed to convert CSV file to JSON. Exiting script.')

# Create each site from the CSV file
def add_geocoding_to_json(data):
for d in data:
# Variables
site_id = None
Expand All @@ -43,9 +36,21 @@ def csv_to_json(file_path):
d['gps'] = gps
d['country_code'] = country_code
d['time_zone'] = time_zone
return data


if __name__ == '__main__':

csv_file_path = os.getcwd() + '/../test_data/sites_with_clients.csv'

# Convert CSV to valid JSON
json_data_without_geocoding = convert_csv_to_json(csv_file_path)

json_data_with_geocoding = add_geocoding_to_json(
json_data_without_geocoding)

juniper_script(
mist_api_token=os.environ['MIST_API_TOKEN'],
org_id=os.environ['ORG_ID'],
data=data
data=json_data_with_geocoding
)
Empty file added test/__init__.py
Empty file.
112 changes: 112 additions & 0 deletions test/test_geocode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import unittest
from unittest.mock import patch, MagicMock
from src.geocode import geocode, find_timezone, find_country_code


class TestGeocode(unittest.TestCase):

@patch('src.geocode.Nominatim.geocode')
def test_given_list_of_valid_addresses_when_geocode_called_then_return_relevant_list_of_gps_locations(self, mock_nominatim):
# Define a list of addresses and their expected results
addresses = [
("40 Mayflower Dr, Plymouth PL2 3DG", (50.3868633, -4.1539256)),
("102 Petty France, London SW1H 9AJ",
(51.499929300000005, -0.13477761285315926)),
("London", (51.4893335, -0.14405508452768728)),
("Met Office, FitzRoy Road, Exeter, Devon, EX1 3PB",
(50.727350349999995, -3.4744726127760086))
]
# Mock the geocode method to return the corresponding latitude and longitude
for address, (lat, lon) in addresses:
mock_nominatim.return_value.latitude = lat
mock_nominatim.return_value.longitude = lon

# Call the geocode function for each address
result = geocode(address)

# Assert that the result matches the expected latitude and longitude
self.assertEqual(result, (lat, lon))

@patch('src.geocode.Nominatim.geocode')
def test_geocode_invalid_address(self, mock_geocode):
# Arrange
address = "Invalid Address"
mock_geocode.return_value = None # Simulate geocode returning None

# Act & Assert
with self.assertRaises(AttributeError) as context:
geocode(address)

expected_error_message = 'geocode unable to find latitude & longitude for {address}'.format(
address=address)
self.assertEqual(str(context.exception), expected_error_message)

@patch('src.geocode.TimezoneFinder')
def test_find_timezone_valid_coordinates(self, mock_timezone_finder):
tf_instance = MagicMock()
tf_instance.timezone_at.return_value = 'America/New_York'
mock_timezone_finder.return_value = tf_instance

gps_coordinates = (40.7128, -74.0060)
result = find_timezone(gps_coordinates)

self.assertEqual(result, 'America/New_York')

@patch('src.geocode.TimezoneFinder')
def test_find_timezone_out_of_bounds(self, mock_timezone_finder):
tf_instance = MagicMock()
tf_instance.timezone_at.side_effect = ValueError(
'The coordinates were out of bounds 40.7128:-74.0060')
mock_timezone_finder.return_value = tf_instance

gps_coordinates = (40.7128, -74.0060)

with self.assertRaises(ValueError) as context:
find_timezone(gps_coordinates)

self.assertEqual(str(context.exception),
'The coordinates were out of bounds 40.7128:-74.006')

@patch('src.geocode.TimezoneFinder')
def test_find_timezone_no_matching_timezone(self, mock_timezone_finder):
tf_instance = MagicMock()
tf_instance.timezone_at.return_value = None
mock_timezone_finder.return_value = tf_instance

gps_coordinates = (40.7128, -74.0060)

with self.assertRaises(ValueError) as context:
find_timezone(gps_coordinates)

self.assertEqual(str(context.exception),
'GPS coordinates did not match a time_zone')


class TestFindCountryCode(unittest.TestCase):
@patch('src.geocode.Nominatim.geocode')
def test_find_country_code_valid_coordinates(self, mock_nominatim):
geolocator_instance = MagicMock()
location_instance = MagicMock()
location_instance.raw = {'address': {'country_code': 'us'}}
geolocator_instance.reverse.return_value = location_instance
mock_nominatim.return_value = geolocator_instance

gps_coordinates = (40.7128, -74.0060)
result = find_country_code(gps_coordinates)

self.assertEqual(result, 'US')

@patch('src.geocode.Nominatim.geocode')
def test_find_country_code_invalid_coordinates(self, mock_nominatim):
geolocator_instance = MagicMock()
geolocator_instance.reverse.side_effect = Exception(
'Invalid coordinates')
mock_nominatim.return_value = geolocator_instance

gps_coordinates = (1000.0, 2000.0) # Invalid coordinates

with self.assertRaises(Exception) as context:
find_country_code(gps_coordinates)

self.assertEqual(str(context.exception),
'Must be a coordinate pair or Point')
56 changes: 56 additions & 0 deletions test/test_juniper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import unittest
from unittest.mock import patch, MagicMock
from src.juniper import juniper_script, Admin


class TestJuniperScript(unittest.TestCase):

@patch('src.juniper.Admin.post')
@patch('src.juniper.Admin.put')
def test_juniper_script(self, mock_put, mock_post):
# Mock Mist API responses
mock_post.return_value = {'id': '123', 'name': 'TestSite'}
mock_put.return_value = {'status': 'success'}

# Sample input data
data = [
{'Site Name': 'TestSite', 'Site Address': '123 Main St',
'gps': [1.23, 4.56], 'country_code': 'US', 'time_zone': 'UTC',
'Enable GovWifi': 'true', 'Enable MoJWifi': 'false',
'Wired NACS Radius Key': 'key1', 'GovWifi Radius Key': 'key2'}
]

# Call the function
juniper_script(data, mist_api_token='your_token', org_id='your_org_id')

# Assertions
mock_post.assert_called_once_with('/api/v1/orgs/your_org_id/sites', {
'name': 'TestSite',
'address': '123 Main St',
'latlng': {'lat': 1.23, 'lng': 4.56},
'country_code': 'US',
'timezone': 'UTC'
})

mock_put.assert_called_once_with('/api/v1/sites/123/setting', {
'vars': {
'Enable GovWifi': 'true',
'Enable MoJWifi': 'false',
'Wired NACS Radius Key': 'key1',
'GovWifi Radius Key': 'key2'
}
})

def test_juniper_script_missing_token(self):
# Test when mist_api_token is missing
with self.assertRaises(SystemExit) as cm:
juniper_script([], org_id='your_org_id')

self.assertEqual(cm.exception.code, 1)

def test_juniper_script_missing_org_id(self):
# Test when org_id is missing
with self.assertRaises(SystemExit) as cm:
juniper_script([], mist_api_token='your_token')

self.assertEqual(cm.exception.code, 1)
92 changes: 92 additions & 0 deletions test/test_main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import unittest
import tempfile
import csv
from unittest.mock import patch
from src.main import convert_csv_to_json, add_geocoding_to_json


class TestCsvToJson(unittest.TestCase):

def setUp(self):
# Create a temporary CSV file for testing
self.csv_data = [
{'Site Name': 'Test location 1', 'Site Address': '40 Mayflower Dr, Plymouth PL2 3DG', 'Enable GovWifi': ' "TRUE"',
'Enable MoJWifi': ' "FALSE"', 'GovWifi Radius Key': '00000DD0000BC0EEE000', 'Wired NACS Radius Key': '00000DD0000BC0EEE000'},
{'Site Name': 'Test location 2', 'Site Address': '102 Petty France, London SW1H 9AJ', 'Enable GovWifi': ' "TRUE"',
'Enable MoJWifi': ' "FALSE"', 'GovWifi Radius Key': '0D0E0DDE000BC0EEE000', 'Wired NACS Radius Key': '00000DD0000BC0EEE000'},
{'Site Name': 'Test location 3', 'Site Address': 'Met Office, FitzRoy Road, Exeter, Devon, EX1 3PB', 'Enable GovWifi': ' "TRUE"',
'Enable MoJWifi': ' "FALSE"', 'GovWifi Radius Key': '0D0E0DDE080BC0EEE000', 'Wired NACS Radius Key': '00000DD0000BC0EEE000'}
]
self.csv_file = tempfile.NamedTemporaryFile(
mode='w', delete=False, newline='', suffix='.csv')
self.csv_writer = csv.DictWriter(self.csv_file, fieldnames=[
'Site Name',
'Site Address',
'Enable GovWifi',
'Enable MoJWifi',
'GovWifi Radius Key',
'Wired NACS Radius Key'
])
self.csv_writer.writeheader()
self.csv_writer.writerows(self.csv_data)
self.csv_file.close()

def test_given_csv_file_when_csv_valid_then_convert_to_json(self):
juddin927 marked this conversation as resolved.
Show resolved Hide resolved
expected_json = self.csv_data
actual_json = convert_csv_to_json(self.csv_file.name)
self.assertEqual(actual_json, expected_json)

def test_given_csv_file_when_csv_file_empty_then_raise_value_error(self):
empty_csv_file = tempfile.NamedTemporaryFile(
mode='w', delete=False, newline='', suffix='.csv')
empty_csv_file.close()
with self.assertRaises(ValueError) as error:
convert_csv_to_json(empty_csv_file.name)
self.assertEqual(str(error.exception),
'Failed to convert CSV file to JSON. Exiting script.')

def test_given_file_path_when_csv_file_not_found_then_raise_FileNotFoundError(self):
# Test if the function handles a nonexistent CSV file correctly
nonexistent_file_path = 'nonexistent.csv'
with self.assertRaises(FileNotFoundError):
convert_csv_to_json(nonexistent_file_path)


class TestAddGeocodingToJson(unittest.TestCase):

@patch('src.main.geocode', side_effect=[
{'latitude': 50.3868633, 'longitude': -4.1539256},
{'latitude': 51.499929300000005, 'longitude': -0.13477761285315926},
{'latitude': 50.727350349999995, 'longitude': -3.4744726127760086},
])
@patch('src.main.find_country_code', return_value='GB')
@patch('src.main.find_timezone', return_value='Europe/London')
def test_given_site_name_and_site_address_in_json_format_when_function_called_then_add_geocode_country_code_and_time_zone(
self,
find_timezone,
mock_find_country_code,
mock_geocode
):
# Test if the function adds geocoding information correctly
data = [
{'Site Name': 'Site1', 'Site Address': '40 Mayflower Dr, Plymouth PL2 3DG'},
{'Site Name': 'Site2', 'Site Address': '102 Petty France, London SW1H 9AJ'},
{'Site Name': 'Site3',
'Site Address': 'Met Office, FitzRoy Road, Exeter, Devon, EX1 3PB'}
]

expected_data = [
{'Site Name': 'Site1', 'Site Address': '40 Mayflower Dr, Plymouth PL2 3DG', 'gps': {
'latitude': 50.3868633, 'longitude': -4.1539256}, 'country_code': 'GB', 'time_zone': 'Europe/London'},
{'Site Name': 'Site2', 'Site Address': '102 Petty France, London SW1H 9AJ', 'gps': {
'latitude': 51.499929300000005, 'longitude': -0.13477761285315926}, 'country_code': 'GB', 'time_zone': 'Europe/London'},
{'Site Name': 'Site3', 'Site Address': 'Met Office, FitzRoy Road, Exeter, Devon, EX1 3PB', 'gps': {
'latitude': 50.727350349999995, 'longitude': -3.4744726127760086}, 'country_code': 'GB', 'time_zone': 'Europe/London'}
]

actual_data = add_geocoding_to_json(data)

self.assertEqual(actual_data, expected_data)
find_timezone.assert_called()
mock_find_country_code.assert_called()
mock_geocode.assert_called()