Skip to content

Test backfill for high-risk code + two latent 500 fixes (#1278 item 5)#1339

Merged
jonfroehlich merged 7 commits into
masterfrom
1278-test-coverage-backfill
Jun 18, 2026
Merged

Test backfill for high-risk code + two latent 500 fixes (#1278 item 5)#1339
jonfroehlich merged 7 commits into
masterfrom
1278-test-coverage-backfill

Conversation

@jonfroehlich

Copy link
Copy Markdown
Member

What & why

Implements item 5 of the testing roadmap (#1278): backfill tests for the high-risk untested code the issue flagged, using the coverage report from item 4 to target. Pure test additions, except two one-line model guards for real bugs the new tests surfaced (see below).

Coverage: 59% → 69% of the website app. Full suite: 343 tests, green (1 pre-existing skip).

Two latent 500s found & fixed (tests-first)

The backfill did exactly what the issue predicted — it caught real, production-reachable crashes:

  1. Artifact.save() crashed on an empty pdf_file. pdf_file is nullable, but the thumbnail block ran os.path.basename(self.pdf_file.name) (i.e. basename(None)) on every non-first save. Any second save of a PDF-less artifact — an admin edit, or the authors_changed m2m signal re-saving to rename files — raised TypeError. Guarded with if self.pdf_file:.
  2. get_project_dates_str() 500'd on a null start_date. start_date is nullable, but the method did self.start_date.year unconditionally, so visiting such a project's public page raised AttributeError. Returns "" now (the template prints date_str verbatim). Caught by the new view smoke-sweep on its first run.

Each fix has a red→green regression test.

Tests added

  • test_delete_unused_files.py — the command runs on every container start and deletes media files (0% → 72%). Runs against a throwaway MEDIA_ROOT: orphans deleted, DB-referenced files kept (pubs/talks/posters), easy-thumbnails _detail cache preserved, accurate (count, bytes), no crash on empty media / no rows / empty pdf_file.
  • test_view_smoke.py — GET every public page route (built via reverse, so a renamed url name fails here) → 200. Catches the NoReverseMatch / template-AttributeError / missing-context-key class.
  • test_timeutils.py (100%), test_fileutils.py (87%), test_ml_utils.py — pure-util unit tests.
  • test_person_save.py — Star Wars image fallback + bio_datetime_modified side effects.

ml_utils lands at 51% by design: the model-/datetime.now-coupled helpers (sort_projects_*, choose_banners*) are left for integration-style tests rather than brittle mocks.

Not in scope

Item 6 (Pa11y in CI) and item 7 (JS) — separate PRs.

UI screenshots

None — no UI/template markup changed. The two fixes are server-side guards that prevent 500s; there is no visual change to capture.

Version

Bumped ML_WEBSITE_VERSION2.12.2 (carries the two 500 fixes).

🤖 Generated with Claude Code

jonfroehlich and others added 7 commits June 18, 2026 12:35
Backfill regression tests for the delete_unused_files management command,
which runs on every container start and deletes media files -- the highest-
risk untested path in the app (zero prior coverage).

Tests run against a throwaway MEDIA_ROOT and pin:
- orphans deleted, DB-referenced files kept (pubs/talks/posters)
- easy-thumbnails _detail cache files preserved (owned by thumbnail_cleanup)
- accurate (count, bytes) tally from the delete helper
- no crash on empty media, no DB rows, or an empty pdf_file FileField

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Found while backfilling tests (item 5). pdf_file is nullable, but the
thumbnail-generation block in Artifact.save() called
os.path.basename(self.pdf_file.name) unconditionally on every non-first
save. self.pdf_file.name is None when the field is empty, raising
TypeError. Any second save of a PDF-less artifact hit it (an admin edit,
or the authors_changed m2m signal re-saving to rename files).

Wrap the block in 'if self.pdf_file:' -- no PDF means no thumbnail to
generate. Adds a DB-backed regression test (red before, green after).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GET every public page route (built via reverse so renamed url names fail
here too) and assert 200. Catches the NoReverseMatch / template-
AttributeError / missing-context-key regression class that only surfaces
through the real URL/view/template stack.

Covers: index, people, publications, awards, projects, news_listing,
view_project_people, member (by id + name), member_artifacts (all 4
types), project detail, news item (by id + slug). The two media routes
are excluded (dedicated tests already cover them). Fixtures form a small
connected graph so populated template branches render, not just empty
states.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Found by the view smoke-sweep (item 5). Project.start_date is nullable,
but get_project_dates_str accessed self.start_date.year unconditionally,
so visiting the public page of a project with no start_date 500'd with
AttributeError. Return '' (template prints date_str as-is, so it renders
nothing). Adds regression tests for all four branches.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tem 5)

Backfill fast SimpleTestCase coverage for previously-untested pure helpers:
- timeutils: humanize_duration buckets, ends_with_year, remove_trailing_year
- ml_utils: slugify_max, school/department abbreviations, get_video_embed
  (youtube/vimeo/unknown), clean_forum_name, get_closest_match, weighted_choice
- fileutils: is_image, get_ckeditor_image_filename, get_filename_no_ext, and
  the canonical get_filename_for_artifact naming (suffix, ext, truncation)

43 tests, no DB. Star Wars image helpers and filesystem-touching fileutils
(thumbnail/PDF) stay covered by their existing/integration tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Backfill the two previously-untested Person.save() behaviors:
- Star Wars fallback: empty image / easter_egg get a random figure; provided
  images are left untouched. Redirects MEDIA_ROOT to a temp dir and patches
  the picker so the branch is exercised without touching real media.
- bio_datetime_modified: stamped on create, updated when bio changes, and
  left alone on unrelated edits.

url_name derivation is already covered by test_recompute_url_names.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jonfroehlich jonfroehlich merged commit 8459c62 into master Jun 18, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant