Skip to content

Commit 1a98c65

Browse files
authored
Merge pull request #77 from TaskarCenterAtUW/stage
Stage to Main
2 parents 7f90b36 + f08aec3 commit 1a98c65

File tree

3 files changed

+256
-14
lines changed

3 files changed

+256
-14
lines changed

.github/workflows/unit_tests.yaml

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ on:
44
branches-ignore:
55
- '**'
66
pull_request:
7-
branches: [master, dev, stage]
7+
branches: [main, dev, stage]
88

99
jobs:
1010
UnitTest:
@@ -27,14 +27,48 @@ jobs:
2727
pip install -r requirements.txt
2828
pip install numpy==1.26.4
2929
30+
- name: Determine output folder
31+
id: set_output_folder
32+
run: |
33+
if [[ $GITHUB_EVENT_NAME == "pull_request" ]]; then
34+
branch_name=$GITHUB_BASE_REF
35+
else
36+
branch_name=$GITHUB_REF_NAME
37+
fi
38+
39+
if [[ $branch_name == "main" ]]; then
40+
echo "output_folder=prod" >> $GITHUB_ENV
41+
elif [[ $branch_name == "stage" ]]; then
42+
echo "output_folder=stage" >> $GITHUB_ENV
43+
elif [[ $branch_name == "dev" ]]; then
44+
echo "output_folder=dev" >> $GITHUB_ENV
45+
else
46+
echo "Unknown branch: $branch_name"
47+
exit 1
48+
fi
49+
3050
- name: Run tests with coverage
3151
run: |
32-
coverage run --source=src -m unittest discover -s tests/unit_tests/
33-
coverage xml
52+
timestamp=$(date '+%Y-%m-%d_%H-%M-%S')
53+
mkdir -p test_results
54+
log_file="test_results/${timestamp}_report.log"
55+
echo -e "\nTest Cases Report Report\n" >> $log_file
56+
# Run the tests and append output to the log file
57+
python -m coverage run --source=src -m unittest discover -s tests/unit_tests >> $log_file 2>&1
58+
echo -e "\nCoverage Report\n" >> $log_file
59+
coverage report >> $log_file
3460
3561
- name: Check coverage
3662
run: |
3763
coverage report --fail-under=85
3864
39-
#- name: Run Coverage Report
40-
# run: coverage run --source=src -m unittest discover -s tests/unit_tests
65+
- name: Upload report to Azure
66+
uses: LanceMcCarthy/Action-AzureBlobUpload@v2
67+
with:
68+
source_folder: 'test_results'
69+
destination_folder: '${{ env.output_folder }}'
70+
connection_string: ${{ secrets.AZURE_STORAGE_CONNECTION_STRING }}
71+
container_name: 'flex-validation-service'
72+
clean_destination_folder: false
73+
delete_if_exists: false
74+

requirements.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ fastapi~=0.111.1
22
pydantic==1.10.16
33
html_testRunner==1.2.1
44
uvicorn==0.20.0
5-
python-ms-core==0.0.22
6-
gtfs-canonical-validator==0.0.5
5+
python-ms-core==0.0.23
6+
gtfs-canonical-validator==0.0.6

tests/unit_tests/test_gtfs_flex_validation.py

Lines changed: 215 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
FAIL3_FILE_NAME = 'flex-bad-foreignkey.zip'
1919
FAIL4_FILE_NAME = 'flex-bad-filename.zip'
2020

21+
2122
class TestBadFile4(unittest.TestCase):
2223

2324
@patch.object(GTFSFlexValidation, 'download_single_file')
@@ -198,7 +199,6 @@ def test(self):
198199
self.assertTrue(is_valid)
199200

200201

201-
202202
class TestSuccessWithMacOSFile(unittest.TestCase):
203203
@patch.object(GTFSFlexValidation, 'download_single_file')
204204
def setUp(self, mock_download_single_file):
@@ -219,7 +219,7 @@ def setUp(self, mock_download_single_file):
219219
mock_download_single_file.return_value = file_path
220220

