Skip to main content
Glama
test_utils.py23.9 kB
"""Unit tests for utility modules.""" import os import tempfile import pytest from pathlib import Path from src.utils.file_utils import FileUtils from src.utils.validation_utils import ValidationUtils from src.utils.conversion_utils import ConversionUtils from src.utils.security_utils import SecurityUtils class TestFileUtils: """Test cases for FileUtils class.""" def test_get_file_extension(self): """Test getting file extension.""" assert FileUtils.get_file_extension("test.docx") == ".docx" assert FileUtils.get_file_extension("path/to/file.pdf") == ".pdf" assert FileUtils.get_file_extension("no_extension") == "" assert FileUtils.get_file_extension("TEST.DOCX") == ".docx" # Case insensitive def test_safe_filename(self): """Test filename sanitization.""" assert FileUtils.safe_filename("test.docx") == "test.docx" assert FileUtils.safe_filename("test<>file.docx") == "testfile.docx" assert FileUtils.safe_filename(" test ") == "test" assert FileUtils.safe_filename("") == "unnamed" assert FileUtils.safe_filename("...") == "unnamed" assert FileUtils.safe_filename("test:file.docx") == "testfile.docx" def test_ensure_directory(self, tmp_path): """Test directory creation.""" test_dir = tmp_path / "new_dir" FileUtils.ensure_directory(str(test_dir)) assert test_dir.exists() # Test idempotency - should not fail if already exists FileUtils.ensure_directory(str(test_dir)) assert test_dir.exists() def test_file_exists(self, tmp_path): """Test file existence check.""" existing_file = tmp_path / "test.txt" existing_file.write_text("test") assert FileUtils.file_exists(str(existing_file)) is True assert FileUtils.file_exists(str(tmp_path / "nonexistent.txt")) is False def test_delete_file(self, tmp_path): """Test file deletion.""" test_file = tmp_path / "to_delete.txt" test_file.write_text("test") assert FileUtils.delete_file(str(test_file)) is True assert not test_file.exists() assert FileUtils.delete_file(str(test_file)) is False # Already deleted def test_read_write_binary(self, tmp_path): """Test binary read/write operations.""" test_file = tmp_path / "binary.dat" test_data = b"binary data \x00\x01\x02" FileUtils.write_binary(str(test_file), test_data) read_data = FileUtils.read_binary(str(test_file)) assert read_data == test_data def test_read_write_text(self, tmp_path): """Test text read/write operations.""" test_file = tmp_path / "text.txt" test_content = "Hello, World!\nSecond line." FileUtils.write_text(str(test_file), test_content) read_content = FileUtils.read_text(str(test_file)) assert read_content == test_content def test_copy_file(self, tmp_path): """Test file copying.""" source = tmp_path / "source.txt" dest = tmp_path / "dest.txt" source.write_text("test content") result = FileUtils.copy_file(str(source), str(dest)) assert dest.exists() assert dest.read_text() == "test content" assert source.exists() # Original still exists def test_move_file(self, tmp_path): """Test file moving.""" source = tmp_path / "source.txt" dest = tmp_path / "dest.txt" source.write_text("test content") result = FileUtils.move_file(str(source), str(dest)) assert dest.exists() assert dest.read_text() == "test content" assert not source.exists() # Original moved def test_get_file_size(self, tmp_path): """Test file size retrieval.""" test_file = tmp_path / "size_test.txt" test_file.write_bytes(b"x" * 100) size = FileUtils.get_file_size(str(test_file)) assert size == 100 def test_get_checksum(self, tmp_path): """Test file checksum calculation.""" test_file = tmp_path / "checksum.txt" test_file.write_text("test content") checksum = FileUtils.get_checksum(str(test_file)) assert len(checksum) == 64 # SHA-256 produces 64 hex chars assert checksum.isalnum() # Same content should produce same checksum checksum2 = FileUtils.get_checksum(str(test_file)) assert checksum == checksum2 def test_create_temp_file(self): """Test temporary file creation.""" temp_file = FileUtils.create_temp_file(suffix=".txt", prefix="test_") try: assert os.path.exists(temp_file) assert temp_file.endswith(".txt") assert "test_" in os.path.basename(temp_file) finally: if os.path.exists(temp_file): os.remove(temp_file) def test_create_temp_file_with_content(self): """Test temporary file creation with content.""" content = b"test content" temp_file = FileUtils.create_temp_file(content=content) try: with open(temp_file, "rb") as f: assert f.read() == content finally: if os.path.exists(temp_file): os.remove(temp_file) def test_create_temp_directory(self): """Test temporary directory creation.""" temp_dir = FileUtils.create_temp_directory(prefix="test_") try: assert os.path.exists(temp_dir) assert os.path.isdir(temp_dir) finally: if os.path.exists(temp_dir): os.rmdir(temp_dir) def test_list_files(self, tmp_path): """Test file listing.""" # Create test files (tmp_path / "file1.txt").write_text("test") (tmp_path / "file2.txt").write_text("test") (tmp_path / "file3.docx").write_text("test") all_files = FileUtils.list_files(str(tmp_path)) assert len(all_files) == 3 txt_files = FileUtils.list_files(str(tmp_path), pattern="*.txt") assert len(txt_files) == 2 def test_list_files_recursive(self, tmp_path): """Test recursive file listing.""" subdir = tmp_path / "subdir" subdir.mkdir() (tmp_path / "file1.txt").write_text("test") (subdir / "file2.txt").write_text("test") files = FileUtils.list_files(str(tmp_path), pattern="*.txt", recursive=True) assert len(files) == 2 def test_get_mime_type(self, tmp_path): """Test MIME type detection.""" test_file = tmp_path / "test.txt" test_file.write_text("test") mime = FileUtils.get_mime_type(str(test_file)) assert "text" in mime class TestValidationUtils: """Test cases for ValidationUtils class.""" def test_is_valid_document_extension(self): """Test document extension validation.""" assert ValidationUtils.is_valid_document_extension("test.docx") is True assert ValidationUtils.is_valid_document_extension("test.doc") is True assert ValidationUtils.is_valid_document_extension("test.pdf") is False assert ValidationUtils.is_valid_document_extension("TEST.DOCX") is True # Case insensitive assert ValidationUtils.is_valid_document_extension("/path/to/file.docx") is True def test_is_valid_image_extension(self): """Test image extension validation.""" assert ValidationUtils.is_valid_image_extension("test.png") is True assert ValidationUtils.is_valid_image_extension("test.jpg") is True assert ValidationUtils.is_valid_image_extension("test.jpeg") is True assert ValidationUtils.is_valid_image_extension("test.gif") is True assert ValidationUtils.is_valid_image_extension("test.bmp") is True assert ValidationUtils.is_valid_image_extension("test.tiff") is True assert ValidationUtils.is_valid_image_extension("test.docx") is False def test_is_valid_email(self): """Test email validation.""" assert ValidationUtils.is_valid_email("test@example.com") is True assert ValidationUtils.is_valid_email("user.name@example.com") is True assert ValidationUtils.is_valid_email("user+tag@example.co.uk") is True assert ValidationUtils.is_valid_email("invalid-email") is False assert ValidationUtils.is_valid_email("@example.com") is False assert ValidationUtils.is_valid_email("test@") is False assert ValidationUtils.is_valid_email("") is False def test_is_valid_uuid(self): """Test UUID validation.""" assert ValidationUtils.is_valid_uuid("550e8400-e29b-41d4-a716-446655440000") is True assert ValidationUtils.is_valid_uuid("550E8400-E29B-41D4-A716-446655440000") is True # Case insensitive assert ValidationUtils.is_valid_uuid("invalid-uuid") is False assert ValidationUtils.is_valid_uuid("550e8400-e29b-41d4-a716") is False # Too short assert ValidationUtils.is_valid_uuid("") is False def test_is_valid_color(self): """Test color validation.""" assert ValidationUtils.is_valid_color("#FF0000") is True assert ValidationUtils.is_valid_color("FF0000") is True assert ValidationUtils.is_valid_color("#F00") is True assert ValidationUtils.is_valid_color("F00") is True assert ValidationUtils.is_valid_color("#ff0000") is True # Lowercase assert ValidationUtils.is_valid_color("invalid") is False assert ValidationUtils.is_valid_color("#GG0000") is False assert ValidationUtils.is_valid_color("") is False def test_validate_pagination(self): """Test pagination validation.""" skip, limit = ValidationUtils.validate_pagination(0, 10) assert skip == 0 assert limit == 10 skip, limit = ValidationUtils.validate_pagination(-5, 200, max_limit=50) assert skip == 0 assert limit == 50 skip, limit = ValidationUtils.validate_pagination(10, 0) assert skip == 10 assert limit == 1 # Minimum limit is 1 def test_is_docx_file(self): """Test DOCX file validation.""" assert ValidationUtils.is_docx_file(b"PK...") is True assert ValidationUtils.is_docx_file(b"Hello") is False assert ValidationUtils.is_docx_file(b"") is False def test_is_valid_file_size(self): """Test file size validation.""" assert ValidationUtils.is_valid_file_size(1024) is True assert ValidationUtils.is_valid_file_size(100 * 1024 * 1024) is True # 100MB assert ValidationUtils.is_valid_file_size(0) is False # Zero not allowed assert ValidationUtils.is_valid_file_size(-1) is False # Negative not allowed assert ValidationUtils.is_valid_file_size(200 * 1024 * 1024) is False # Over 100MB # Test custom max size assert ValidationUtils.is_valid_file_size(5 * 1024 * 1024, max_size=10 * 1024 * 1024) is True assert ValidationUtils.is_valid_file_size(15 * 1024 * 1024, max_size=10 * 1024 * 1024) is False def test_sanitize_string(self): """Test string sanitization.""" assert ValidationUtils.sanitize_string(" test ") == "test" assert ValidationUtils.sanitize_string("test", max_length=2) == "te" assert ValidationUtils.sanitize_string(" test ", strip=False) == " test " assert ValidationUtils.sanitize_string("hello world", max_length=5) == "hello" def test_is_valid_paragraph_index(self): """Test paragraph index validation.""" assert ValidationUtils.is_valid_paragraph_index(0, 10) is True assert ValidationUtils.is_valid_paragraph_index(10, 10) is True assert ValidationUtils.is_valid_paragraph_index(5, 10) is True assert ValidationUtils.is_valid_paragraph_index(-1, 10) is False assert ValidationUtils.is_valid_paragraph_index(11, 10) is False def test_is_valid_table_coords(self): """Test table coordinate validation.""" assert ValidationUtils.is_valid_table_coords(0, 0, 5, 5) is True assert ValidationUtils.is_valid_table_coords(4, 4, 5, 5) is True assert ValidationUtils.is_valid_table_coords(-1, 0, 5, 5) is False assert ValidationUtils.is_valid_table_coords(0, -1, 5, 5) is False assert ValidationUtils.is_valid_table_coords(5, 5, 5, 5) is False # Out of bounds def test_validate_font_size(self): """Test font size validation.""" assert ValidationUtils.validate_font_size(12) is True assert ValidationUtils.validate_font_size(1) is True assert ValidationUtils.validate_font_size(999) is True assert ValidationUtils.validate_font_size(0) is False assert ValidationUtils.validate_font_size(1000) is False assert ValidationUtils.validate_font_size(-5) is False def test_validate_margin(self): """Test margin validation.""" assert ValidationUtils.validate_margin(1.0) is True assert ValidationUtils.validate_margin(0) is True assert ValidationUtils.validate_margin(10) is True assert ValidationUtils.validate_margin(-0.1) is False assert ValidationUtils.validate_margin(10.1) is False def test_validate_required_fields(self): """Test required fields validation.""" data = {"name": "test", "email": "test@example.com"} missing = ValidationUtils.validate_required_fields(data, ["name", "email"]) assert len(missing) == 0 missing = ValidationUtils.validate_required_fields(data, ["name", "email", "phone"]) assert "phone" in missing data_with_none = {"name": "test", "email": None} missing = ValidationUtils.validate_required_fields(data_with_none, ["name", "email"]) assert "email" in missing class TestConversionUtils: """Test cases for ConversionUtils class.""" def test_inches_to_cm(self): """Test inches to cm conversion.""" result = ConversionUtils.inches_to_cm(1) assert abs(result - 2.54) < 0.01 assert ConversionUtils.inches_to_cm(0) == 0 def test_cm_to_inches(self): """Test cm to inches conversion.""" result = ConversionUtils.cm_to_inches(2.54) assert abs(result - 1) < 0.01 assert ConversionUtils.cm_to_inches(0) == 0 def test_hex_to_rgb(self): """Test hex to RGB conversion.""" r, g, b = ConversionUtils.hex_to_rgb("#FF0000") assert (r, g, b) == (255, 0, 0) r, g, b = ConversionUtils.hex_to_rgb("00FF00") assert (r, g, b) == (0, 255, 0) # Test 3-char hex r, g, b = ConversionUtils.hex_to_rgb("#FFF") assert (r, g, b) == (255, 255, 255) def test_rgb_to_hex(self): """Test RGB to hex conversion.""" assert ConversionUtils.rgb_to_hex(255, 0, 0) == "#ff0000" assert ConversionUtils.rgb_to_hex(0, 255, 0) == "#00ff00" assert ConversionUtils.rgb_to_hex(0, 0, 0) == "#000000" def test_bytes_to_human_readable(self): """Test bytes to human readable conversion.""" assert "1.00 KB" in ConversionUtils.bytes_to_human_readable(1024) assert "1.00 MB" in ConversionUtils.bytes_to_human_readable(1024 * 1024) assert "B" in ConversionUtils.bytes_to_human_readable(500) def test_points_to_inches(self): """Test points to inches conversion.""" result = ConversionUtils.points_to_inches(72) assert abs(result - 1) < 0.01 def test_inches_to_points(self): """Test inches to points conversion.""" result = ConversionUtils.inches_to_points(1) assert abs(result - 72) < 0.01 def test_emu_to_inches(self): """Test EMU to inches conversion.""" result = ConversionUtils.emu_to_inches(914400) assert abs(result - 1) < 0.01 def test_inches_to_emu(self): """Test inches to EMU conversion.""" result = ConversionUtils.inches_to_emu(1) assert result == 914400 def test_twips_to_inches(self): """Test twips to inches conversion.""" result = ConversionUtils.twips_to_inches(1440) assert abs(result - 1) < 0.01 def test_inches_to_twips(self): """Test inches to twips conversion.""" result = ConversionUtils.inches_to_twips(1) assert result == 1440 def test_to_docx_inches(self): """Test conversion to docx Inches object.""" from docx.shared import Inches result = ConversionUtils.to_docx_inches(1) assert isinstance(result, Inches) def test_to_docx_pt(self): """Test conversion to docx Pt object.""" from docx.shared import Pt result = ConversionUtils.to_docx_pt(12) assert isinstance(result, Pt) def test_to_docx_cm(self): """Test conversion to docx Cm object.""" from docx.shared import Cm result = ConversionUtils.to_docx_cm(2.54) assert isinstance(result, Cm) def test_dict_to_xml_safe(self): """Test converting dict to XML-safe strings.""" data = { "string": "test", "number": 123, "boolean_true": True, "boolean_false": False, "none": None, } result = ConversionUtils.dict_to_xml_safe(data) assert result["string"] == "test" assert result["number"] == "123" assert result["boolean_true"] == "true" assert result["boolean_false"] == "false" assert result["none"] == "" class TestSecurityUtils: """Test cases for SecurityUtils class.""" def test_hash_and_verify_password(self): """Test password hashing and verification.""" password = "test_password_123" hashed = SecurityUtils.hash_password(password) assert hashed != password assert SecurityUtils.verify_password(password, hashed) is True assert SecurityUtils.verify_password("wrong_password", hashed) is False def test_generate_token(self): """Test token generation.""" token = SecurityUtils.generate_token(16) assert len(token) == 32 # Hex representation is 2x length assert token.isalnum() # Test different lengths token2 = SecurityUtils.generate_token(64) assert len(token2) == 128 def test_generate_api_key(self): """Test API key generation.""" api_key = SecurityUtils.generate_api_key() assert api_key.startswith("docx_") assert len(api_key) > 10 def test_sha256_hash(self): """Test SHA-256 hashing.""" hash1 = SecurityUtils.sha256_hash("test") hash2 = SecurityUtils.sha256_hash("test") hash3 = SecurityUtils.sha256_hash("different") assert hash1 == hash2 assert hash1 != hash3 assert len(hash1) == 64 # Test bytes input hash4 = SecurityUtils.sha256_hash(b"test") assert hash4 == hash1 def test_mask_sensitive_data(self): """Test data masking.""" masked = SecurityUtils.mask_sensitive_data("1234567890", visible_chars=4) assert masked == "1234******" # Test short string masked2 = SecurityUtils.mask_sensitive_data("123", visible_chars=4) assert masked2 == "***" # Test custom mask char masked3 = SecurityUtils.mask_sensitive_data("1234567890", visible_chars=4, mask_char="X") assert masked3 == "1234XXXXXX" def test_is_strong_password(self): """Test password strength validation.""" is_strong, issues = SecurityUtils.is_strong_password("Weak") assert is_strong is False assert len(issues) > 0 is_strong, issues = SecurityUtils.is_strong_password("Strong@Password1") assert is_strong is True assert len(issues) == 0 # Test specific requirements is_strong, issues = SecurityUtils.is_strong_password("short") assert "at least 8 characters" in issues[0].lower() is_strong, issues = SecurityUtils.is_strong_password("nouppercase123!") assert any("uppercase" in issue.lower() for issue in issues) def test_generate_url_safe_token(self): """Test URL-safe token generation.""" token = SecurityUtils.generate_url_safe_token(32) assert len(token) > 0 # URL-safe tokens should not contain +, /, or = assert "+" not in token or "/" not in token def test_sha512_hash(self): """Test SHA-512 hashing.""" hash1 = SecurityUtils.sha512_hash("test") hash2 = SecurityUtils.sha512_hash(b"test") assert len(hash1) == 128 # SHA-512 produces 128 hex chars assert hash1 == hash2 def test_hmac_sign(self): """Test HMAC signing.""" data = "test data" key = "secret key" signature = SecurityUtils.hmac_sign(data, key) assert len(signature) == 64 # SHA-256 HMAC # Same data and key should produce same signature signature2 = SecurityUtils.hmac_sign(data, key) assert signature == signature2 # Different key should produce different signature signature3 = SecurityUtils.hmac_sign(data, "different key") assert signature != signature3 def test_verify_hmac(self): """Test HMAC verification.""" data = "test data" key = "secret key" signature = SecurityUtils.hmac_sign(data, key) assert SecurityUtils.verify_hmac(data, signature, key) is True assert SecurityUtils.verify_hmac(data, "wrong signature", key) is False assert SecurityUtils.verify_hmac(data, signature, "wrong key") is False def test_base64_encode_decode(self): """Test base64 encoding/decoding.""" data = b"test data 123" encoded = SecurityUtils.base64_encode(data) decoded = SecurityUtils.base64_decode(encoded) assert isinstance(encoded, str) assert decoded == data def test_constant_time_compare(self): """Test constant-time string comparison.""" assert SecurityUtils.constant_time_compare("test", "test") is True assert SecurityUtils.constant_time_compare("test", "different") is False def test_sanitize_input(self): """Test input sanitization.""" dangerous = "<script>alert('XSS')</script>" sanitized = SecurityUtils.sanitize_input(dangerous) assert "script" not in sanitized.lower() safe = "Normal text" assert SecurityUtils.sanitize_input(safe) == safe # Test various injection patterns assert "javascript" not in SecurityUtils.sanitize_input("javascript:alert(1)").lower() assert "onerror" not in SecurityUtils.sanitize_input("onerror=alert(1)").lower() def test_long_password_truncation(self): """Test that very long passwords are handled correctly.""" # Create a password longer than 72 bytes long_password = "A" * 100 + "B1!" hashed = SecurityUtils.hash_password(long_password) # Should verify successfully assert SecurityUtils.verify_password(long_password, hashed) is True # Test that our truncation is consistent - bcrypt limits to 72 bytes # So two passwords that are identical up to byte 72 will hash the same way # This is expected bcrypt behavior

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Fu-Jie/MCP-OPENAPI-DOCX'

If you have feedback or need assistance with the MCP directory API, please join our Discord server