Skip to content

moooon

Motir

Vibe your whole project. Bring an idea — Motir's three AI layers plan it, track it, and ship it, end to end. You're looking at Motir, built in Motir.

  • Vibe Project
  • Open Source
  • AI Agent
  • AI Loop
1
requests
0
upvotes
145
planned
1,361
shipped

Motir · Work items

MOTIR-319Done

5.7.7 Vitest — `Notification` model + fan-in recipients (actor-excluded, mention-deduped, registry-extensible) + the read/mark API + preference gating

Estimate: 30m · Depends on: 5.7.3, 5.7.4, 5.7.6

The integration verification across the 5.7 model + fan-in + API + preference gate (real Postgres, getSession mock only — the standing rule). The cross-cutting assertions the per-subtask tests don't fully own; the Playwright journey is 5.7.8.

Model (5.7.2): cascade behaviour (recipient/work-item delete removes rows; actor delete nulls), the partial-index unread count ignores read rows, the idempotency @@unique rejects a duplicate (eventId × recipient), cursor paging edges (walk, empty page, count).

Fan-in job (5.7.3): a work-item/mentioned event writes one row per eligible recipient with the right data payload + category: direct; the actor is excluded; a user mentioned twice → ONE row; a non-viewer recipient → no row; a replayed event → no duplicate (idempotent). Extensibility: registering a SYNTHETIC event type produces rows through the same handler with no core change (the 5.4/6.6 seam, asserted WITHOUT depending on 5.4/6.6 code); an unregistered type is a clean no-op.

API (5.7.4): list is per-user-scoped + cursor-paged + category-filterable; reading another user's notification → 404; mark-read is idempotent and returns the fresh count; mark-all-read is one bulk op returning zero.

Preference gate (5.7.6): in_app OFF for an event type → the fan-in writes no row for that user; email OFF → the 5.1.6 email job suppresses the mail (asserted via the [EMAIL]/job harness) while in-app still writes — proving BOTH channels read the same resolver; an unset preference resolves to the documented default.

Acceptance criteria

  • The Vitest matrix covers the model (cascade / partial-count / idempotency / paging), the fan-in (recipients, actor-excluded, mention-dedupe, view-recheck, idempotent, the registry-extensible synthetic-event case), the API (scoping / 404 / paging / mark-read / mark-all), and the preference gate (in_app + email both honoured, default resolution); pnpm test:coverage keeps all 5.7 files ≥90% branch/fn/line.
  • The extensibility test registers a synthetic event type and sees rows WITHOUT importing Story 5.4 / 6.6 code (proving the seam carries no forward dep).
  • The flaky-isolation rule is respected (no reliance on sibling-session DB state).

Context refs

  • tests/integration/ service-test conventions (real DB, getSession mock only) + the @inngest/test job harness (the 5.1.6 / 1.6 pattern for fan-in + DLQ assertions)
  • 5.7.2 / 5.7.3 / 5.7.4 / 5.7.6 — everything under test; the coverage gate (direct empty-input guards on new repo methods)
  • Story 5.7 description — the actor-exclude / mention-dedupe / view-recheck / channel-gate rules being asserted