mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-11 14:40:52 -07:00
136 lines
3.9 KiB
TypeScript
136 lines
3.9 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2025 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import { describe, it, expect, beforeEach } from 'vitest';
|
|
import { ActivityLogger, type NetworkLog } from './activityLogger.js';
|
|
import type { ConsoleLogPayload } from '@google/gemini-cli-core';
|
|
|
|
describe('ActivityLogger', () => {
|
|
let logger: ActivityLogger;
|
|
|
|
beforeEach(() => {
|
|
logger = ActivityLogger.getInstance();
|
|
logger.clearBufferedLogs();
|
|
});
|
|
|
|
it('buffers the last 10 requests with all their events grouped', () => {
|
|
// Emit 15 requests, each with an initial + response event
|
|
for (let i = 0; i < 15; i++) {
|
|
const initial: NetworkLog = {
|
|
id: `req-${i}`,
|
|
timestamp: i * 2,
|
|
method: 'GET',
|
|
url: 'http://example.com',
|
|
headers: {},
|
|
pending: true,
|
|
};
|
|
logger.emitNetworkEvent(initial);
|
|
logger.emitNetworkEvent({
|
|
id: `req-${i}`,
|
|
pending: false,
|
|
response: {
|
|
status: 200,
|
|
headers: {},
|
|
body: 'ok',
|
|
durationMs: 10,
|
|
},
|
|
});
|
|
}
|
|
|
|
const logs = logger.getBufferedLogs();
|
|
// 10 requests * 2 events each = 20 events
|
|
expect(logs.network.length).toBe(20);
|
|
// Oldest kept should be req-5 (first 5 evicted)
|
|
expect(logs.network[0].id).toBe('req-5');
|
|
// Last should be req-14
|
|
expect(logs.network[19].id).toBe('req-14');
|
|
});
|
|
|
|
it('keeps all chunk events for a buffered request', () => {
|
|
// One request with many chunks
|
|
logger.emitNetworkEvent({
|
|
id: 'chunked',
|
|
timestamp: 1,
|
|
method: 'POST',
|
|
url: 'http://example.com',
|
|
headers: {},
|
|
pending: true,
|
|
});
|
|
for (let i = 0; i < 5; i++) {
|
|
logger.emitNetworkEvent({
|
|
id: 'chunked',
|
|
pending: true,
|
|
chunk: { index: i, data: `chunk-${i}`, timestamp: 2 + i },
|
|
});
|
|
}
|
|
logger.emitNetworkEvent({
|
|
id: 'chunked',
|
|
pending: false,
|
|
response: { status: 200, headers: {}, body: 'done', durationMs: 50 },
|
|
});
|
|
|
|
const logs = logger.getBufferedLogs();
|
|
// 1 initial + 5 chunks + 1 response = 7 events, all for 'chunked'
|
|
expect(logs.network.length).toBe(7);
|
|
expect(logs.network.every((l) => l.id === 'chunked')).toBe(true);
|
|
});
|
|
|
|
it('buffers only the last 10 console logs', () => {
|
|
for (let i = 0; i < 15; i++) {
|
|
const log: ConsoleLogPayload = { content: `log-${i}`, type: 'log' };
|
|
logger.logConsole(log);
|
|
}
|
|
|
|
const logs = logger.getBufferedLogs();
|
|
expect(logs.console.length).toBe(10);
|
|
expect(logs.console[0].content).toBe('log-5');
|
|
expect(logs.console[9].content).toBe('log-14');
|
|
});
|
|
|
|
it('getBufferedLogs is non-destructive', () => {
|
|
logger.logConsole({ content: 'test', type: 'log' });
|
|
const first = logger.getBufferedLogs();
|
|
const second = logger.getBufferedLogs();
|
|
expect(first.console.length).toBe(1);
|
|
expect(second.console.length).toBe(1);
|
|
});
|
|
|
|
it('clearBufferedLogs empties both buffers', () => {
|
|
logger.logConsole({ content: 'test', type: 'log' });
|
|
logger.emitNetworkEvent({
|
|
id: 'r1',
|
|
timestamp: 1,
|
|
method: 'GET',
|
|
url: 'http://example.com',
|
|
headers: {},
|
|
});
|
|
logger.clearBufferedLogs();
|
|
const logs = logger.getBufferedLogs();
|
|
expect(logs.console.length).toBe(0);
|
|
expect(logs.network.length).toBe(0);
|
|
});
|
|
|
|
it('drainBufferedLogs returns and clears atomically', () => {
|
|
logger.logConsole({ content: 'drain-test', type: 'log' });
|
|
logger.emitNetworkEvent({
|
|
id: 'r1',
|
|
timestamp: 1,
|
|
method: 'GET',
|
|
url: 'http://example.com',
|
|
headers: {},
|
|
});
|
|
|
|
const drained = logger.drainBufferedLogs();
|
|
expect(drained.console.length).toBe(1);
|
|
expect(drained.network.length).toBe(1);
|
|
|
|
// Buffer should now be empty
|
|
const after = logger.getBufferedLogs();
|
|
expect(after.console.length).toBe(0);
|
|
expect(after.network.length).toBe(0);
|
|
});
|
|
});
|