Add ingest, filename-parsing, EXIF, and duplicate-detection tests #7
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Ingest is the entry point for every photo in the system and has the most parsing
surface area: filename conventions, EXIF quirks, and duplicate detection. None of
it is covered by tests today.
Scope
Tests for
app/services/ingest.py,filename_parser.py,exif_extractor.py, andSHA-256 duplicate detection.
Implementation notes
partial dates (
198X,1987-06), front/back indicators — plus the cases that shouldnot parse. Malformed filenames must degrade to "no evidence" rather than raising or
inventing a date.
DateTimeOriginal,DateTimeDigitized, andDateTimeprecedence; missing EXIF;corrupt EXIF blocks; and a scanner-set timestamp (the date the scan was made, not the
date the photo was taken) — this last case matters because a scan date is misleading
evidence if trusted at face value.
destroy neither record. Assert that both rows survive and are linked.
same filename, are distinct cases — cover both.
photo_ingestedaudit events and creates evidence rows for eachdetected signal.
hash and that re-ingest does not write a second copy of identical bytes.
Done when
References
backend/app/services/ingest.py,filename_parser.py,exif_extractor.py,storage.pybackend/app/api/routes/ingest.pydocs/circa-spec.md(filename conventions)Depends on: the backend test harness.
Done in
a6c2e9f.What was actually missing
filename_parser.pyhad no tests at all.exif_extractor.pyand duplicate detection were largely covered already — bytest_exif_trust.py(tag precedence, scanner timestamps),test_exif_failure_visibility.py(missing, corrupt, unopenable), andtest_ingest_integrity.py(exact-duplicate ingest, content-addressed keys, the canonical-row race). So the work concentrated where the coverage was zero, plus the two named cases nothing reached.The parser had four defects
The issue's rule — malformed filenames degrade to "no evidence" rather than raising or inventing a date — held in three of the four ways it can fail. Writing the cases found:
.year/.month/.daypopulated with no range at all. A caller reading those fields built evidence out of a date the parser had itself rejected. This is the "inventing" failure, arrived at by leaving half a result behind.3999-01-01parsed but3999did not. The dashed patterns accepted any four digits while the bare-year pattern required19xx/20xx.0000-01-01left a month and day behind after the year was refused.<album>_<year>— a four-digit year matches_SEQ_PATTERNperfectly well. Photo "1983" of the album, sorting after 400 and before 2000. Wrong on every such name, in an ordering nobody would think to check.None of these were cosmetic.
_infer_precisioniningest.pyreads.dayand.month, so3999-01-01produced day-precision filename evidence atmediumreliability — andmediumis what promotes a photo toneeds_reviewunderprojections.fold(). The tests now follow a name through ingest to the evidence row rather than stopping at the parser.The fix
_plausible_year()— one bound for every pattern, shared with EXIF trust (#74), so the archive has one answer to "could this be a date in this collection"._resolve()— degrades to the coarsest reading that is actually true, and never leaves partial state.1983-02-30is not the thirtieth of February, but it is still evidence of February 1983; discarding the whole match would lose a legible date over an illegible digit.parse_filename()extracts the date first, records its span, and looks for the sequence outside it.The invariant is asserted directly for every shape: no component outlives the range that supports it — which is what makes
.yearsafe to read without also checking.date_low.One of my own tests was wrong and got corrected rather than the code: I had asserted an impossible date should yield nothing at all, which throws away a true coarser reading.
Ingest cases added
duplicate_ofis what the review queue filters on, so a batch of scans all namedscan.jpg— what an unattended scanner produces — would collapse to one visible photograph with the rest recorded as redundant. Nothing raises; they are simply gone from the queue.Done when
787 passed, 8 skipped; ruff clean.