""" Test suite for file utility functions. """ import os import tempfile from pathlib import Path import pytest from markitect_utils.file_utils import ( safe_filename, ensure_extension, get_file_size, is_text_file, normalize_path, ) class TestSafeFilename: """Test cases for the safe_filename function.""" def test_basic_sanitization(self): """Test basic filename sanitization.""" assert safe_filename("normal_file.txt") == "normal_file.txt" assert safe_filename("file with spaces.txt") == "file with spaces.txt" def test_unsafe_characters(self): """Test removal of unsafe characters.""" assert safe_filename("file<>name.txt") == "file__name.txt" assert safe_filename('file"name.txt') == "file_name.txt" assert safe_filename("file/path\\name.txt") == "file_path_name.txt" assert safe_filename("file|name.txt") == "file_name.txt" def test_custom_replacement(self): """Test custom replacement character.""" assert safe_filename("file<>name.txt", "-") == "file--name.txt" assert safe_filename("file/path\\name.txt", "") == "filepathname.txt" def test_reserved_names(self): """Test handling of Windows reserved names.""" assert safe_filename("CON") == "file_CON" assert safe_filename("PRN.txt") == "file_PRN.txt" assert safe_filename("COM1") == "file_COM1" assert safe_filename("con") == "file_con" # case insensitive def test_edge_cases(self): """Test edge cases.""" assert safe_filename("") == "" # Empty input returns empty assert safe_filename(" ") == "file_" # Whitespace only gets prefix assert safe_filename("...") == "file_" # Dots only gets prefix assert safe_filename(".hidden") == "hidden" # Leading dot gets stripped class TestEnsureExtension: """Test cases for the ensure_extension function.""" def test_add_extension(self): """Test adding extension to filename.""" assert ensure_extension("document", ".md") == "document.md" assert ensure_extension("file", "txt") == "file.txt" def test_existing_extension(self): """Test when extension already exists.""" assert ensure_extension("document.md", ".md") == "document.md" assert ensure_extension("file.txt", "txt") == "file.txt" def test_different_extension(self): """Test adding extension when different one exists.""" assert ensure_extension("document.txt", ".md") == "document.txt.md" assert ensure_extension("file.doc", "pdf") == "file.doc.pdf" def test_edge_cases(self): """Test edge cases.""" assert ensure_extension("", ".md") == "" assert ensure_extension("file", "") == "file" assert ensure_extension("file.md", "") == "file.md" class TestGetFileSize: """Test cases for the get_file_size function.""" def test_existing_file(self): """Test getting size of existing file.""" with tempfile.NamedTemporaryFile(mode='w', delete=False) as f: f.write("Hello, World!") temp_path = f.name try: size = get_file_size(temp_path) assert size is not None assert size > 0 finally: os.unlink(temp_path) def test_nonexistent_file(self): """Test getting size of non-existent file.""" assert get_file_size("/path/that/does/not/exist") is None def test_empty_file(self): """Test getting size of empty file.""" with tempfile.NamedTemporaryFile(delete=False) as f: temp_path = f.name try: size = get_file_size(temp_path) assert size == 0 finally: os.unlink(temp_path) def test_path_object(self): """Test with Path object.""" with tempfile.NamedTemporaryFile(mode='w', delete=False) as f: f.write("test content") temp_path = Path(f.name) try: size = get_file_size(temp_path) assert size is not None assert size > 0 finally: os.unlink(temp_path) class TestIsTextFile: """Test cases for the is_text_file function.""" def test_text_file(self): """Test with actual text file.""" with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as f: f.write("This is a text file with some content.") temp_path = f.name try: assert is_text_file(temp_path) is True finally: os.unlink(temp_path) def test_binary_file(self): """Test with binary file.""" with tempfile.NamedTemporaryFile(mode='wb', delete=False, suffix='.bin') as f: f.write(b'\x00\x01\x02\x03\x04\x05') temp_path = f.name try: assert is_text_file(temp_path) is False finally: os.unlink(temp_path) def test_empty_file(self): """Test with empty file.""" with tempfile.NamedTemporaryFile(delete=False) as f: temp_path = f.name try: assert is_text_file(temp_path) is True # Empty files are considered text finally: os.unlink(temp_path) def test_unicode_file(self): """Test with Unicode text file.""" with tempfile.NamedTemporaryFile(mode='w', delete=False, encoding='utf-8') as f: f.write("Hello 世界! This is UTF-8 text.") temp_path = f.name try: assert is_text_file(temp_path) is True finally: os.unlink(temp_path) def test_nonexistent_file(self): """Test with non-existent file.""" assert is_text_file("/path/that/does/not/exist") is False class TestNormalizePath: """Test cases for the normalize_path function.""" def test_relative_path(self): """Test normalizing relative paths.""" # Note: These tests are environment-dependent result = normalize_path("./test") assert os.path.isabs(result) assert result.endswith("test") def test_path_with_dots(self): """Test path with dot components.""" result = normalize_path("./dir/../file.txt") assert os.path.isabs(result) assert result.endswith("file.txt") def test_already_absolute(self): """Test already absolute path.""" abs_path = "/tmp/test/file.txt" result = normalize_path(abs_path) assert result == abs_path def test_path_object(self): """Test with Path object.""" path_obj = Path("./test/file.txt") result = normalize_path(path_obj) assert os.path.isabs(result) assert isinstance(result, str) def test_edge_cases(self): """Test edge cases.""" assert normalize_path("") == "" # Current directory should normalize to absolute path result = normalize_path(".") assert os.path.isabs(result)