mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-20 16:57:46 +00:00
pre-release commit
This commit is contained in:
62
packages/cli/src/test-utils/mockCommandContext.test.ts
Normal file
62
packages/cli/src/test-utils/mockCommandContext.test.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { vi, describe, it, expect } from 'vitest';
|
||||
import { createMockCommandContext } from './mockCommandContext.js';
|
||||
|
||||
describe('createMockCommandContext', () => {
|
||||
it('should return a valid CommandContext object with default mocks', () => {
|
||||
const context = createMockCommandContext();
|
||||
|
||||
// Just a few spot checks to ensure the structure is correct
|
||||
// and functions are mocks.
|
||||
expect(context).toBeDefined();
|
||||
expect(context.ui.addItem).toBeInstanceOf(Function);
|
||||
expect(vi.isMockFunction(context.ui.addItem)).toBe(true);
|
||||
});
|
||||
|
||||
it('should apply top-level overrides correctly', () => {
|
||||
const mockClear = vi.fn();
|
||||
const overrides = {
|
||||
ui: {
|
||||
clear: mockClear,
|
||||
},
|
||||
};
|
||||
|
||||
const context = createMockCommandContext(overrides);
|
||||
|
||||
// Call the function to see if the override was used
|
||||
context.ui.clear();
|
||||
|
||||
// Assert that our specific mock was called, not the default
|
||||
expect(mockClear).toHaveBeenCalled();
|
||||
// And that other defaults are still in place
|
||||
expect(vi.isMockFunction(context.ui.addItem)).toBe(true);
|
||||
});
|
||||
|
||||
it('should apply deeply nested overrides correctly', () => {
|
||||
// This is the most important test for factory's logic.
|
||||
const mockConfig = {
|
||||
getProjectRoot: () => '/test/project',
|
||||
getModel: () => 'gemini-pro',
|
||||
};
|
||||
|
||||
const overrides = {
|
||||
services: {
|
||||
config: mockConfig,
|
||||
},
|
||||
};
|
||||
|
||||
const context = createMockCommandContext(overrides);
|
||||
|
||||
expect(context.services.config).toBeDefined();
|
||||
expect(context.services.config?.getModel()).toBe('gemini-pro');
|
||||
expect(context.services.config?.getProjectRoot()).toBe('/test/project');
|
||||
|
||||
// Verify a default property on the same nested object is still there
|
||||
expect(context.services.logger).toBeDefined();
|
||||
});
|
||||
});
|
||||
94
packages/cli/src/test-utils/mockCommandContext.ts
Normal file
94
packages/cli/src/test-utils/mockCommandContext.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { vi } from 'vitest';
|
||||
import { CommandContext } from '../ui/commands/types.js';
|
||||
import { LoadedSettings } from '../config/settings.js';
|
||||
import { GitService } from '@google/gemini-cli-core';
|
||||
import { SessionStatsState } from '../ui/contexts/SessionContext.js';
|
||||
|
||||
// A utility type to make all properties of an object, and its nested objects, partial.
|
||||
type DeepPartial<T> = T extends object
|
||||
? {
|
||||
[P in keyof T]?: DeepPartial<T[P]>;
|
||||
}
|
||||
: T;
|
||||
|
||||
/**
|
||||
* Creates a deep, fully-typed mock of the CommandContext for use in tests.
|
||||
* All functions are pre-mocked with `vi.fn()`.
|
||||
*
|
||||
* @param overrides - A deep partial object to override any default mock values.
|
||||
* @returns A complete, mocked CommandContext object.
|
||||
*/
|
||||
export const createMockCommandContext = (
|
||||
overrides: DeepPartial<CommandContext> = {},
|
||||
): CommandContext => {
|
||||
const defaultMocks: CommandContext = {
|
||||
services: {
|
||||
config: null,
|
||||
settings: { merged: {} } as LoadedSettings,
|
||||
git: undefined as GitService | undefined,
|
||||
logger: {
|
||||
log: vi.fn(),
|
||||
logMessage: vi.fn(),
|
||||
saveCheckpoint: vi.fn(),
|
||||
loadCheckpoint: vi.fn().mockResolvedValue([]),
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} as any, // Cast because Logger is a class.
|
||||
},
|
||||
ui: {
|
||||
addItem: vi.fn(),
|
||||
clear: vi.fn(),
|
||||
setDebugMessage: vi.fn(),
|
||||
},
|
||||
session: {
|
||||
stats: {
|
||||
sessionStartTime: new Date(),
|
||||
lastPromptTokenCount: 0,
|
||||
metrics: {
|
||||
models: {},
|
||||
tools: {
|
||||
totalCalls: 0,
|
||||
totalSuccess: 0,
|
||||
totalFail: 0,
|
||||
totalDurationMs: 0,
|
||||
totalDecisions: { accept: 0, reject: 0, modify: 0 },
|
||||
byName: {},
|
||||
},
|
||||
},
|
||||
} as SessionStatsState,
|
||||
},
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const merge = (target: any, source: any): any => {
|
||||
const output = { ...target };
|
||||
|
||||
for (const key in source) {
|
||||
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
||||
const sourceValue = source[key];
|
||||
const targetValue = output[key];
|
||||
|
||||
if (
|
||||
sourceValue &&
|
||||
typeof sourceValue === 'object' &&
|
||||
!Array.isArray(sourceValue) &&
|
||||
targetValue &&
|
||||
typeof targetValue === 'object' &&
|
||||
!Array.isArray(targetValue)
|
||||
) {
|
||||
output[key] = merge(targetValue, sourceValue);
|
||||
} else {
|
||||
output[key] = sourceValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return output;
|
||||
};
|
||||
|
||||
return merge(defaultMocks, overrides);
|
||||
};
|
||||
Reference in New Issue
Block a user