File size: 8,726 Bytes
d5a003e
71c8aa1
d5a003e
1084ca5
 
 
 
71c8aa1
 
1084ca5
f9a80bc
 
1084ca5
71c8aa1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f9a80bc
71c8aa1
 
 
 
1084ca5
71c8aa1
 
 
1084ca5
71c8aa1
 
f9a80bc
71c8aa1
 
f9a80bc
71c8aa1
 
f9a80bc
71c8aa1
 
f9a80bc
71c8aa1
 
 
f9a80bc
71c8aa1
 
 
f9a80bc
71c8aa1
 
f9a80bc
71c8aa1
 
f9a80bc
71c8aa1
f9a80bc
71c8aa1
 
1084ca5
 
71c8aa1
 
f9a80bc
71c8aa1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f9a80bc
71c8aa1
f9a80bc
71c8aa1
 
 
 
 
 
f9a80bc
71c8aa1
 
f9a80bc
71c8aa1
 
f9a80bc
71c8aa1
 
1084ca5
71c8aa1
1084ca5
71c8aa1
f9a80bc
71c8aa1
f9a80bc
71c8aa1
 
f9a80bc
71c8aa1
 
f9a80bc
71c8aa1
 
 
 
 
 
 
f9a80bc
71c8aa1
f9a80bc
71c8aa1
 
 
 
 
 
f9a80bc
 
71c8aa1
 
1084ca5
71c8aa1
 
1084ca5
71c8aa1
 
f9a80bc
71c8aa1
 
 
f9a80bc
71c8aa1
 
f9a80bc
71c8aa1
 
f9a80bc
71c8aa1
 
 
 
f9a80bc
71c8aa1
 
f9a80bc
71c8aa1
 
f9a80bc
71c8aa1
 
 
f9a80bc
71c8aa1
 
f9a80bc
71c8aa1
 
f9a80bc
71c8aa1
 
 
f9a80bc
71c8aa1
 
f9a80bc
71c8aa1
 
1084ca5
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
"""
Unit tests for the linkedin_resume module.
"""

import unittest
import tempfile
import os
from pathlib import Path
from functions import linkedin_resume

# pylint: disable=protected-access


class TestExtractText(unittest.TestCase):
    """Test cases for the extract_text function."""

    def test_extract_text_with_real_pdf(self):
        """Test text extraction using the actual test PDF file."""
        # Get path to the test PDF file
        test_pdf_path = Path(__file__).parent / "test_data" / "linkedin_profile.pdf"

        # Verify the test file exists
        self.assertTrue(test_pdf_path.exists(), f"Test PDF file not found: {test_pdf_path}")

        # Call extract_text with the real PDF
        result = linkedin_resume.extract_text(str(test_pdf_path))

        # Verify we get a result (should be a dict with sections)
        if result is not None:
            self.assertIsInstance(result, dict)
            # Check that we have at least some content
            self.assertGreater(len(result), 0)
            # Each value should be a string
            for _, content in result.items():
                self.assertIsInstance(content, str)
        else:
            # If result is None, it means the PDF couldn't be processed
            # This might happen with some PDF formats, which is acceptable
            self.assertIsNone(result)

    def test_extract_text_success(self):
        """Test successful text extraction from the actual test PDF file."""
        # Get path to the test PDF file
        test_pdf_path = Path(__file__).parent / "test_data" / "linkedin_profile.pdf"

        # Verify the test file exists
        self.assertTrue(test_pdf_path.exists(), f"Test PDF file not found: {test_pdf_path}")

        # Call extract_text with the real PDF
        result = linkedin_resume.extract_text(str(test_pdf_path))

        # Verify we get a result (should be a dict with sections)
        if result is not None:
            self.assertIsInstance(result, dict)

            # Check that we have at least some content
            self.assertGreater(len(result), 0)

            # Each value should be a string
            for section_name, content in result.items():
                self.assertIsInstance(content, str)
                self.assertGreater(
                    len(content.strip()),
                    0,
                    f"Section {section_name} should have content"
                )

        else:
            # If result is None, it means the PDF couldn't be processed
            # This might happen with some PDF formats, which is acceptable
            self.assertIsNone(result)

    def test_extract_text_with_invalid_pdf(self):
        """Test handling of invalid PDF content by creating a temporary invalid file."""

        # Create a temporary file with invalid content
        with tempfile.NamedTemporaryFile(mode='w', suffix='.pdf', delete=False) as temp_file:
            temp_file.write("This is not a valid PDF file")
            temp_path = temp_file.name

        try:
            # This should return None due to invalid PDF format
            result = linkedin_resume.extract_text(temp_path)
            self.assertIsNone(result)

        finally:
            # Clean up the temporary file
            os.unlink(temp_path)

    def test_extract_text_parsing_behavior(self):
        """Test text extraction and parsing with the real PDF file."""

        # Get path to the test PDF file
        test_pdf_path = Path(__file__).parent / "test_data" / "linkedin_profile.pdf"

        # Verify the test file exists
        self.assertTrue(test_pdf_path.exists(), f"Test PDF file not found: {test_pdf_path}")

        # Call extract_text with the real PDF
        result = linkedin_resume.extract_text(str(test_pdf_path))

        # Test the parsing behavior - if we get a result, it should be structured properly
        if result is not None:
            self.assertIsInstance(result, dict)

            # If we have content, verify it's been parsed into logical sections
            for _, content in result.items():
                self.assertIsInstance(content, str)

                # Content should be cleaned (no excessive whitespace at start/end)
                self.assertEqual(content, content.strip())

    def test_extract_text_file_not_found(self):
        """Test handling when file doesn't exist."""

        result = linkedin_resume.extract_text("/nonexistent/file.pdf")

        # Should return None when file not found
        self.assertIsNone(result)


