basant307/AI_Governance_Project
048
1/**2 * @license3 * Copyright 2025 Google LLC4 * SPDX-License-Identifier: Apache-2.05 */6 7import { describe, it, expect, vi, beforeEach } from 'vitest';8import { getExtensionManager, extensionToOutputString } from './utils.js';9import type { Extension, ExtensionManager } from '@qwen-code/qwen-code-core';10 11const mockRefreshCache = vi.fn();12const mockExtensionManagerInstance = {13 refreshCache: mockRefreshCache,14};15 16vi.mock('@qwen-code/qwen-code-core', async (importOriginal) => {17 const actual =18 await importOriginal<typeof import('@qwen-code/qwen-code-core')>();19 return {20 ...actual,21 ExtensionManager: vi22 .fn()23 .mockImplementation(() => mockExtensionManagerInstance),24 };25});26 27vi.mock('../../config/settings.js', () => ({28 SettingScope: {29 User: 'User',30 Workspace: 'Workspace',31 System: 'System',32 SystemDefaults: 'SystemDefaults',33 },34 loadSettings: vi.fn().mockReturnValue({35 merged: {},36 }),37}));38 39vi.mock('../../config/trustedFolders.js', () => ({40 isWorkspaceTrusted: vi.fn().mockReturnValue({ isTrusted: true }),41}));42 43vi.mock('./consent.js', () => ({44 requestConsentOrFail: vi.fn(),45 requestConsentNonInteractive: vi.fn(),46 requestChoicePluginNonInteractive: vi.fn(),47}));48 49describe('getExtensionManager', () => {50 beforeEach(() => {51 vi.clearAllMocks();52 mockRefreshCache.mockResolvedValue(undefined);53 });54 55 it('should return an ExtensionManager instance', async () => {56 const manager = await getExtensionManager();57 58 expect(manager).toBeDefined();59 expect(manager).toBe(mockExtensionManagerInstance);60 });61 62 it('should call refreshCache on the ExtensionManager', async () => {63 await getExtensionManager();64 65 expect(mockRefreshCache).toHaveBeenCalled();66 });67 68 it('should use current working directory as workspace', async () => {69 const { ExtensionManager } = await import('@qwen-code/qwen-code-core');70 71 await getExtensionManager();72 73 expect(ExtensionManager).toHaveBeenCalledWith(74 expect.objectContaining({75 workspaceDir: process.cwd(),76 }),77 );78 });79});80 81describe('extensionToOutputString', () => {82 const mockIsEnabled = vi.fn();83 const mockExtensionManager = {84 isEnabled: mockIsEnabled,85 } as unknown as ExtensionManager;86 87 const createMockExtension = (overrides = {}): Extension => ({88 id: 'test-ext-id',89 name: 'test-extension',90 version: '1.0.0',91 isActive: true,92 path: '/path/to/extension',93 contextFiles: [],94 config: { name: 'test-extension', version: '1.0.0' },95 ...overrides,96 });97 98 beforeEach(() => {99 vi.clearAllMocks();100 mockIsEnabled.mockReturnValue(true);101 });102 103 it('should include status icon when inline is false', () => {104 const extension = createMockExtension();105 const result = extensionToOutputString(106 extension,107 mockExtensionManager,108 '/workspace',109 false,110 );111 112 // Should contain either ✓ or ✗ (with ANSI color codes)113 expect(result).toMatch(/test-extension/);114 expect(result).toContain('(1.0.0)');115 });116 117 it('should exclude status icon when inline is true', () => {118 const extension = createMockExtension();119 const result = extensionToOutputString(120 extension,121 mockExtensionManager,122 '/workspace',123 true,124 );125 126 // Should start with extension name (after stripping potential whitespace)127 expect(result.trim()).toMatch(/^test-extension/);128 });129 130 it('should default inline to false', () => {131 const extension = createMockExtension();132 const resultWithoutInline = extensionToOutputString(133 extension,134 mockExtensionManager,135 '/workspace',136 );137 const resultWithInlineFalse = extensionToOutputString(138 extension,139 mockExtensionManager,140 '/workspace',141 false,142 );143 144 expect(resultWithoutInline).toEqual(resultWithInlineFalse);145 });146 147 it('should include description when present', () => {148 const extension = createMockExtension({149 config: {150 name: 'test-extension',151 version: '1.0.0',152 description: 'A helpful test extension',153 },154 });155 const result = extensionToOutputString(156 extension,157 mockExtensionManager,158 '/workspace',159 );160 161 expect(result).toContain('Description:');162 expect(result).toContain('A helpful test extension');163 });164 165 it('should strip ANSI escape codes from description', () => {166 const extension = createMockExtension({167 config: {168 name: 'test-extension',169 version: '1.0.0',170 description: '\x1b[31mMalicious\x1b[0m description',171 },172 });173 const result = extensionToOutputString(174 extension,175 mockExtensionManager,176 '/workspace',177 );178 179 expect(result).toContain('Malicious description');180 expect(result).not.toContain('\x1b[31m');181 });182 183 it('should handle non-string description gracefully', () => {184 const extension = createMockExtension({185 config: {186 name: 'test-extension',187 version: '1.0.0',188 description: 42,189 },190 });191 const result = extensionToOutputString(192 extension,193 mockExtensionManager,194 '/workspace',195 );196 197 expect(result).not.toContain('Description:');198 });199 200 it('should not include description line when absent', () => {201 const extension = createMockExtension();202 const result = extensionToOutputString(203 extension,204 mockExtensionManager,205 '/workspace',206 );207 208 expect(result).not.toContain('Description:');209 });210 211 it('should redact URL credentials in install source output', () => {212 const extension = createMockExtension({213 installMetadata: {214 type: 'git',215 source: 'https://user:token@example.com/owner/repo.git',216 },217 });218 219 const result = extensionToOutputString(220 extension,221 mockExtensionManager,222 '/workspace',223 true,224 );225 226 expect(result).toContain(227 'https://***REDACTED***@example.com/owner/repo.git',228 );229 expect(result).not.toContain('user');230 expect(result).not.toContain('token');231 });232});233 