mirror of
https://github.com/nexu-io/open-design.git
synced 2026-06-01 03:14:35 +07:00
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>
68 lines
2.6 KiB
TypeScript
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,
|
|
});
|
|
});
|
|
});
|