CoolFace
Datasetpublic

basant307/AI_Governance_Project

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
0likes48downloads
envVarResolver.test.ts298 linesDownload Raw Back to utils
1/**2 * @license3 * Copyright 2025 Google LLC4 * SPDX-License-Identifier: Apache-2.05 */6 7import { describe, it, expect, beforeEach, afterEach } from 'vitest';8import {9  resolveEnvVarsInString,10  resolveEnvVarsInObject,11} from './envVarResolver.js';12 13describe('resolveEnvVarsInString', () => {14  let originalEnv: NodeJS.ProcessEnv;15 16  beforeEach(() => {17    originalEnv = { ...process.env };18  });19 20  afterEach(() => {21    process.env = originalEnv;22  });23 24  it('should resolve $VAR_NAME format', () => {25    process.env['TEST_VAR'] = 'test-value';26 27    const result = resolveEnvVarsInString('Value is $TEST_VAR');28 29    expect(result).toBe('Value is test-value');30  });31 32  it('should resolve ${VAR_NAME} format', () => {33    process.env['TEST_VAR'] = 'test-value';34 35    const result = resolveEnvVarsInString('Value is ${TEST_VAR}');36 37    expect(result).toBe('Value is test-value');38  });39 40  it('should resolve multiple variables in the same string', () => {41    process.env['HOST'] = 'localhost';42    process.env['PORT'] = '3000';43 44    const result = resolveEnvVarsInString('URL: http://$HOST:${PORT}/api');45 46    expect(result).toBe('URL: http://localhost:3000/api');47  });48 49  it('should leave undefined variables unchanged', () => {50    const result = resolveEnvVarsInString('Value is $UNDEFINED_VAR');51 52    expect(result).toBe('Value is $UNDEFINED_VAR');53  });54 55  it('should leave undefined variables with braces unchanged', () => {56    const result = resolveEnvVarsInString('Value is ${UNDEFINED_VAR}');57 58    expect(result).toBe('Value is ${UNDEFINED_VAR}');59  });60 61  it('should handle empty string', () => {62    const result = resolveEnvVarsInString('');63 64    expect(result).toBe('');65  });66 67  it('should handle string without variables', () => {68    const result = resolveEnvVarsInString('No variables here');69 70    expect(result).toBe('No variables here');71  });72 73  it('should handle mixed defined and undefined variables', () => {74    process.env['DEFINED'] = 'value';75 76    const result = resolveEnvVarsInString('$DEFINED and $UNDEFINED mixed');77 78    expect(result).toBe('value and $UNDEFINED mixed');79  });80});81 82describe('resolveEnvVarsInObject', () => {83  let originalEnv: NodeJS.ProcessEnv;84 85  beforeEach(() => {86    originalEnv = { ...process.env };87  });88 89  afterEach(() => {90    process.env = originalEnv;91  });92 93  it('should resolve variables in nested objects', () => {94    process.env['API_KEY'] = 'secret-123';95    process.env['DB_URL'] = 'postgresql://localhost/test';96 97    const config = {98      server: {99        auth: {100          key: '$API_KEY',101        },102        database: '${DB_URL}',103      },104      port: 3000,105    };106 107    const result = resolveEnvVarsInObject(config);108 109    expect(result).toEqual({110      server: {111        auth: {112          key: 'secret-123',113        },114        database: 'postgresql://localhost/test',115      },116      port: 3000,117    });118  });119 120  it('should resolve variables in arrays', () => {121    process.env['ENV'] = 'production';122    process.env['VERSION'] = '1.0.0';123 124    const config = {125      tags: ['$ENV', 'app', '${VERSION}'],126      metadata: {127        env: '$ENV',128      },129    };130 131    const result = resolveEnvVarsInObject(config);132 133    expect(result).toEqual({134      tags: ['production', 'app', '1.0.0'],135      metadata: {136        env: 'production',137      },138    });139  });140 141  it('should preserve non-string types', () => {142    const config = {143      enabled: true,144      count: 42,145      value: null,146      data: undefined,147      tags: ['item1', 'item2'],148    };149 150    const result = resolveEnvVarsInObject(config);151 152    expect(result).toEqual(config);153  });154 155  it('should handle MCP server config structure', () => {156    process.env['API_TOKEN'] = 'token-123';157    process.env['SERVER_PORT'] = '8080';158 159    const extensionConfig = {160      name: 'test-extension',161      version: '1.0.0',162      mcpServers: {163        'test-server': {164          command: 'node',165          args: ['server.js', '--port', '${SERVER_PORT}'],166          env: {167            API_KEY: '$API_TOKEN',168            STATIC_VALUE: 'unchanged',169          },170          timeout: 5000,171        },172      },173    };174 175    const result = resolveEnvVarsInObject(extensionConfig);176 177    expect(result).toEqual({178      name: 'test-extension',179      version: '1.0.0',180      mcpServers: {181        'test-server': {182          command: 'node',183          args: ['server.js', '--port', '8080'],184          env: {185            API_KEY: 'token-123',186            STATIC_VALUE: 'unchanged',187          },188          timeout: 5000,189        },190      },191    });192  });193 194  it('should handle empty and null values', () => {195    const config = {196      empty: '',197      nullValue: null,198      undefinedValue: undefined,199      zero: 0,200      false: false,201    };202 203    const result = resolveEnvVarsInObject(config);204 205    expect(result).toEqual(config);206  });207 208  it('should handle circular references in objects without infinite recursion', () => {209    process.env['TEST_VAR'] = 'resolved-value';210 211    type ConfigWithCircularRef = {212      name: string;213      value: number;214      self?: ConfigWithCircularRef;215    };216 217    const config: ConfigWithCircularRef = {218      name: '$TEST_VAR',219      value: 42,220    };221    // Create circular reference222    config.self = config;223 224    const result = resolveEnvVarsInObject(config);225 226    expect(result.name).toBe('resolved-value');227    expect(result.value).toBe(42);228    expect(result.self).toBeDefined();229    expect(result.self?.name).toBe('$TEST_VAR'); // Circular reference should be shallow copied230    expect(result.self?.value).toBe(42);231    // Verify it doesn't create infinite recursion by checking it's not the same object232    expect(result.self).not.toBe(result);233  });234 235  it('should handle circular references in arrays without infinite recursion', () => {236    process.env['ARRAY_VAR'] = 'array-value';237 238    type ArrayWithCircularRef = Array<string | number | ArrayWithCircularRef>;239    const arr: ArrayWithCircularRef = ['$ARRAY_VAR', 123];240    // Create circular reference241    arr.push(arr);242 243    const result = resolveEnvVarsInObject(arr) as ArrayWithCircularRef;244 245    expect(result[0]).toBe('array-value');246    expect(result[1]).toBe(123);247    expect(Array.isArray(result[2])).toBe(true);248    const subArray = result[2] as ArrayWithCircularRef;249    expect(subArray[0]).toBe('$ARRAY_VAR'); // Circular reference should be shallow copied250    expect(subArray[1]).toBe(123);251    // Verify it doesn't create infinite recursion252    expect(result[2]).not.toBe(result);253  });254 255  it('should handle complex nested circular references', () => {256    process.env['NESTED_VAR'] = 'nested-resolved';257 258    type ObjWithRef = {259      name: string;260      id: number;261      ref?: ObjWithRef;262    };263 264    const obj1: ObjWithRef = { name: '$NESTED_VAR', id: 1 };265    const obj2: ObjWithRef = { name: 'static', id: 2 };266 267    // Create cross-references268    obj1.ref = obj2;269    obj2.ref = obj1;270 271    const config = {272      primary: obj1,273      secondary: obj2,274      value: '$NESTED_VAR',275    };276 277    const result = resolveEnvVarsInObject(config);278 279    expect(result.value).toBe('nested-resolved');280    expect(result.primary.name).toBe('nested-resolved');281    expect(result.primary.id).toBe(1);282    expect(result.secondary.name).toBe('static');283    expect(result.secondary.id).toBe(2);284 285    // Check that circular references are handled (shallow copied)286    expect(result.primary.ref).toBeDefined();287    expect(result.secondary.ref).toBeDefined();288    expect(result.primary.ref?.name).toBe('static'); // Should be shallow copy289    expect(result.secondary.ref?.name).toBe('nested-resolved'); // The shallow copy still gets processed290 291    // Most importantly: verify no infinite recursion by checking objects are different292    expect(result.primary.ref).not.toBe(result.secondary);293    expect(result.secondary.ref).not.toBe(result.primary);294    expect(result.primary).not.toBe(obj1); // New object created295    expect(result.secondary).not.toBe(obj2); // New object created296  });297});298 
basant307/AI_Governance_Project · CoolFace