diff --git a/CHANGELOG.md b/CHANGELOG.md index ed322096..26693f06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -232,6 +232,8 @@ per-file diff commands. before changing rank metadata; current-day deadlines, explicit future extensions, invalid dates, dry runs, and scoring errors keep their behavior. +- **Salary workbook conversion skips whitespace-only companies** (#576) - blank-looking company cells no longer produce empty company entries. + - **`/reset profile` keeps the `framework_version` frontmatter on the two files it rewrites** (`.claude/commands/reset.md` Step 3, `tests/test_reset_command.py`) - the full-file skeletons for `01-candidate-profile.md` and `02-behavioral-profile.md` started at the `#` title, while diff --git a/tests/test_salary_empty_company_cli.py b/tests/test_salary_empty_company_cli.py new file mode 100644 index 00000000..278cb3db --- /dev/null +++ b/tests/test_salary_empty_company_cli.py @@ -0,0 +1,52 @@ +"""Workbook blank rows must not make the generated salary data invalid.""" +import json +import shutil +import subprocess +import sys +import unittest +from pathlib import Path +from tempfile import TemporaryDirectory + +try: + import openpyxl +except ImportError: + openpyxl = None + +REPO = Path(__file__).resolve().parent.parent + + +@unittest.skipUnless(openpyxl, "requires optional openpyxl (installed in CI)") +class EmptyCompanyWorkbookCLI(unittest.TestCase): + def convert_and_validate(self, blank): + with TemporaryDirectory() as directory: + root = Path(directory) + workbook = root / "salary.xlsx" + output = root / "salary_data.json" + book = openpyxl.Workbook() + try: + book.active.append(["Company", "Count", "Index"]) + book.active.append(["Acme Corp", 12, 105]) + book.active.append([blank, 20, 110]) + book.save(workbook) + finally: + book.close() + conversion = subprocess.run( + [sys.executable, str(REPO / "tools/convert_salary_excel.py"), str(workbook), "--output", str(output)], + capture_output=True, text=True, encoding="utf-8", + ) + self.assertEqual(conversion.returncode, 0, conversion.stderr) + shutil.copyfile(REPO / "salary_lookup.py", root / "salary_lookup.py") + validation = subprocess.run( + [sys.executable, str(root / "salary_lookup.py"), "--validate"], + capture_output=True, text=True, encoding="utf-8", + ) + self.assertEqual(validation.returncode, 0, validation.stdout + validation.stderr) + data = json.loads(output.read_text(encoding="utf-8")) + self.assertEqual(data["companies"], [{"company": "Acme Corp", "city": "", "categories": { + "all_employees": {"count": 12, "index": 105.0}}}]) + + def test_space_only_company_rows_are_skipped(self): + self.convert_and_validate(" \u00a0 ") + + def test_truly_empty_company_rows_are_still_skipped(self): + self.convert_and_validate(None) diff --git a/tools/convert_salary_excel.py b/tools/convert_salary_excel.py index f6e521b3..eda8d92c 100644 --- a/tools/convert_salary_excel.py +++ b/tools/convert_salary_excel.py @@ -280,6 +280,8 @@ def parse_sheet(ws, sheet_label=None): continue company_name = str(row[company_col]).strip() + if not company_name: + continue if city_col is not None and city_col < len(row) and row[city_col]: city_name = str(row[city_col]).strip() else: