mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-11-05 06:27:56 +00:00
fix(notion-sync): re-key a row matched by URL when its Key has drifted (#761)
* fix(notion-sync): re-key a row matched by URL when its Key has drifted `tools/job_key.py --audit --migrate` (#637) renames keys in seen_jobs.json, but Step 4 upserted on Key alone, so the next sync created a duplicate row for every migrated key. On a Key miss, look up keyed rows whose URL equals the job's URL exactly: one match gets the new Key and is updated, several are reported and left untouched, none creates as before. Rows without a Key stay invisible, and the report counts re-keyed rows separately. Shape from Mads Lorentzen's review on #637. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: drop the CHANGELOG hunk, frozen by #800 (release notes come from the PR title) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * test(notion-sync): pin the key-collision guard and name the skip counter for both ambiguous cases Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a3ba937be8
commit
a813602972
2 files changed
+34
-6
No files matched your search
@@ -80,6 +80,30 @@ class NotionSyncCommandSpec(unittest.TestCase):
|
||||
self.assertIn("Match, expired-only entry** → update **Status only** to `expired`", step4)
|
||||
self.assertIn("Never write or rebuild a body for an expired-only entry, including when `--rebuild` is set.", step5)
|
||||
|
||||
def test_key_miss_falls_back_to_an_exact_url_match_and_rekeys_the_row(self):
|
||||
# `job_key.py --audit --migrate` (#637) renames keys in seen_jobs.json, but
|
||||
# Step 4 upserted on Key alone, so the next sync created a second row for
|
||||
# every migrated job. On a Key miss, one keyed row with the same URL gets
|
||||
# the new Key; several are reported and left alone.
|
||||
text = COMMAND.read_text(encoding="utf-8")
|
||||
step4 = text.split("## Step 4:", 1)[1].split("## Step 5:", 1)[0]
|
||||
step6 = text.split("## Step 6:", 1)[1].split("## Important Rules", 1)[0]
|
||||
rules = text.split("## Important Rules", 1)[1].split("## Adapting", 1)[0]
|
||||
|
||||
self.assertIn("**No match on `Key`** → fall back to the posting URL before creating anything", step4)
|
||||
self.assertIn("whose `URL` equals the job's URL exactly", step4)
|
||||
self.assertIn("**Exactly one URL match** → re-key it: set its `Key` to the job's key", step4)
|
||||
self.assertIn("If the row's current `Key` is the key of another job in this sync set, it is not drifted; treat it like a multiple match instead.", step4)
|
||||
self.assertIn("**More than one URL match** → create nothing and update nothing for this job", step4)
|
||||
self.assertIn("**No URL match** → if the job is an expired-only entry, skip it", step4)
|
||||
# Hand-added rows (no Key) stay invisible, fallback included.
|
||||
self.assertIn("**and** whose `Key` is non-empty", step4)
|
||||
self.assertIn("invisible to this command, including to the URL fallback", step4)
|
||||
self.assertIn("<R> re-keyed by URL", step6)
|
||||
self.assertIn("<A> skipped (ambiguous URL match)", step6)
|
||||
self.assertIn("never on fuzzy title comparison", rules)
|
||||
self.assertIn("exact match on the `URL` property", rules)
|
||||
|
||||
@unittest.skipUnless(
|
||||
_HAVE_YAML,
|
||||
"PyYAML not installed (the CI Python-test job omits it; the lint job runs lint_skills.py directly)",
|
||||
|
||||
Reference in new issue
Block a user