Skip to content

test: two guards in constituent_events_to_features go untested — empty-after-filter and missing sourceSystem #51

Description

@shivamlalakiya

What's wrong

constituent_events_to_features has two small guards that no test exercises:

  1. After dropping events whose createdAt can't be parsed to a timestamp, the function checks if df.empty and returns the typed empty frame. The existing "unparseable timestamp" test only drops some rows (one bad event alongside one good one), so the frame is never empty at that check — it's never reached with a non-trivial input. Note this is a different code path from the "call with []" test, which returns before any of this filtering runs.
  2. When the sourceSystem column is entirely absent from the input, distinct_source_systems defaults to 0. The existing test_distinct_source_systems test only covers the column being present.

Where

File Locate with
philanthropy/ingest/_constituent_events.py grep -n "return _empty_feature_frame()" philanthropy/ingest/_constituent_events.py
philanthropy/ingest/_constituent_events.py grep -n "distinct_source_systems.*= 0" philanthropy/ingest/_constituent_events.py

What to change

No source change — this is test-only.

  1. Open tests/test_ingest.py and find test_unparseable_timestamp_row_dropped (locate with grep -n "def test_unparseable_timestamp_row_dropped" tests/test_ingest.py). Add a sibling test right after it where every event has an unparseable createdAt, and assert the result is the typed empty frame (same shape assertions as test_empty_input_returns_typed_empty_frame).
  2. Find test_distinct_source_systems (locate with grep -n "def test_distinct_source_systems" tests/test_ingest.py). The _event helper always sets sourceSystem (default "GIVECAMPUS"), so it can't produce a missing column — build the event as a plain dict instead, the way test_missing_event_type_does_not_crash builds its dict without eventType. Add a sibling test right after test_distinct_source_systems using one or more dicts with constituentEmail/createdAt/amount but no sourceSystem key at all, and assert distinct_source_systems == 0.

Tests to add or extend

  • File: tests/test_ingest.py
  • Add: test_all_unparseable_timestamps_returns_empty_frame
  • Add: test_distinct_source_systems_defaults_to_zero_when_column_absent

Done when

python -m pytest tests/test_ingest.py --cov=philanthropy.ingest._constituent_events -q
python -m coverage report --include='philanthropy/ingest/_constituent_events.py' --fail-under=100
make ci
make riskcov

The coverage report --fail-under=100 line exits non-zero until _constituent_events.py reads fully covered; make ci and make riskcov must also stay green.

First time here?

Read CONTRIBUTING.md and AGENTS.md. Add a ## [Unreleased] CHANGELOG entry and yourself to CONTRIBUTORS.md in the same PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    beginnerNo fundraising domain knowledge neededeasySmall, well-scoped, single-file changegood first issueGood for newcomershelp wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions