Unify retained operation history in Activity
This commit is contained in:
@@ -102,6 +102,27 @@ describe('host operation history', () => {
|
||||
expect(api.listHostOperationHistory(1, 'important', { host: api.second.id }).operations.map(operation => operation.id)).toEqual([other.id]);
|
||||
});
|
||||
|
||||
it('finds an exact old operation without walking pages and keeps all scope and retention filters', async () => {
|
||||
const api = await seedHistory(1003);
|
||||
const operation = api.ids[0];
|
||||
api.handle.prepare("UPDATE ec_host_operations SET server_name='server-one' WHERE id=?").run(operation);
|
||||
const prepare = vi.spyOn(api.handle, 'prepare');
|
||||
const history = api.listHostOperationHistory(999, 'all', { operation, host: api.first.id, server: 'server-one' });
|
||||
const exactCount = prepare.mock.calls.map(([sql]) => sql).find(sql => sql.startsWith('SELECT 1 AS total'))!;
|
||||
prepare.mockRestore();
|
||||
expect(history).toMatchObject({ page: 1, total: 1, totalPages: 1 });
|
||||
expect(history.operations.map(row => row.id)).toEqual([operation]);
|
||||
expect(JSON.stringify(history)).not.toContain('secret');
|
||||
const plan = api.handle.prepare(`EXPLAIN QUERY PLAN ${exactCount}`).all(api.first.id, 'server-one', operation) as { detail: string }[];
|
||||
expect(plan.some(row => row.detail.includes('USING INDEX sqlite_autoindex_ec_host_operations_1'))).toBe(true);
|
||||
expect(api.listHostOperationHistory(1, 'all', { operation, host: api.second.id }).total).toBe(0);
|
||||
expect(api.listHostOperationHistory(1, 'all', { operation, server: 'another-server' }).total).toBe(0);
|
||||
expect(api.listHostOperationHistory(1, 'routine', { operation }).total).toBe(0);
|
||||
expect(api.listHostOperationHistory(1, 'all', { operation: 'missing-operation' }).total).toBe(0);
|
||||
api.handle.prepare('UPDATE ec_host_operations SET history_expired_at=? WHERE id=?').run(new Date().toISOString(), operation);
|
||||
expect(api.listHostOperationHistory(1, 'all', { operation })).toMatchObject({ operations: [], page: 1, total: 0 });
|
||||
});
|
||||
|
||||
it('pages all hosts in stable newest-first order, retaining deleted-host records', async () => {
|
||||
const api = await seedHistory();
|
||||
const pages = [1, 2, 3].map(page => api.listHostOperationHistory(page, 'all'));
|
||||
@@ -309,6 +330,17 @@ describe('host operation history', () => {
|
||||
expect(await request('?filter=all')).toEqual(api.listHostOperationHistory(1, 'all'));
|
||||
});
|
||||
|
||||
it('serves an exact old operation through the API without exposing command data', async () => {
|
||||
const api = await seedHistory();
|
||||
const operation = api.ids[0];
|
||||
const { GET } = await import('../../src/routes/api/hosts/operations/+server.js');
|
||||
const response = GET({ request: new Request(`https://ec.test/api/hosts/operations?filter=all&operation=${operation}`) });
|
||||
const body = await response.json();
|
||||
expect(body).toMatchObject({ page: 1, total: 1, totalPages: 1 });
|
||||
expect(body.operations.map((row: { id: string }) => row.id)).toEqual([operation]);
|
||||
expect(JSON.stringify(body)).not.toContain('secret');
|
||||
});
|
||||
|
||||
it('rejects invalid API pages and keeps host management access restrictions', async () => {
|
||||
const { GET } = await import('../../src/routes/api/hosts/operations/+server.js');
|
||||
const url = 'https://ec.test/api/hosts/operations';
|
||||
@@ -318,6 +350,9 @@ describe('host operation history', () => {
|
||||
for (const filter of ['', 'unknown', 'ALL', 'important%27%20OR%201=1']) {
|
||||
expect(() => GET({ request: new Request(`${url}?filter=${filter}`) })).toThrow(expect.objectContaining({ status: 400 }));
|
||||
}
|
||||
for (const operation of ['', 'x'.repeat(513), 'operation%00bad', 'operation%0Abad']) {
|
||||
expect(() => GET({ request: new Request(`${url}?operation=${operation}`) })).toThrow(expect.objectContaining({ status: 400 }));
|
||||
}
|
||||
const blockedHeaders: HeadersInit[] = [
|
||||
{ origin: 'https://other.test' },
|
||||
{ 'sec-fetch-site': 'cross-site' },
|
||||
|
||||
Reference in New Issue
Block a user