gperdrizet commited on
Commit
b91ffb5
Β·
verified Β·
1 Parent(s): 32f0097

Removed default document loading logic

Browse files
functions/gradio.py CHANGED
@@ -5,13 +5,11 @@ Functions for handling Gradio UI interactions and processing user inputs.
5
  """
6
 
7
  import logging
8
- import shutil
9
  from pathlib import Path
10
- from functions.linkedin_resume import extract_text_from_linkedin_pdf, check_default_linkedin_pdf
11
  from functions.github import get_github_repositories
12
- from functions.job_call import load_default_job_call, summarize_job_call
13
  from functions.writer_agent import write_resume
14
- from configuration import DEFAULT_GITHUB_PROFILE
15
 
16
  # pylint: disable=broad-exception-caught
17
 
@@ -20,35 +18,12 @@ logging.basicConfig(level=logging.INFO)
20
  logger = logging.getLogger(__name__)
21
 
22
 
23
- def process_with_default_option(
24
- use_default_pdf,
25
- linkedin_pdf,
26
- github_profile,
27
- job_post,
28
- user_instructions
29
- ):
30
- """Process inputs with consideration for default PDF option."""
31
-
32
- has_default, default_path = check_default_linkedin_pdf()
33
-
34
- # Determine which PDF file to use
35
- pdf_file = None
36
-
37
- if use_default_pdf and has_default:
38
- pdf_file = MockFile(default_path)
39
-
40
- elif linkedin_pdf is not None:
41
- pdf_file = linkedin_pdf
42
-
43
- return process_inputs(pdf_file, github_profile, job_post, user_instructions)
44
-
45
-
46
  def process_inputs(linkedin_pdf, github_url, job_post_text, user_instructions):
47
  """
48
  Process the input files and URLs from the Gradio interface.
49
 
50
  Args:
51
- linkedin_pdf: Uploaded LinkedIn resume export PDF file or mock file object with path
52
  github_url (str): GitHub profile URL
53
  job_post_text (str): Job post text content
54
  user_instructions (str): Additional instructions from the user
@@ -62,33 +37,12 @@ def process_inputs(linkedin_pdf, github_url, job_post_text, user_instructions):
62
 
63
  # Process LinkedIn PDF file
64
  if linkedin_pdf is not None:
65
-
66
- # Handle both file objects and mock file objects with path strings
67
  file_path = linkedin_pdf.name
68
  file_display_name = Path(file_path).name
69
 
70
  result += "βœ… LinkedIn Resume PDF provided\n"
71
  logger.info("Processing LinkedIn PDF: %s", file_display_name)
72
 
73
- # Save uploaded file as new default (only if it's not already the default)
74
- project_root = Path(__file__).parent.parent
75
- default_pdf_path = project_root / "data" / "linkedin_profile.pdf"
76
-
77
- # Check if this is an uploaded file (not the default file)
78
- if not isinstance(linkedin_pdf, MockFile):
79
- try:
80
- # Create data directory if it doesn't exist
81
- default_pdf_path.parent.mkdir(exist_ok=True)
82
-
83
- # Copy uploaded file to default location
84
- shutil.copy2(file_path, default_pdf_path)
85
- result += " βœ… Saved as new default LinkedIn profile\n"
86
- logger.info("Saved uploaded LinkedIn PDF as new default: %s", default_pdf_path)
87
-
88
- except Exception as save_error:
89
- result += f" ⚠️ Could not save as default: {str(save_error)}\n"
90
- logger.warning("Failed to save LinkedIn PDF as default: %s", str(save_error))
91
-
92
  # Extract and structure text from the PDF
93
  extraction_result = extract_text_from_linkedin_pdf(file_path)
94
 
@@ -107,24 +61,12 @@ def process_inputs(linkedin_pdf, github_url, job_post_text, user_instructions):
107
  logger.info("No LinkedIn PDF file provided")
108
 
109
  # Process GitHub profile
110
- # Use default GitHub profile if none provided
111
  if github_url and github_url.strip():
