import hashlib import json from datetime import datetime, timezone import pytest from auto_archiver.utils.misc import ( mkdir_if_not_exists, expand_url, getattr_or, DateTimeEncoder, dump_payload, get_datetime_from_str, update_nested_dict, calculate_file_hash, random_str, get_timestamp ) @pytest.fixture def sample_file(tmp_path): file_path = tmp_path / "test.txt" file_path.write_text("test content") return file_path class TestDirectoryUtils: def test_mkdir_creates_new_directory(self, tmp_path): new_dir = tmp_path / "new_folder" mkdir_if_not_exists(new_dir) assert new_dir.exists() assert new_dir.is_dir() def test_mkdir_exists_quietly(self, tmp_path): existing_dir = tmp_path / "existing" existing_dir.mkdir() mkdir_if_not_exists(existing_dir) assert existing_dir.exists() class TestURLExpansion: @pytest.mark.parametrize("input_url,expected", [ ("https://example.com", "https://example.com"), ("https://t.co/test", "https://expanded.url") ]) def test_expand_url(self, input_url, expected, mocker): mock_response = mocker.Mock() mock_response.url = "https://expanded.url" mocker.patch('requests.get', return_value=mock_response) result = expand_url(input_url) assert result == expected def test_expand_url_handles_errors(self, caplog, mocker): mocker.patch('requests.get', side_effect=Exception("Connection error")) url = "https://t.co/error" result = expand_url(url) assert result == url assert f"Failed to expand url {url}" in caplog.text class TestAttributeHandling: class Sample: exists = "value" none = None @pytest.mark.parametrize("obj,attr,default,expected", [ (Sample(), "exists", "default", "value"), (Sample(), "none", "default", "default"), (Sample(), "missing", "default", "default"), (None, "anything", "fallback", "fallback"), ]) def test_getattr_or(self, obj, attr, default, expected): # Test gets attribute or returns a default value assert getattr_or(obj, attr, default) == expected class TestDateTimeHandling: def test_datetime_encoder(self, sample_datetime): result = json.dumps({"dt": sample_datetime}, cls=DateTimeEncoder) loaded = json.loads(result) assert loaded["dt"] == str(sample_datetime) def test_dump_payload(self, sample_datetime): payload = {"timestamp": sample_datetime} result = dump_payload(payload) assert str(sample_datetime) in result @pytest.mark.parametrize("dt_str,fmt,expected", [ ("2023-01-01 12:00:00+00:00", None, datetime(2023, 1, 1, 12, 0, tzinfo=timezone.utc)), ("20230101 120000", "%Y%m%d %H%M%S", datetime(2023, 1, 1, 12, 0)), ("invalid", None, None), ]) def test_datetime_from_string(self, dt_str, fmt, expected): result = get_datetime_from_str(dt_str, fmt) if expected is None: assert result is None else: assert result == expected.replace(tzinfo=result.tzinfo) class TestDictUtils: @pytest.mark.parametrize("original,update,expected", [ ({"a": 1}, {"b": 2}, {"a": 1, "b": 2}), ({"nested": {"a": 1}}, {"nested": {"b": 2}}, {"nested": {"a": 1, "b": 2}}), ({"a": {"b": {"c": 1}}}, {"a": {"b": {"c": 2}}}, {"a": {"b": {"c": 2}}}), ]) def test_update_nested_dict(self, original, update, expected): update_nested_dict(original, update) assert original == expected class TestHashingUtils: def test_file_hashing(self, sample_file): expected = hashlib.sha256(b"test content").hexdigest() assert calculate_file_hash(str(sample_file)) == expected def test_large_file_hashing(self, tmp_path): file_path = tmp_path / "large.bin" content = b"0" * 16_000_000 * 2 # 32MB file_path.write_bytes(content) expected = hashlib.sha256(content).hexdigest() assert calculate_file_hash(str(file_path)) == expected class TestMiscUtils: def test_random_str_length(self): for length in [8, 16, 32]: assert len(random_str(length)) == length def test_random_str_raises_too_long(self): with pytest.raises(AssertionError) as exc_info: random_str(64) assert "length must be less than 32 as UUID4 is used" == str(exc_info.value) def test_random_str_uniqueness(self): assert random_str() != random_str() @pytest.mark.parametrize("ts_input,utc,iso,expected_type", [ (datetime.now(), True, True, str), ("2023-01-01T12:00:00+00:00", False, False, datetime), (1672574400, True, True, str), ]) def test_timestamp_parsing(self, ts_input, utc, iso, expected_type): result = get_timestamp(ts_input, utc=utc, iso=iso) assert isinstance(result, expected_type) def test_invalid_timestamp_returns_none(self): assert get_timestamp("invalid-date") is None