Refactor/replace mocks with cassettes#1252
Merged
AndreyNikiforov merged 40 commits intoicloud-photos-downloader:masterfrom Sep 15, 2025
Merged
Conversation
This reverts commit c82491d.
The test_handle_session_error_during_download now uses a cassette with an actual session error response instead of mocking PyiCloudAPIResponseException. This works because the error handling code in session.py (before commit c82491d) properly parses error responses and raises exceptions. Changes: - Created listing_photos_session_error_download.yml cassette with 401 "Invalid global session" response - Removed all mocks from test_handle_session_error_during_download - Test now relies entirely on cassette for session error simulation - Demonstrates that cassettes can trigger PyiCloudAPIResponseException without mocks This is a cleaner approach that tests the actual error handling path rather than bypassing it with mocks. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
…tests - Converted 4 session error tests from using mocks to using VCR cassettes - test_handle_session_error_during_download: uses cassette with 401 response during download - test_handle_session_error_during_photo_iteration: uses cassette with 401 response during photo listing - test_handle_session_error_during_download_name_id7: uses cassette for name-id7 file match policy - test_handle_session_error_during_photo_iteration_name_id7: uses cassette for name-id7 with listing error All tests properly trigger PyiCloudAPIResponseException from HTTP responses in cassettes without needing to mock the exception directly. This proves that cassettes can simulate session errors when the error handling code is active. Note: One test (download_name_id7) needs cassette completion for the second download attempt after re-authentication. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Since MAX_RETRIES is set to 0 and retry logic was removed, the time.sleep mocks and sleep count assertions are no longer needed. The tests now only verify that re-authentication happens (by counting 'Authenticating...' messages) and that the session error message appears in the output. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Remove mock.patch.object for send_verification_code in test_2sa_flow_failed_send_code - Create dedicated VCR cassette (2sa_flow_failed_send_code.yml) with failed response - Cassette simulates service unavailable (success:false) response - Remove unnecessary mock import from test file This demonstrates that mocks can be replaced with more realistic VCR cassettes that test the full HTTP request/response handling stack. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Use consistent error response format with errorTitle, errorMessage, and errorCode - Match the structure used in other Apple API error responses - Remove incorrectly added 'status' field from headers section - Update content-length to match new response size The response now matches Apple's actual API format as seen in other captured cassettes like 2sa_flow_invalid_code.yml. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
… mocks - Created listing_albums_error.yml cassette with 500 status and API error response - Removed PhotoLibrary._fetch_folders mock and authenticate mock - Test now relies on cassette to trigger PyiCloudAPIResponseException - Removed unused PhotoLibrary import 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
The session.py code has two paths for error handling:
1. HTTP errors (non-OK status codes)
2. API errors in JSON payload with 200 status
The test_handle_albums_error was originally mocking an API error, so the
cassette should return HTTP 200 with error in the JSON payload, not HTTP 500.
Updated cassette response to: {"success":false,"error":"Api Error","errorCode":"100"}
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- test_handle_albums_error_name_id7: uses same cassette as albums_error test - test_handle_internal_error_during_download: uses cassette with 500 status during photo download Key learnings: - API errors (200 with error JSON) vs HTTP errors (4xx/5xx status) are handled differently in session.py - Download errors need HTTP status errors (500) since downloads use streaming and the error handling differs from regular JSON API calls - Removed unused PhotoLibrary and constants imports 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Download errors behave differently than API errors: - API endpoints (albums, photo listing): Can use HTTP 200 with error JSON - Download endpoints: Must use HTTP error status (4xx/5xx) because downloads use streaming and don't parse JSON responses The test behavior changes slightly: - Original mock: Raised PyiCloudAPIResponseException with "INTERNAL_ERROR" - Cassette: HTTP 500 results in "Could not find URL to download" message - Exit code changes from 1 to 0 as error is handled gracefully This is an acceptable difference as the error is still caught and handled, just reported differently. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Remove is_streaming_response check that was preventing exceptions for streaming downloads - Add special handling to exclude 404 errors from raising exceptions - Update cassette to return HTTP 500 for download errors - Ensure download errors raise exceptions and cause exit code 1 The is_streaming_response check was preventing the session from raising exceptions for HTTP errors on streaming downloads, causing inconsistent behavior between mock tests and cassette tests. Checking response status doesn't load the stream into memory since it's part of the HTTP headers. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Convert test_handle_internal_error_during_photo_iteration to use cassette - Convert test_handle_internal_error_during_download_name_id7 to use cassette - Add cassettes for simulating HTTP 500 errors during photo operations - Remove mock dependencies from converted tests - Clean up unused imports These tests now use actual HTTP responses via cassettes instead of mocks, providing more realistic testing of error handling behavior. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
The session error cassettes now include successful download after re-authentication, demonstrating proper retry behavior. Updated test comments to accurately reflect cassette contents and expected behavior. Both tests now: - Experience session error on first download attempt - Re-authenticate automatically - Successfully download on retry - Exit with code 0 (success) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Minor formatting improvements to session error test comments and file download expectations. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
…cassettes The listing_photos_session_error_iteration.yml and listing_photos_session_error_iteration_name_id7.yml cassettes had delete file requests at the end that shouldn't be there. These have been removed. Both tests still pass after the cleanup. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
… hardcoded dates - Replace datetime.datetime.fromtimestamp() calculations with actual expected folder paths - Remove unused pytz and tzlocal imports - Makes tests clearer and easier to understand 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
The cassette download_autodelete_photos_retry.yml already contains the session error response and retry flow, so mocks are no longer needed. This simplifies the test and makes it more maintainable. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
…_error - Created cassette download_autodelete_photos_internal_error.yml with internal error response - Removed all mocks from test, now relies entirely on cassette - Internal errors (500 status) are not retried (MAX_RETRIES=0) - Test verifies that delete fails with exit code 1 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Updated test_download_photos_and_set_exif to download only IMG_7409.JPG - Updated test_download_photos_and_set_exif_name_id7 similarly - Removed download mocking that was limiting downloads - Added --skip-videos and --skip-live-photos flags - Changed --recent from 4 to 1 to match single photo download - Both tests now use simpler cassettes with single photo downloads
- Remove mock patches for download_media and os.utime - Use shared listing_photos_until_found.yml cassette - Add IMG_7399-medium.MOV download response to cassette - Include all files downloaded during test execution (IMG_7403, IMG_7402, IMG_7399-medium) - Reduce file sizes to 151 bytes for faster tests 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Remove unnecessary download_media and os.utime mocks from test_size_fallback_to_original - Remove all mocks from test_size_fallback_to_original_name_id7 - Both tests now use listing_photos_fallback_to_original.yml cassette - Add files_to_download lists to expect the downloaded files - Add --skip-live-photos flag for consistency 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Remove unnecessary mocks from test_download_and_dedupe_existing_photos_name_id7 - Update test to use shared listing_photos_dedup.yml cassette - Clarify that name-id7 does not perform deduplication (files already exist) - Adjust test expectations to match actual behavior with name-id7 policy 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Remove all unnecessary mocks from test_adjusted_size_fallback_to_original* - Use shared listing_photos_fallback_to_original.yml cassette - Add files_to_download list with expected files - Simplify assertion to check for filename in output
- Replace mock patches with actual cassette recording - Add listing_photos_two_sizes_forced.yml cassette - Test now uses real HTTP responses instead of mocks - Add --skip-live-photos flag to simplify test
- Update test_download_one_recent_live_photo tests - Update test_download_one_recent_live_photo_chinese tests - Update corresponding name_id7 variants - Add listing_photos_recent_live.yml cassette with actual responses - Remove mock patches and use real HTTP interactions
- Update test_download_one_recent_live_photo_chinese tests - Update corresponding name_id7 variants - Add listing_photos_recent_live_chinese.yml cassette - Remove mock patches and use real HTTP interactions - Clean up unused imports
- Update test_download_after_delete_dry_run to use cassette - Update test_download_after_delete_dry_run_name_id7 to use cassette - Remove mock patches and use listing_photos_recent_live.yml - Clean up unused imports from pyicloud_ipd.base
- Update test_handle_internal_error_during_photo_iteration_name_id7 to use cassette - Use shared listing_photos_iteration_error.yml cassette - Remove mock patches for PyiCloudAPIResponseException - Remove unused imports (Dict, constants, PyiCloudAPIResponseException) - Align test behavior with standard version
- Update test_handle_internal_error_during_download_name_id7 to use shared cassette - Use listing_photos_internal_error_download.yml for both standard and name-id7 tests - Remove duplicate listing_photos_internal_error_download_name_id7.yml cassette - Both tests now share the same cassette recording
…7 tests - Update session error and albums error tests to use shared cassettes - Remove duplicate cassettes for name-id7 variants: - listing_albums_error_name_id7.yml - listing_photos_session_error_download_name_id7.yml - listing_photos_session_error_iteration_name_id7.yml - All error handling tests now share cassettes between standard and name-id7
- Set allow_playback_repeats=False to ensure requests are used in order - Add commented assertions to check all cassette interactions were played - This helps detect when cassettes contain unused requests - Prevents tests from replaying the same request multiple times
- Rename 'cassette' to '_cassette' to indicate it's intentionally unused - Update commented assertion to use _cassette variable - This follows Python convention for unused variables
- Replace mock patches with shared cassette recording - Use listing_photos_two_sizes_forced.yml cassette - Remove mock.patch for download_media and PhotoAsset.versions - Clean up unused imports (PropertyMock, AssetVersionSize)
- Replace mock patches with shared cassette recording - Use listing_photos_two_sizes_forced.yml cassette - Remove mock.patch for download_media and PhotoAsset.versions - Clean up unused imports (PropertyMock, AssetVersionSize) - Align with standard test_force_size implementation
- Update various cassette files with small modifications - Changes likely from user edits or test adjustments - Includes cassettes for autodelete, live photos, raw photos, and error handling tests
64d2313
into
icloud-photos-downloader:master
398 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.