221221
def tearDown(self):
222-
pass #GTFSFlexValidation.clean_up(os.path.join(DOWNLOAD_FILE_PATH, self.validator.prefix))
222+
pass # GTFSFlexValidation.clean_up(os.path.join(DOWNLOAD_FILE_PATH, self.validator.prefix))
223223

224224
def test_validate_with_valid_file(self):
225225
# Arrange
@@ -254,7 +254,7 @@ def setUp(self, mock_download_single_file):
254254
self.validator = GTFSFlexValidation(file_path=file_path, storage_client=MagicMock())
255255
self.validator.file_path = file_path
256256
self.validator.file_relative_path = SUCCESS_FILE_NAME
257-
self.validator.container_name = None
257+
self.validator.container_name = None
258258
self.validator.prefix = Settings().get_unique_id()
259259
mock_download_single_file.return_value = os.path.join(dl_folder_path, SUCCESS_FILE_NAME)
260260

@@ -360,7 +360,7 @@ def setUp(self, mock_download_single_file):
360360
mock_download_single_file.return_value = file_path
361361

362362
def tearDown(self):
363-
pass #GTFSFlexValidation.clean_up(os.path.join(DOWNLOAD_FILE_PATH, self.validator.prefix))
363+
pass # GTFSFlexValidation.clean_up(os.path.join(DOWNLOAD_FILE_PATH, self.validator.prefix))
364364

365365
def test_validate_with_invalid_file(self):
366366
# Arrange
@@ -410,15 +410,223 @@ def test_download_single_file_exception(self):
410410
self.validator.storage_client.get_file_from_url = MagicMock()
411411
file = MagicMock()
412412
file.file_path = 'text_file.txt'
413-
file.get_stream = MagicMock(side_effect=FileNotFoundError("Mocked FileNotFoundError"))
413+
file.get_stream = MagicMock(side_effect=FileNotFoundError('Mocked FileNotFoundError'))
414414
self.validator.storage_client.get_file_from_url.return_value = file
415415

416416
dl_folder_path = os.path.join(DOWNLOAD_FILE_PATH)
417417
os.makedirs(dl_folder_path, exist_ok=True)
418418

