Skip to content

Commit 52ea704

Browse files
Pavan-MicrosoftRoopan-MicrosoftAjitPadhi-Microsoftross-p-smithgpickett
authored
fix: fix unit testcases (#1918)
Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: Roopan-Microsoft <[email protected]> Co-authored-by: Ajit Padhi <[email protected]> Co-authored-by: Roopan P M <[email protected]> Co-authored-by: Ross Smith <[email protected]> Co-authored-by: gpickett <[email protected]> Co-authored-by: Francia Riesco <[email protected]> Co-authored-by: Francia Riesco <[email protected]> Co-authored-by: Prajwal D C <[email protected]> Co-authored-by: Harmanpreet-Microsoft <[email protected]> Co-authored-by: UtkarshMishra-Microsoft <[email protected]> Co-authored-by: Priyanka-Microsoft <[email protected]> Co-authored-by: Prasanjeet-Microsoft <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Kiran-Siluveru-Microsoft <[email protected]> Co-authored-by: Prashant-Microsoft <[email protected]> Co-authored-by: Rohini-Microsoft <[email protected]> Co-authored-by: Avijit-Microsoft <[email protected]> Co-authored-by: RaviKiran-Microsoft <[email protected]> Co-authored-by: Somesh Joshi <[email protected]> Co-authored-by: Himanshi Agrawal <[email protected]> Co-authored-by: pradeepjha-microsoft <[email protected]> Co-authored-by: Harmanpreet Kaur <[email protected]> Co-authored-by: Bangarraju-Microsoft <[email protected]> Co-authored-by: Harsh-Microsoft <[email protected]> Co-authored-by: Kanchan-Microsoft <[email protected]> Co-authored-by: Cristopher Coronado <[email protected]> Co-authored-by: Cristopher Coronado Moreira <[email protected]> Co-authored-by: Vamshi-Microsoft <[email protected]> Co-authored-by: Thanusree-Microsoft <[email protected]> Co-authored-by: Niraj Chaudhari (Persistent Systems Inc) <[email protected]> Co-authored-by: Rohini-Microsoft <[email protected]>
1 parent aa7a26f commit 52ea704

File tree

11 files changed

+310
-16
lines changed

11 files changed

+310
-16
lines changed

code/backend/batch/local.settings.json.sample

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"IsEncrypted": false,
33
"Values": {
44
"FUNCTIONS_WORKER_RUNTIME": "python",
5-
"AzureWebJobsStorage": "",
5+
"AzureWebJobsStorage__accountName": "",
66
"MyBindingConnection": "",
77
"AzureWebJobs.HttpExample.Disabled": "true"
88
},
@@ -11,4 +11,4 @@
1111
"CORS": "*",
1212
"CORSCredentials": false
1313
}
14-
}
14+
}

code/create_app.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@
99
from os import path
1010
import sys
1111
import re
12+
from urllib.parse import quote
13+
1214
import requests
1315
from openai import AzureOpenAI, Stream, APIStatusError
1416
from openai.types.chat import ChatCompletionChunk
1517
from flask import Flask, Response, request, Request, jsonify
1618
from dotenv import load_dotenv
17-
from urllib.parse import quote, urlparse
1819
from backend.batch.utilities.helpers.env_helper import EnvHelper
1920
from backend.batch.utilities.helpers.azure_search_helper import AzureSearchHelper
2021
from backend.batch.utilities.helpers.orchestrator_helper import Orchestrator

code/tests/chat_history/test_postgresdbservice.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ async def test_connect(mock_credential, mock_connect, postgres_client, mock_conn
3939
database="test_db",
4040
password="mock_token",
4141
port=5432,
42-
ssl="require",
42+
ssl=True,
4343
)
4444
assert postgres_client.conn == mock_connection
4545

