Exclude maintenance history from activity log
This commit is contained in:
@@ -58,6 +58,37 @@ describe('Activity query parameters', () => {
|
||||
});
|
||||
|
||||
describe('unified retained Activity', () => {
|
||||
it('excludes everything in Maintenance history, including shared recovery campaigns, without changing that history', async () => {
|
||||
const api = await setup();
|
||||
api.maintenance(1);
|
||||
api.maintenance(2);
|
||||
api.handle.prepare("UPDATE maintenance_jobs SET kind='schedule' WHERE id=2").run();
|
||||
api.operation('shared');
|
||||
api.maintenance(3, 'failed', 'shared');
|
||||
api.link('shared', 'campaign', 'release');
|
||||
api.incident('health');
|
||||
api.incident('drift', 'drift');
|
||||
api.remote('check', { kind: 'instance.inspect', routine: 1 });
|
||||
for (const [id, trigger] of [['recovery', 'watchdog'], ['release', 'ui']]) {
|
||||
api.handle.prepare("INSERT INTO deployments(id,action,started_at,triggered_by) VALUES(?,'drift',?,?)").run(id, at, trigger);
|
||||
api.handle.prepare("INSERT INTO deployment_servers(deployment_id,server,files_changed,restart_state) VALUES(?,?,1,'done')").run(id, server);
|
||||
}
|
||||
const maintenance = api.activityHistory({ source: 'maintenance' });
|
||||
expect(maintenance.total).toBe(6);
|
||||
const query = { excludeProvisioning: true, excludeMaintenance: true, limit: 1 };
|
||||
const first = api.activityHistory(query);
|
||||
const second = api.activityHistory({ ...query, offset: 1 });
|
||||
expect(first.total).toBe(2);
|
||||
expect(second.total).toBe(2);
|
||||
expect([...first.rows, ...second.rows].map(row => row.id).sort()).toEqual([`campaign:release:${server}`, 'host:check']);
|
||||
for (const source of ['maintenance', 'recovery'] as const) {
|
||||
expect(api.activityHistory({ ...query, source }).total).toBe(0);
|
||||
}
|
||||
expect(api.activityHistory({ ...query, source: 'campaign' }).total).toBe(1);
|
||||
expect(api.activityHistory({ source: 'maintenance' })).toEqual(maintenance);
|
||||
expect(api.activityHistory({ server }).total).toBe(8);
|
||||
});
|
||||
|
||||
it('excludes provisioning jobs from log totals and pages regardless of action or trigger, while retaining contextual history', async () => {
|
||||
const api = await setup();
|
||||
for (const action of ['create', 'start', 'restart', 'rebuild', 'stop', 'destroy']) {
|
||||
@@ -233,7 +264,9 @@ describe('unified retained Activity', () => {
|
||||
const api = await setup();
|
||||
api.operation('a'); api.operation('b', { state: 'failed' });
|
||||
api.remote('failed-check', { kind: 'instance.inspect', routine: 1, state: 'failed' });
|
||||
const url = new URL('http://localhost/activity?state=failed&limit=1&excludeProvisioning=true');
|
||||
api.maintenance(1, 'failed');
|
||||
api.incident('finding', 'health', 'attention');
|
||||
const url = new URL('http://localhost/activity?state=failed&limit=1&excludeProvisioning=true&excludeMaintenance=true');
|
||||
const { GET } = await import('../../src/routes/api/activity/+server.js');
|
||||
const { load } = await import('../../src/routes/activity/+page.server.js');
|
||||
const response = GET({ url });
|
||||
@@ -241,5 +274,6 @@ describe('unified retained Activity', () => {
|
||||
expect(await response.json()).toEqual(JSON.parse(JSON.stringify(load({ url }).activity)));
|
||||
expect(load({ url }).activity.rows[0].id).toBe('host:failed-check');
|
||||
expect(load({ url: new URL('http://localhost/activity?excludeProvisioning=false') }).activity.rows.some(row => row.id.startsWith('provisioning:'))).toBe(false);
|
||||
expect(load({ url: new URL('http://localhost/activity?excludeMaintenance=false') }).activity.rows.map(row => row.id)).toEqual(['host:failed-check']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -46,6 +46,18 @@ it('keeps provisioning excluded from log polling after filtering or clearing fil
|
||||
expect(activityQuery(new URL('http://localhost/servers/town?tab=history')).excludeProvisioning).toBeUndefined();
|
||||
});
|
||||
|
||||
it('keeps maintenance excluded from log polling while preserving the Maintenance history scope', () => {
|
||||
const url = new URL('http://localhost/activity?source=maintenance&excludeMaintenance=false');
|
||||
const logScope = { excludeProvisioning: true, excludeMaintenance: true };
|
||||
expect(new URL(activityApiHref(url, logScope), url).searchParams.get('excludeMaintenance')).toBe('true');
|
||||
const cleared = new URL(activityHistoryHref(url, { type: '', source: '', offset: '' }), url);
|
||||
expect(activityQuery(cleared, logScope).excludeMaintenance).toBe(true);
|
||||
const maintenance = new URL('http://localhost/maintenance?view=activity');
|
||||
const query = new URL(activityApiHref(maintenance, { source: 'maintenance', excludeMaintenance: false }), maintenance).searchParams;
|
||||
expect(query.get('source')).toBe('maintenance');
|
||||
expect(query.has('excludeMaintenance')).toBe(false);
|
||||
});
|
||||
|
||||
it('shows relative recent timestamps and preserves accurate older dates and invalid evidence', () => {
|
||||
const now = Date.parse('2026-10-07T18:00:00Z');
|
||||
expect(activityTimeLabel('2026-10-07 17:59:45', 'America/New_York', now)).toBe('15 s ago');
|
||||
@@ -61,6 +73,8 @@ it('renders one page heading with provisioning management and filters removed',
|
||||
expect(body.match(/<h1[ >]/g)).toHaveLength(1); expect(body).not.toContain('Manage servers'); expect(body).not.toContain('href="/provisioning"');
|
||||
expect(body).not.toContain('<option value="create"'); expect(body).not.toContain('<option value="provisioning"');
|
||||
expect(body).not.toContain('<option value="restart"');
|
||||
for (const value of ['update', 'health', 'maintenance', 'recovery']) expect(body).not.toContain(`<option value="${value}"`);
|
||||
expect(body).toContain('<option value="drift"');
|
||||
const viewer = render(ActivityPage, { props: { data: { activity: report(), user: { role: 'viewer' }, setupRequired: false } as any } }).body;
|
||||
expect(viewer).not.toContain('Manage servers');
|
||||
});
|
||||
@@ -91,6 +105,7 @@ it('offers one mobile filter sheet with distinct IDs and hides fixed scope contr
|
||||
scope('/maintenance?view=activity&state=failed');
|
||||
const fixed = render(ActivityLog, { props: { initial: report(), server: row.server!, source: 'maintenance' } }).body;
|
||||
expect(fixed).not.toContain('id="activity-server'); expect(fixed).not.toContain('id="activity-source');
|
||||
expect(fixed).toContain('<option value="update"'); expect(fixed).toContain('<option value="health"');
|
||||
expect(fixed).toContain('view=activity'); expect(fixed).toContain('state=failed'); expect(fixed).toContain('Filters · 1');
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user