gperdrizet commited on
Commit
a974c1c
·
verified ·
1 Parent(s): 00268c0

Added mocking to test to avoid API calls where possible

Browse files
functions/writer_agent.py CHANGED
@@ -187,7 +187,8 @@ def _write_projects_section(project_repos: list, job_call: dict) -> str:
187
  },
188
  {
189
  'role': 'user',
190
- 'content': f'JOB CALL\n{json.dumps(job_call)}\n\nREPOSITORIES\n{json.dumps(project_repos)}'
 
191
  }
192
  ]
193
 
@@ -206,4 +207,4 @@ def _write_projects_section(project_repos: list, job_call: dict) -> str:
206
  if response is not None:
207
  response = response.choices[0].message.content
208
 
209
- return response
 
187
  },
188
  {
189
  'role': 'user',
190
+ 'content': (f'JOB CALL\n{json.dumps(job_call)}\n\n' +
191
+ f'REPOSITORIES\n{json.dumps(project_repos)}')
192
  }
193
  ]
194
 
 
207
  if response is not None:
208
  response = response.choices[0].message.content
209
 
210
+ return response
tests/test_gradio.py CHANGED
@@ -22,24 +22,45 @@ class TestProcessInputs(unittest.TestCase):
22
  self.skipTest(f"Test PDF file not found: {test_pdf_path}")
23
 
24
  with patch('functions.gradio.extract_text') as mock_extract, \
25
- patch('functions.gradio.get_github_repositories') as mock_github:
 
 
26
 
27
  mock_extract.return_value = {"test": "data"}
28
  mock_github.return_value = [{"name": "test-repo"}]
 
 
29
 
30
  result = gradio.process_inputs(
31
  linkedin_pdf_path=str(test_pdf_path),
32
  github_username="testuser",
33
- job_post_text="Software engineer position",
34
- user_instructions="Custom instructions"
35
  )
36
 
37
- # Function currently returns empty string
38
- self.assertEqual(result, "")
 
 
 
 
 
 
 
 
 
 
39
 
 
 
40
  @patch('functions.gradio.extract_text')
41
  @patch('functions.gradio.get_github_repositories')
42
- def test_process_inputs_with_pdf_path_mocked(self, mock_github, mock_extract):
 
 
 
 
 
 
43
  """Test process_inputs with a PDF file path (mocked for controlled testing)."""
44
 
45
  # Mock successful LinkedIn text extraction
@@ -49,12 +70,16 @@ class TestProcessInputs(unittest.TestCase):
49
  "experience": "Software Engineer at Company"
50
  }
51
  mock_github.return_value = [{"name": "test-repo"}]
 
 
 
 
 
52
 
53
  result = gradio.process_inputs(
54
  linkedin_pdf_path="/path/to/resume.pdf",
55
  github_username="testuser",
56
- job_post_text="Software engineer position",
57
- user_instructions="Custom instructions"
58
  )
59
 
60
  # Verify extract_text was called with the correct path
@@ -63,125 +88,287 @@ class TestProcessInputs(unittest.TestCase):
63
  # Verify get_github_repositories was called with username
64
  mock_github.assert_called_once_with("testuser")
65
 
66
- # Function currently returns empty string
67
- self.assertEqual(result, "")
 
 
 
68
 
 
 
 
 
 
69
  @patch('functions.gradio.extract_text')
70
  @patch('functions.gradio.get_github_repositories')
71
- def test_process_inputs_extraction_failure(self, mock_github, mock_extract):
 
 
 
 
 
72
  """Test process_inputs when LinkedIn extraction fails."""
73
 
74
  # Mock failed LinkedIn text extraction
75
  mock_extract.return_value = None
76
  mock_github.return_value = None
 
77
 
78
  result = gradio.process_inputs(
79
  linkedin_pdf_path="/path/to/resume.pdf",
80
  github_username="testuser",
81
- job_post_text="Software engineer position",
82
- user_instructions="Custom instructions"
83
  )
84
 
85
  # Verify extract_text was called
86
  mock_extract.assert_called_once_with("/path/to/resume.pdf")
