CoolFace
Datasetpublic

basant307/AI_Governance_Project

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
0likes48downloads
consent.test.ts378 linesDownload Raw Back to extensions
1/**2 * @license3 * Copyright 2025 Google LLC4 * SPDX-License-Identifier: Apache-2.05 */6 7import { describe, it, expect, vi, beforeEach } from 'vitest';8import {9  extensionConsentString,10  requestConsentOrFail,11  requestChoicePluginNonInteractive,12} from './consent.js';13import type {14  ExtensionConfig,15  ClaudeMarketplaceConfig,16} from '@qwen-code/qwen-code-core';17import prompts from 'prompts';18 19vi.mock('../../i18n/index.js', () => ({20  t: vi.fn((str: string, params?: Record<string, string>) => {21    if (params) {22      return Object.entries(params).reduce(23        (acc, [key, value]) => acc.replace(`{{${key}}}`, value),24        str,25      );26    }27    return str;28  }),29}));30 31vi.mock('prompts');32 33describe('extensionConsentString', () => {34  it('should include extension name', () => {35    const config: ExtensionConfig = {36      name: 'test-extension',37      version: '1.0.0',38      commands: [],39    };40 41    const result = extensionConsentString(config);42 43    expect(result).toContain('Installing extension "test-extension".');44  });45 46  it('should include description when present', () => {47    const config: ExtensionConfig = {48      name: 'test-extension',49      version: '1.0.0',50      description: 'A helpful test extension',51    };52 53    const result = extensionConsentString(config);54 55    expect(result).toContain('A helpful test extension');56  });57 58  it('should strip ANSI escape codes from description', () => {59    const config: ExtensionConfig = {60      name: 'test-extension',61      version: '1.0.0',62      description: '\x1b[31mMalicious\x1b[0m description',63    };64 65    const result = extensionConsentString(config);66 67    expect(result).toContain('Malicious description');68    expect(result).not.toContain('\x1b[31m');69  });70 71  it('should handle non-string description gracefully', () => {72    const config = {73      name: 'test-extension',74      version: '1.0.0',75      description: 123,76    } as unknown as ExtensionConfig;77 78    const result = extensionConsentString(config);79 80    expect(result).not.toContain('123');81  });82 83  it('should not include description when absent', () => {84    const config: ExtensionConfig = {85      name: 'test-extension',86      version: '1.0.0',87    };88 89    const result = extensionConsentString(config);90 91    const lines = result.split('\n');92    expect(lines[0]).toContain('Installing extension "test-extension".');93    expect(lines[1]).toContain('Extensions may introduce unexpected behavior');94  });95 96  it('should include warning message', () => {97    const config: ExtensionConfig = {98      name: 'test-extension',99      version: '1.0.0',100    };101 102    const result = extensionConsentString(config);103 104    expect(result).toContain('Extensions may introduce unexpected behavior');105  });106 107  it('should include MCP servers when present', () => {108    const config: ExtensionConfig = {109      name: 'test-extension',110      version: '1.0.0',111      mcpServers: {112        'test-server': {113          command: 'node',114          args: ['server.js'],115        },116      },117    };118 119    const result = extensionConsentString(config);120 121    expect(result).toContain(122      'This extension will run the following MCP servers',123    );124    expect(result).toContain('test-server');125    expect(result).toContain('local');126    expect(result).toContain('node server.js');127  });128 129  it('should include remote MCP servers', () => {130    const config: ExtensionConfig = {131      name: 'test-extension',132      version: '1.0.0',133      mcpServers: {134        'remote-server': {135          httpUrl: 'https://example.com/mcp',136        },137      },138    };139 140    const result = extensionConsentString(config);141 142    expect(result).toContain('remote');143    expect(result).toContain('https://example.com/mcp');144  });145 146  it('should include commands when present', () => {147    const config: ExtensionConfig = {148      name: 'test-extension',149      version: '1.0.0',150    };151 152    const result = extensionConsentString(config, ['command1', 'command2']);153 154    expect(result).toContain('This extension will add the following commands');155    expect(result).toContain('command1, command2');156  });157 158  it('should include context file name when present (string)', () => {159    const config: ExtensionConfig = {160      name: 'test-extension',161      version: '1.0.0',162      contextFileName: 'CUSTOM.md',163    };164 165    const result = extensionConsentString(config);166 167    expect(result).toContain('CUSTOM.md');168  });169 170  it('should include context file name when present (array)', () => {171    const config: ExtensionConfig = {172      name: 'test-extension',173      version: '1.0.0',174      contextFileName: ['FILE1.md', 'FILE2.md'],175    };176 177    const result = extensionConsentString(config);178 179    expect(result).toContain('FILE1.md, FILE2.md');180  });181 182  it('should include skills when present', () => {183    const config: ExtensionConfig = {184      name: 'test-extension',185      version: '1.0.0',186    };187 188    const result = extensionConsentString(189      config,190      [],191      [192        {193          name: 'skill1',194          description: 'Skill 1 description',195          level: 'extension',196          filePath: '/test/skill1',197          body: 'skill body',198        },199        {200          name: 'skill2',201          description: 'Skill 2 description',202          level: 'extension',203          filePath: '/test/skill2',204          body: 'skill body',205        },206      ],207    );208 209    expect(result).toContain(210      'This extension will install the following skills',211    );212    expect(result).toContain('skill1');213    expect(result).toContain('Skill 1 description');214  });215 216  it('should include subagents when present', () => {217    const config: ExtensionConfig = {218      name: 'test-extension',219      version: '1.0.0',220    };221 222    const result = extensionConsentString(223      config,224      [],225      [],226      [227        {228          name: 'agent1',229          description: 'Agent 1 description',230          systemPrompt: 'You are agent1',231          level: 'extension',232        },233      ],234    );235 236    expect(result).toContain(237      'This extension will install the following subagents',238    );239    expect(result).toContain('agent1');240    expect(result).toContain('Agent 1 description');241  });242});243 244describe('requestConsentOrFail', () => {245  let mockRequestConsent: ReturnType<typeof vi.fn>;246 247  beforeEach(() => {248    mockRequestConsent = vi.fn();249    vi.clearAllMocks();250  });251 252  it('should do nothing when options is undefined', async () => {253    await requestConsentOrFail(mockRequestConsent, undefined);254 255    expect(mockRequestConsent).not.toHaveBeenCalled();256  });257 258  it('should request consent for new extension', async () => {259    mockRequestConsent.mockResolvedValueOnce(true);260 261    await requestConsentOrFail(mockRequestConsent, {262      extensionConfig: { name: 'test-extension', version: '1.0.0' },263      originSource: 'QwenCode',264    });265 266    expect(mockRequestConsent).toHaveBeenCalled();267  });268 269  it('should throw error when user declines consent', async () => {270    mockRequestConsent.mockResolvedValueOnce(false);271 272    await expect(273      requestConsentOrFail(mockRequestConsent, {274        extensionConfig: { name: 'test-extension', version: '1.0.0' },275        originSource: 'QwenCode',276      }),277    ).rejects.toThrow('Installation cancelled for "test-extension".');278  });279 280  it('should skip consent when consent string is unchanged', async () => {281    const extensionConfig: ExtensionConfig = {282      name: 'test-extension',283      version: '1.0.0',284    };285 286    await requestConsentOrFail(mockRequestConsent, {287      extensionConfig,288      previousExtensionConfig: extensionConfig,289      originSource: 'QwenCode',290    });291 292    expect(mockRequestConsent).not.toHaveBeenCalled();293  });294 295  it('should request consent when commands change', async () => {296    mockRequestConsent.mockResolvedValueOnce(true);297 298    await requestConsentOrFail(mockRequestConsent, {299      extensionConfig: { name: 'test-extension', version: '1.0.0' },300      commands: ['command1'],301      previousExtensionConfig: { name: 'test-extension', version: '1.0.0' },302      previousCommands: [],303      originSource: 'QwenCode',304    });305 306    expect(mockRequestConsent).toHaveBeenCalled();307  });308});309 310describe('requestChoicePluginNonInteractive', () => {311  beforeEach(() => {312    vi.clearAllMocks();313  });314 315  it('should throw error when plugins array is empty', async () => {316    const marketplace: ClaudeMarketplaceConfig = {317      name: 'test-marketplace',318      owner: { name: 'Test Owner', email: 'test@example.com' },319      plugins: [],320    };321 322    await expect(323      requestChoicePluginNonInteractive(marketplace),324    ).rejects.toThrow('No plugins available in this marketplace.');325  });326 327  it('should return selected plugin name', async () => {328    const marketplace: ClaudeMarketplaceConfig = {329      name: 'test-marketplace',330      owner: { name: 'Test Owner', email: 'test@example.com' },331      plugins: [332        {333          name: 'plugin1',334          description: 'Plugin 1',335          version: '1.0.0',336          source: 'src1',337        },338        {339          name: 'plugin2',340          description: 'Plugin 2',341          version: '1.0.0',342          source: 'src2',343        },344      ],345    };346 347    vi.mocked(prompts).mockResolvedValueOnce({ plugin: 'plugin2' });348 349    const result = await requestChoicePluginNonInteractive(marketplace);350 351    expect(result).toBe('plugin2');352    expect(prompts).toHaveBeenCalledWith(353      expect.objectContaining({354        type: 'select',355        name: 'plugin',356        choices: expect.arrayContaining([357          expect.objectContaining({ value: 'plugin1' }),358          expect.objectContaining({ value: 'plugin2' }),359        ]),360      }),361    );362  });363 364  it('should throw error when selection is cancelled', async () => {365    const marketplace: ClaudeMarketplaceConfig = {366      name: 'test-marketplace',367      owner: { name: 'Test Owner', email: 'test@example.com' },368      plugins: [{ name: 'plugin1', version: '1.0.0', source: 'src1' }],369    };370 371    vi.mocked(prompts).mockResolvedValueOnce({ plugin: undefined });372 373    await expect(374      requestChoicePluginNonInteractive(marketplace),375    ).rejects.toThrow('Plugin selection cancelled.');376  });377});378 
basant307/AI_Governance_Project · CoolFace