|
| 1 | +"""Unit tests for the OutputProcessor.""" |
| 2 | + |
| 3 | +import pytest |
| 4 | +from src.services.execution.output import OutputProcessor |
| 5 | + |
| 6 | + |
| 7 | +class TestSanitizeFilename: |
| 8 | + """Tests for the sanitize_filename method.""" |
| 9 | + |
| 10 | + def test_spaces_replaced_with_underscores(self): |
| 11 | + """Test that spaces are replaced with underscores.""" |
| 12 | + result = OutputProcessor.sanitize_filename("file with spaces.txt") |
| 13 | + assert result == "file_with_spaces.txt" |
| 14 | + |
| 15 | + def test_parentheses_replaced_with_underscores(self): |
| 16 | + """Test that parentheses are replaced with underscores.""" |
| 17 | + result = OutputProcessor.sanitize_filename("manufacturing_analysis (v2).xlsx") |
| 18 | + assert result == "manufacturing_analysis__v2_.xlsx" |
| 19 | + |
| 20 | + def test_special_characters_replaced(self): |
| 21 | + """Test that special characters are replaced with underscores.""" |
| 22 | + result = OutputProcessor.sanitize_filename("special@chars#here!.pdf") |
| 23 | + assert result == "special_chars_here_.pdf" |
| 24 | + |
| 25 | + def test_already_valid_unchanged(self): |
| 26 | + """Test that already valid filenames are unchanged.""" |
| 27 | + result = OutputProcessor.sanitize_filename("already-valid.txt") |
| 28 | + assert result == "already-valid.txt" |
| 29 | + |
| 30 | + def test_uppercase_preserved(self): |
| 31 | + """Test that uppercase letters are preserved.""" |
| 32 | + result = OutputProcessor.sanitize_filename("UPPERCASE.TXT") |
| 33 | + assert result == "UPPERCASE.TXT" |
| 34 | + |
| 35 | + def test_numbers_preserved(self): |
| 36 | + """Test that numbers are preserved.""" |
| 37 | + result = OutputProcessor.sanitize_filename("123numbers.doc") |
| 38 | + assert result == "123numbers.doc" |
| 39 | + |
| 40 | + def test_hidden_file_prefixed(self): |
| 41 | + """Test that hidden files (starting with dot) get underscore prefix.""" |
| 42 | + result = OutputProcessor.sanitize_filename(".hidden") |
| 43 | + assert result == "_.hidden" |
| 44 | + |
| 45 | + def test_empty_string_returns_underscore(self): |
| 46 | + """Test that empty string returns underscore.""" |
| 47 | + result = OutputProcessor.sanitize_filename("") |
| 48 | + assert result == "_" |
| 49 | + |
| 50 | + def test_none_returns_underscore(self): |
| 51 | + """Test that None returns underscore.""" |
| 52 | + result = OutputProcessor.sanitize_filename(None) |
| 53 | + assert result == "_" |
| 54 | + |
| 55 | + def test_path_traversal_stripped(self): |
| 56 | + """Test that path traversal attempts are stripped.""" |
| 57 | + result = OutputProcessor.sanitize_filename("../../../etc/passwd") |
| 58 | + assert result == "passwd" |
| 59 | + |
| 60 | + def test_absolute_path_stripped(self): |
| 61 | + """Test that absolute paths are stripped to basename.""" |
| 62 | + result = OutputProcessor.sanitize_filename("/absolute/path/file.txt") |
| 63 | + assert result == "file.txt" |
| 64 | + |
| 65 | + def test_unicode_characters_replaced(self): |
| 66 | + """Test that non-ASCII characters are replaced.""" |
| 67 | + result = OutputProcessor.sanitize_filename("résumé.docx") |
| 68 | + assert result == "r_sum_.docx" |
| 69 | + |
| 70 | + def test_brackets_replaced(self): |
| 71 | + """Test that brackets are replaced with underscores.""" |
| 72 | + result = OutputProcessor.sanitize_filename("[brackets].txt") |
| 73 | + assert result == "_brackets_.txt" |
| 74 | + |
| 75 | + def test_leading_parenthesis_prefixed(self): |
| 76 | + """Test that filename starting with parenthesis is handled.""" |
| 77 | + result = OutputProcessor.sanitize_filename("(parentheses).txt") |
| 78 | + assert result == "_parentheses_.txt" |
| 79 | + |
| 80 | + def test_dashes_preserved(self): |
| 81 | + """Test that dashes are preserved.""" |
| 82 | + result = OutputProcessor.sanitize_filename("file-name.with-dashes.txt") |
| 83 | + assert result == "file-name.with-dashes.txt" |
| 84 | + |
| 85 | + def test_dots_preserved(self): |
| 86 | + """Test that dots in filename are preserved.""" |
| 87 | + result = OutputProcessor.sanitize_filename("file.name.multiple.dots.txt") |
| 88 | + assert result == "file.name.multiple.dots.txt" |
| 89 | + |
| 90 | + def test_simple_filename_unchanged(self): |
| 91 | + """Test that simple alphanumeric filename is unchanged.""" |
| 92 | + result = OutputProcessor.sanitize_filename("SimpleFile123.pdf") |
| 93 | + assert result == "SimpleFile123.pdf" |
| 94 | + |
| 95 | + def test_long_filename_truncated(self): |
| 96 | + """Test that filenames over 255 chars are truncated with hash suffix.""" |
| 97 | + long_name = "a" * 300 + ".txt" |
| 98 | + result = OutputProcessor.sanitize_filename(long_name) |
| 99 | + # Should be 255 chars or less |
| 100 | + assert len(result) <= 255 |
| 101 | + # Should end with .txt |
| 102 | + assert result.endswith(".txt") |
| 103 | + # Should have a random suffix before extension |
| 104 | + assert "-" in result |
| 105 | + |
| 106 | + |
| 107 | +class TestNormalizeFilename: |
| 108 | + """Tests for the deprecated normalize_filename method.""" |
| 109 | + |
| 110 | + def test_delegates_to_sanitize_filename(self): |
| 111 | + """Test that normalize_filename delegates to sanitize_filename.""" |
| 112 | + result = OutputProcessor.normalize_filename("file with spaces.txt") |
| 113 | + expected = OutputProcessor.sanitize_filename("file with spaces.txt") |
| 114 | + assert result == expected |
| 115 | + |
| 116 | + def test_parentheses_now_replaced(self): |
| 117 | + """Test that normalize_filename now also replaces parentheses.""" |
| 118 | + result = OutputProcessor.normalize_filename("file (v2).xlsx") |
| 119 | + assert result == "file__v2_.xlsx" |
0 commit comments