87
  mock_github.assert_called_once_with("testuser")
 
 
 
 
88
 
89
- # Function currently returns empty string
90
  self.assertEqual(result, "")
91
 
 
 
92
  @patch('functions.gradio.extract_text')
93
  @patch('functions.gradio.get_github_repositories')
94
- def test_process_inputs_no_pdf_path(self, mock_github, mock_extract):
 
 
 
 
 
 
95
  """Test process_inputs with no PDF path provided."""
 
96
  mock_extract.return_value = None
97
  mock_github.return_value = []
 
98
 
99
  result = gradio.process_inputs(
100
  linkedin_pdf_path=None,
101
  github_username="testuser",
102
- job_post_text="Software engineer position",
103
- user_instructions="Custom instructions"
104
  )
105
 
106
  # extract_text should be called with None
107
  mock_extract.assert_called_once_with(None)
108
  mock_github.assert_called_once_with("testuser")
 
109
 
110
- # Function currently returns empty string
 
 
 
111
  self.assertEqual(result, "")
112
 
 
 
113
  @patch('functions.gradio.extract_text')
114
  @patch('functions.gradio.get_github_repositories')
115
- def test_process_inputs_with_long_job_post(self, mock_github, mock_extract):
 
 
 
 
 
 
116
  """Test process_inputs with a long job post text (for logging truncation)."""
117
 
118
  mock_extract.return_value = {
119
  "summary": "Test summary"
120
  }
121
  mock_github.return_value = []
 
122
 
123
  long_job_post = "This is a very long job posting " * 50 # Make it longer than 100 chars
124
 
125
  result = gradio.process_inputs(
126
  linkedin_pdf_path="/path/to/resume.pdf",
127
  github_username="testuser",
128
- job_post_text=long_job_post,
129
- user_instructions="Custom instructions"
130
  )
131
 
132
  # Verify extract_text was called
133
  mock_extract.assert_called_once_with("/path/to/resume.pdf")
134
  mock_github.assert_called_once_with("testuser")
 
 
 
 
135
 
136
- # Function currently returns empty string
137
  self.assertEqual(result, "")
138
 
 
 
139
  @patch('functions.gradio.extract_text')
140
  @patch('functions.gradio.get_github_repositories')
141
- def test_process_inputs_github_username_whitespace(self, mock_github, mock_extract):
 
 
 
 
 
 
142
  """Test that github_username is properly stripped of whitespace."""
143
 
144
  mock_extract.return_value = None
145
  mock_github.return_value = []
 
146
 
147
  result = gradio.process_inputs(
148
  linkedin_pdf_path=None,
149
  github_username=" testuser ",
150
- job_post_text="",
151
- user_instructions=""
152
  )
153
 
154
  # Verify get_github_repositories was called with stripped username
155
  mock_github.assert_called_once_with("testuser")
 
156
  self.assertEqual(result, "")
157
 
 
 
158
  @patch('functions.gradio.extract_text')
159
  @patch('functions.gradio.get_github_repositories')
160
  @patch('logging.getLogger')
161
- def test_logging_calls(self, mock_get_logger, mock_github, mock_extract):
 
 
 
 
 
 
 
162
  """Test that appropriate logging calls are made."""
163
 
164
  mock_logger = mock_get_logger.return_value
165
  mock_extract.return_value = {"test": "data"}
166
  mock_github.return_value = [{"name": "repo"}]
 
 
167
 
168
- gradio.process_inputs(
169
  linkedin_pdf_path="/path/to/resume.pdf",
170
  github_username="testuser",
171
- job_post_text="Job description here",
172
- user_instructions="Custom instructions"
173
  )
174
 
175
  # Verify logging calls were made
176
  mock_logger.info.assert_called()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
 
 
 
178
  @patch('functions.gradio.extract_text')
179
  @patch('functions.gradio.get_github_repositories')
180
- def test_process_inputs_none_github_username(self, mock_github, mock_extract):
 
 
 
 
 
 
181
  """Test process_inputs with None github_username (should handle gracefully)."""
182
 
183
  mock_extract.return_value = None
