3.4 KiB
3.4 KiB
QA Plan: Signal Revocation Controls
Scope
Unit tests in entities/revocation.rs, integration tests in
tidal/tests/m10_revocation.rs, and property tests via proptest.
1. Unit Tests (--lib)
1.1 entities/revocation.rs
| ID | Scenario | Pass Condition |
|---|---|---|
| U1 | SignalType scope, no bounds | Matches correct type; rejects other types |
| U2 | SignalType scope, since bound | Rejects signals before since_ns |
| U3 | SignalType scope, until bound | Rejects signals after until_ns |
| U4 | SignalType scope, both bounds | Window boundary inclusive on both sides |
| U5 | TimeRange scope | Inclusive on both ends; any signal type |
| U6 | AgentSession scope | Matches by session_id_raw; rejects None or wrong id |
| U7 | is_suppressed: no revocations | Returns false (fast path) |
| U8 | is_suppressed: matching active | Returns true |
| U9 | is_suppressed: inactive revocation | Returns false |
| U10 | cancel: unknown id | Returns false |
| U11 | cancel: deactivates | is_suppressed returns false after cancel |
| U12 | list: includes cancelled | Vec contains both active and cancelled |
| U13 | overlapping revocations | Union semantics — either matching returns true |
| U14 | upsert replaces existing by id | Vec length stays 1 after replacing |
| U15 | RevocationId bytes roundtrip | from_bytes(as_bytes()) == original |
1.2 signals/ledger/core.rs
| ID | Scenario | Pass Condition |
|---|---|---|
| U16 | entry_last_update_ns: no entry | Returns 0 |
| U17 | entry_last_update_ns: after signal | Returns correct timestamp |
2. Integration Tests (tidal/tests/m10_revocation.rs)
| ID | Test name | Scenario |
|---|---|---|
| I1 | basic_signal_type_revocation | Revoke "view"; decay score suppressed; "like" unaffected |
| I2 | time_range_revocation | Signals in range suppressed; outside unaffected |
| I3 | agent_session_revocation | Session signals suppressed; non-session unaffected |
| I4 | cancel_revocation_lifts_suppression | Revoke; verify suppressed; cancel; verify restored |
| I5 | revocation_survives_restart | Revoke; close; reopen; verify still suppressed |
| I6 | overlapping_revocations | Two overlapping revocations both apply |
| I7 | revoke_unknown_signal_type | Returns TidalError::Schema |
| I8 | revoke_invalid_time_range | Returns TidalError::InvalidInput |
| I9 | cancel_nonexistent_revocation | Returns false (not an error) |
| I10 | preference_vector_rebuild_after_revocation | Pref vector excludes revoked items |
3. Property Tests (proptest)
| ID | Property |
|---|---|
| P1 | No revocations → is_suppressed always false |
| P2 | scope.matches deterministic (same inputs = same output) |
| P3 | Union semantics: if any revocation matches, is_suppressed is true |
| P4 | RevocationId bytes roundtrip |
4. Static Analysis
cargo clippy --manifest-path tidal/Cargo.toml -- -D warnings— zero warningscargo fmt --manifest-path tidal/Cargo.toml --check— clean
5. Performance Sanity
is_suppressedwith 0 revocations: single DashMap shard miss, ~10nsis_suppressedwith 5 active revocations: linear scan of 5 items, ~50ns- No measurable overhead on ranking hot path for users with 0 revocations
Pass Criteria
- All lib tests pass (1299+ tests, zero failures)
- All 10 m10_revocation integration tests pass
- clippy -D warnings: zero warnings
- Revocations survive restart (I5 passes)