Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 11 additions & 145 deletions src/commands/logs-stats.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@

import { statsCommand, StatsCommandOptions } from './logs-stats';
import { logger } from '../logger';
import { LogSource } from '../types';
import { createLogCommandTestHarness } from './test-helpers.test-utils';
import { createLogCommandTests, createLogCommandTestHarness } from './test-helpers.test-utils';

// Mock dependencies
jest.mock('../logs/log-discovery');
Expand All @@ -20,152 +19,19 @@ jest.mock('../logger', () => ({
},
}));

describe('logs-stats command', () => {
const harness = createLogCommandTestHarness();

it('should discover and use most recent log source', async () => {
const mockSource: LogSource = {
type: 'preserved',
path: '/tmp/squid-logs-123',
timestamp: Date.now(),
dateStr: new Date().toLocaleString(),
};

harness.mockedDiscovery.discoverLogSources.mockResolvedValue([mockSource]);
harness.mockedDiscovery.selectMostRecent.mockReturnValue(mockSource);
harness.mockedAggregator.loadAndAggregate.mockResolvedValue({
totalRequests: 10,
allowedRequests: 8,
deniedRequests: 2,
uniqueDomains: 3,
byDomain: new Map(),
timeRange: { start: 1000, end: 2000 },
});
harness.mockedFormatter.formatStats.mockReturnValue('formatted output');

const options: StatsCommandOptions = {
format: 'pretty',
};

await statsCommand(options);

expect(harness.mockedDiscovery.discoverLogSources).toHaveBeenCalled();
expect(harness.mockedDiscovery.selectMostRecent).toHaveBeenCalled();
expect(harness.mockedAggregator.loadAndAggregate).toHaveBeenCalledWith(mockSource);
expect(harness.mockedFormatter.formatStats).toHaveBeenCalled();
expect(harness.mockConsoleLog).toHaveBeenCalledWith('formatted output');
});

it('should use specified source when provided', async () => {
const mockSource: LogSource = {
type: 'preserved',
path: '/custom/path',
};

harness.mockedDiscovery.discoverLogSources.mockResolvedValue([]);
harness.mockedDiscovery.validateSource.mockResolvedValue(mockSource);
harness.mockedAggregator.loadAndAggregate.mockResolvedValue({
totalRequests: 5,
allowedRequests: 5,
deniedRequests: 0,
uniqueDomains: 2,
byDomain: new Map(),
timeRange: null,
});
harness.mockedFormatter.formatStats.mockReturnValue('formatted');

const options: StatsCommandOptions = {
format: 'json',
source: '/custom/path',
};

await statsCommand(options);

expect(harness.mockedDiscovery.validateSource).toHaveBeenCalledWith('/custom/path');
expect(harness.mockedAggregator.loadAndAggregate).toHaveBeenCalledWith(mockSource);
});

it('should exit with error if no sources found', async () => {
harness.mockedDiscovery.discoverLogSources.mockResolvedValue([]);

const options: StatsCommandOptions = {
format: 'pretty',
};

await expect(statsCommand(options)).rejects.toThrow('process.exit called');
expect(harness.mockExit).toHaveBeenCalledWith(1);
});

it('should exit with error if specified source is invalid', async () => {
harness.mockedDiscovery.discoverLogSources.mockResolvedValue([]);
harness.mockedDiscovery.validateSource.mockRejectedValue(new Error('Source not found'));
createLogCommandTests<StatsCommandOptions>(
'stats',
statsCommand,
'pretty',
(overrides?) => ({ format: 'pretty', ...overrides } as StatsCommandOptions),
);

const options: StatsCommandOptions = {
format: 'pretty',
source: '/invalid/path',
};

await expect(statsCommand(options)).rejects.toThrow('process.exit called');
expect(harness.mockExit).toHaveBeenCalledWith(1);
});

it('should pass correct format to formatter', async () => {
const mockSource: LogSource = { type: 'running', containerName: 'awf-squid' };

harness.mockedDiscovery.discoverLogSources.mockResolvedValue([mockSource]);
harness.mockedDiscovery.selectMostRecent.mockReturnValue(mockSource);
harness.mockedAggregator.loadAndAggregate.mockResolvedValue({
totalRequests: 0,
allowedRequests: 0,
deniedRequests: 0,
uniqueDomains: 0,
byDomain: new Map(),
timeRange: null,
});
harness.mockedFormatter.formatStats.mockReturnValue('{}');

await statsCommand({ format: 'json' });
expect(harness.mockedFormatter.formatStats).toHaveBeenCalledWith(
expect.anything(),
'json',
expect.any(Boolean)
);

harness.mockedFormatter.formatStats.mockClear();
await statsCommand({ format: 'markdown' });
expect(harness.mockedFormatter.formatStats).toHaveBeenCalledWith(
expect.anything(),
'markdown',
expect.any(Boolean)
);

harness.mockedFormatter.formatStats.mockClear();
await statsCommand({ format: 'pretty' });
expect(harness.mockedFormatter.formatStats).toHaveBeenCalledWith(
expect.anything(),
'pretty',
expect.any(Boolean)
);
});

it('should handle aggregation errors gracefully', async () => {
const mockSource: LogSource = { type: 'running', containerName: 'awf-squid' };

harness.mockedDiscovery.discoverLogSources.mockResolvedValue([mockSource]);
harness.mockedDiscovery.selectMostRecent.mockReturnValue(mockSource);
harness.mockedAggregator.loadAndAggregate.mockRejectedValue(new Error('Failed to load'));

const options: StatsCommandOptions = {
format: 'pretty',
};

await expect(statsCommand(options)).rejects.toThrow('process.exit called');
expect(harness.mockExit).toHaveBeenCalledWith(1);
});
describe('logs-stats command - logging behavior', () => {
const harness = createLogCommandTestHarness();

it('should emit source-selection info logs for non-JSON formats but suppress them for JSON', async () => {
const mockSource: LogSource = {
type: 'preserved',
const mockSource = {
type: 'preserved' as const,
path: '/tmp/squid-logs-123',
dateStr: 'Mon Jan 01 2024',
};
Expand Down
163 changes: 13 additions & 150 deletions src/commands/logs-summary.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@

import { summaryCommand, SummaryCommandOptions } from './logs-summary';
import { logger } from '../logger';
import { LogSource } from '../types';
import { createLogCommandTestHarness } from './test-helpers.test-utils';
import { createLogCommandTests, createLogCommandTestHarness } from './test-helpers.test-utils';

// Mock dependencies
jest.mock('../logs/log-discovery');
Expand All @@ -20,44 +19,18 @@ jest.mock('../logger', () => ({
},
}));

describe('logs-summary command', () => {
const harness = createLogCommandTestHarness();

it('should discover and use most recent log source', async () => {
const mockSource: LogSource = {
type: 'preserved',
path: '/tmp/squid-logs-123',
timestamp: Date.now(),
dateStr: new Date().toLocaleString(),
};

harness.mockedDiscovery.discoverLogSources.mockResolvedValue([mockSource]);
harness.mockedDiscovery.selectMostRecent.mockReturnValue(mockSource);
harness.mockedAggregator.loadAndAggregate.mockResolvedValue({
totalRequests: 10,
allowedRequests: 8,
deniedRequests: 2,
uniqueDomains: 3,
byDomain: new Map(),
timeRange: { start: 1000, end: 2000 },
});
harness.mockedFormatter.formatStats.mockReturnValue('markdown summary');
createLogCommandTests<SummaryCommandOptions>(
'summary',
summaryCommand,
'markdown',
(overrides?) => ({ format: 'markdown', ...overrides } as SummaryCommandOptions),
);

const options: SummaryCommandOptions = {
format: 'markdown',
};

await summaryCommand(options);

expect(harness.mockedDiscovery.discoverLogSources).toHaveBeenCalled();
expect(harness.mockedDiscovery.selectMostRecent).toHaveBeenCalled();
expect(harness.mockedAggregator.loadAndAggregate).toHaveBeenCalledWith(mockSource);
expect(harness.mockedFormatter.formatStats).toHaveBeenCalled();
expect(harness.mockConsoleLog).toHaveBeenCalledWith('markdown summary');
});
describe('logs-summary command - logging behavior', () => {
const harness = createLogCommandTestHarness();

it('should default to markdown format', async () => {
const mockSource: LogSource = { type: 'running', containerName: 'awf-squid' };
const mockSource = { type: 'running' as const, containerName: 'awf-squid' };

harness.mockedDiscovery.discoverLogSources.mockResolvedValue([mockSource]);
harness.mockedDiscovery.selectMostRecent.mockReturnValue(mockSource);
Expand All @@ -77,123 +50,13 @@ describe('logs-summary command', () => {
expect(harness.mockedFormatter.formatStats).toHaveBeenCalledWith(
expect.anything(),
'markdown',
expect.any(Boolean)
expect.any(Boolean),
);
});

it('should use specified source when provided', async () => {
const mockSource: LogSource = {
type: 'preserved',
path: '/custom/path',
};

harness.mockedDiscovery.discoverLogSources.mockResolvedValue([]);
harness.mockedDiscovery.validateSource.mockResolvedValue(mockSource);
harness.mockedAggregator.loadAndAggregate.mockResolvedValue({
totalRequests: 5,
allowedRequests: 5,
deniedRequests: 0,
uniqueDomains: 2,
byDomain: new Map(),
timeRange: null,
});
harness.mockedFormatter.formatStats.mockReturnValue('formatted');

const options: SummaryCommandOptions = {
format: 'markdown',
source: '/custom/path',
};

await summaryCommand(options);

expect(harness.mockedDiscovery.validateSource).toHaveBeenCalledWith('/custom/path');
expect(harness.mockedAggregator.loadAndAggregate).toHaveBeenCalledWith(mockSource);
});

it('should exit with error if no sources found', async () => {
harness.mockedDiscovery.discoverLogSources.mockResolvedValue([]);

const options: SummaryCommandOptions = {
format: 'markdown',
};

await expect(summaryCommand(options)).rejects.toThrow('process.exit called');
expect(harness.mockExit).toHaveBeenCalledWith(1);
});

it('should exit with error if specified source is invalid', async () => {
harness.mockedDiscovery.discoverLogSources.mockResolvedValue([]);
harness.mockedDiscovery.validateSource.mockRejectedValue(new Error('Source not found'));

const options: SummaryCommandOptions = {
format: 'markdown',
source: '/invalid/path',
};

await expect(summaryCommand(options)).rejects.toThrow('process.exit called');
expect(harness.mockExit).toHaveBeenCalledWith(1);
});

it('should support all output formats', async () => {
const mockSource: LogSource = { type: 'running', containerName: 'awf-squid' };

harness.mockedDiscovery.discoverLogSources.mockResolvedValue([mockSource]);
harness.mockedDiscovery.selectMostRecent.mockReturnValue(mockSource);
harness.mockedAggregator.loadAndAggregate.mockResolvedValue({
totalRequests: 0,
allowedRequests: 0,
deniedRequests: 0,
uniqueDomains: 0,
byDomain: new Map(),
timeRange: null,
});
harness.mockedFormatter.formatStats.mockReturnValue('output');

// Test JSON format
await summaryCommand({ format: 'json' });
expect(harness.mockedFormatter.formatStats).toHaveBeenCalledWith(
expect.anything(),
'json',
expect.any(Boolean)
);

// Test markdown format
harness.mockedFormatter.formatStats.mockClear();
await summaryCommand({ format: 'markdown' });
expect(harness.mockedFormatter.formatStats).toHaveBeenCalledWith(
expect.anything(),
'markdown',
expect.any(Boolean)
);

// Test pretty format
harness.mockedFormatter.formatStats.mockClear();
await summaryCommand({ format: 'pretty' });
expect(harness.mockedFormatter.formatStats).toHaveBeenCalledWith(
expect.anything(),
'pretty',
expect.any(Boolean)
);
});

it('should handle aggregation errors gracefully', async () => {
const mockSource: LogSource = { type: 'running', containerName: 'awf-squid' };

harness.mockedDiscovery.discoverLogSources.mockResolvedValue([mockSource]);
harness.mockedDiscovery.selectMostRecent.mockReturnValue(mockSource);
harness.mockedAggregator.loadAndAggregate.mockRejectedValue(new Error('Failed to load'));

const options: SummaryCommandOptions = {
format: 'markdown',
};

await expect(summaryCommand(options)).rejects.toThrow('process.exit called');
expect(harness.mockExit).toHaveBeenCalledWith(1);
});

it('should emit source-selection info logs only for pretty format, suppress them for markdown and json', async () => {
const mockSource: LogSource = {
type: 'preserved',
const mockSource = {
type: 'preserved' as const,
path: '/tmp/squid-logs-123',
dateStr: 'Mon Jan 01 2024',
};
Expand Down
Loading
Loading