Skip to content

Commit 4f51293

Browse files
test: Adding missing tests or correcting existing tests (#2035)
1 parent 647a907 commit 4f51293

19 files changed

Lines changed: 4770 additions & 251 deletions

.github/workflows/tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: Test Workflow with Coverage
22

33
on:
4-
workflow_dispatch:
4+
workflow_dispatch:
55
push:
66
branches: [main, dev, demo]
77
paths:
@@ -91,7 +91,7 @@ jobs:
9191
9292
echo "MIN_COVERAGE=$MIN_COVERAGE" >> "$GITHUB_OUTPUT"
9393
- name: Run Python Tests
94-
run: make python-test optional_args="--junitxml=coverage-junit.xml --cov=. --cov-report xml:coverage.xml --cov-fail-under ${{ steps.coverage-value.outputs.MIN_COVERAGE }} ./code/tests"
94+
run: make python-test optional_args="--junitxml=coverage-junit.xml --cov=code --cov-report=term-missing --cov-report=xml:coverage.xml --cov-fail-under=${{ steps.coverage-value.outputs.MIN_COVERAGE }} ./code/tests"
9595
- uses: actions/upload-artifact@v6
9696
if: ${{ !cancelled() }}
9797
with:
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
"""
2+
Unit tests for auth_utils.py
3+
Tests authentication utility functions for EasyAuth header processing.
4+
"""
5+
6+
import base64
7+
import json
8+
from unittest.mock import patch, MagicMock
9+
10+
from backend.batch.utilities.chat_history.auth_utils import (
11+
get_authenticated_user_details,
12+
get_tenantid,
13+
)
14+
15+
16+
class TestGetAuthenticatedUserDetails:
17+
"""Tests for get_authenticated_user_details function."""
18+
19+
def test_with_all_easyauth_headers_present(self):
20+
"""Test production mode: extracts all EasyAuth headers into user object."""
21+
request_headers = {
22+
"X-Ms-Client-Principal-Id": "test-principal-id",
23+
"X-Ms-Client-Principal-Name": "test@example.com",
24+
"X-Ms-Client-Principal-Idp": "aad",
25+
"X-Ms-Token-Aad-Id-Token": "test-aad-token",
26+
"X-Ms-Client-Principal": "base64-principal-data",
27+
}
28+
29+
result = get_authenticated_user_details(request_headers)
30+
31+
# Business logic: maps specific EasyAuth headers to user object fields
32+
assert result["user_principal_id"] == "test-principal-id"
33+
assert result["user_name"] == "test@example.com"
34+
assert result["auth_provider"] == "aad"
35+
assert result["aad_id_token"] == "test-aad-token"
36+
assert result["client_principal_b64"] == "base64-principal-data"
37+
assert result["auth_token"] == "test-aad-token"
38+
39+
def test_without_principal_id_header_uses_sample_user(self):
40+
"""Test development mode: falls back to sample user when principal ID missing."""
41+
request_headers = {
42+
"Content-Type": "application/json",
43+
"Host": "localhost",
44+
}
45+
46+
mock_sample_user = MagicMock()
47+
mock_sample_user.sample_user = {
48+
"X-Ms-Client-Principal-Id": "sample-principal-id",
49+
"X-Ms-Client-Principal-Name": "sample@example.com",
50+
"X-Ms-Client-Principal-Idp": "aad",
51+
"X-Ms-Token-Aad-Id-Token": "sample-aad-token",
52+
"X-Ms-Client-Principal": "sample-base64-data",
53+
}
54+
55+
with patch.dict(
56+
"sys.modules",
57+
{"backend.batch.utilities.chat_history.sample_user": mock_sample_user},
58+
):
59+
result = get_authenticated_user_details(request_headers)
60+
61+
# Business logic: dev mode uses sample_user instead of request headers
62+
assert result["user_principal_id"] == "sample-principal-id"
63+
assert result["user_name"] == "sample@example.com"
64+
assert result["auth_provider"] == "aad"
65+
66+
def test_extracts_correct_header_keys(self):
67+
"""Test header mapping: verifies correct EasyAuth headers are mapped to user fields."""
68+
request_headers = {
69+
"X-Ms-Client-Principal-Id": "principal-123",
70+
"X-Ms-Client-Principal-Name": "user@domain.com",
71+
"X-Ms-Client-Principal-Idp": "github",
72+
"X-Ms-Token-Aad-Id-Token": "token-abc",
73+
"X-Ms-Client-Principal": "encoded-data",
74+
"Other-Header": "ignored-value",
75+
}
76+
77+
result = get_authenticated_user_details(request_headers)
78+
79+
# Business logic: only specific headers are extracted, others ignored
80+
assert result["user_principal_id"] == "principal-123"
81+
assert result["user_name"] == "user@domain.com"
82+
assert result["auth_provider"] == "github"
83+
assert result["aad_id_token"] == "token-abc"
84+
assert result["client_principal_b64"] == "encoded-data"
85+
assert result["auth_token"] == "token-abc"
86+
87+
88+
class TestGetTenantId:
89+
"""Tests for get_tenantid function."""
90+
91+
def test_with_valid_base64_and_valid_json_returns_tenant_id(self):
92+
"""Test successful path: base64 decode → JSON parse → extract tid."""
93+
tenant_data = {"tid": "tenant-12345"}
94+
json_str = json.dumps(tenant_data)
95+
base64_encoded = base64.b64encode(json_str.encode("utf-8")).decode("utf-8")
96+
97+
result = get_tenantid(base64_encoded)
98+
99+
# Business logic: extracts tenant ID from encoded principal
100+
assert result == "tenant-12345"
101+
102+
def test_with_invalid_base64_returns_empty_string(self):
103+
"""Test error handling: invalid base64 returns empty string and logs exception."""
104+
invalid_base64 = "not-valid-base64!!!"
105+
106+
with patch("logging.getLogger") as mock_get_logger:
107+
mock_logger = MagicMock()
108+
mock_get_logger.return_value = mock_logger
109+
110+
result = get_tenantid(invalid_base64)
111+
112+
# Business logic: error handling returns empty string
113+
assert result == ""
114+
mock_logger.exception.assert_called_once()
115+
116+
def test_with_valid_base64_invalid_json_returns_empty_string(self):
117+
"""Test error handling: valid base64 but invalid JSON returns empty string."""
118+
invalid_json = "not valid json"
119+
base64_encoded = base64.b64encode(invalid_json.encode("utf-8")).decode("utf-8")
120+
121+
with patch("logging.getLogger") as mock_get_logger:
122+
mock_logger = MagicMock()
123+
mock_get_logger.return_value = mock_logger
124+
125+
result = get_tenantid(base64_encoded)
126+
127+
# Business logic: JSON parsing error returns empty string
128+
assert result == ""
129+
mock_logger.exception.assert_called_once()
130+
131+
def test_with_none_input_returns_empty_string(self):
132+
"""Test guard clause: None input bypassed by if check, returns empty string."""
133+
result = get_tenantid(None)
134+
135+
# Business logic: if check protects against None input
136+
assert result == ""
137+
138+
def test_base64_decoding_process(self):
139+
"""Test full pipeline: verifies only tid field extracted, other fields ignored."""
140+
tenant_data = {
141+
"tid": "complex-tenant-id-abc-123",
142+
"sub": "subject-id",
143+
"name": "Test User",
144+
"roles": ["admin", "user"],
145+
}
146+
json_str = json.dumps(tenant_data)
147+
base64_encoded = base64.b64encode(json_str.encode("utf-8")).decode("utf-8")
148+
149+
result = get_tenantid(base64_encoded)
150+
151+
# Business logic: only 'tid' field is extracted from principal
152+
assert result == "complex-tenant-id-abc-123"

0 commit comments

Comments
 (0)