419+
def test_clean_up_non_existent_path(self):
420+
# Arrange
421+
non_existent_path = f'{DOWNLOAD_FILE_PATH}/non_existent_file_or_folder'
422+
423+
# Act
424+
try:
425+
GTFSFlexValidation.clean_up(non_existent_path)
426+
success = True
427+
except Exception:
428+
success = False
429+
430+
# Assert
431+
self.assertTrue(success)
432+
433+
def test_download_single_file_file_not_found_exception(self):
434+
# Arrange
435+
self.validator.storage_client = MagicMock()
436+
self.validator.storage_client.get_file_from_url = MagicMock()
437+
self.validator.storage_client.get_file_from_url.side_effect = FileNotFoundError('File not found')
438+
self.validator.prefix = Settings().get_unique_id()
439+
419440
# Act & Assert
420-
# with self.assertRaises(FileNotFoundError):
421-
# self.validator.download_single_file(file_upload_path=file_upload_path)
441+
with self.assertRaises(FileNotFoundError):
442+
self.validator.download_single_file(file_upload_path='mock_nonexistent_file')
443+
444+
@patch('src.gtfs_flex_validation.CanonicalValidator')
445+
def test_is_gtfs_flex_valid_with_errors(self, mock_canonical_validator):
446+
# Arrange
447+
mock_result = MagicMock()
448+
mock_result.status = False
449+
mock_result.error = [
450+
{'code': 'INVALID_FIELD', 'sampleNotices': [{'fieldName': 'invalid_field', 'filename': 'flex_data'}]}
451+
]
452+
mock_canonical_validator.return_value.validate.return_value = mock_result
453+
454+
expected_downloaded_file_path = f'{SAVED_FILE_PATH}/fail_schema_1.zip'
455+
self.validator.download_single_file = MagicMock(return_value=expected_downloaded_file_path)
456+
457+
# Act
458+
is_valid, validation_message = self.validator.is_gtfs_flex_valid()
459+
460+
# Assert
461+
self.assertFalse(is_valid)
462+
self.assertIn('INVALID_FIELD', validation_message)
463+
464+
@patch('src.gtfs_flex_validation.CanonicalValidator')
465+
def test_is_gtfs_flex_valid_with_pathways_items(self, mock_canonical_validator):
466+
# Arrange
467+
mock_result = MagicMock()
468+
mock_result.status = False
469+
mock_result.error = [
470+
{'code': 'INVALID_FIELD',
471+
'sampleNotices': [{'fieldName': 'start_pickup_dropoff_window', 'filename': 'stop_times.txt'},
472+
{'fieldName': 'end_pickup_dropoff_window', 'filename': 'stop_times.txt'}]}
473+
]
474+
mock_canonical_validator.return_value.validate.return_value = mock_result
475+
476+
expected_downloaded_file_path = f'{SAVED_FILE_PATH}/fail_schema_1.zip'
477+
self.validator.download_single_file = MagicMock(return_value=expected_downloaded_file_path)
478+
479+
# Act
480+
is_valid, validation_message = self.validator.is_gtfs_flex_valid()
481+
482+
# Assert
483+
self.assertFalse(is_valid)
484+
self.assertIn('INVALID_FIELD', validation_message)
485+
486+
@patch('src.gtfs_flex_validation.CanonicalValidator')
487+
def test_is_gtfs_flex_valid_with_pathways_parent_items(self, mock_canonical_validator):
488+
# Arrange
489+
mock_result = MagicMock()
490+
mock_result.status = False
491+
mock_result.error = [
492+
{'code': 'INVALID_FIELD',
493+
'sampleNotices': [{'fieldName': 'start_pickup_dropoff_window', 'childFilename': 'locations.geojson'},
494+
{'fieldName': 'end_pickup_dropoff_window', 'childFilename': 'booking_rules.txt'}]}
495+
]
496+
mock_canonical_validator.return_value.validate.return_value = mock_result
497+
498+
expected_downloaded_file_path = f'{SAVED_FILE_PATH}/fail_schema_1.zip'
499+
self.validator.download_single_file = MagicMock(return_value=expected_downloaded_file_path)
500+
501+
# Act
502+
is_valid, validation_message = self.validator.is_gtfs_flex_valid()
503+
504+
# Assert
505+
self.assertFalse(is_valid)
506+
self.assertIn('INVALID_FIELD', validation_message)
507+
508+
@patch('src.gtfs_flex_validation.CanonicalValidator')
509+
def test_is_gtfs_flex_valid_with_no_errors(self, mock_canonical_validator):
510+
# Arrange
511+
mock_result = MagicMock()
512+
mock_result.status = True
513+
mock_result.error = []
514+
mock_canonical_validator.return_value.validate.return_value = mock_result
515+
516+
expected_downloaded_file_path = f'{SAVED_FILE_PATH}/{SUCCESS2_FILE_NAME}'
517+
self.validator.download_single_file = MagicMock(return_value=expected_downloaded_file_path)
518+
519+
# Act
520+
is_valid, validation_message = self.validator.is_gtfs_flex_valid()
521+
522+
# Assert
523+
self.assertTrue(is_valid)
524+
525+
def test_is_gtfs_flex_valid_with_invalid_file_extension(self):
526+
self.validator.file_relative_path = f'{SAVED_FILE_PATH}/fail_schema_1.txt'
527+
528+
# Act
529+
is_valid, validation_message = self.validator.is_gtfs_flex_valid()
530+
531+
# Assert
532+
self.assertFalse(is_valid)
533+
534+
@patch.object(GTFSFlexValidation, 'is_gtfs_flex_valid')
535+
def test_validate_facade(self, mock_is_gtfs_flex_valid):
536+
# Arrange
537+
mock_is_gtfs_flex_valid.return_value = (True, 'Valid file')
538+
539+
# Act
540+
is_valid, validation_message = self.validator.validate()
541+
542+
# Assert
543+
mock_is_gtfs_flex_valid.assert_called_once()
544+
self.assertTrue(is_valid)
545+
self.assertEqual(validation_message, 'Valid file')
546+
547+
@patch('os.makedirs')
548+
@patch('builtins.open', new_callable=MagicMock)
549+
def test_download_single_file_creates_directory(self, mock_open, mock_makedirs):
550+
# Arrange
551+
mock_file = MagicMock()
552+
mock_file.file_path = 'file.zip'
553+
mock_file.get_stream = MagicMock(return_value=b'file_content')
554+
555+
self.validator.storage_client = MagicMock()
556+
self.validator.storage_client.get_file_from_url = MagicMock(return_value=mock_file)
557+
558+
self.validator.prefix = "test-prefix" # Mock prefix
559+
expected_dl_folder_path = f"{DOWNLOAD_FILE_PATH}/{self.validator.prefix}"
560+
expected_file_path = f"{expected_dl_folder_path}/file.zip"
561+
562+
# Act
563+
downloaded_file_path = self.validator.download_single_file(file_upload_path='mock_file.zip')
564+
565+
# Assert
566+
mock_makedirs.assert_called_once_with(expected_dl_folder_path, exist_ok=True)
567+
mock_open.assert_called_once_with(expected_file_path, 'wb')
568+
self.assertEqual(downloaded_file_path, expected_file_path)
569+
mock_file.get_stream.assert_called_once()
570+
571+
572+
class TestGTFSFlexValidationInit(unittest.TestCase):
573+
@patch('src.gtfs_flex_validation.Settings')
574+
def test_initialization(self, mock_settings):
575+
# Arrange
576+
mock_storage_client = MagicMock()
577+
mock_container = MagicMock()
578+
mock_storage_client.get_container.return_value = mock_container
579+
580+
mock_settings_instance = mock_settings.return_value
581+
mock_settings_instance.storage_container_name = "test_container"
582+
mock_settings_instance.get_unique_id.return_value = "unique_prefix"
583+
584+
file_path = "path/to/file.zip"
585+
586+
# Act
587+
validation_instance = GTFSFlexValidation(
588+
file_path=file_path,
589+
storage_client=mock_storage_client,
590+
prefix=None
591+
)
592+
593+
# Assert
594+
self.assertEqual(validation_instance.file_path, file_path)
595+
self.assertEqual(validation_instance.file_relative_path, "file.zip")
596+
self.assertEqual(validation_instance.container_name, "test_container")
597+
self.assertEqual(validation_instance.prefix, "unique_prefix")
598+
self.assertEqual(validation_instance.client, mock_container)
599+
600+
mock_storage_client.get_container.assert_called_once_with(container_name="test_container")
601+
mock_settings_instance.get_unique_id.assert_called_once()
602+
603+
@patch('src.gtfs_flex_validation.Settings')
604+
def test_initialization_with_prefix(self, mock_settings):
605+
# Arrange
606+
mock_storage_client = MagicMock()
607+
mock_container = MagicMock()
608+
mock_storage_client.get_container.return_value = mock_container
609+
610+
mock_settings_instance = mock_settings.return_value
611+
mock_settings_instance.storage_container_name = "test_container"
612+
613+
file_path = "path/to/file.zip"
614+
custom_prefix = "custom_prefix"
615+
616+
# Act
617+
validation_instance = GTFSFlexValidation(
618+
file_path=file_path,
619+
storage_client=mock_storage_client,
620+
prefix=custom_prefix
621+
)
622+
623+
# Assert
624+
self.assertEqual(validation_instance.prefix, custom_prefix)
625+
self.assertEqual(validation_instance.container_name, "test_container")
626+
self.assertEqual(validation_instance.file_relative_path, "file.zip")
627+
628+
mock_storage_client.get_container.assert_called_once_with(container_name="test_container")
629+
mock_settings_instance.get_unique_id.assert_not_called()
422630

423631

424632
if __name__ == '__main__':

0 commit comments

Comments
 (0)