mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-09 04:41:19 -07:00
240 lines
8.5 KiB
TypeScript
240 lines
8.5 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2025 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
import type { Config } from '../config/config.js';
|
|
import { initializeTelemetry, shutdownTelemetry } from './sdk.js';
|
|
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';
|
|
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-grpc';
|
|
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc';
|
|
import { OTLPTraceExporter as OTLPTraceExporterHttp } from '@opentelemetry/exporter-trace-otlp-http';
|
|
import { OTLPLogExporter as OTLPLogExporterHttp } from '@opentelemetry/exporter-logs-otlp-http';
|
|
import { OTLPMetricExporter as OTLPMetricExporterHttp } from '@opentelemetry/exporter-metrics-otlp-http';
|
|
import { NodeSDK } from '@opentelemetry/sdk-node';
|
|
import {
|
|
GcpTraceExporter,
|
|
GcpLogExporter,
|
|
GcpMetricExporter,
|
|
} from './gcp-exporters.js';
|
|
import { TelemetryTarget } from './index.js';
|
|
|
|
import * as os from 'node:os';
|
|
import * as path from 'node:path';
|
|
|
|
vi.mock('@opentelemetry/exporter-trace-otlp-grpc');
|
|
vi.mock('@opentelemetry/exporter-logs-otlp-grpc');
|
|
vi.mock('@opentelemetry/exporter-metrics-otlp-grpc');
|
|
vi.mock('@opentelemetry/exporter-trace-otlp-http');
|
|
vi.mock('@opentelemetry/exporter-logs-otlp-http');
|
|
vi.mock('@opentelemetry/exporter-metrics-otlp-http');
|
|
vi.mock('@opentelemetry/sdk-node');
|
|
vi.mock('./gcp-exporters.js');
|
|
|
|
describe('Telemetry SDK', () => {
|
|
let mockConfig: Config;
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mockConfig = {
|
|
getTelemetryEnabled: () => true,
|
|
getTelemetryOtlpEndpoint: () => 'http://localhost:4317',
|
|
getTelemetryOtlpProtocol: () => 'grpc',
|
|
getTelemetryTarget: () => 'local',
|
|
getTelemetryUseCollector: () => false,
|
|
getTelemetryOutfile: () => undefined,
|
|
getDebugMode: () => false,
|
|
getSessionId: () => 'test-session',
|
|
} as unknown as Config;
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await shutdownTelemetry(mockConfig);
|
|
});
|
|
|
|
it('should use gRPC exporters when protocol is grpc', () => {
|
|
initializeTelemetry(mockConfig);
|
|
|
|
expect(OTLPTraceExporter).toHaveBeenCalledWith({
|
|
url: 'http://localhost:4317',
|
|
compression: 'gzip',
|
|
});
|
|
expect(OTLPLogExporter).toHaveBeenCalledWith({
|
|
url: 'http://localhost:4317',
|
|
compression: 'gzip',
|
|
});
|
|
expect(OTLPMetricExporter).toHaveBeenCalledWith({
|
|
url: 'http://localhost:4317',
|
|
compression: 'gzip',
|
|
});
|
|
expect(NodeSDK.prototype.start).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should use HTTP exporters when protocol is http', () => {
|
|
vi.spyOn(mockConfig, 'getTelemetryEnabled').mockReturnValue(true);
|
|
vi.spyOn(mockConfig, 'getTelemetryOtlpProtocol').mockReturnValue('http');
|
|
vi.spyOn(mockConfig, 'getTelemetryOtlpEndpoint').mockReturnValue(
|
|
'http://localhost:4318',
|
|
);
|
|
|
|
initializeTelemetry(mockConfig);
|
|
|
|
expect(OTLPTraceExporterHttp).toHaveBeenCalledWith({
|
|
url: 'http://localhost:4318/',
|
|
});
|
|
expect(OTLPLogExporterHttp).toHaveBeenCalledWith({
|
|
url: 'http://localhost:4318/',
|
|
});
|
|
expect(OTLPMetricExporterHttp).toHaveBeenCalledWith({
|
|
url: 'http://localhost:4318/',
|
|
});
|
|
expect(NodeSDK.prototype.start).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should parse gRPC endpoint correctly', () => {
|
|
vi.spyOn(mockConfig, 'getTelemetryOtlpEndpoint').mockReturnValue(
|
|
'https://my-collector.com',
|
|
);
|
|
initializeTelemetry(mockConfig);
|
|
expect(OTLPTraceExporter).toHaveBeenCalledWith(
|
|
expect.objectContaining({ url: 'https://my-collector.com' }),
|
|
);
|
|
});
|
|
|
|
it('should parse HTTP endpoint correctly', () => {
|
|
vi.spyOn(mockConfig, 'getTelemetryOtlpProtocol').mockReturnValue('http');
|
|
vi.spyOn(mockConfig, 'getTelemetryOtlpEndpoint').mockReturnValue(
|
|
'https://my-collector.com',
|
|
);
|
|
initializeTelemetry(mockConfig);
|
|
expect(OTLPTraceExporterHttp).toHaveBeenCalledWith(
|
|
expect.objectContaining({ url: 'https://my-collector.com/' }),
|
|
);
|
|
});
|
|
|
|
it('should use direct GCP exporters when target is gcp, project ID is set, and useCollector is false', () => {
|
|
vi.spyOn(mockConfig, 'getTelemetryTarget').mockReturnValue(
|
|
TelemetryTarget.GCP,
|
|
);
|
|
vi.spyOn(mockConfig, 'getTelemetryUseCollector').mockReturnValue(false);
|
|
vi.spyOn(mockConfig, 'getTelemetryOtlpEndpoint').mockReturnValue('');
|
|
|
|
const originalEnv = process.env['OTLP_GOOGLE_CLOUD_PROJECT'];
|
|
process.env['OTLP_GOOGLE_CLOUD_PROJECT'] = 'test-project';
|
|
|
|
try {
|
|
initializeTelemetry(mockConfig);
|
|
|
|
expect(GcpTraceExporter).toHaveBeenCalledWith('test-project');
|
|
expect(GcpLogExporter).toHaveBeenCalledWith('test-project');
|
|
expect(GcpMetricExporter).toHaveBeenCalledWith('test-project');
|
|
expect(NodeSDK.prototype.start).toHaveBeenCalled();
|
|
} finally {
|
|
if (originalEnv) {
|
|
process.env['OTLP_GOOGLE_CLOUD_PROJECT'] = originalEnv;
|
|
} else {
|
|
delete process.env['OTLP_GOOGLE_CLOUD_PROJECT'];
|
|
}
|
|
}
|
|
});
|
|
|
|
it('should use OTLP exporters when target is gcp but useCollector is true', () => {
|
|
vi.spyOn(mockConfig, 'getTelemetryTarget').mockReturnValue(
|
|
TelemetryTarget.GCP,
|
|
);
|
|
vi.spyOn(mockConfig, 'getTelemetryUseCollector').mockReturnValue(true);
|
|
|
|
initializeTelemetry(mockConfig);
|
|
|
|
expect(OTLPTraceExporter).toHaveBeenCalledWith({
|
|
url: 'http://localhost:4317',
|
|
compression: 'gzip',
|
|
});
|
|
expect(OTLPLogExporter).toHaveBeenCalledWith({
|
|
url: 'http://localhost:4317',
|
|
compression: 'gzip',
|
|
});
|
|
expect(OTLPMetricExporter).toHaveBeenCalledWith({
|
|
url: 'http://localhost:4317',
|
|
compression: 'gzip',
|
|
});
|
|
});
|
|
|
|
it('should not use GCP exporters when project ID environment variable is not set', () => {
|
|
vi.spyOn(mockConfig, 'getTelemetryTarget').mockReturnValue(
|
|
TelemetryTarget.GCP,
|
|
);
|
|
vi.spyOn(mockConfig, 'getTelemetryUseCollector').mockReturnValue(false);
|
|
vi.spyOn(mockConfig, 'getTelemetryOtlpEndpoint').mockReturnValue('');
|
|
|
|
const originalOtlpEnv = process.env['OTLP_GOOGLE_CLOUD_PROJECT'];
|
|
const originalGoogleEnv = process.env['GOOGLE_CLOUD_PROJECT'];
|
|
delete process.env['OTLP_GOOGLE_CLOUD_PROJECT'];
|
|
delete process.env['GOOGLE_CLOUD_PROJECT'];
|
|
|
|
try {
|
|
initializeTelemetry(mockConfig);
|
|
|
|
expect(GcpTraceExporter).not.toHaveBeenCalled();
|
|
expect(GcpLogExporter).not.toHaveBeenCalled();
|
|
expect(GcpMetricExporter).not.toHaveBeenCalled();
|
|
expect(NodeSDK.prototype.start).toHaveBeenCalled();
|
|
} finally {
|
|
if (originalOtlpEnv) {
|
|
process.env['OTLP_GOOGLE_CLOUD_PROJECT'] = originalOtlpEnv;
|
|
}
|
|
if (originalGoogleEnv) {
|
|
process.env['GOOGLE_CLOUD_PROJECT'] = originalGoogleEnv;
|
|
}
|
|
}
|
|
});
|
|
|
|
it('should use GOOGLE_CLOUD_PROJECT as fallback when OTLP_GOOGLE_CLOUD_PROJECT is not set', () => {
|
|
vi.spyOn(mockConfig, 'getTelemetryTarget').mockReturnValue(
|
|
TelemetryTarget.GCP,
|
|
);
|
|
vi.spyOn(mockConfig, 'getTelemetryUseCollector').mockReturnValue(false);
|
|
vi.spyOn(mockConfig, 'getTelemetryOtlpEndpoint').mockReturnValue('');
|
|
|
|
const originalOtlpEnv = process.env['OTLP_GOOGLE_CLOUD_PROJECT'];
|
|
const originalGoogleEnv = process.env['GOOGLE_CLOUD_PROJECT'];
|
|
delete process.env['OTLP_GOOGLE_CLOUD_PROJECT'];
|
|
process.env['GOOGLE_CLOUD_PROJECT'] = 'fallback-project';
|
|
|
|
try {
|
|
initializeTelemetry(mockConfig);
|
|
|
|
expect(GcpTraceExporter).toHaveBeenCalledWith('fallback-project');
|
|
expect(GcpLogExporter).toHaveBeenCalledWith('fallback-project');
|
|
expect(GcpMetricExporter).toHaveBeenCalledWith('fallback-project');
|
|
expect(NodeSDK.prototype.start).toHaveBeenCalled();
|
|
} finally {
|
|
if (originalOtlpEnv) {
|
|
process.env['OTLP_GOOGLE_CLOUD_PROJECT'] = originalOtlpEnv;
|
|
}
|
|
if (originalGoogleEnv) {
|
|
process.env['GOOGLE_CLOUD_PROJECT'] = originalGoogleEnv;
|
|
} else {
|
|
delete process.env['GOOGLE_CLOUD_PROJECT'];
|
|
}
|
|
}
|
|
});
|
|
|
|
it('should not use OTLP exporters when telemetryOutfile is set', () => {
|
|
vi.spyOn(mockConfig, 'getTelemetryOutfile').mockReturnValue(
|
|
path.join(os.tmpdir(), 'test.log'),
|
|
);
|
|
initializeTelemetry(mockConfig);
|
|
|
|
expect(OTLPTraceExporter).not.toHaveBeenCalled();
|
|
expect(OTLPLogExporter).not.toHaveBeenCalled();
|
|
expect(OTLPMetricExporter).not.toHaveBeenCalled();
|
|
expect(OTLPTraceExporterHttp).not.toHaveBeenCalled();
|
|
expect(OTLPLogExporterHttp).not.toHaveBeenCalled();
|
|
expect(OTLPMetricExporterHttp).not.toHaveBeenCalled();
|
|
expect(NodeSDK.prototype.start).toHaveBeenCalled();
|
|
});
|
|
});
|