Download packages/cli/src/config/extensions/variables.test.ts from SaylorTwift/gemini-cli: direct link, hf CLI and curl.
- Browser
- Download file 4.34 kB
-
https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/cli/src/config/extensions/variables.test.ts
- Command line
-
hf download hf://SaylorTwift/gemini-cli/packages/cli/src/config/extensions/variables.test.ts
-
curl -L -o variables.test.ts https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/cli/src/config/extensions/variables.test.ts
4.34 kB
| /** | |
| * @license | |
| * Copyright 2025 Google LLC | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| import { expect, describe, it } from 'vitest'; | |
| import { | |
| hydrateString, | |
| recursivelyHydrateStrings, | |
| validateVariables, | |
| type VariableContext, | |
| } from './variables.js'; | |
| describe('validateVariables', () => { | |
| it('should not throw if all required variables are present', () => { | |
| const schema = { | |
| extensionPath: { type: 'string', description: 'test', required: true }, | |
| } as const; | |
| const context = { extensionPath: 'value' }; | |
| expect(() => validateVariables(context, schema)).not.toThrow(); | |
| }); | |
| it('should throw if a required variable is missing', () => { | |
| const schema = { | |
| extensionPath: { type: 'string', description: 'test', required: true }, | |
| } as const; | |
| const context = {}; | |
| expect(() => validateVariables(context, schema)).toThrow( | |
| 'Missing required variable: extensionPath', | |
| ); | |
| }); | |
| }); | |
| describe('hydrateString', () => { | |
| it('should replace a single variable', () => { | |
| const context = { | |
| extensionPath: 'path/my-extension', | |
| }; | |
| const result = hydrateString('Hello, ${extensionPath}!', context); | |
| expect(result).toBe('Hello, path/my-extension!'); | |
| }); | |
| it('should replace multiple variables', () => { | |
| const context = { | |
| extensionPath: 'path/my-extension', | |
| workspacePath: '/ws', | |
| }; | |
| const result = hydrateString( | |
| 'Ext: ${extensionPath}, WS: ${workspacePath}', | |
| context, | |
| ); | |
| expect(result).toBe('Ext: path/my-extension, WS: /ws'); | |
| }); | |
| it('should ignore unknown variables', () => { | |
| const context = { | |
| extensionPath: 'path/my-extension', | |
| }; | |
| const result = hydrateString('Hello, ${unknown}!', context); | |
| expect(result).toBe('Hello, ${unknown}!'); | |
| }); | |
| it('should handle null and undefined context values', () => { | |
| const context: VariableContext = { | |
| extensionPath: undefined, | |
| }; | |
| const result = hydrateString( | |
| 'Ext: ${extensionPath}, WS: ${workspacePath}', | |
| context, | |
| ); | |
| expect(result).toBe('Ext: ${extensionPath}, WS: ${workspacePath}'); | |
| }); | |
| }); | |
| describe('recursivelyHydrateStrings', () => { | |
| const context = { | |
| extensionPath: 'path/my-extension', | |
| workspacePath: '/ws', | |
| }; | |
| it('should hydrate strings in a flat object', () => { | |
| const obj = { | |
| a: 'Hello, ${workspacePath}', | |
| b: 'Hi, ${extensionPath}', | |
| }; | |
| const result = recursivelyHydrateStrings(obj, context); | |
| expect(result).toEqual({ | |
| a: 'Hello, /ws', | |
| b: 'Hi, path/my-extension', | |
| }); | |
| }); | |
| it('should hydrate strings in an array', () => { | |
| const arr = ['${workspacePath}', '${extensionPath}']; | |
| const result = recursivelyHydrateStrings(arr, context); | |
| expect(result).toEqual(['/ws', 'path/my-extension']); | |
| }); | |
| it('should hydrate strings in a nested object', () => { | |
| const obj = { | |
| a: 'Hello, ${workspacePath}', | |
| b: { | |
| c: 'Hi, ${extensionPath}', | |
| d: ['${workspacePath}/foo'], | |
| }, | |
| }; | |
| const result = recursivelyHydrateStrings(obj, context); | |
| expect(result).toEqual({ | |
| a: 'Hello, /ws', | |
| b: { | |
| c: 'Hi, path/my-extension', | |
| d: ['/ws/foo'], | |
| }, | |
| }); | |
| }); | |
| it('should not modify non-string values', () => { | |
| const obj = { | |
| a: 123, | |
| b: true, | |
| c: null, | |
| }; | |
| const result = recursivelyHydrateStrings(obj, context); | |
| expect(result).toEqual(obj); | |
| }); | |
| it('should not allow prototype pollution via __proto__', () => { | |
| const payload = JSON.parse('{"__proto__": {"polluted": "yes"}}'); | |
| const result = recursivelyHydrateStrings(payload, context); | |
| expect(result.polluted).toBeUndefined(); | |
| expect(Object.prototype.hasOwnProperty.call(result, 'polluted')).toBe( | |
| false, | |
| ); | |
| }); | |
| it('should not allow prototype pollution via constructor', () => { | |
| const payload = JSON.parse( | |
| '{"constructor": {"prototype": {"polluted": "yes"}}}', | |
| ); | |
| const result = recursivelyHydrateStrings(payload, context); | |
| expect(result.polluted).toBeUndefined(); | |
| }); | |
| it('should not allow prototype pollution via prototype', () => { | |
| const payload = JSON.parse('{"prototype": {"polluted": "yes"}}'); | |
| const result = recursivelyHydrateStrings(payload, context); | |
| expect(result.polluted).toBeUndefined(); | |
| }); | |
| }); | |