112
- github_url_to_use = github_url.strip()
113
-
114
- else:
115
- github_url_to_use = DEFAULT_GITHUB_PROFILE
116
-
117
- if github_url_to_use:
118
- if github_url and github_url.strip():
119
- result += "βœ… GitHub Profile URL provided\n"
120
-
121
- else:
122
- result += "βœ… Using default GitHub Profile URL\n"
123
-
124
- logger.info("Processing GitHub URL: %s", github_url_to_use)
125
 
126
  # Retrieve repositories from GitHub
127
- github_result = get_github_repositories(github_url_to_use)
128
 
129
  if github_result["status"] == "success":
130
  result += " βœ… GitHub list download successful\n\n"
@@ -144,42 +86,15 @@ def process_inputs(linkedin_pdf, github_url, job_post_text, user_instructions):
144
  if job_post_text and job_post_text.strip():
145
  result += "βœ… Job post text provided\n"
146
  logger.info("Job post text provided (%d characters)", len(job_post_text))
147
- job_text_to_use = job_post_text.strip()
148
-
149
- else:
150
- result += "ℹ️ No job post provided, attempting to use default\n"
151
- logger.info("No job post text provided, trying default")
152
-
153
- # Try to load default job call
154
- default_job = load_default_job_call()
155
 
156
- if default_job:
157
- job_text_to_use = default_job
 
158
 
159
- else:
160
- result += "ℹ️ No default job post available, proceeding without job post\n"
161
- logger.info("No default job post available, proceeding without job analysis")
162
- job_text_to_use = None
163
-
164
- # Generate job summary (will use default if job_text_to_use is None)
165
- summary = None
166
-
167
- if job_text_to_use:
168
- summary = summarize_job_call(job_text_to_use)
169
-
170
- if summary:
171
- if job_post_text and job_post_text.strip():
172
- result += " βœ… Job post summary generated\n"
173
- else:
174
- result += "βœ… Using default job post\n"
175
- result += " βœ… Job post summary generated\n"
176
- logger.info("Job post summary generated (%d characters)", len(summary))
177
- else:
178
- result += " ❌ Job post summary generation failed\n"
179
- logger.warning("Job post summary generation failed")
180
  else:
181
- result += "ℹ️ Proceeding without job post analysis\n"
182
- logger.info("No job post available for analysis")
 
183
 
184
  # Process user instructions
185
  if user_instructions and user_instructions.strip():
@@ -227,17 +142,6 @@ def get_processed_data(linkedin_pdf, github_url, job_post_text, instructions):
227
  job_post_text = job_post_text.strip() if job_post_text and job_post_text.strip() else None
228
  instructions = instructions.strip() if instructions and instructions.strip() else None
229
 
