🎨 Palette: Add friendly API pre-flight check#121
Conversation
Added a `check_api_access` function that runs before the main sync process. This validates credentials (Token/Profile ID) and provides specific, actionable error messages for 401, 403, and 404 responses, preventing cascade failures and helping users debug configuration issues faster. - Validates Token and Profile ID validity immediately - Returns helpful "Check your token at URL" messages - Avoids retrying on permanent Auth errors
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
|
😎 Merged manually by Abhi Mehrotra (@abhimehro) - details. |
| **Action:** Use `\033[K` for dynamic CLI updates and favor visual bars for waits > 5 seconds. | ||
|
|
||
| ## 2024-05-24 - Fail Fast & Friendly | ||
| **Learning:** In CLI tools involving APIs, cascade failures (hundreds of "Failed to X") caused by basic auth issues (401/403) are overwhelming and confusing. A dedicated "Pre-flight Check" that validates credentials *before* attempting the main workload allows for specific, actionable error messages (e.g. "Check your token at [URL]") instead of generic HTTP errors. |
Check notice
Code scanning / Remark-lint (reported by Codacy)
Warn when references to undefined definitions are found. Note
| **Action:** Use `\033[K` for dynamic CLI updates and favor visual bars for waits > 5 seconds. | ||
|
|
||
| ## 2024-05-24 - Fail Fast & Friendly | ||
| **Learning:** In CLI tools involving APIs, cascade failures (hundreds of "Failed to X") caused by basic auth issues (401/403) are overwhelming and confusing. A dedicated "Pre-flight Check" that validates credentials *before* attempting the main workload allows for specific, actionable error messages (e.g. "Check your token at [URL]") instead of generic HTTP errors. |
Check notice
Code scanning / Remark-lint (reported by Codacy)
Warn when shortcut reference links are used. Note
| resp = client.get(url) | ||
| resp.raise_for_status() | ||
| return True | ||
| except httpx.HTTPStatusError as e: |
Check warning
Code scanning / Pylint (reported by Codacy)
Variable name "e" doesn't conform to snake_case naming style Warning
| except httpx.HTTPStatusError as e: | ||
| code = e.response.status_code | ||
| if code == 401: | ||
| log.critical(f"{Colors.FAIL}❌ Authentication Failed: The API Token is invalid.{Colors.ENDC}") |
Check warning
Code scanning / Pylint (reported by Codacy)
Line too long (105/100) Warning
| code = e.response.status_code | ||
| if code == 401: | ||
| log.critical(f"{Colors.FAIL}❌ Authentication Failed: The API Token is invalid.{Colors.ENDC}") | ||
| log.critical(f"{Colors.FAIL} Please check your token at: https://controld.com/account/manage-account{Colors.ENDC}") |
Check warning
Code scanning / Pylint (reported by Codacy)
Line too long (129/100) Warning
| log.critical(f"{Colors.FAIL}❌ Authentication Failed: The API Token is invalid.{Colors.ENDC}") | ||
| log.critical(f"{Colors.FAIL} Please check your token at: https://controld.com/account/manage-account{Colors.ENDC}") | ||
| elif code == 403: | ||
| log.critical(f"{Colors.FAIL}🚫 Access Denied: Token lacks permission for Profile {profile_id}.{Colors.ENDC}") |
Check warning
Code scanning / Pylint (reported by Codacy)
Line too long (120/100) Warning
| elif code == 403: | ||
| log.critical(f"{Colors.FAIL}🚫 Access Denied: Token lacks permission for Profile {profile_id}.{Colors.ENDC}") | ||
| elif code == 404: | ||
| log.critical(f"{Colors.FAIL}🔍 Profile Not Found: The ID '{profile_id}' does not exist.{Colors.ENDC}") |
Check warning
Code scanning / Pylint (reported by Codacy)
Line too long (113/100) Warning
| log.critical(f"{Colors.FAIL}🚫 Access Denied: Token lacks permission for Profile {profile_id}.{Colors.ENDC}") | ||
| elif code == 404: | ||
| log.critical(f"{Colors.FAIL}🔍 Profile Not Found: The ID '{profile_id}' does not exist.{Colors.ENDC}") | ||
| log.critical(f"{Colors.FAIL} Please verify the Profile ID from your Control D Dashboard URL.{Colors.ENDC}") |
Check warning
Code scanning / Pylint (reported by Codacy)
Line too long (121/100) Warning
| else: | ||
| log.error(f"API Access Check Failed ({code}): {e}") | ||
| return False | ||
| except httpx.RequestError as e: |
Check warning
Code scanning / Pylint (reported by Codacy)
Variable name "e" doesn't conform to snake_case naming style Warning
| except httpx.HTTPStatusError as e: | ||
| code = e.response.status_code | ||
| if code == 401: | ||
| log.critical(f"{Colors.FAIL}❌ Authentication Failed: The API Token is invalid.{Colors.ENDC}") |
Check warning
Code scanning / Prospector (reported by Codacy)
Use lazy % formatting in logging functions (logging-fstring-interpolation) Warning
| code = e.response.status_code | ||
| if code == 401: | ||
| log.critical(f"{Colors.FAIL}❌ Authentication Failed: The API Token is invalid.{Colors.ENDC}") | ||
| log.critical(f"{Colors.FAIL} Please check your token at: https://controld.com/account/manage-account{Colors.ENDC}") |
Check warning
Code scanning / Prospector (reported by Codacy)
Use lazy % formatting in logging functions (logging-fstring-interpolation) Warning
| log.critical(f"{Colors.FAIL}❌ Authentication Failed: The API Token is invalid.{Colors.ENDC}") | ||
| log.critical(f"{Colors.FAIL} Please check your token at: https://controld.com/account/manage-account{Colors.ENDC}") | ||
| elif code == 403: | ||
| log.critical(f"{Colors.FAIL}🚫 Access Denied: Token lacks permission for Profile {profile_id}.{Colors.ENDC}") |
Check warning
Code scanning / Prospector (reported by Codacy)
Use lazy % formatting in logging functions (logging-fstring-interpolation) Warning
| elif code == 403: | ||
| log.critical(f"{Colors.FAIL}🚫 Access Denied: Token lacks permission for Profile {profile_id}.{Colors.ENDC}") | ||
| elif code == 404: | ||
| log.critical(f"{Colors.FAIL}🔍 Profile Not Found: The ID '{profile_id}' does not exist.{Colors.ENDC}") |
Check warning
Code scanning / Prospector (reported by Codacy)
Use lazy % formatting in logging functions (logging-fstring-interpolation) Warning
| log.critical(f"{Colors.FAIL}🚫 Access Denied: Token lacks permission for Profile {profile_id}.{Colors.ENDC}") | ||
| elif code == 404: | ||
| log.critical(f"{Colors.FAIL}🔍 Profile Not Found: The ID '{profile_id}' does not exist.{Colors.ENDC}") | ||
| log.critical(f"{Colors.FAIL} Please verify the Profile ID from your Control D Dashboard URL.{Colors.ENDC}") |
Check warning
Code scanning / Prospector (reported by Codacy)
Use lazy % formatting in logging functions (logging-fstring-interpolation) Warning
| log.critical(f"{Colors.FAIL}🔍 Profile Not Found: The ID '{profile_id}' does not exist.{Colors.ENDC}") | ||
| log.critical(f"{Colors.FAIL} Please verify the Profile ID from your Control D Dashboard URL.{Colors.ENDC}") | ||
| else: | ||
| log.error(f"API Access Check Failed ({code}): {e}") |
Check warning
Code scanning / Prospector (reported by Codacy)
Use lazy % formatting in logging functions (logging-fstring-interpolation) Warning
| log.error(f"API Access Check Failed ({code}): {e}") | ||
| return False | ||
| except httpx.RequestError as e: | ||
| log.error(f"Network Error during access check: {e}") |
Check warning
Code scanning / Prospector (reported by Codacy)
Use lazy % formatting in logging functions (logging-fstring-interpolation) Warning
| resp = client.get(url) | ||
| resp.raise_for_status() | ||
| return True | ||
| except httpx.HTTPStatusError as e: |
Check warning
Code scanning / Pylintpython3 (reported by Codacy)
Variable name "e" doesn't conform to snake_case naming style Warning
| except httpx.HTTPStatusError as e: | ||
| code = e.response.status_code | ||
| if code == 401: | ||
| log.critical(f"{Colors.FAIL}❌ Authentication Failed: The API Token is invalid.{Colors.ENDC}") |
Check warning
Code scanning / Pylintpython3 (reported by Codacy)
Line too long (105/100) Warning
| code = e.response.status_code | ||
| if code == 401: | ||
| log.critical(f"{Colors.FAIL}❌ Authentication Failed: The API Token is invalid.{Colors.ENDC}") | ||
| log.critical(f"{Colors.FAIL} Please check your token at: https://controld.com/account/manage-account{Colors.ENDC}") |
Check warning
Code scanning / Pylintpython3 (reported by Codacy)
Line too long (129/100) Warning
| log.critical(f"{Colors.FAIL}❌ Authentication Failed: The API Token is invalid.{Colors.ENDC}") | ||
| log.critical(f"{Colors.FAIL} Please check your token at: https://controld.com/account/manage-account{Colors.ENDC}") | ||
| elif code == 403: | ||
| log.critical(f"{Colors.FAIL}🚫 Access Denied: Token lacks permission for Profile {profile_id}.{Colors.ENDC}") |
Check warning
Code scanning / Pylintpython3 (reported by Codacy)
Line too long (120/100) Warning
| elif code == 403: | ||
| log.critical(f"{Colors.FAIL}🚫 Access Denied: Token lacks permission for Profile {profile_id}.{Colors.ENDC}") | ||
| elif code == 404: | ||
| log.critical(f"{Colors.FAIL}🔍 Profile Not Found: The ID '{profile_id}' does not exist.{Colors.ENDC}") |
Check warning
Code scanning / Pylintpython3 (reported by Codacy)
Line too long (113/100) Warning
| log.critical(f"{Colors.FAIL}❌ Authentication Failed: The API Token is invalid.{Colors.ENDC}") | ||
| log.critical(f"{Colors.FAIL} Please check your token at: https://controld.com/account/manage-account{Colors.ENDC}") | ||
| elif code == 403: | ||
| log.critical(f"{Colors.FAIL}🚫 Access Denied: Token lacks permission for Profile {profile_id}.{Colors.ENDC}") |
Check notice
Code scanning / Pylintpython3 (reported by Codacy)
Use lazy % formatting in logging functions Note
| elif code == 403: | ||
| log.critical(f"{Colors.FAIL}🚫 Access Denied: Token lacks permission for Profile {profile_id}.{Colors.ENDC}") | ||
| elif code == 404: | ||
| log.critical(f"{Colors.FAIL}🔍 Profile Not Found: The ID '{profile_id}' does not exist.{Colors.ENDC}") |
Check notice
Code scanning / Pylintpython3 (reported by Codacy)
Use lazy % formatting in logging functions Note
| log.critical(f"{Colors.FAIL}🚫 Access Denied: Token lacks permission for Profile {profile_id}.{Colors.ENDC}") | ||
| elif code == 404: | ||
| log.critical(f"{Colors.FAIL}🔍 Profile Not Found: The ID '{profile_id}' does not exist.{Colors.ENDC}") | ||
| log.critical(f"{Colors.FAIL} Please verify the Profile ID from your Control D Dashboard URL.{Colors.ENDC}") |
Check notice
Code scanning / Pylintpython3 (reported by Codacy)
Use lazy % formatting in logging functions Note
| log.critical(f"{Colors.FAIL}🔍 Profile Not Found: The ID '{profile_id}' does not exist.{Colors.ENDC}") | ||
| log.critical(f"{Colors.FAIL} Please verify the Profile ID from your Control D Dashboard URL.{Colors.ENDC}") | ||
| else: | ||
| log.error(f"API Access Check Failed ({code}): {e}") |
Check notice
Code scanning / Pylintpython3 (reported by Codacy)
Use lazy % formatting in logging functions Note
| log.error(f"API Access Check Failed ({code}): {e}") | ||
| return False | ||
| except httpx.RequestError as e: | ||
| log.error(f"Network Error during access check: {e}") |
Check notice
Code scanning / Pylintpython3 (reported by Codacy)
Use lazy % formatting in logging functions Note
There was a problem hiding this comment.
Pull request overview
This PR adds a pre-flight API validation check to improve user experience when authentication or authorization fails. Instead of receiving a cascade of generic HTTP errors during the sync process, users now get clear, actionable error messages upfront.
Changes:
- Added
check_api_access()function to validate API credentials and profile access before starting the sync - Integrated the pre-flight check into
sync_profile()to fail fast with helpful error messages - Documented the "Fail Fast & Friendly" pattern in palette.md for future reference
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| main.py | Added pre-flight API check function and integrated it at the start of the sync workflow to validate credentials before heavy processing |
| .Jules/palette.md | Documented the learning about failing fast with friendly error messages for API authentication issues |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def check_api_access(client: httpx.Client, profile_id: str) -> bool: | ||
| """ | ||
| Verifies API access and Profile existence before starting heavy work. | ||
| Returns True if access is good, False otherwise (with helpful logs). | ||
| """ | ||
| url = f"{API_BASE}/{profile_id}/groups" | ||
| try: | ||
| # We use a raw request here to avoid the automatic retries of _retry_request | ||
| # for auth errors, which are permanent. | ||
| resp = client.get(url) | ||
| resp.raise_for_status() | ||
| return True | ||
| except httpx.HTTPStatusError as e: | ||
| code = e.response.status_code | ||
| if code == 401: | ||
| log.critical(f"{Colors.FAIL}❌ Authentication Failed: The API Token is invalid.{Colors.ENDC}") | ||
| log.critical(f"{Colors.FAIL} Please check your token at: https://controld.com/account/manage-account{Colors.ENDC}") | ||
| elif code == 403: | ||
| log.critical(f"{Colors.FAIL}🚫 Access Denied: Token lacks permission for Profile {profile_id}.{Colors.ENDC}") | ||
| elif code == 404: | ||
| log.critical(f"{Colors.FAIL}🔍 Profile Not Found: The ID '{profile_id}' does not exist.{Colors.ENDC}") | ||
| log.critical(f"{Colors.FAIL} Please verify the Profile ID from your Control D Dashboard URL.{Colors.ENDC}") | ||
| else: | ||
| log.error(f"API Access Check Failed ({code}): {e}") | ||
| return False | ||
| except httpx.RequestError as e: | ||
| log.error(f"Network Error during access check: {e}") | ||
| return False |
There was a problem hiding this comment.
The new check_api_access function lacks test coverage. Since the repository has comprehensive test coverage for similar functions (e.g., get_all_existing_rules, push_rules), this function should also have tests to validate the different error scenarios (401, 403, 404) and success case.
There was a problem hiding this comment.
Hi @google-lab-jules, please open a new pull request to apply any changes based on the suggestions in this thread. Thanks!
There was a problem hiding this comment.
Hi! I don't see any specific suggestions in this thread. Could you please clarify what changes you'd like me to make? I'm happy to address them!
There was a problem hiding this comment.
Of course! The suggestions were the following: "The new check_api_access function lacks test coverage. Since the repository has comprehensive test coverage for similar functions (e.g., get_all_existing_rules, push_rules), this function should also have tests to validate the different error scenarios (401, 403, 404) and success case." Let me know if this provides proper clarification, but it seems the task would be to potentially add tests to validate the different error scenarios (401, 403, 404). I appreciate your help, @google-lab-jules!
There was a problem hiding this comment.
Thank you for the clarification! I've added comprehensive tests for check_api_access covering 401, 403, 404, generic HTTP errors, and network errors.
Added unit tests for the `check_api_access` function in `test_main.py`. The tests cover: - Success case (returns True) - 401 Unauthorized (returns False, logs critical) - 403 Forbidden (returns False, logs critical) - 404 Not Found (returns False, logs critical) - Generic HTTP 500 Error (returns False, logs error) - Network/Request Exception (returns False, logs error) This ensures the new pre-flight check logic is robust and handles all expected failure modes correctly.
| def test_check_api_access_success(monkeypatch): | ||
| m = reload_main_with_env(monkeypatch) | ||
| mock_client = MagicMock() | ||
| mock_client.get.return_value.raise_for_status.return_value = None |
Check notice
Code scanning / Bandit
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
| mock_client = MagicMock() | ||
| mock_client.get.return_value.raise_for_status.return_value = None | ||
|
|
||
| assert m.check_api_access(mock_client, "valid_profile") is True |
Check notice
Code scanning / Bandit
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
|
|
||
| # Simulate 401 response | ||
| mock_response = MagicMock() | ||
| mock_response.status_code = 401 |
Check notice
Code scanning / Bandit
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
|
|
||
| def test_check_api_access_403(monkeypatch): | ||
| m = reload_main_with_env(monkeypatch) | ||
| mock_client = MagicMock() |
Check notice
Code scanning / Bandit
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
| def test_check_api_access_403(monkeypatch): | ||
| m = reload_main_with_env(monkeypatch) | ||
| mock_client = MagicMock() | ||
|
|
Check notice
Code scanning / Bandit
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
| mock_client = MagicMock() | ||
|
|
||
| # Simulate 500 response | ||
| mock_response = MagicMock() |
Check notice
Code scanning / Bandit
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
|
|
||
| # Simulate 500 response | ||
| mock_response = MagicMock() | ||
| mock_response.status_code = 500 |
Check notice
Code scanning / Bandit
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
| m = reload_main_with_env(monkeypatch) | ||
| mock_client = MagicMock() | ||
|
|
||
| # Simulate network error |
Check notice
Code scanning / Bandit
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
| mock_client = MagicMock() | ||
|
|
||
| # Simulate network error | ||
| error = httpx.RequestError("Network failure", request=MagicMock()) |
Check notice
Code scanning / Bandit
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
|
|
||
| # Simulate network error | ||
| error = httpx.RequestError("Network failure", request=MagicMock()) | ||
| mock_client.get.side_effect = error |
Check notice
Code scanning / Bandit
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
| assert m.check_api_access(mock_client, "invalid_token") is False | ||
| assert mock_log.critical.call_count >= 1 | ||
| # Check for authentication failed message | ||
| args = str(mock_log.critical.call_args_list) |
Check warning
Code scanning / Pylint (reported by Codacy)
Missing function docstring Warning test
| m = reload_main_with_env(monkeypatch) | ||
| mock_client = MagicMock() | ||
|
|
||
| # Simulate 403 response |
Check warning
Code scanning / Pylint (reported by Codacy)
Missing function docstring Warning test
| mock_client = MagicMock() | ||
|
|
||
| # Simulate 403 response | ||
| mock_response = MagicMock() |
Check warning
Code scanning / Pylint (reported by Codacy)
Variable name "m" doesn't conform to snake_case naming style Warning test
| # Simulate 404 response | ||
| mock_response = MagicMock() | ||
| mock_response.status_code = 404 | ||
| error = httpx.HTTPStatusError("404 Not Found", request=MagicMock(), response=mock_response) |
Check warning
Code scanning / Pylint (reported by Codacy)
Missing function docstring Warning test
| mock_response = MagicMock() | ||
| mock_response.status_code = 404 | ||
| error = httpx.HTTPStatusError("404 Not Found", request=MagicMock(), response=mock_response) | ||
| mock_client.get.return_value.raise_for_status.side_effect = error |
Check warning
Code scanning / Pylint (reported by Codacy)
Variable name "m" doesn't conform to snake_case naming style Warning test
| mock_response = MagicMock() | ||
| mock_response.status_code = 500 | ||
| error = httpx.HTTPStatusError("500 Server Error", request=MagicMock(), response=mock_response) | ||
| mock_client.get.return_value.raise_for_status.side_effect = error |
Check warning
Code scanning / Pylint (reported by Codacy)
Variable name "m" doesn't conform to snake_case naming style Warning test
| # Simulate network error | ||
| error = httpx.RequestError("Network failure", request=MagicMock()) | ||
| mock_client.get.side_effect = error | ||
|
|
Check warning
Code scanning / Pylint (reported by Codacy)
Missing function docstring Warning test
| error = httpx.RequestError("Network failure", request=MagicMock()) | ||
| mock_client.get.side_effect = error | ||
|
|
||
| mock_log = MagicMock() |
Check warning
Code scanning / Pylint (reported by Codacy)
Variable name "m" doesn't conform to snake_case naming style Warning test
test_main.py
Outdated
Check warning
Code scanning / Pylint (reported by Codacy)
Missing function docstring Warning test
test_main.py
Outdated
Check warning
Code scanning / Pylint (reported by Codacy)
Variable name "m" doesn't conform to snake_case naming style Warning test
test_main.py
Outdated
Check warning
Code scanning / Pylintpython3 (reported by Codacy)
Variable name "m" doesn't conform to snake_case naming style Warning test
test_main.py
Outdated
Check warning
Code scanning / Pylintpython3 (reported by Codacy)
Missing function or method docstring Warning test
| error = httpx.RequestError("Network failure", request=MagicMock()) | ||
| mock_client.get.side_effect = error | ||
|
|
||
| mock_log = MagicMock() |
Check warning
Code scanning / Pylintpython3 (reported by Codacy)
Variable name "m" doesn't conform to snake_case naming style Warning test
| # Simulate network error | ||
| error = httpx.RequestError("Network failure", request=MagicMock()) | ||
| mock_client.get.side_effect = error | ||
|
|
Check warning
Code scanning / Pylintpython3 (reported by Codacy)
Missing function or method docstring Warning test
| mock_response = MagicMock() | ||
| mock_response.status_code = 500 | ||
| error = httpx.HTTPStatusError("500 Server Error", request=MagicMock(), response=mock_response) | ||
| mock_client.get.return_value.raise_for_status.side_effect = error |
Check warning
Code scanning / Pylintpython3 (reported by Codacy)
Variable name "m" doesn't conform to snake_case naming style Warning test
| mock_response = MagicMock() | ||
| mock_response.status_code = 404 | ||
| error = httpx.HTTPStatusError("404 Not Found", request=MagicMock(), response=mock_response) | ||
| mock_client.get.return_value.raise_for_status.side_effect = error |
Check warning
Code scanning / Pylintpython3 (reported by Codacy)
Variable name "m" doesn't conform to snake_case naming style Warning test
| # Simulate 404 response | ||
| mock_response = MagicMock() | ||
| mock_response.status_code = 404 | ||
| error = httpx.HTTPStatusError("404 Not Found", request=MagicMock(), response=mock_response) |
Check warning
Code scanning / Pylintpython3 (reported by Codacy)
Missing function or method docstring Warning test
| m = reload_main_with_env(monkeypatch) | ||
| mock_client = MagicMock() | ||
|
|
||
| # Simulate 403 response |
Check warning
Code scanning / Pylintpython3 (reported by Codacy)
Missing function or method docstring Warning test
| assert mock_log.critical.call_count >= 1 | ||
| # Check for authentication failed message | ||
| args = str(mock_log.critical.call_args_list) | ||
| assert "Authentication Failed" in args |
Check warning
Code scanning / Pylintpython3 (reported by Codacy)
Variable name "m" doesn't conform to snake_case naming style Warning test
| assert m.check_api_access(mock_client, "invalid_token") is False | ||
| assert mock_log.critical.call_count >= 1 | ||
| # Check for authentication failed message | ||
| args = str(mock_log.critical.call_args_list) |
Check warning
Code scanning / Pylintpython3 (reported by Codacy)
Missing function or method docstring Warning test
|
|
||
| # Mock log to verify output | ||
| mock_log = MagicMock() | ||
| monkeypatch.setattr(m, "log", mock_log) |
Check notice
Code scanning / Bandit (reported by Codacy)
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
| # Mock log to verify output | ||
| mock_log = MagicMock() | ||
| monkeypatch.setattr(m, "log", mock_log) | ||
|
|
Check notice
Code scanning / Bandit (reported by Codacy)
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
|
|
||
| def test_check_api_access_403(monkeypatch): | ||
| m = reload_main_with_env(monkeypatch) | ||
| mock_client = MagicMock() |
Check notice
Code scanning / Bandit (reported by Codacy)
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
| assert "Access Denied" in str(mock_log.critical.call_args) | ||
|
|
||
| def test_check_api_access_404(monkeypatch): | ||
| m = reload_main_with_env(monkeypatch) |
Check notice
Code scanning / Bandit (reported by Codacy)
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
|
|
||
| def test_check_api_access_404(monkeypatch): | ||
| m = reload_main_with_env(monkeypatch) | ||
| mock_client = MagicMock() |
Check notice
Code scanning / Bandit (reported by Codacy)
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
| m = reload_main_with_env(monkeypatch) | ||
| mock_client = MagicMock() | ||
|
|
||
| # Simulate network error |
Check notice
Code scanning / Bandit (reported by Codacy)
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
| mock_client = MagicMock() | ||
|
|
||
| # Simulate network error | ||
| error = httpx.RequestError("Network failure", request=MagicMock()) |
Check notice
Code scanning / Bandit (reported by Codacy)
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
test_main.py
Outdated
Check notice
Code scanning / Bandit (reported by Codacy)
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
test_main.py
Outdated
Check notice
Code scanning / Bandit (reported by Codacy)
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
test_main.py
Outdated
Check notice
Code scanning / Bandit (reported by Codacy)
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Added a pre-flight check to validate API credentials before starting the sync process. This improves UX by providing clear, actionable error messages for authentication failures instead of a screen full of generic errors.
PR created automatically by Jules for task 14442985379801282171 started by @abhimehro