Increase code coverage for core packages (#12872)

This commit is contained in:
Megha Bansal
2025-11-11 20:06:43 -08:00
committed by GitHub
parent e8038c727f
commit 11a0a9b911
18 changed files with 2265 additions and 47 deletions

View File

@@ -0,0 +1,112 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { ReleaseChannel, getReleaseChannel } from '../../utils/channel.js';
// Mock dependencies before importing the module under test
vi.mock('../../utils/channel.js', async () => {
const actual = await vi.importActual('../../utils/channel.js');
return {
...(actual as object),
getReleaseChannel: vi.fn(),
};
});
describe('client_metadata', () => {
const originalPlatform = process.platform;
const originalArch = process.arch;
const originalCliVersion = process.env['CLI_VERSION'];
const originalNodeVersion = process.version;
beforeEach(async () => {
// Reset modules to clear the cached `clientMetadataPromise`
vi.resetModules();
// Re-import the module to get a fresh instance
await import('./client_metadata.js');
// Provide a default mock implementation for each test
vi.mocked(getReleaseChannel).mockResolvedValue(ReleaseChannel.STABLE);
});
afterEach(() => {
// Restore original process properties to avoid side-effects between tests
Object.defineProperty(process, 'platform', { value: originalPlatform });
Object.defineProperty(process, 'arch', { value: originalArch });
process.env['CLI_VERSION'] = originalCliVersion;
Object.defineProperty(process, 'version', { value: originalNodeVersion });
vi.clearAllMocks();
});
describe('getPlatform', () => {
const testCases = [
{ platform: 'darwin', arch: 'x64', expected: 'DARWIN_AMD64' },
{ platform: 'darwin', arch: 'arm64', expected: 'DARWIN_ARM64' },
{ platform: 'linux', arch: 'x64', expected: 'LINUX_AMD64' },
{ platform: 'linux', arch: 'arm64', expected: 'LINUX_ARM64' },
{ platform: 'win32', arch: 'x64', expected: 'WINDOWS_AMD64' },
{ platform: 'sunos', arch: 'x64', expected: 'PLATFORM_UNSPECIFIED' },
{ platform: 'win32', arch: 'arm', expected: 'PLATFORM_UNSPECIFIED' },
];
for (const { platform, arch, expected } of testCases) {
it(`should return ${expected} for platform ${platform} and arch ${arch}`, async () => {
Object.defineProperty(process, 'platform', { value: platform });
Object.defineProperty(process, 'arch', { value: arch });
const { getClientMetadata } = await import('./client_metadata.js');
const metadata = await getClientMetadata();
expect(metadata.platform).toBe(expected);
});
}
});
describe('getClientMetadata', () => {
it('should use CLI_VERSION for ideVersion if set', async () => {
process.env['CLI_VERSION'] = '1.2.3';
Object.defineProperty(process, 'version', { value: 'v18.0.0' });
const { getClientMetadata } = await import('./client_metadata.js');
const metadata = await getClientMetadata();
expect(metadata.ideVersion).toBe('1.2.3');
});
it('should use process.version for ideVersion as a fallback', async () => {
delete process.env['CLI_VERSION'];
Object.defineProperty(process, 'version', { value: 'v20.0.0' });
const { getClientMetadata } = await import('./client_metadata.js');
const metadata = await getClientMetadata();
expect(metadata.ideVersion).toBe('v20.0.0');
});
it('should call getReleaseChannel to get the update channel', async () => {
vi.mocked(getReleaseChannel).mockResolvedValue(ReleaseChannel.NIGHTLY);
const { getClientMetadata } = await import('./client_metadata.js');
const metadata = await getClientMetadata();
expect(metadata.updateChannel).toBe('nightly');
expect(getReleaseChannel).toHaveBeenCalled();
});
it('should cache the client metadata promise', async () => {
const { getClientMetadata } = await import('./client_metadata.js');
const firstCall = await getClientMetadata();
const secondCall = await getClientMetadata();
expect(firstCall).toBe(secondCall);
// Ensure the underlying functions are only called once
expect(getReleaseChannel).toHaveBeenCalledTimes(1);
});
it('should always return the IDE name as GEMINI_CLI', async () => {
const { getClientMetadata } = await import('./client_metadata.js');
const metadata = await getClientMetadata();
expect(metadata.ideName).toBe('GEMINI_CLI');
});
});
});

View File

@@ -0,0 +1,115 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import type { CodeAssistServer } from '../server.js';
import { getClientMetadata } from './client_metadata.js';
import type { ListExperimentsResponse, Flag } from './types.js';
// Mock dependencies before importing the module under test
vi.mock('../server.js');
vi.mock('./client_metadata.js');
describe('experiments', () => {
let mockServer: CodeAssistServer;
beforeEach(() => {
// Reset modules to clear the cached `experimentsPromise`
vi.resetModules();
// Mock the dependencies that `getExperiments` relies on
vi.mocked(getClientMetadata).mockResolvedValue({
ideName: 'GEMINI_CLI',
ideVersion: '1.0.0',
platform: 'LINUX_AMD64',
updateChannel: 'stable',
});
// Create a mock instance of the server for each test
mockServer = {
listExperiments: vi.fn(),
} as unknown as CodeAssistServer;
});
afterEach(() => {
vi.clearAllMocks();
});
it('should fetch and parse experiments from the server', async () => {
const { getExperiments } = await import('./experiments.js');
const mockApiResponse: ListExperimentsResponse = {
flags: [
{ name: 'flag1', boolValue: true },
{ name: 'flag2', stringValue: 'value' },
],
experimentIds: [123, 456],
};
vi.mocked(mockServer.listExperiments).mockResolvedValue(mockApiResponse);
const experiments = await getExperiments(mockServer);
// Verify that the dependencies were called
expect(getClientMetadata).toHaveBeenCalled();
expect(mockServer.listExperiments).toHaveBeenCalledWith(
await getClientMetadata(),
);
// Verify that the response was parsed correctly
expect(experiments.flags['flag1']).toEqual({
name: 'flag1',
boolValue: true,
});
expect(experiments.flags['flag2']).toEqual({
name: 'flag2',
stringValue: 'value',
});
expect(experiments.experimentIds).toEqual([123, 456]);
});
it('should handle an empty or partial response from the server', async () => {
const { getExperiments } = await import('./experiments.js');
const mockApiResponse: ListExperimentsResponse = {}; // No flags or experimentIds
vi.mocked(mockServer.listExperiments).mockResolvedValue(mockApiResponse);
const experiments = await getExperiments(mockServer);
expect(experiments.flags).toEqual({});
expect(experiments.experimentIds).toEqual([]);
});
it('should ignore flags that are missing a name', async () => {
const { getExperiments } = await import('./experiments.js');
const mockApiResponse: ListExperimentsResponse = {
flags: [
{ boolValue: true } as Flag, // No name
{ name: 'flag2', stringValue: 'value' },
],
};
vi.mocked(mockServer.listExperiments).mockResolvedValue(mockApiResponse);
const experiments = await getExperiments(mockServer);
expect(Object.keys(experiments.flags)).toHaveLength(1);
expect(experiments.flags['flag2']).toBeDefined();
expect(experiments.flags['undefined']).toBeUndefined();
});
it('should cache the experiments promise to avoid multiple fetches', async () => {
const { getExperiments } = await import('./experiments.js');
const mockApiResponse: ListExperimentsResponse = {
experimentIds: [1, 2, 3],
};
vi.mocked(mockServer.listExperiments).mockResolvedValue(mockApiResponse);
const firstCall = await getExperiments(mockServer);
const secondCall = await getExperiments(mockServer);
expect(firstCall).toBe(secondCall); // Should be the exact same promise object
// Verify the underlying functions were only called once
expect(getClientMetadata).toHaveBeenCalledTimes(1);
expect(mockServer.listExperiments).toHaveBeenCalledTimes(1);
});
});