code/tests/functional/tests/functions/integrated_vectorization/test_integrated_vectorization_resource_creation.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ def test_integrated_vectorization_datasouce_created(
102102
"container": {
103103
"name": f"{app_config.get_from_json('AZURE_BLOB_STORAGE_INFO','containerName')}"
104104
},
105+
"identity": {
106+
"@odata.type": "#Microsoft.Azure.Search.DataUserAssignedIdentity",
107+
"userAssignedIdentity": ""
108+
},
105109
"dataDeletionDetectionPolicy": {
106110
"@odata.type": "#Microsoft.Azure.Search.NativeBlobSoftDeleteDeletionDetectionPolicy"
107111
},
@@ -367,6 +371,10 @@ def test_integrated_vectorization_skillset_created(
367371
"resourceUri": f"https://localhost:{httpserver.port}/",
368372
"deploymentId": f"{app_config.get_from_json('AZURE_OPENAI_EMBEDDING_MODEL_INFO','model')}",
369373
"apiKey": f"{app_config.get('AZURE_OPENAI_API_KEY')}",
374+
"authIdentity": {
375+
"@odata.type": "#Microsoft.Azure.Search.DataUserAssignedIdentity",
376+
"userAssignedIdentity": ""
377+
},
370378
},
371379
],
372380
"indexProjections": {

code/tests/test_app.py

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from unittest.mock import AsyncMock, MagicMock, Mock, patch
66

7+
from azure.core.exceptions import ClientAuthenticationError, ResourceNotFoundError, ServiceRequestError
78
from openai import RateLimitError, BadRequestError, InternalServerError
89
import pytest
910
from flask.testing import FlaskClient
@@ -923,3 +924,228 @@ def test_conversation_azure_byod_returns_correct_response_when_streaming_without
923924
data
924925
== '{"id": "response.id", "model": "mock-openai-model", "created": 0, "object": "response.object", "choices": [{"messages": [{"role": "assistant", "content": "mock content"}]}]}\n'
925926
)
927+
928+
929+
class TestGetFile:
930+
"""Test the get_file endpoint for downloading files from blob storage."""
931+
932+
@patch("create_app.AzureBlobStorageClient")
933+
def test_get_file_success(self, mock_blob_client_class, client):
934+
"""Test successful file download with proper headers."""
935+
# given
936+
filename = "test_document.pdf"
937+
file_content = b"Mock file content for PDF document"
938+
939+
mock_blob_client = MagicMock()
940+
mock_blob_client_class.return_value = mock_blob_client
941+
mock_blob_client.file_exists.return_value = True
942+
mock_blob_client.download_file.return_value = file_content
943+
944+
# when
945+
response = client.get(f"/api/files/{filename}")
946+
947+
# then
948+
assert response.status_code == 200
949+
assert response.data == file_content
950+
assert response.headers["Content-Type"] == "application/pdf"
951+
assert response.headers["Content-Disposition"] == f'inline; filename="{filename}"'
952+
assert response.headers["Content-Length"] == str(len(file_content))
953+
assert response.headers["Cache-Control"] == "public, max-age=3600"
954+
assert response.headers["X-Content-Type-Options"] == "nosniff"
955+
assert response.headers["X-Frame-Options"] == "DENY"
956+
assert response.headers["Content-Security-Policy"] == "default-src 'none'"
957+
958+
# Verify blob client was initialized with correct container
959+
mock_blob_client_class.assert_called_once_with(container_name="documents")
960+
mock_blob_client.file_exists.assert_called_once_with(filename)
961+
mock_blob_client.download_file.assert_called_once_with(filename)
962+
963+
@patch("create_app.AzureBlobStorageClient")
964+
def test_get_file_with_unknown_mime_type(self, mock_blob_client_class, client):
965+
"""Test file download with unknown file extension."""
966+
# given
967+
filename = "test_file.unknownext"
968+
file_content = b"Mock file content"
969+
970+
mock_blob_client = MagicMock()
971+
mock_blob_client_class.return_value = mock_blob_client
972+
mock_blob_client.file_exists.return_value = True
973+
mock_blob_client.download_file.return_value = file_content
974+
975+
# when
976+
response = client.get(f"/api/files/{filename}")
977+
978+
# then
979+
assert response.status_code == 200
980+
assert response.headers["Content-Type"] == "application/octet-stream"
981+
982+
@patch("create_app.AzureBlobStorageClient")
983+
def test_get_file_large_file_warning(self, mock_blob_client_class, client):
984+
"""Test that large files are handled properly with logging."""
985+
# given
986+
filename = "large_document.pdf"
987+
file_content = b"x" * (11 * 1024 * 1024) # 11MB file
988+
989+
mock_blob_client = MagicMock()
990+
mock_blob_client_class.return_value = mock_blob_client
991+
mock_blob_client.file_exists.return_value = True
992+
mock_blob_client.download_file.return_value = file_content
993+
994+
# when
995+
response = client.get(f"/api/files/{filename}")
996+
997+
# then
998+
assert response.status_code == 200
999+
assert len(response.data) == len(file_content)
1000+
1001+
def test_get_file_empty_filename(self, client):
1002+
"""Test error response when filename is empty."""
1003+
# when
1004+
response = client.get("/api/files/")
1005+
1006+
# then
1007+
# This should result in a 404 as the route won't match
1008+
assert response.status_code == 404
1009+
1010+
def test_get_file_invalid_filename_too_long(self, client):
1011+
"""Test error response for filenames that are too long."""
1012+
# given
1013+
filename = "a" * 256 # 256 characters, exceeds 255 limit
1014+
1015+
# when
1016+
response = client.get(f"/api/files/{filename}")
1017+
1018+
# then
1019+
assert response.status_code == 400
1020+
assert response.json == {"error": "Filename too long"}
1021+
1022+
@patch("create_app.AzureBlobStorageClient")
1023+
def test_get_file_not_exists_in_storage(self, mock_blob_client_class, client):
1024+
"""Test error response when file doesn't exist in blob storage."""
1025+
# given
1026+
filename = "nonexistent.pdf"
1027+
1028+
mock_blob_client = MagicMock()
1029+
mock_blob_client_class.return_value = mock_blob_client
1030+
mock_blob_client.file_exists.return_value = False
1031+
1032+
# when
1033+
response = client.get(f"/api/files/{filename}")
1034+
1035+
# then
1036+
assert response.status_code == 404
1037+
assert response.json == {"error": "File not found"}
1038+
mock_blob_client.file_exists.assert_called_once_with(filename)
1039+
mock_blob_client.download_file.assert_not_called()
1040+
1041+
@patch("create_app.AzureBlobStorageClient")
1042+
def test_get_file_client_authentication_error(self, mock_blob_client_class, client):
1043+
"""Test handling of Azure ClientAuthenticationError."""
1044+
# given
1045+
filename = "test.pdf"
1046+
1047+
mock_blob_client = MagicMock()
1048+
mock_blob_client_class.return_value = mock_blob_client
1049+
mock_blob_client.file_exists.side_effect = ClientAuthenticationError("Auth failed")
1050+
1051+
# when
1052+
response = client.get(f"/api/files/{filename}")
1053+
1054+
# then
1055+
assert response.status_code == 401
1056+
assert response.json == {"error": "Authentication failed"}
1057+
1058+
@patch("create_app.AzureBlobStorageClient")
1059+
def test_get_file_resource_not_found_error(self, mock_blob_client_class, client):
1060+
"""Test handling of Azure ResourceNotFoundError."""
1061+
# given
1062+
filename = "test.pdf"
1063+
1064+
mock_blob_client = MagicMock()
1065+
mock_blob_client_class.return_value = mock_blob_client
1066+
mock_blob_client.file_exists.side_effect = ResourceNotFoundError("Resource not found")
1067+
1068+
# when
1069+
response = client.get(f"/api/files/{filename}")
1070+
1071+
# then
1072+
assert response.status_code == 404
1073+
assert response.json == {"error": "File not found"}
1074+
1075+
@patch("create_app.AzureBlobStorageClient")
1076+
def test_get_file_service_request_error(self, mock_blob_client_class, client):
1077+
"""Test handling of Azure ServiceRequestError."""
1078+
# given
1079+
filename = "test.pdf"
1080+
1081+
mock_blob_client = MagicMock()
1082+
mock_blob_client_class.return_value = mock_blob_client
1083+
mock_blob_client.file_exists.side_effect = ServiceRequestError("Service unavailable")
1084+
1085+
# when
1086+
response = client.get(f"/api/files/{filename}")
1087+
1088+
# then
1089+
assert response.status_code == 503
1090+
assert response.json == {"error": "Storage service unavailable"}
1091+
1092+
@patch("create_app.AzureBlobStorageClient")
1093+
def test_get_file_unexpected_exception(self, mock_blob_client_class, client):
1094+
"""Test handling of unexpected exceptions."""
1095+
# given
1096+
filename = "test.pdf"
1097+
1098+
mock_blob_client = MagicMock()
1099+
mock_blob_client_class.return_value = mock_blob_client
1100+
mock_blob_client.file_exists.side_effect = Exception("Unexpected error")
1101+
1102+
# when
1103+
response = client.get(f"/api/files/{filename}")
1104+
1105+
# then
1106+
assert response.status_code == 500
1107+
assert response.json == {"error": "Internal server error"}
1108+
1109+
@patch("create_app.AzureBlobStorageClient")
1110+
def test_get_file_download_exception(self, mock_blob_client_class, client):
1111+
"""Test handling of exceptions during file download."""
1112+
# given
1113+
filename = "test.pdf"
1114+
1115+
mock_blob_client = MagicMock()
1116+
mock_blob_client_class.return_value = mock_blob_client
1117+
mock_blob_client.file_exists.return_value = True
1118+
mock_blob_client.download_file.side_effect = Exception("Download failed")
1119+
1120+
# when
1121+
response = client.get(f"/api/files/{filename}")
1122+
1123+
# then
1124+
assert response.status_code == 500
1125+
assert response.json == {"error": "Internal server error"}
1126+
1127+
def test_get_file_valid_filenames(self, client):
1128+
"""Test that valid filenames with allowed characters pass validation."""
1129+
# Mock the blob client to avoid actual Azure calls
1130+
with patch("create_app.AzureBlobStorageClient") as mock_blob_client_class:
1131+
mock_blob_client = MagicMock()
1132+
mock_blob_client_class.return_value = mock_blob_client
1133+
mock_blob_client.file_exists.return_value = True
1134+
mock_blob_client.download_file.return_value = b"test content"
1135+
1136+
valid_filenames = [
1137+
"document.pdf",
1138+
"file_name.txt",
1139+
"file-name.docx",
1140+
"file name.xlsx",
1141+
"test123.json",
1142+
"a.b",
1143+
"very_long_but_valid_filename_with_underscores.pdf"
1144+
]
1145+
1146+
for filename in valid_filenames:
1147+
# when
1148+
response = client.get(f"/api/files/{filename}")
1149+
1150+
# then
1151+
assert response.status_code == 200, f"Failed for filename: {filename}"

