chore: move notifications backend to proper DI

The send() dispatcher (services/notificationService.ts) and the in-app store
(services/inAppNotifications.ts) fold together into the DI-native
NotificationsService over DatabaseService + RealtimeService; the prefs
matrix, transports, channel registry and inAppNotificationActions stay plain
infra modules. A 1-export notifications.bridge covers scheduler, legacy
adminService/memories and the six lazy fire-and-forget sends; AdminController
and the plugin RPC host (24th factory dep) inject. The 5-tool registrar +
notifications-in-app resource move to notifications.mcp.ts. Tests move with
case IDs preserved (NSVC-*, INOTIF-*, + NSVC-020 bridge pin); ~18 suites
repoint path mocks/warm-ups; the module e2e goes DI-native.

Also carries the verified-quirk fixes that would normally trail as a
separate fix(server) commit (file-overlap with the fold): toUtcIso on all
three created_at paths (INOTIF-013/014), respond claims the response CAS
before running the action handler with release-on-failure (INOTIF-011/012),
synology's fire-and-forget send gains .catch, the dispatch-failure log
unwraps Error messages (NSVC-021), and the MCP resource description drops
its false 'unread first' claim.
This commit is contained in:
jubnl
2026-08-02 11:36:06 +02:00
parent c814653f4e
commit d203d8cccd
54 changed files with 1257 additions and 968 deletions
@@ -1,9 +1,13 @@
/**
* Unit tests for MCP notification tools:
* Unit tests for the notifications MCP surface (NotificationsMcp,
* DI-discovered since the notifications fold):
* list_notifications, get_unread_notification_count, mark_notification_read,
* mark_notification_unread, mark_all_notifications_read, delete_notification,
* delete_all_notifications.
* mark_notification_unread, mark_all_notifications_read.
* Also covers the resource trek://notifications/in-app.
*
* All of it attaches via the nest-mcp registry inside registerTools, so every
* harness here keeps withTools on (the resource is NOT registered by the
* legacy registerResources fan-out anymore).
*/
import { describe, it, expect, vi, beforeAll, beforeEach, afterAll } from 'vitest';
@@ -80,7 +84,9 @@ async function withHarness(userId: number, fn: (h: McpHarness) => Promise<void>)
}
async function withResourceHarness(userId: number, fn: (h: McpHarness) => Promise<void>) {
const h = await createMcpHarness({ userId, withTools: false, withResources: true });
// The in-app resource attaches via the nest-mcp registry (withTools), not
// the legacy registerResources fan-out.
const h = await createMcpHarness({ userId, withTools: true, withResources: false });
try { await fn(h); } finally { await h.cleanup(); }
}