class TestParseResumeText(unittest.TestCase):
    """Test cases for the _parse_resume_text function."""

    def test_parse_with_sections(self):
        """Test parsing text with recognizable sections."""
        text = """
        Contact Information
        John Doe
        john@example.com
        
        Summary
        Experienced software engineer with 5 years experience
        
        Experience
        Software Engineer at Tech Company
        Built web applications
        
        Skills
        Python, JavaScript, React
        
        Education
        Bachelor's in Computer Science
        University of Technology
        """

        result = linkedin_resume._parse_resume_text(text)

        self.assertIsInstance(result, dict)
        self.assertIn("contact_info", result)
        self.assertIn("summary", result)
        self.assertIn("experience", result)
        self.assertIn("skills", result)
        self.assertIn("education", result)

    def test_parse_empty_text(self):
        """Test parsing empty or None text."""

        self.assertIsNone(linkedin_resume._parse_resume_text(""))
        self.assertIsNone(linkedin_resume._parse_resume_text(None))

    def test_parse_text_no_sections(self):
        """Test parsing text without recognizable sections."""

        text = "Just some random text without any section headers"

        result = linkedin_resume._parse_resume_text(text)

        self.assertIsInstance(result, dict)

        # Should still return a dict with at least the general section
        self.assertIn("general", result)

    def test_parse_calls_clean_section(self):
        """Test that parsing calls _clean_section on each section using real text processing."""

        text = """
        Summary
        Some summary text with   extra    spaces
        
        Experience
        Some experience text
        """

        result = linkedin_resume._parse_resume_text(text)

        # Should be called and content should be cleaned
        if result:
            for _, content in result.items():
                # Verify that cleaning has occurred (no excessive spaces)
                self.assertNotIn("   ", content)  # No triple spaces should remain
                self.assertEqual(content, content.strip())  # Should be stripped


class TestCleanSection(unittest.TestCase):
    """Test cases for the _clean_section function."""

    def test_clean_unicode_normalization(self):
        """Test unicode normalization."""

        text = "Café résumé naïve"  # Text with accented characters
        result = linkedin_resume._clean_section(text)

        # Should normalize unicode characters
        self.assertIsInstance(result, str)
        self.assertNotEqual(result, "")

    def test_clean_remove_page_numbers(self):
        """Test removal of LinkedIn page numbers."""

        text = "Some content\nPage 1 of 3\nMore content"
        result = linkedin_resume._clean_section(text)

        # Should remove page indicators
        self.assertNotIn("Page 1 of 3", result)
        self.assertIn("Some content", result)
        self.assertIn("More content", result)

    def test_clean_calls_whitespace_cleaner(self):
        """Test that _clean_section properly cleans whitespace."""

        text = "Some  text  with   spaces"
        result = linkedin_resume._clean_section(text)

        # Should clean multiple spaces to single spaces
        self.assertNotIn("  ", result)  # No double spaces should remain
        self.assertIn("Some text with spaces", result)  # Should have single spaces

    def test_clean_strip_whitespace(self):
        """Test stripping leading/trailing whitespace."""

        text = "   Some content   "
        result = linkedin_resume._clean_section(text)

        # Should strip leading and trailing whitespace
        self.assertFalse(result.startswith(" "))
        self.assertFalse(result.endswith(" "))

    def test_clean_empty_input(self):
        """Test handling of empty input."""

        self.assertEqual(linkedin_resume._clean_section(""), "")
        self.assertEqual(linkedin_resume._clean_section("   "), "")


if __name__ == '__main__':
    unittest.main()