open-design/apps/daemon/tests/plugins-events-stats.test.ts
Cursor Agent a43f34f00c
feat(plugins): od plugin events snapshot/stats + tail filters (Phase 4)
Plan KK1 + KK2 + KK3.

KK1: `od plugin events snapshot` non-SSE one-shot read.
  New GET /api/plugins/events/snapshot returns
  { events, count, generatedAt }. Supports the same ?since=<id>
  trim as the SSE route. Useful for dashboards that don't want
  to hold a long-lived connection.

KK2: `od plugin events stats` rollup helper.
  New summarisePluginEvents(events) pure helper +
  GET /api/plugins/events/stats route. Counts by kind +
  byPluginId (skipping empty plugin ids from marketplace
  events) + oldest/newest timestamps + id range. CLI
  pretty-prints the rollup with sorted-key counts so output
  is byte-deterministic.

KK3: --kind / --plugin-id filter flags on tail / snapshot.
  Both subcommands accept the same filter knobs. tail filters
  client-side post-render (so the SSE backlog still arrives
  but the renderer drops non-matching entries); snapshot does
  the same client-side filter on the JSON response. Lets ops
  write 'show me only plugin.trust-changed for slack-bot' as
  a one-liner.

CLI summary: helpers in cli.ts now reuse formatCounts /
formatTimestamp from §3.GG1 for deterministic output across
status-style commands.

Daemon tests: 1803 \u2192 1808 (+5 cases on plugins-events-stats:
zero-shape on empty buffer, byKind aggregation, byPluginId
skipping empty ids, oldest+newest+id-range, roll-up over a
pre-filtered slice respects the input order).

Co-authored-by: Tom Huang <1043269994@qq.com>
2026-05-09 18:10:50 +00:00

68 lines
2.6 KiB
TypeScript

// Plan §3.KK2 — summarisePluginEvents() pure roll-up.
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import {
__resetPluginEventBufferForTests,
pluginEventSnapshot,
recordPluginEvent,
summarisePluginEvents,
} from '../src/plugins/events.js';
beforeEach(() => __resetPluginEventBufferForTests());
afterEach(() => __resetPluginEventBufferForTests());
describe('summarisePluginEvents', () => {
it('returns zero-shape for an empty list', () => {
const stats = summarisePluginEvents([]);
expect(stats.total).toBe(0);
expect(stats.byKind).toEqual({});
expect(stats.byPluginId).toEqual({});
expect(stats.oldestAt).toBeNull();
expect(stats.newestAt).toBeNull();
expect(stats.firstId).toBeNull();
expect(stats.lastId).toBeNull();
});
it('counts byKind across the buffer', () => {
recordPluginEvent({ kind: 'plugin.installed', pluginId: 'a' });
recordPluginEvent({ kind: 'plugin.installed', pluginId: 'b' });
recordPluginEvent({ kind: 'plugin.uninstalled', pluginId: 'a' });
const stats = summarisePluginEvents(pluginEventSnapshot());
expect(stats.byKind).toEqual({
'plugin.installed': 2,
'plugin.uninstalled': 1,
});
});
it('counts byPluginId, skipping empty plugin ids (e.g. marketplace events)', () => {
recordPluginEvent({ kind: 'plugin.installed', pluginId: 'a' });
recordPluginEvent({ kind: 'plugin.installed', pluginId: 'a' });
recordPluginEvent({ kind: 'plugin.installed', pluginId: 'b' });
recordPluginEvent({ kind: 'plugin.marketplace-refreshed', pluginId: '' });
const stats = summarisePluginEvents(pluginEventSnapshot());
expect(stats.byPluginId).toEqual({ a: 2, b: 1 });
});
it('records oldestAt + newestAt + id range', () => {
recordPluginEvent({ kind: 'plugin.installed', pluginId: 'a' });
recordPluginEvent({ kind: 'plugin.upgraded', pluginId: 'a' });
const stats = summarisePluginEvents(pluginEventSnapshot());
expect(stats.firstId).toBe(1);
expect(stats.lastId).toBe(2);
expect(stats.oldestAt).toBeLessThanOrEqual(stats.newestAt!);
expect(stats.total).toBe(2);
});
it('roll-up over a filtered slice respects the input order', () => {
recordPluginEvent({ kind: 'plugin.installed', pluginId: 'a' });
recordPluginEvent({ kind: 'plugin.uninstalled', pluginId: 'a' });
recordPluginEvent({ kind: 'plugin.installed', pluginId: 'b' });
const onlyA = pluginEventSnapshot().filter((e) => e.pluginId === 'a');
const stats = summarisePluginEvents(onlyA);
expect(stats.total).toBe(2);
expect(stats.byKind).toEqual({
'plugin.installed': 1,
'plugin.uninstalled': 1,
});
});
});