Skip to content
This repository was archived by the owner on Jun 13, 2025. It is now read-only.

Commit f80d9d2

Browse files
🤖 Add tests for TokenlessAzureHandler timestamp handling (#1069)
Co-authored-by: sentry-autofix[bot] <157164994+sentry-autofix[bot]@users.noreply.github.com> Co-authored-by: trent-codecov <[email protected]>
1 parent ad92471 commit f80d9d2

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
from datetime import datetime, timedelta
2+
from unittest.mock import patch
3+
4+
import pytest
5+
from rest_framework.exceptions import NotFound
6+
7+
from upload.tokenless.azure import TokenlessAzureHandler
8+
9+
10+
@pytest.fixture
11+
def upload_params():
12+
return {
13+
"job": "899861",
14+
"project": "public",
15+
"server_uri": "https://dev.azure.com/dnceng-public/",
16+
"build": "20241219.14",
17+
"commit": "0f6e31fec5876be932f9e52f739ce1a2e04f11e3",
18+
}
19+
20+
21+
def test_verify_handles_nanosecond_timestamp(upload_params):
22+
"""
23+
Test that the handler correctly processes timestamps with nanosecond precision
24+
from the Azure DevOps API.
25+
"""
26+
handler = TokenlessAzureHandler(upload_params)
27+
28+
# Mock a response with nanosecond precision timestamp (7 digits after decimal)
29+
current_time = datetime.now()
30+
timestamp = current_time.strftime("%Y-%m-%dT%H:%M:%S.1234567Z")
31+
32+
mock_build_response = {
33+
"status": "completed",
34+
"finishTime": timestamp,
35+
"buildNumber": "20241219.14",
36+
"sourceVersion": "0f6e31fec5876be932f9e52f739ce1a2e04f11e3",
37+
"repository": {"type": "GitHub"},
38+
}
39+
40+
with patch.object(handler, "get_build", return_value=mock_build_response):
41+
service = handler.verify()
42+
assert service == "github"
43+
44+
45+
def test_verify_handles_microsecond_timestamp(upload_params):
46+
"""
47+
Test that the handler still works correctly with regular microsecond precision
48+
timestamps.
49+
"""
50+
handler = TokenlessAzureHandler(upload_params)
51+
52+
# Mock a response with microsecond precision (6 digits after decimal)
53+
current_time = datetime.now()
54+
timestamp = current_time.strftime("%Y-%m-%dT%H:%M:%S.123456Z")
55+
56+
mock_build_response = {
57+
"status": "completed",
58+
"finishTime": timestamp,
59+
"buildNumber": "20241219.14",
60+
"sourceVersion": "0f6e31fec5876be932f9e52f739ce1a2e04f11e3",
61+
"repository": {"type": "GitHub"},
62+
}
63+
64+
with patch.object(handler, "get_build", return_value=mock_build_response):
65+
service = handler.verify()
66+
assert service == "github"
67+
68+
69+
def test_verify_rejects_old_timestamp(upload_params):
70+
"""
71+
Test that the handler correctly rejects timestamps older than 4 minutes,
72+
even with nanosecond precision.
73+
"""
74+
handler = TokenlessAzureHandler(upload_params)
75+
76+
# Create a timestamp that's more than 4 minutes old
77+
old_time = datetime.now() - timedelta(minutes=5)
78+
timestamp = old_time.strftime("%Y-%m-%dT%H:%M:%S.1234567Z")
79+
80+
mock_build_response = {
81+
"status": "completed",
82+
"finishTime": timestamp,
83+
"buildNumber": "20241219.14",
84+
"sourceVersion": "0f6e31fec5876be932f9e52f739ce1a2e04f11e3",
85+
"repository": {"type": "GitHub"},
86+
}
87+
88+
with patch.object(handler, "get_build", return_value=mock_build_response):
89+
with pytest.raises(NotFound, match="Azure build has already finished"):
90+
handler.verify()

‎upload/tokenless/azure.py‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ def verify(self):
7878
# Build should have finished within the last 4 mins OR should have an 'inProgress' flag
7979
if build["status"] == "completed":
8080
finishTimestamp = build["finishTime"].replace("T", " ").replace("Z", "")
81+
# Azure DevOps API returns nanosecond precision (7 digits), but Python only supports
82+
# microsecond precision (6 digits). Truncate to 6 digits after decimal.
83+
if "." in finishTimestamp:
84+
base, fraction = finishTimestamp.rsplit(".", 1)
85+
finishTimestamp = f"{base}.{fraction[:6]}"
8186
buildFinishDateObj = datetime.strptime(
8287
finishTimestamp, "%Y-%m-%d %H:%M:%S.%f"
8388
)

0 commit comments

Comments
 (0)