reformatting
formatting_check / formatting_check (push) Failing after 3s
Python Test / test (.ps1, windows-latest, 3.9) (push) Has been cancelled
Python Test / test (.sh, macos-latest, 3.9) (push) Has been cancelled
Python Test / test (.sh, ubuntu-latest, 3.9) (push) Has been cancelled

This commit is contained in:
2026-07-14 21:32:55 +12:00
parent 3d0658b483
commit 0157f462cc
17 changed files with 439 additions and 377 deletions
+7 -5
View File
@@ -4,14 +4,15 @@ Test script to verify that the duplicate folder creation fix works correctly.
This script simulates the scenario where duplicate posts would previously create empty folders.
"""
import tempfile
import shutil
from pathlib import Path
from unittest.mock import MagicMock
# Add the bdfr module to the path
import sys
sys.path.insert(0, '.')
import tempfile
from pathlib import Path
from unittest.mock import MagicMock
sys.path.insert(0, ".")
from bdfr.configuration import Configuration
from bdfr.connector import RedditConnector
@@ -69,6 +70,7 @@ def test_duplicate_folder_creation_fix():
# Mock the download factory
import bdfr.site_downloaders.download_factory as df
original_pull_lever = df.DownloadFactory.pull_lever
df.DownloadFactory.pull_lever = MagicMock(return_value=mock_downloader_class)
@@ -93,4 +95,4 @@ def test_duplicate_folder_creation_fix():
if __name__ == "__main__":
test_duplicate_folder_creation_fix()
print("All tests passed! The duplicate folder creation fix is working correctly.")
print("All tests passed! The duplicate folder creation fix is working correctly.")
+11 -5
View File
@@ -3,8 +3,10 @@
Test extension case normalization functionality
"""
import pytest
from unittest.mock import MagicMock
import pytest
from bdfr.resource import Resource
@@ -43,7 +45,9 @@ class TestExtensionNormalization:
mock_submission.id = "test123"
resource = Resource(mock_submission, url, lambda: None)
assert resource.extension == expected, f"Reddit media URL {url} should normalize to {expected}, got {resource.extension}"
assert (
resource.extension == expected
), f"Reddit media URL {url} should normalize to {expected}, got {resource.extension}"
def test_constructor_extensions_normalized(self):
"""Test that extensions passed to constructor are normalized"""
@@ -66,7 +70,9 @@ class TestExtensionNormalization:
mock_submission.id = "test123"
resource = Resource(mock_submission, "https://example.com/test", lambda: None, input_ext)
assert resource.extension == expected, f"Constructor extension {input_ext} should normalize to {expected}, got {resource.extension}"
assert (
resource.extension == expected
), f"Constructor extension {input_ext} should normalize to {expected}, got {resource.extension}"
def test_magic_number_detection_normalized(self):
"""Test that magic number detection returns normalized extensions"""
@@ -74,7 +80,7 @@ class TestExtensionNormalization:
mock_submission.id = "test123"
# Test JPEG magic number detection
jpeg_content = b'\xFF\xD8\xFF' + b'0' * 100 # JPEG magic number
jpeg_content = b"\xff\xd8\xff" + b"0" * 100 # JPEG magic number
resource = Resource(mock_submission, "https://example.com/no-extension", lambda params: jpeg_content)
resource.download() # Trigger content-based detection
assert resource.extension == ".jpg", f"Magic number detection should return .jpg, got {resource.extension}"
assert resource.extension == ".jpg", f"Magic number detection should return .jpg, got {resource.extension}"
+6 -8
View File
@@ -3,13 +3,16 @@
Test script to debug file extension detection issues
"""
import sys
import os
import sys
sys.path.insert(0, os.path.dirname(__file__))
from unittest.mock import MagicMock
from bdfr.resource import Resource
def test_extension_detection():
"""Test extension detection with various URL patterns"""
@@ -18,26 +21,20 @@ def test_extension_detection():
("https://example.com/image.jpg", ".jpg"),
("https://example.com/video.mp4", ".mp4"),
("https://files.example.com/document.pdf", ".pdf"),
# URLs without extensions
("https://example.com/api/data", None),
("https://example.com/path/without/extension", None),
# URLs with query parameters
("https://example.com/image.jpg?size=large", ".jpg"),
("https://example.com/video.mp4?utm_source=test", ".mp4"),
# URLs with fragments
("https://example.com/image.png#section", ".png"),
# Complex paths
("https://imgur.com/a/gallery123", None),
("https://reddit.com/r/test/abc123_def456_789", None),
# Edge cases that might cause weird names
("https://example.com/L7SW9E~G", None),
("https://example.com/temp/file", None),
# Reddit media URLs (the actual issue)
("https://www.reddit.com/media?url=https%3A%2F%2Fi.redd.it%2Fr2mv10i4vkfd1.jpeg", ".jpeg"),
("https://i.redd.it/r2mv10i4vkfd1.jpeg", ".jpeg"),
@@ -60,5 +57,6 @@ def test_extension_detection():
print(f"Match: {'YES' if resource.extension == expected else 'NO'}")
print("-" * 40)
if __name__ == "__main__":
test_extension_detection()
test_extension_detection()
+12 -9
View File
@@ -7,16 +7,18 @@ This script simulates the scenario where a download fails and then tries to redo
import asyncio
import logging
import os
# Add the bdfr module to the path
import sys
import tempfile
import time
from pathlib import Path
# Add the bdfr module to the path
import sys
sys.path.insert(0, str(Path(__file__).parent))
from bdfr.api import BDFRManager, DownloadType, LoggingCallback, ProgressEvent
class TestProgressCallback(LoggingCallback):
"""Test callback that simulates a failure"""
@@ -36,6 +38,7 @@ class TestProgressCallback(LoggingCallback):
self.events.append(event)
await super().on_completed(event)
def test_file_locking_fix():
"""Test that the file locking issue is resolved"""
print("Testing file locking fix...")
@@ -53,7 +56,7 @@ def test_file_locking_fix():
download_id1 = manager.create_download(
DownloadType.USER,
"test_user_12345", # This user doesn't exist, should fail
progress_callbacks=[TestProgressCallback()]
progress_callbacks=[TestProgressCallback()],
)
# Start the download (it should fail)
@@ -71,7 +74,7 @@ def test_file_locking_fix():
download_id2 = manager.create_download(
DownloadType.USER,
"test_user_67890", # This user also doesn't exist, should fail
progress_callbacks=[TestProgressCallback()]
progress_callbacks=[TestProgressCallback()],
)
# Start the second download
@@ -100,7 +103,7 @@ def test_file_locking_fix():
print(f" - {log_file.name}")
# Check if file is accessible (not locked)
try:
with open(log_file, 'r') as f:
with open(log_file, "r") as f:
content = f.read()
print(f" SUCCESS: Log file is accessible ({len(content)} characters)")
except PermissionError:
@@ -112,9 +115,7 @@ def test_file_locking_fix():
# Test 4: Try to create a third download to ensure cleanup worked
print("\n4. Creating third download to verify cleanup...")
download_id3 = manager.create_download(
DownloadType.USER,
"test_user_cleanup",
progress_callbacks=[TestProgressCallback()]
DownloadType.USER, "test_user_cleanup", progress_callbacks=[TestProgressCallback()]
)
success3 = manager.start_download(download_id3)
@@ -132,6 +133,7 @@ def test_file_locking_fix():
print("\nSUCCESS: All tests passed! File locking issue appears to be fixed.")
return True
if __name__ == "__main__":
try:
success = test_file_locking_fix()
@@ -144,5 +146,6 @@ if __name__ == "__main__":
except Exception as e:
print(f"\nTest failed with exception: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
sys.exit(1)
+2 -2
View File
@@ -538,7 +538,7 @@ def test_strip_unicode_chars(input_string: str, expected: str):
def test_unicode_stripping_enabled(submission: MagicMock):
"""Test that Unicode stripping is applied when enabled"""
submission.title = 'Test 💕 emoji'
submission.title = "Test 💕 emoji"
formatter = FileNameFormatter("{TITLE}", "", "", strip_unicode=True)
result = formatter._format_name(submission, "{TITLE}")
assert "💕" not in result
@@ -547,7 +547,7 @@ def test_unicode_stripping_enabled(submission: MagicMock):
def test_unicode_stripping_disabled(submission: MagicMock):
"""Test that Unicode stripping is not applied when disabled"""
submission.title = 'Test 💕 emoji'
submission.title = "Test 💕 emoji"
formatter = FileNameFormatter("{TITLE}", "", "", strip_unicode=False)
result = formatter._format_name(submission, "{TITLE}")
assert "💕" in result
+43 -41
View File
@@ -3,14 +3,15 @@
Test script to verify hash persistence functionality.
"""
import json
import tempfile
import shutil
from pathlib import Path
from unittest.mock import Mock
# Import the necessary modules
import sys
sys.path.insert(0, '/Users/Daniel/Documents/GitHub/bulk-downloader-for-reddit')
import tempfile
from pathlib import Path
from unittest.mock import Mock
sys.path.insert(0, "/Users/Daniel/Documents/GitHub/bulk-downloader-for-reddit")
from bdfr.configuration import Configuration
from bdfr.downloader import RedditDownloader
@@ -59,7 +60,7 @@ def test_hash_persistence():
# Test 2: Save empty hash list
print("Test 2: Saving empty hash list")
downloader._save_hash_list()
hash_file = temp_path / '.bdfr_hashes.json'
hash_file = temp_path / ".bdfr_hashes.json"
assert hash_file.exists(), "Hash file should be created even when empty"
print("PASS Passed")
@@ -71,18 +72,18 @@ def test_hash_persistence():
# Test 4: Add some test data and save
print("Test 4: Adding test data and saving")
test_file = temp_path / 'test.txt'
test_file = temp_path / "test.txt"
test_file.write_text("test content")
downloader.master_hash_list['test_hash_123'] = test_file
downloader.master_hash_list["test_hash_123"] = test_file
downloader._save_hash_list()
# Verify the saved JSON structure
with open(hash_file, 'r') as f:
with open(hash_file, "r") as f:
saved_data = json.load(f)
assert 'test_hash_123' in saved_data, "Test hash should be in saved data"
assert saved_data['test_hash_123'] == 'test.txt', f"Expected 'test.txt', got {saved_data['test_hash_123']}"
assert "test_hash_123" in saved_data, "Test hash should be in saved data"
assert saved_data["test_hash_123"] == "test.txt", f"Expected 'test.txt', got {saved_data['test_hash_123']}"
print("PASS Passed")
# Test 5: Load hash list and verify data is restored
@@ -100,13 +101,15 @@ def test_hash_persistence():
loaded_hash_list = new_downloader._load_hash_list()
assert len(loaded_hash_list) == 1, f"Expected 1 hash, got {len(loaded_hash_list)}"
assert 'test_hash_123' in loaded_hash_list, "Test hash should be loaded"
assert loaded_hash_list['test_hash_123'] == test_file, f"File path should match: {loaded_hash_list['test_hash_123']} != {test_file}"
assert "test_hash_123" in loaded_hash_list, "Test hash should be loaded"
assert (
loaded_hash_list["test_hash_123"] == test_file
), f"File path should match: {loaded_hash_list['test_hash_123']} != {test_file}"
print("PASS Passed")
# Test 6: Test corrupted hash file handling
print("Test 6: Testing corrupted hash file handling")
with open(hash_file, 'w') as f:
with open(hash_file, "w") as f:
f.write("invalid json content")
corrupted_downloader = RedditDownloader.__new__(RedditDownloader)
@@ -122,7 +125,9 @@ def test_hash_persistence():
# Should handle corrupted file gracefully and return empty dict
corrupted_hash_list = corrupted_downloader._load_hash_list()
assert len(corrupted_hash_list) == 0, f"Expected empty hash list for corrupted file, got {len(corrupted_hash_list)}"
assert (
len(corrupted_hash_list) == 0
), f"Expected empty hash list for corrupted file, got {len(corrupted_hash_list)}"
print("PASS Passed")
print("\nAll tests passed! Hash persistence functionality is working correctly.")
@@ -174,7 +179,7 @@ def test_simple_check_functionality():
# Test 2: Add test data and save with simple_check format
print("Test 2: Adding test data and saving with simple_check format")
test_file = temp_path / 'test.txt'
test_file = temp_path / "test.txt"
test_file.write_text("test content")
test_url = "https://example.com/test.txt"
test_hash = "test_hash_123"
@@ -185,16 +190,16 @@ def test_simple_check_functionality():
downloader._save_hash_list()
# Verify the saved JSON structure has enhanced format
with open(temp_path / '.bdfr_hashes.json', 'r') as f:
with open(temp_path / ".bdfr_hashes.json", "r") as f:
saved_data = json.load(f)
assert 'files' in saved_data, "Enhanced format should have 'files' section"
assert 'urls' in saved_data, "Enhanced format should have 'urls' section"
assert 'metadata' in saved_data, "Enhanced format should have 'metadata' section"
assert test_hash in saved_data['files'], "Test hash should be in files section"
assert test_url in saved_data['urls'], "Test URL should be in urls section"
assert saved_data['metadata']['version'] == '2.0', "Version should be 2.0"
assert saved_data['metadata']['created_with'] == 'simple_check', "Should be created with simple_check"
assert "files" in saved_data, "Enhanced format should have 'files' section"
assert "urls" in saved_data, "Enhanced format should have 'urls' section"
assert "metadata" in saved_data, "Enhanced format should have 'metadata' section"
assert test_hash in saved_data["files"], "Test hash should be in files section"
assert test_url in saved_data["urls"], "Test URL should be in urls section"
assert saved_data["metadata"]["version"] == "2.0", "Version should be 2.0"
assert saved_data["metadata"]["created_with"] == "simple_check", "Should be created with simple_check"
print("PASS")
# Test 3: Load hash list and verify URL mapping is restored
@@ -228,7 +233,7 @@ def test_simple_check_functionality():
mock_resource.hash.hexdigest.return_value = test_hash
# Create a mock destination that exists
mock_destination = temp_path / 'existing_file.txt'
mock_destination = temp_path / "existing_file.txt"
mock_destination.parent.mkdir(parents=True, exist_ok=True)
mock_destination.write_text("existing content")
@@ -260,17 +265,14 @@ def test_backward_compatibility():
temp_path = Path(temp_dir)
# Create old-format hash file manually
(temp_path / 'relative' / 'path').mkdir(parents=True, exist_ok=True)
(temp_path / 'relative' / 'path' / 'file1.txt').write_text("content1")
(temp_path / 'relative' / 'path' / 'file2.txt').write_text("content2")
(temp_path / "relative" / "path").mkdir(parents=True, exist_ok=True)
(temp_path / "relative" / "path" / "file1.txt").write_text("content1")
(temp_path / "relative" / "path" / "file2.txt").write_text("content2")
old_hash_data = {
"hash1": "relative/path/file1.txt",
"hash2": "relative/path/file2.txt"
}
old_hash_data = {"hash1": "relative/path/file1.txt", "hash2": "relative/path/file2.txt"}
hash_file = temp_path / '.bdfr_hashes.json'
with open(hash_file, 'w') as f:
hash_file = temp_path / ".bdfr_hashes.json"
with open(hash_file, "w") as f:
json.dump(old_hash_data, f)
# Create downloader and load old format
@@ -294,21 +296,21 @@ def test_backward_compatibility():
print("PASS - Old format loaded correctly")
# Test saving in new format
(temp_path / 'another').mkdir(parents=True, exist_ok=True)
test_file = temp_path / 'another' / 'new_file.txt'
(temp_path / "another").mkdir(parents=True, exist_ok=True)
test_file = temp_path / "another" / "new_file.txt"
test_file.write_text("new content")
downloader.master_hash_list["new_hash"] = test_file
downloader._save_hash_list()
# Verify new format was created
with open(hash_file, 'r') as f:
with open(hash_file, "r") as f:
new_data = json.load(f)
assert 'files' in new_data, "New format should have 'files' section"
assert 'urls' in new_data, "New format should have 'urls' section"
assert 'metadata' in new_data, "New format should have 'metadata' section"
assert new_data['metadata']['version'] == '2.0', "Should be version 2.0"
assert "files" in new_data, "New format should have 'files' section"
assert "urls" in new_data, "New format should have 'urls' section"
assert "metadata" in new_data, "New format should have 'metadata' section"
assert new_data["metadata"]["version"] == "2.0", "Should be version 2.0"
print("PASS - Old format upgraded to new format correctly")
@@ -318,4 +320,4 @@ def test_backward_compatibility():
if __name__ == "__main__":
test_hash_persistence()
test_simple_check_functionality()
test_backward_compatibility()
test_backward_compatibility()
+31 -35
View File
@@ -14,10 +14,11 @@ import tempfile
from pathlib import Path
# Set UTF-8 encoding for Windows console
if sys.platform == 'win32':
if sys.platform == "win32":
import codecs
sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer, 'strict')
sys.stderr = codecs.getwriter('utf-8')(sys.stderr.buffer, 'strict')
sys.stdout = codecs.getwriter("utf-8")(sys.stdout.buffer, "strict")
sys.stderr = codecs.getwriter("utf-8")(sys.stderr.buffer, "strict")
from bdfr.api import BDFRManager, DownloadType
from bdfr.configuration import Configuration
@@ -26,27 +27,24 @@ from bdfr.configuration import Configuration
def test_subreddit_directory_structure():
"""Test that subreddit downloads use correct directory structure"""
print("\n=== Testing Subreddit Directory Structure ===")
with tempfile.TemporaryDirectory() as tmpdir:
manager = BDFRManager(download_directory=tmpdir)
# Create a download for a subreddit
download_id = manager.create_download(
DownloadType.SUBREDDIT,
"test_subreddit"
)
download_id = manager.create_download(DownloadType.SUBREDDIT, "test_subreddit")
download_info = manager.get_download_status(download_id)
config = download_info["config"]
expected_dir = str(Path(tmpdir))
actual_dir = config.directory
print(f"Expected directory: {expected_dir}")
print(f"Actual directory: {actual_dir}")
print(f"Subreddit config: {config.subreddit}")
print(f"Folder scheme: {config.folder_scheme}")
assert actual_dir == expected_dir, f"Subreddit directory mismatch!"
print("[OK] Subreddit directory structure is correct")
print(f" Files will be saved to: {actual_dir}/{{SUBREDDIT}}/{{files}}")
@@ -55,28 +53,25 @@ def test_subreddit_directory_structure():
def test_user_directory_structure():
"""Test that user downloads use correct directory structure"""
print("\n=== Testing User Directory Structure ===")
with tempfile.TemporaryDirectory() as tmpdir:
manager = BDFRManager(download_directory=tmpdir)
# Create a download for a user
username = "test_user"
download_id = manager.create_download(
DownloadType.USER,
username
)
download_id = manager.create_download(DownloadType.USER, username)
download_info = manager.get_download_status(download_id)
config = download_info["config"]
expected_dir = str(Path(tmpdir) / username)
actual_dir = config.directory
print(f"Expected directory: {expected_dir}")
print(f"Actual directory: {actual_dir}")
print(f"User config: {config.user}")
print(f"Folder scheme: {config.folder_scheme}")
assert actual_dir == expected_dir, f"User directory mismatch!"
print("[OK] User directory structure is correct")
print(f" Files will be saved to: {actual_dir}/{{SUBREDDIT}}/{{files}}")
@@ -85,26 +80,26 @@ def test_user_directory_structure():
def test_convenience_method():
"""Test the convenience method download_user()"""
print("\n=== Testing download_user() Convenience Method ===")
with tempfile.TemporaryDirectory() as tmpdir:
manager = BDFRManager(download_directory=tmpdir)
# Don't actually start the download, just check the config
username = "convenience_test_user"
# Create config manually like the convenience method does
config = Configuration()
user_directory = Path(tmpdir) / username
config.directory = str(user_directory)
config.user = [username]
expected_dir = str(Path(tmpdir) / username)
actual_dir = config.directory
print(f"Expected directory: {expected_dir}")
print(f"Actual directory: {actual_dir}")
print(f"User config: {config.user}")
assert actual_dir == expected_dir, f"Convenience method directory mismatch!"
print("[OK] Convenience method directory structure is correct")
@@ -118,7 +113,7 @@ def demonstrate_folder_structure():
print(" ├── file1.jpg")
print(" ├── file2.png")
print(" └── file3.mp4")
print("\nWhen downloading from a USER 'spez' who posts to multiple subreddits:")
print(" downloads/")
print(" └── spez/")
@@ -129,7 +124,7 @@ def demonstrate_folder_structure():
print(" │ └── file3.jpg")
print(" └── pics/")
print(" └── file4.png")
print("\n[OK] This structure allows:")
print(" 1. Easy identification of user-specific downloads")
print(" 2. Organization by subreddit within each user folder")
@@ -140,22 +135,23 @@ if __name__ == "__main__":
print("=" * 60)
print("Testing User Folder Structure Changes")
print("=" * 60)
try:
test_subreddit_directory_structure()
test_user_directory_structure()
test_convenience_method()
demonstrate_folder_structure()
print("\n" + "=" * 60)
print("[SUCCESS] All tests passed!")
print("=" * 60)
except AssertionError as e:
print(f"\n[FAIL] Test failed: {e}")
exit(1)
except Exception as e:
print(f"\n[ERROR] Unexpected error: {e}")
import traceback
traceback.print_exc()
exit(1)
exit(1)