mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-11-02 13:12:50 +00:00
fix(salary): accept BOM-prefixed UTF-8 data files (#602)
* fix(salary): accept BOM-prefixed UTF-8 data files Fixes #594. Thanks to @rahul05ranjan for reporting the failure and identifying the decoder change. * docs(changelog): entry for #602 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Mads Lorentzen <madslorentzen_17@hotmail.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
3 files changed
+57
-1
No files matched your search
@@ -68,6 +68,13 @@ per-file diff commands.
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- **`salary_lookup.py` accepts a BOM-prefixed `salary_data.json`** (#594, #602, `salary_lookup.py`,
|
||||||
|
`tests/test_salary_lookup.py`) - the data file is opened with `utf-8-sig`, so a file saved by
|
||||||
|
Windows PowerShell `Out-File`/`Set-Content` or by an editor that prepends a byte-order mark now
|
||||||
|
validates and loads instead of exiting 1 with "Unexpected UTF-8 BOM". Plain UTF-8 files read
|
||||||
|
identically, and malformed JSON after a BOM still reports its line and column. Reported by
|
||||||
|
rahul05ranjan; same class as #485 and #509.
|
||||||
|
|
||||||
- **Upstream triage emits cherry-pick suggestions in dependency order**
|
- **Upstream triage emits cherry-pick suggestions in dependency order**
|
||||||
(`tools/upstream_triage.py`) - ready-to-run commands now list ancestors before
|
(`tools/upstream_triage.py`) - ready-to-run commands now list ancestors before
|
||||||
descendants, so a follow-up patch is not suggested before the change it edits.
|
descendants, so a follow-up patch is not suggested before the change it edits.
|
||||||
|
|||||||
+1
-1
@@ -149,7 +149,7 @@ def read_raw_data():
|
|||||||
print("step will be skipped during /apply.", file=sys.stderr)
|
print("step will be skipped during /apply.", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
try:
|
try:
|
||||||
with open(DATA_FILE, "r", encoding="utf-8") as f:
|
with open(DATA_FILE, "r", encoding="utf-8-sig") as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
except json.JSONDecodeError as exc:
|
except json.JSONDecodeError as exc:
|
||||||
fail_data_error(f"invalid JSON at line {exc.lineno}, column {exc.colno}: {exc.msg}")
|
fail_data_error(f"invalid JSON at line {exc.lineno}, column {exc.colno}: {exc.msg}")
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
"""Tests for salary_lookup.py — format_entry, match_score, and search_company."""
|
"""Tests for salary_lookup.py — format_entry, match_score, and search_company."""
|
||||||
|
|
||||||
import io
|
import io
|
||||||
|
import json
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
from contextlib import redirect_stderr, redirect_stdout
|
from contextlib import redirect_stderr, redirect_stdout
|
||||||
@@ -600,5 +604,50 @@ class TestSearchCompanyScoreThreshold(unittest.TestCase):
|
|||||||
self.assertEqual(results[0]["company"], "Novo Nordisk")
|
self.assertEqual(results[0]["company"], "Novo Nordisk")
|
||||||
|
|
||||||
|
|
||||||
|
class TestSalaryFileEncoding(unittest.TestCase):
|
||||||
|
"""Exercise user-created UTF-8 files through the documented CLI."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
directory = tempfile.TemporaryDirectory()
|
||||||
|
self.addCleanup(directory.cleanup)
|
||||||
|
self.root = Path(directory.name)
|
||||||
|
self.script = self.root / "salary_lookup.py"
|
||||||
|
shutil.copyfile(salary_lookup.__file__, self.script)
|
||||||
|
self.data_file = self.root / "salary_data.json"
|
||||||
|
self.entry = {
|
||||||
|
"company": "Novo Nordisk A/S",
|
||||||
|
"city": "Bagsværd",
|
||||||
|
"categories": {"engineering": {"count": 120, "index": 112.3}},
|
||||||
|
}
|
||||||
|
|
||||||
|
def run_cli(self, *args):
|
||||||
|
return subprocess.run(
|
||||||
|
[sys.executable, str(self.script), *args],
|
||||||
|
cwd=self.root, capture_output=True, text=True, encoding="utf-8",
|
||||||
|
timeout=30,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_utf8_with_and_without_bom(self):
|
||||||
|
payload = {"metadata": {"index_baseline": 100}, "companies": [self.entry]}
|
||||||
|
for encoding in ("utf-8", "utf-8-sig"):
|
||||||
|
with self.subTest(encoding=encoding):
|
||||||
|
self.data_file.write_text(
|
||||||
|
json.dumps(payload, ensure_ascii=False), encoding=encoding,
|
||||||
|
)
|
||||||
|
validated = self.run_cli("--validate")
|
||||||
|
self.assertEqual(validated.returncode, 0, validated.stderr)
|
||||||
|
self.assertIn("OK - no issues found.", validated.stdout)
|
||||||
|
lookup = self.run_cli("Novo Nordisk", "--json")
|
||||||
|
self.assertEqual(lookup.returncode, 0, lookup.stderr)
|
||||||
|
self.assertEqual(json.loads(lookup.stdout), [self.entry])
|
||||||
|
|
||||||
|
def test_bom_does_not_hide_invalid_json(self):
|
||||||
|
self.data_file.write_text('{"companies": [}', encoding="utf-8-sig")
|
||||||
|
result = self.run_cli("--validate")
|
||||||
|
self.assertEqual(result.returncode, 1)
|
||||||
|
self.assertIn("invalid JSON at line 1, column 16", result.stderr)
|
||||||
|
self.assertNotIn("Unexpected UTF-8 BOM", result.stderr)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
Reference in new issue
Block a user