90 lines
3.4 KiB
Markdown
90 lines
3.4 KiB
Markdown
# 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 warnings
|
|
- `cargo fmt --manifest-path tidal/Cargo.toml --check` — clean
|
|
|
|
---
|
|
|
|
## 5. Performance Sanity
|
|
|
|
- `is_suppressed` with 0 revocations: single DashMap shard miss, ~10ns
|
|
- `is_suppressed` with 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)
|