184
  mock_github.return_value = None
 
185
 
186
  # This should raise a TypeError due to the bug in gradio.py
187
  # where it tries to slice job_post_text[:100] when job_post_text is None
@@ -189,8 +376,7 @@ class TestProcessInputs(unittest.TestCase):
189
  gradio.process_inputs(
190
  linkedin_pdf_path=None,
191
  github_username=None,
192
- job_post_text=None,
193
- user_instructions=None
194
  )
195
 
196
 
 
22
  self.skipTest(f"Test PDF file not found: {test_pdf_path}")
23
 
24
  with patch('functions.gradio.extract_text') as mock_extract, \
25
+ patch('functions.gradio.get_github_repositories') as mock_github, \
26
+ patch('functions.gradio.summarize_job_call') as mock_job_call, \
27
+ patch('functions.gradio.write_resume') as mock_write_resume:
28
 
29
  mock_extract.return_value = {"test": "data"}
30
  mock_github.return_value = [{"name": "test-repo"}]
31
+ mock_job_call.return_value = {"title": "Software Engineer", "requirements": ["Python"]}
32
+ mock_write_resume.return_value = "# Generated Resume\n\nTest resume content"
33
 
34
  result = gradio.process_inputs(
35
  linkedin_pdf_path=str(test_pdf_path),
36
  github_username="testuser",
37
+ job_post_text="Software engineer position"
 
38
  )
39
 
40
+ # Verify all functions were called
41
+ mock_extract.assert_called_once_with(str(test_pdf_path))
42
+ mock_github.assert_called_once_with("testuser")
43
+ mock_job_call.assert_called_once_with("Software engineer position")
44
+ mock_write_resume.assert_called_once_with(
45
+ {"test": "data"},
46
+ [{"name": "test-repo"}],
47
+ {"title": "Software Engineer", "requirements": ["Python"]}
48
+ )
49
+
50
+ # Function should return the generated resume
51
+ self.assertEqual(result, "# Generated Resume\n\nTest resume content")
52
 
53
+ @patch('functions.gradio.write_resume')
54
+ @patch('functions.gradio.summarize_job_call')
55
  @patch('functions.gradio.extract_text')
56
  @patch('functions.gradio.get_github_repositories')
57
+ def test_process_inputs_with_pdf_path_mocked(
58
+ self,
59
+ mock_github,
60
+ mock_extract,
61
+ mock_job_call,
62
+ mock_write_resume
63
+ ):
64
  """Test process_inputs with a PDF file path (mocked for controlled testing)."""
65
 
66
  # Mock successful LinkedIn text extraction
 
70
  "experience": "Software Engineer at Company"
71
  }
72
  mock_github.return_value = [{"name": "test-repo"}]
73
+ mock_job_call.return_value = {
74
+ "title": "Software Engineer",
75
+ "requirements": ["Python", "JavaScript"]
76
+ }
77
+ mock_write_resume.return_value = "# John Doe\n\n## Summary\nExperienced software engineer"
78
 
79
  result = gradio.process_inputs(
80
  linkedin_pdf_path="/path/to/resume.pdf",
81
  github_username="testuser",
82
+ job_post_text="Software engineer position"
 
83
  )
84
 
85
  # Verify extract_text was called with the correct path
 
88
  # Verify get_github_repositories was called with username
89
  mock_github.assert_called_once_with("testuser")
90
 
91
+ # Verify job post was processed
92
+ mock_job_call.assert_called_once_with("Software engineer position")
93
+
94
+ # Verify resume generation was called with correct arguments
95
+ mock_write_resume.assert_called_once()
96
 
97
+ # Function should return the generated resume content
98
+ self.assertEqual(result, "# John Doe\n\n## Summary\nExperienced software engineer")
99
+
100
+ @patch('functions.gradio.write_resume')
101
+ @patch('functions.gradio.summarize_job_call')
102
  @patch('functions.gradio.extract_text')
103
  @patch('functions.gradio.get_github_repositories')
104
+ def test_process_inputs_extraction_failure(
105
+ self, mock_github,
106
+ mock_extract,
107
+ mock_job_call,
108
+ mock_write_resume
109
+ ):
110
  """Test process_inputs when LinkedIn extraction fails."""
111
 
112
  # Mock failed LinkedIn text extraction
113
  mock_extract.return_value = None
114
  mock_github.return_value = None
115
+ mock_job_call.return_value = None
116
 
117
  result = gradio.process_inputs(
118
  linkedin_pdf_path="/path/to/resume.pdf",
119
  github_username="testuser",
120
+ job_post_text="Software engineer position"
 
121
  )
122
 
123
  # Verify extract_text was called
124
  mock_extract.assert_called_once_with("/path/to/resume.pdf")
125
  mock_github.assert_called_once_with("testuser")
126
+ mock_job_call.assert_called_once_with("Software engineer position")
127
+
128
+ # write_resume should NOT be called when data is missing
129
+ mock_write_resume.assert_not_called()
130
 
131
+ # Function should return empty string when processing fails
132
  self.assertEqual(result, "")
133
 
134
+ @patch('functions.gradio.write_resume')
135
+ @patch('functions.gradio.summarize_job_call')
136
  @patch('functions.gradio.extract_text')
137
  @patch('functions.gradio.get_github_repositories')
138
+ def test_process_inputs_no_pdf_path(
139
+ self,
140
+ mock_github,
141
+ mock_extract,
142
+ mock_job_call,
143
+ mock_write_resume
144
+ ):
145
  """Test process_inputs with no PDF path provided."""
146
+
147
  mock_extract.return_value = None
148
  mock_github.return_value = []
149
+ mock_job_call.return_value = {"title": "Software Engineer"}
150
 
151
  result = gradio.process_inputs(
152
  linkedin_pdf_path=None,
153
  github_username="testuser",
154
+ job_post_text="Software engineer position"
 
155
  )
156
 
157
  # extract_text should be called with None
158
  mock_extract.assert_called_once_with(None)
159
  mock_github.assert_called_once_with("testuser")
160
+ mock_job_call.assert_called_once_with("Software engineer position")
161
 
162
+ # write_resume should NOT be called when LinkedIn data is missing
163
+ mock_write_resume.assert_not_called()
164
+
165
+ # Function should return empty string when data is insufficient
166
  self.assertEqual(result, "")
167
 
168
+ @patch('functions.gradio.write_resume')
169
+ @patch('functions.gradio.summarize_job_call')
170
  @patch('functions.gradio.extract_text')
171
  @patch('functions.gradio.get_github_repositories')
172
+ def test_process_inputs_with_long_job_post(
173
+ self,
174
+ mock_github,
175
+ mock_extract,
176
+ mock_job_call,
177
+ mock_write_resume
178
+ ):
179
  """Test process_inputs with a long job post text (for logging truncation)."""
180
 
181
  mock_extract.return_value = {
182
  "summary": "Test summary"
183
  }
184
  mock_github.return_value = []
185
+ mock_job_call.return_value = {"title": "Software Engineer", "requirements": ["Python"]}
186
 
187
  long_job_post = "This is a very long job posting " * 50 # Make it longer than 100 chars
188
 
189
  result = gradio.process_inputs(
190
  linkedin_pdf_path="/path/to/resume.pdf",
191
  github_username="testuser",
192
+ job_post_text=long_job_post
 
193
  )
194
 
195
  # Verify extract_text was called
196
  mock_extract.assert_called_once_with("/path/to/resume.pdf")
197
  mock_github.assert_called_once_with("testuser")
198
+ mock_job_call.assert_called_once_with(long_job_post.strip())
199
+
200
+ # write_resume should NOT be called when GitHub repos are empty
201
+ mock_write_resume.assert_not_called()
202
 
203
+ # Function should return empty string when GitHub data is missing
204
  self.assertEqual(result, "")
205
 
206
+ @patch('functions.gradio.write_resume')
207
+ @patch('functions.gradio.summarize_job_call')
208
  @patch('functions.gradio.extract_text')
209
  @patch('functions.gradio.get_github_repositories')
210
+ def test_process_inputs_github_username_whitespace(
211
+ self,
212
+ mock_github,
213
+ mock_extract,
214
+ mock_job_call,
215
+ mock_write_resume
216
+ ):
217
  """Test that github_username is properly stripped of whitespace."""
218
 
219
  mock_extract.return_value = None
220
  mock_github.return_value = []
221
+ mock_job_call.return_value = {"title": "Engineer"}
222
 
223
  result = gradio.process_inputs(
224
  linkedin_pdf_path=None,
225
  github_username=" testuser ",
226
+ job_post_text=""
 
227
  )
228
 
229
  # Verify get_github_repositories was called with stripped username
230
  mock_github.assert_called_once_with("testuser")
231
+ mock_write_resume.assert_not_called()
232
  self.assertEqual(result, "")
233
 
234
+ @patch('functions.gradio.write_resume')
235
+ @patch('functions.gradio.summarize_job_call')
236
  @patch('functions.gradio.extract_text')
237
  @patch('functions.gradio.get_github_repositories')
238
  @patch('logging.getLogger')
239
+ def test_logging_calls(
240
+ self,
241
+ mock_get_logger,
242
+ mock_github,
243
+ mock_extract,
244
+ mock_job_call,
245
+ mock_write_resume
246
+ ):
247
  """Test that appropriate logging calls are made."""
248
 
249
  mock_logger = mock_get_logger.return_value
250
  mock_extract.return_value = {"test": "data"}
251
  mock_github.return_value = [{"name": "repo"}]
252
+ mock_job_call.return_value = {"title": "Engineer"}
253
+ mock_write_resume.return_value = "# Resume Content"
254
 
255
+ result = gradio.process_inputs(
256
  linkedin_pdf_path="/path/to/resume.pdf",
257
  github_username="testuser",
258
+ job_post_text="Job description here"
 
259
  )
260
 
261
  # Verify logging calls were made
262
  mock_logger.info.assert_called()
263
+ # Verify resume was generated successfully
264
+ self.assertEqual(result, "# Resume Content")
265
+
266
+ @patch('functions.gradio.write_resume')
267
+ @patch('functions.gradio.summarize_job_call')
268
+ @patch('functions.gradio.extract_text')
269
+ @patch('functions.gradio.get_github_repositories')
270
+ def test_process_inputs_write_resume_exception(
271
+ self,
272
+ mock_github,
273
+ mock_extract,
274
+ mock_job_call,
275
+ mock_write_resume
276
+ ):
277
+ """Test process_inputs when write_resume raises an exception."""
278
+
279
+ mock_extract.return_value = {"test": "data"}
280
+ mock_github.return_value = [{"name": "repo"}]
281
+ mock_job_call.return_value = {"title": "Engineer"}
282
+ mock_write_resume.side_effect = Exception("API Error")
283
+
284
+ result = gradio.process_inputs(
285
+ linkedin_pdf_path="/path/to/resume.pdf",
286
+ github_username="testuser",
287
+ job_post_text="Job description here"
288
+ )
289
+
290
+ # Verify all functions were called
291
+ mock_extract.assert_called_once_with("/path/to/resume.pdf")
292
+ mock_github.assert_called_once_with("testuser")
293
+ mock_job_call.assert_called_once_with("Job description here")
294
+ mock_write_resume.assert_called_once()
295
+
296
+ # Function should return empty string when write_resume fails
297
+ self.assertEqual(result, "")
298
+
299
+ @patch('functions.gradio.write_resume')
300
+ @patch('functions.gradio.summarize_job_call')
301
+ @patch('functions.gradio.extract_text')
302
+ @patch('functions.gradio.get_github_repositories')
303
+ def test_process_inputs_complete_success_flow(
304
+ self,
305
+ mock_github,
306
+ mock_extract,
307
+ mock_job_call,
308
+ mock_write_resume
309
+ ):
310
+ """Test the complete successful flow with all components working."""
311
+
312
+ # Mock all successful responses
313
+ linkedin_data = {
314
+ "contact_info": "Jane Doe, [email protected]",
315
+ "summary": "Senior Python Developer",
316
+ "experience": "5 years experience in Python development"
317
+ }
318
+ github_repos = [
319
+ {"name": "awesome-python-app", "description": "A Python web application"},
320
+ {"name": "data-analysis-tool", "description": "Data analysis with pandas"}
321
+ ]
322
+ job_data = {
323
+ "title": "Senior Python Developer",
324
+ "requirements": ["Python", "Django", "PostgreSQL"],
325
+ "company": "Tech Corp"
326
+ }
327
+ resume_content = (
328
+ "# Jane Doe\n\n## Experience\n"
329
+ "Senior Python Developer with 5 years experience..."
330
+ )
331
+
332
+ mock_extract.return_value = linkedin_data
333
+ mock_github.return_value = github_repos
334
+ mock_job_call.return_value = job_data
335
+ mock_write_resume.return_value = resume_content
336
+
337
+ result = gradio.process_inputs(
338
+ linkedin_pdf_path="/path/to/jane_resume.pdf",
339
+ github_username="jane_dev",
340
+ job_post_text="We are looking for a Senior Python Developer with Django experience..."
341
+ )
342
+
343
+ # Verify all functions were called with correct arguments
344
+ mock_extract.assert_called_once_with("/path/to/jane_resume.pdf")
345
+ mock_github.assert_called_once_with("jane_dev")
346
+ mock_job_call.assert_called_once_with(
347
+ "We are looking for a Senior Python Developer with Django experience..."
348
+ )
349
+ mock_write_resume.assert_called_once_with(linkedin_data, github_repos, job_data)
350
+
351
+ # Verify the complete resume is returned
352
+ self.assertEqual(result, resume_content)
353
+ self.assertIn("Jane Doe", result)
354
+ self.assertIn("Senior Python Developer", result)
355
 
356
+ @patch('functions.gradio.write_resume')
357
+ @patch('functions.gradio.summarize_job_call')
358
  @patch('functions.gradio.extract_text')
359
  @patch('functions.gradio.get_github_repositories')
360
+ def test_process_inputs_none_github_username(
361
+ self,
362
+ mock_github,
363
+ mock_extract,
364
+ mock_job_call,
365
+ mock_write_resume
366
+ ):
367
  """Test process_inputs with None github_username (should handle gracefully)."""
368
 
369
  mock_extract.return_value = None
370
  mock_github.return_value = None
371
+ mock_job_call.return_value = None
372
 
373
  # This should raise a TypeError due to the bug in gradio.py
374
  # where it tries to slice job_post_text[:100] when job_post_text is None
 
376
  gradio.process_inputs(
377
  linkedin_pdf_path=None,
378
  github_username=None,
379
+ job_post_text=None
 
380
  )
381
 
382
 
tests/test_resumate.py CHANGED
@@ -19,8 +19,6 @@ class TestResumeGeneration(unittest.TestCase):
19
  with open('tests/test_data/sample_job.txt', 'r', encoding='utf-8') as f:
20
  self.job_post_text = f.read().strip()
21
 
22
- self.user_instructions = ""
23
-
24
  with open('tests/test_data/github_repos.json', 'r', encoding='utf-8') as f:
25
  self.github_repositories = json.load(f)
26
 
@@ -38,7 +36,6 @@ class TestResumeGeneration(unittest.TestCase):
38
  linkedin_pdf_path=self.linkedin_pdf_path,
39
  github_username=self.github_username,
40
  job_post_text=self.job_post_text,
41
- user_instructions=self.user_instructions
42
  )
43
 
44
  print(result)
 
19
  with open('tests/test_data/sample_job.txt', 'r', encoding='utf-8') as f:
20
  self.job_post_text = f.read().strip()
21
 
 
 
22
  with open('tests/test_data/github_repos.json', 'r', encoding='utf-8') as f:
23
  self.github_repositories = json.load(f)
24
 
 
36
  linkedin_pdf_path=self.linkedin_pdf_path,
37
  github_username=self.github_username,
38
  job_post_text=self.job_post_text,
 
39
  )
40
 
41
  print(result)