230
- # If no job post text provided, try to get default
231
- if not job_post_text:
232
- default_job = load_default_job_call()
233
-
234
- if default_job:
235
- job_post_text = default_job
236
- else:
237
- # No job post provided and no default available
238
- logger.info("No job post provided and no default available")
239
- job_post_text = None
240
-
241
  processed_data = {
242
  "linkedin": None,
243
  "github": None,
@@ -248,8 +152,6 @@ def get_processed_data(linkedin_pdf, github_url, job_post_text, instructions):
248
 
249
  # Process LinkedIn PDF
250
  if linkedin_pdf is not None:
251
-
252
- # Handle both file objects and mock file objects with path strings
253
  file_path = linkedin_pdf.name
254
  extraction_result = extract_text_from_linkedin_pdf(file_path)
255
 
@@ -270,10 +172,3 @@ def get_processed_data(linkedin_pdf, github_url, job_post_text, instructions):
270
  processed_data["errors"].append(f"GitHub: {github_result['message']}")
271
 
272
  return processed_data
273
-
274
-
275
- class MockFile:
276
- """Mock file object that mimics uploaded file interface with just a file path."""
277
-
278
- def __init__(self, path):
279
- self.name = path
 
5
  """
6
 
7
  import logging
 
8
  from pathlib import Path
9
+ from functions.linkedin_resume import extract_text_from_linkedin_pdf
10
  from functions.github import get_github_repositories
11
+ from functions.job_call import summarize_job_call
12
  from functions.writer_agent import write_resume
 
13
 
14
  # pylint: disable=broad-exception-caught
15
 
 
18
  logger = logging.getLogger(__name__)
19
 
20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  def process_inputs(linkedin_pdf, github_url, job_post_text, user_instructions):
22
  """
23
  Process the input files and URLs from the Gradio interface.
24
 
25
  Args:
26
+ linkedin_pdf: Uploaded LinkedIn resume export PDF file
27
  github_url (str): GitHub profile URL
28
  job_post_text (str): Job post text content
29
  user_instructions (str): Additional instructions from the user
 
37
 
38
  # Process LinkedIn PDF file
39
  if linkedin_pdf is not None:
 
 
40
  file_path = linkedin_pdf.name
41
  file_display_name = Path(file_path).name
42
 
43
  result += "βœ… LinkedIn Resume PDF provided\n"
44
  logger.info("Processing LinkedIn PDF: %s", file_display_name)
45
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  # Extract and structure text from the PDF
47
  extraction_result = extract_text_from_linkedin_pdf(file_path)
48
 
 
61
  logger.info("No LinkedIn PDF file provided")
62
 
63
  # Process GitHub profile
 
64
  if github_url and github_url.strip():
65
+ result += "βœ… GitHub Profile URL provided\n"
66
+ logger.info("Processing GitHub URL: %s", github_url.strip())
 
 
 
 
 
 
 
 
 
 
 
67
 
68
  # Retrieve repositories from GitHub
69
+ github_result = get_github_repositories(github_url.strip())
70
 
71
  if github_result["status"] == "success":
72
  result += " βœ… GitHub list download successful\n\n"
 
86
  if job_post_text and job_post_text.strip():
87
  result += "βœ… Job post text provided\n"
88
  logger.info("Job post text provided (%d characters)", len(job_post_text))
 
 
 
 
 
 
 
 
89
 
90
+ summary = summarize_job_call(job_post_text.strip())
91
+ result += " βœ… Job post summary generated\n"
92
+ logger.info("Job post summary generated (%d characters)", len(summary))
93
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  else:
95
+ result += "❌ Job post not provided\n"
96
+ logger.info("No job post text provided")
97
+ summary = None
98
 
99
  # Process user instructions
100
  if user_instructions and user_instructions.strip():
 
142
  job_post_text = job_post_text.strip() if job_post_text and job_post_text.strip() else None
143
  instructions = instructions.strip() if instructions and instructions.strip() else None
144
 
 
 
 
 
 
 
 
 
 
 
 
145
  processed_data = {
146
  "linkedin": None,
147
  "github": None,
 
152
 
153
  # Process LinkedIn PDF
154
  if linkedin_pdf is not None:
 
 
155
  file_path = linkedin_pdf.name
156
  extraction_result = extract_text_from_linkedin_pdf(file_path)
157
 
 
172
  processed_data["errors"].append(f"GitHub: {github_result['message']}")
173
 
174
  return processed_data
 
 
 
 
 
 
 
functions/writer_agent.py CHANGED
@@ -11,7 +11,7 @@ from configuration import INFERENCE_URL, AGENT_MODEL, AGENT_INSTRUCTIONS
11
  logging.basicConfig(level=logging.INFO)
12
  logger = logging.getLogger(__name__)
13
 
14
- def write_resume(content: str, user_instructions: str = None, job_summary: str = None) -> str:
15
 
16
  """
17
  Generates a resume based on the provided content.
@@ -19,7 +19,7 @@ def write_resume(content: str, user_instructions: str = None, job_summary: str =
19
  Args:
20
  content (str): The content to be used for generating the resume.
21
  user_instructions (str, optional): Additional instructions from the user.
22
- job_summary (str, optional): Extracted/summarized job call information.
23
 
24
  Returns:
25
  str: The generated resume.
@@ -46,9 +46,9 @@ def write_resume(content: str, user_instructions: str = None, job_summary: str =
46
  # Prepare instructions - combine default with user instructions and job summary
47
  instructions = AGENT_INSTRUCTIONS
48
 
49
- if job_summary is not None and job_summary.strip():
50
- instructions += f"\n\nJob Requirements and Details:\n{job_summary.strip()}"
51
- logger.info("Added job summary to agent prompt (%d characters)", len(job_summary))
52
 
53
  if user_instructions and user_instructions.strip():
54
 
 
11
  logging.basicConfig(level=logging.INFO)
12
  logger = logging.getLogger(__name__)
13
 
14
+ def write_resume(content: str, user_instructions: str = None, job_summary: dict = None) -> str:
15
 
16
  """
17
  Generates a resume based on the provided content.
 
19
  Args:
20
  content (str): The content to be used for generating the resume.
21
  user_instructions (str, optional): Additional instructions from the user.
22
+ job_summary (dict, optional): Extracted/summarized job call information.
23
 
24
  Returns:
25
  str: The generated resume.
 
46
  # Prepare instructions - combine default with user instructions and job summary
47
  instructions = AGENT_INSTRUCTIONS
48
 
49
+ if job_summary is not None:
50
+ instructions += f"\n\nJob Requirements and Details:\n{json.dumps(job_summary)}"
51
+ logger.info("Added job summary to agent prompt")
52
 
53
  if user_instructions and user_instructions.strip():
54
 
resumate.py CHANGED
@@ -13,14 +13,8 @@ Upon submission, the input values are processed and displayed in the output box.
13
  To run:
14
  python resumate.py
15
  """
16
- from pathlib import Path
17
-
18
  import gradio as gr
19
- from functions.gradio import check_default_linkedin_pdf, process_with_default_option
20
-
21
-
22
- # Check if default PDF exists at startup
23
- has_default, default_path = check_default_linkedin_pdf()
24
 
25
  with gr.Blocks() as demo:
26
  gr.Markdown("# Resumate: tailored resume generator")
@@ -41,21 +35,6 @@ with gr.Blocks() as demo:
41
  **Tip**: Make sure your LinkedIn profile is complete and up-to-date before exporting for best results!
42
  """)
43
 
44
- # Default PDF option
45
- if has_default:
46
- use_default_pdf = gr.Checkbox(
47
- label=f"Use default LinkedIn PDF ({Path(default_path).name})",
48
- value=False,
49
- info="Use the default LinkedIn PDF stored in the data directory"
50
- )
51
- else:
52
- use_default_pdf = gr.Checkbox(
53
- label="Use default LinkedIn PDF (not available)",
54
- value=False,
55
- interactive=False,
56
- info="No default LinkedIn PDF found in data directory"
57
- )
58
-
59
  linkedin_pdf = gr.File(
60
  label="LinkedIn Resume Export PDF",
61
  file_types=[".pdf"],
@@ -102,8 +81,8 @@ with gr.Blocks() as demo:
102
  output = gr.Textbox(label="Output", lines=5, max_lines=50, show_copy_button=True)
103
 
104
  submit_btn.click( # pylint: disable=no-member
105
- process_with_default_option,
106
- inputs=[use_default_pdf, linkedin_pdf, github_profile, job_post, user_instructions],
107
  outputs=output
108
  )
109
 
 
13
  To run:
14
  python resumate.py
15
  """
 
 
16
  import gradio as gr
17
+ from functions.gradio import process_inputs
 
 
 
 
18
 
19
  with gr.Blocks() as demo:
20
  gr.Markdown("# Resumate: tailored resume generator")
 
35
  **Tip**: Make sure your LinkedIn profile is complete and up-to-date before exporting for best results!
36
  """)
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  linkedin_pdf = gr.File(
39
  label="LinkedIn Resume Export PDF",
40
  file_types=[".pdf"],
 
81
  output = gr.Textbox(label="Output", lines=5, max_lines=50, show_copy_button=True)
82
 
83
  submit_btn.click( # pylint: disable=no-member
84
+ process_inputs,
85
+ inputs=[linkedin_pdf, github_profile, job_post, user_instructions],
86
  outputs=output
87
  )
88
 
tests/test_gradio.py CHANGED
@@ -10,12 +10,8 @@ from functions import gradio
10
  class TestProcessInputs(unittest.TestCase):
11
  """Test cases for the process_inputs function."""
12
 
13
- @patch('functions.gradio.load_default_job_call')
14
- def test_no_inputs_provided(self, mock_load_default):
15
- """Test when no inputs are provided and default job is available."""
16
- # Mock default job call loading to return content
17
- mock_load_default.return_value = "Default job content from sample_job.txt"
18
-
19
  with patch('functions.gradio.get_github_repositories') as mock_github, \
20
  patch('functions.gradio.summarize_job_call') as mock_summarize:
21
  mock_github.return_value = {"status": "success", "metadata": {"username": "gperdrizet"}}
@@ -24,18 +20,13 @@ class TestProcessInputs(unittest.TestCase):
24
  result = gradio.process_inputs(None, "", "", "")
25
 
26
  self.assertIn("❌ No LinkedIn resume PDF file uploaded", result)
27
- self.assertIn("βœ… Using default GitHub Profile URL", result)
28
- self.assertIn("ℹ️ No job post provided, attempting to use default", result)
29
- self.assertIn("βœ… Using default job post", result)
30
  self.assertIn("ℹ️ No additional instructions provided", result)
31
  self.assertIn("❌ Cannot generate resume: No valid LinkedIn data extracted", result)
32
 
33
- @patch('functions.gradio.load_default_job_call')
34
- def test_no_inputs_no_default_job(self, mock_load_default):
35
- """Test when no inputs are provided and no default job is available."""
36
- # Mock default job call loading to return None (no default available)
37
- mock_load_default.return_value = None
38
-
39
  with patch('functions.gradio.get_github_repositories') as mock_github, \
40
  patch('functions.gradio.summarize_job_call') as mock_summarize:
41
  mock_github.return_value = {"status": "success", "metadata": {"username": "gperdrizet"}}
@@ -44,10 +35,9 @@ class TestProcessInputs(unittest.TestCase):
44
  result = gradio.process_inputs(None, "", "", "")
45
 
46
  self.assertIn("❌ No LinkedIn resume PDF file uploaded", result)
47
- self.assertIn("βœ… Using default GitHub Profile URL", result)
48
- self.assertIn("ℹ️ No job post provided, attempting to use default", result)
49
- self.assertIn("ℹ️ No default job post available, proceeding without job post", result)
50
- self.assertIn("ℹ️ Proceeding without job post analysis", result)
51
  self.assertIn("ℹ️ No additional instructions provided", result)
52
  self.assertIn("❌ Cannot generate resume: No valid LinkedIn data extracted", result)
53
 
@@ -177,12 +167,8 @@ class TestProcessInputs(unittest.TestCase):
177
  # Verify write_resume was NOT called since no LinkedIn data
178
  mock_write_resume.assert_not_called()
179
 
180
- @patch('functions.gradio.load_default_job_call')
181
- def test_whitespace_only_inputs(self, mock_load_default):
182
- """Test inputs with only whitespace and default job available."""
183
- # Mock default job call loading to return content
184
- mock_load_default.return_value = "Default job content from sample_job.txt"
185
-
186
  with patch('functions.gradio.get_github_repositories') as mock_github, \
187
  patch('functions.gradio.summarize_job_call') as mock_summarize:
188
  mock_github.return_value = {"status": "success", "metadata": {"username": "gperdrizet"}}
@@ -191,18 +177,13 @@ class TestProcessInputs(unittest.TestCase):
191
  result = gradio.process_inputs(None, " ", " ", " ")
192
 
193
  self.assertIn("❌ No LinkedIn resume PDF file uploaded", result)
194
- self.assertIn("βœ… Using default GitHub Profile URL", result)
195
- self.assertIn("ℹ️ No job post provided, attempting to use default", result)
196
- self.assertIn("βœ… Using default job post", result)
197
  self.assertIn("ℹ️ No additional instructions provided", result)
198
  self.assertIn("❌ Cannot generate resume: No valid LinkedIn data extracted", result)
199
 
200
- @patch('functions.gradio.load_default_job_call')
201
- def test_whitespace_only_inputs_no_default(self, mock_load_default):
202
- """Test inputs with only whitespace and no default job available."""
203
- # Mock default job call loading to return None
204
- mock_load_default.return_value = None
205
-
206
  with patch('functions.gradio.get_github_repositories') as mock_github, \
207
  patch('functions.gradio.summarize_job_call') as mock_summarize:
208
  mock_github.return_value = {"status": "success", "metadata": {"username": "gperdrizet"}}
@@ -211,10 +192,8 @@ class TestProcessInputs(unittest.TestCase):
211
  result = gradio.process_inputs(None, " ", " ", " ")
212
 
213
  self.assertIn("❌ No LinkedIn resume PDF file uploaded", result)
214
- self.assertIn("βœ… Using default GitHub Profile URL", result)
215
- self.assertIn("ℹ️ No job post provided, attempting to use default", result)
216
- self.assertIn("ℹ️ No default job post available, proceeding without job post", result)
217
- self.assertIn("ℹ️ Proceeding without job post analysis", result)
218
  self.assertIn("ℹ️ No additional instructions provided", result)
219
  self.assertIn("❌ Cannot generate resume: No valid LinkedIn data extracted", result)
220
 
@@ -302,26 +281,18 @@ class TestProcessInputs(unittest.TestCase):
302
  class TestGetProcessedData(unittest.TestCase):
303
  """Test cases for the get_processed_data function."""
304
 
305
- @patch('functions.gradio.load_default_job_call')
306
- def test_no_inputs(self, mock_load_default):
307
  """Test with no inputs provided."""
308
- # Mock the default job call loading
309
- mock_load_default.return_value = "Default job call content from sample_job.txt"
310
-
311
  result = gradio.get_processed_data(None, "", "", "")
312
 
313
  self.assertIsNone(result["linkedin"])
314
  self.assertIsNone(result["github"])
315
- self.assertEqual(result["job_post"], "Default job call content from sample_job.txt")
316
  self.assertIsNone(result["user_instructions"])
317
  self.assertEqual(len(result["errors"]), 0)
318
 
319
- @patch('functions.gradio.load_default_job_call')
320
- def test_no_inputs_no_default_job(self, mock_load_default):
321
- """Test with no inputs provided and no default job available."""
322
- # Mock the default job call loading to return None
323
- mock_load_default.return_value = None
324
-
325
  result = gradio.get_processed_data(None, "", "", "")
326
 
327
  self.assertIsNone(result["linkedin"])
@@ -430,23 +401,19 @@ class TestGetProcessedData(unittest.TestCase):
430
  self.assertIn("LinkedIn: LinkedIn error", result["errors"])
431
  self.assertIn("GitHub: GitHub error", result["errors"])
432
 
433
- @patch('functions.gradio.load_default_job_call')
434
- def test_job_post_whitespace_handling(self, mock_load_default):
435
  """Test job post whitespace handling."""
436
- # Mock the default job call loading
437
- mock_load_default.return_value = "Default job content"
438
-
439
  # Test with leading/trailing whitespace
440
  result = gradio.get_processed_data(None, "", " Job content ", "")
441
  self.assertEqual(result["job_post"], "Job content")
442
 
443
- # Test with only whitespace - should load default
444
  result = gradio.get_processed_data(None, "", " ", "")
445
- self.assertEqual(result["job_post"], "Default job content")
446
 
447
- # Test with empty string - should load default
448
  result = gradio.get_processed_data(None, "", "", "")
449
- self.assertEqual(result["job_post"], "Default job content")
450
 
451
  def test_github_url_whitespace_handling(self):
452
  """Test GitHub URL whitespace handling."""
 
10
  class TestProcessInputs(unittest.TestCase):
11
  """Test cases for the process_inputs function."""
12
 
13
+ def test_no_inputs_provided(self):
14
+ """Test when no inputs are provided."""
 
 
 
 
15
  with patch('functions.gradio.get_github_repositories') as mock_github, \
16
  patch('functions.gradio.summarize_job_call') as mock_summarize:
17
  mock_github.return_value = {"status": "success", "metadata": {"username": "gperdrizet"}}
 
20
  result = gradio.process_inputs(None, "", "", "")
21
 
22
  self.assertIn("❌ No LinkedIn resume PDF file uploaded", result)
23
+ self.assertIn("❌ No GitHub profile URL provided", result)
24
+ self.assertIn("❌ Job post not provided", result)
 
25
  self.assertIn("ℹ️ No additional instructions provided", result)
26
  self.assertIn("❌ Cannot generate resume: No valid LinkedIn data extracted", result)
27
 
28
+ def test_no_inputs_no_default_job(self):
29
+ """Test when no inputs are provided (same as test_no_inputs_provided now)."""
 
 
 
 
30
  with patch('functions.gradio.get_github_repositories') as mock_github, \
31
  patch('functions.gradio.summarize_job_call') as mock_summarize:
32
  mock_github.return_value = {"status": "success", "metadata": {"username": "gperdrizet"}}
 
35
  result = gradio.process_inputs(None, "", "", "")
36
 
37
  self.assertIn("❌ No LinkedIn resume PDF file uploaded", result)
38
+ self.assertIn("❌ No GitHub profile URL provided", result)
39
+ self.assertIn("❌ Job post not provided", result)
40
+ self.assertIn("ℹ️ No additional instructions provided", result)
 
41
  self.assertIn("ℹ️ No additional instructions provided", result)
42
  self.assertIn("❌ Cannot generate resume: No valid LinkedIn data extracted", result)
43
 
 
167
  # Verify write_resume was NOT called since no LinkedIn data
168
  mock_write_resume.assert_not_called()
169
 
170
+ def test_whitespace_only_inputs(self):
171
+ """Test inputs with only whitespace."""
 
 
 
 
172
  with patch('functions.gradio.get_github_repositories') as mock_github, \
173
  patch('functions.gradio.summarize_job_call') as mock_summarize:
174
  mock_github.return_value = {"status": "success", "metadata": {"username": "gperdrizet"}}
 
177
  result = gradio.process_inputs(None, " ", " ", " ")
178
 
179
  self.assertIn("❌ No LinkedIn resume PDF file uploaded", result)
180
+ self.assertIn("❌ No GitHub profile URL provided", result)
181
+ self.assertIn("❌ Job post not provided", result)
 
182
  self.assertIn("ℹ️ No additional instructions provided", result)
183
  self.assertIn("❌ Cannot generate resume: No valid LinkedIn data extracted", result)
184
 
185
+ def test_whitespace_only_inputs_no_default(self):
186
+ """Test inputs with only whitespace (same as test_whitespace_only_inputs now)."""
 
 
 
 
187
  with patch('functions.gradio.get_github_repositories') as mock_github, \
188
  patch('functions.gradio.summarize_job_call') as mock_summarize:
189
  mock_github.return_value = {"status": "success", "metadata": {"username": "gperdrizet"}}
 
192
  result = gradio.process_inputs(None, " ", " ", " ")
193
 
194
  self.assertIn("❌ No LinkedIn resume PDF file uploaded", result)
195
+ self.assertIn("❌ No GitHub profile URL provided", result)
196
+ self.assertIn("❌ Job post not provided", result)
 
 
197
  self.assertIn("ℹ️ No additional instructions provided", result)
198
  self.assertIn("❌ Cannot generate resume: No valid LinkedIn data extracted", result)
199
 
 
281
  class TestGetProcessedData(unittest.TestCase):
282
  """Test cases for the get_processed_data function."""
283
 
284
+ def test_no_inputs(self):
 
285
  """Test with no inputs provided."""
 
 
 
286
  result = gradio.get_processed_data(None, "", "", "")
287
 
288
  self.assertIsNone(result["linkedin"])
289
  self.assertIsNone(result["github"])
290
+ self.assertIsNone(result["job_post"])
291
  self.assertIsNone(result["user_instructions"])
292
  self.assertEqual(len(result["errors"]), 0)
293
 
294
+ def test_no_inputs_no_default_job(self):
295
+ """Test with no inputs provided (same as test_no_inputs now)."""
 
 
 
 
296
  result = gradio.get_processed_data(None, "", "", "")
297
 
298
  self.assertIsNone(result["linkedin"])
 
401
  self.assertIn("LinkedIn: LinkedIn error", result["errors"])
402
  self.assertIn("GitHub: GitHub error", result["errors"])
403
 
404
+ def test_job_post_whitespace_handling(self):
 
405
  """Test job post whitespace handling."""
 
 
 
406
  # Test with leading/trailing whitespace
407
  result = gradio.get_processed_data(None, "", " Job content ", "")
408
  self.assertEqual(result["job_post"], "Job content")
409
 
410
+ # Test with only whitespace - should be None
411
  result = gradio.get_processed_data(None, "", " ", "")
412
+ self.assertIsNone(result["job_post"])
413
 
414
+ # Test with empty string - should be None
415
  result = gradio.get_processed_data(None, "", "", "")
416
+ self.assertIsNone(result["job_post"])
417
 
418
  def test_github_url_whitespace_handling(self):
419
  """Test GitHub URL whitespace handling."""