code/tests/utilities/helpers/test_azure_postgres_helper.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def test_create_search_client_success(self, mock_connect, mock_credential):
3030
"https://ossrdbms-aad.database.windows.net/.default"
3131
)
3232
mock_connect.assert_called_once_with(
33-
"host=mock_host user=mock_user dbname=mock_database password=mock-access-token"
33+
"host=mock_host user=mock_user dbname=mock_database password=mock-access-token sslmode=require"
3434
)
3535

3636
@patch("backend.batch.utilities.helpers.azure_postgres_helper.psycopg2.connect")
@@ -92,7 +92,7 @@ def test_get_vector_store_success(self, mock_cursor, mock_connect, mock_credenti
9292
# Assert
9393
self.assertEqual(results, mock_results)
9494
mock_connect.assert_called_once_with(
95-
"host=mock_host user=mock_user dbname=mock_database password=mock-access-token"
95+
"host=mock_host user=mock_user dbname=mock_database password=mock-access-token sslmode=require"
9696
)
9797

9898
@patch("backend.batch.utilities.helpers.azure_postgres_helper.get_azure_credential")

code/tests/utilities/helpers/test_azure_search_helper.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ def env_helper_mock():
9090
env_helper.AZURE_SEARCH_CONVERSATIONS_LOG_INDEX = (
9191
AZURE_SEARCH_CONVERSATIONS_LOG_INDEX
9292
)
93+
env_helper.MANAGED_IDENTITY_CLIENT_ID = "mock-client-id"
9394

9495
env_helper.USE_ADVANCED_IMAGE_PROCESSING = USE_ADVANCED_IMAGE_PROCESSING
9596
env_helper.is_auth_type_keys.return_value = True
@@ -156,7 +157,7 @@ def test_creates_search_clients_with_rabc(
156157
AzureSearchHelper()
157158

158159
# then
159-
default_azure_credential_mock.assert_called_once_with()
160+
default_azure_credential_mock.assert_called_once_with("mock-client-id")
160161
search_client_mock.assert_called_once_with(
161162
endpoint=AZURE_SEARCH_SERVICE,
162163
index_name=AZURE_SEARCH_INDEX,

code/tests/utilities/helpers/test_secret_helper.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ def test_get_secret_returns_value_from_secret_client_when_use_key_vault_is_true(
2525
secret_name = "MY_SECRET"
2626
expected_value = ""
2727
monkeypatch.setenv("USE_KEY_VAULT", "true")
28+
monkeypatch.setenv("AZURE_KEY_VAULT_ENDPOINT", "https://test-vault.vault.azure.net/")
2829
secret_client.return_value.get_secret.return_value.value = expected_value
2930
secret_helper = SecretHelper()
3031

0 commit comments

Comments
 (0)