tidaldb/.sdlc/features/m9-retroactive-purge/qa-plan.md

3.0 KiB

QA Plan: Retroactive Signal Purge

Test Strategy

Unit tests in the relevant modules validate individual components. Integration tests in tidal/tests/m9_retroactive_purge.rs validate the end-to-end purge flow via the public TidalDb API.

Unit Test Cases

QA-U1: Contribution log push and drain

  • Push 3 records for user A (cohort "x") and 2 for user B (cohort "x").
  • drain_for(user_A, "x") returns 3 records and leaves 2 for user B.
  • After drain, log has 2 entries.

QA-U2: Contribution log eviction

  • Create log with cap=3.
  • Push 5 records.
  • eviction_count() == 2.
  • Log length == 3 (last 3 entries).

QA-U3: PurgeCoordinator drains and retracts

  • Seed cohort ledger with 2 view signals for entity 10.
  • Push matching contribution records.
  • request_purge(user, "cohort") → score decreases, manifest has 2 entries.

QA-U4: Second purge is no-op on ledger

  • After first purge, contribution log is empty.
  • Second request_purge returns manifest with 0 entries.
  • Ledger score unchanged from first purge.

QA-U5: Manifest JSON roundtrip

  • Build a PurgeManifest with known fields.
  • to_json()from_json() restores all fields.

QA-U6: Malformed JSON returns None

  • PurgeManifest::from_json(b"") == None.
  • PurgeManifest::from_json(b"not json") == None.

Integration Test Cases

QA-I1: basic_purge_retracts_score

  • Define cohort "en_users" (locale=en).
  • User 1 (en), signals item 100 twice.
  • request_community_purge(1, "en_users") → score decreases, manifest non-empty.

QA-I2: second_purge_is_idempotent

  • User 2 signals item 200 once.
  • First purge: score decreases, manifest has 1 entry.
  • Second purge: manifest has 0 entries, score unchanged from after first purge.

QA-I3: purge_does_not_affect_other_users

  • Users 3 and 4 both signal item 300.
  • Purge user 3 only.
  • Score still positive (user 4's contribution intact).

QA-I4: manifest_persisted_and_listable

  • User 5 signals item 500, then purged.
  • list_purge_manifests(5) returns 1 manifest with correct purge_id.
  • Second purge creates second manifest; list returns 2.

QA-I5: purge_unknown_cohort_is_noop

  • User 6 exists, no cohort defined.
  • request_community_purge(6, "nonexistent") succeeds with 0-entry manifest.

QA-I6: score_never_goes_negative

  • User 7 signals once; purge applied.
  • read_decay_score(...) >= 0.0.

QA-I7: multiple_items_purged_together

  • User 8 signals items 801 and 802.
  • request_community_purge(8, "en_users") → manifest has 2 entries; both scores decrease.

QA-I8: non_cohort_member_purge_is_noop

  • User 9 (en) and user 10 (fr) signal item 900.
  • Only user 9 lands in cohort "en_users".
  • Purge user 10 from "en_users" → 0-entry manifest, score unchanged.

Coverage Requirements

  • All 8 integration tests must pass.
  • All 6 unit tests in cohort::purge::tests must pass.
  • All contribution log unit tests in cohort::contribution::tests must pass.
  • cargo test --lib 1299+ tests passing.
  • cargo fmt -- --check clean.
  • cargo build clean (no errors, no relevant warnings).