Skip to main content
Glama
ValidateEnvironmentHandler.test.ts8.05 kB
import { McpError } from '@modelcontextprotocol/sdk/types.js'; import { describe, test, expect, beforeEach, vi } from 'vitest'; import type { IBrunoCLI } from '../../../interfaces.js'; import * as security from '../../../security.js'; import { ValidateEnvironmentHandler } from '../../../tools/handlers/ValidateEnvironmentHandler.js'; vi.mock('../../../security.js', async () => { const actual = await vi.importActual('../../../security.js'); return { ...actual, validateToolParameters: vi.fn(), maskSecretsInError: vi.fn((error: Error) => error.message) }; }); describe('ValidateEnvironmentHandler', () => { let handler: ValidateEnvironmentHandler; let mockBrunoCLI: IBrunoCLI; const mockValidEnvironment = { valid: true, exists: true, errors: [], warnings: [], variables: { API_URL: 'https://api.example.com', API_KEY: 'test-key-123' } }; const mockInvalidEnvironment = { valid: false, exists: true, errors: ['Invalid variable syntax', 'Missing required variable: API_URL'], warnings: [], variables: {} }; const mockNonExistentEnvironment = { valid: false, exists: false, errors: ['Environment file not found: prod.bru'], warnings: [], variables: undefined }; beforeEach(() => { vi.clearAllMocks(); mockBrunoCLI = { isAvailable: vi.fn(), listRequests: vi.fn(), listEnvironments: vi.fn(), runRequest: vi.fn(), runCollection: vi.fn(), getRequestDetails: vi.fn(), validateCollection: vi.fn(), validateEnvironment: vi.fn().mockResolvedValue(mockValidEnvironment) }; handler = new ValidateEnvironmentHandler(mockBrunoCLI); vi.mocked(security.validateToolParameters).mockResolvedValue({ valid: true, errors: [] }); }); test('should return correct tool name', () => { expect(handler.getName()).toBe('bruno_validate_environment'); }); test('should validate a valid environment successfully', async () => { const result = await handler.handle({ collectionPath: '/valid/path/to/collection', environmentName: 'dev' }); expect(security.validateToolParameters).toHaveBeenCalledWith({ collectionPath: '/valid/path/to/collection', requestName: 'dev' }); expect(mockBrunoCLI.validateEnvironment).toHaveBeenCalledWith( '/valid/path/to/collection', 'dev' ); expect(result.content).toHaveLength(1); expect(result.content[0].type).toBe('text'); expect((result.content[0] as any).text).toContain('✅ Status: Valid'); }); test('should display environment variables', async () => { const result = await handler.handle({ collectionPath: '/valid/path', environmentName: 'dev' }); const output = (result.content[0] as any).text; expect(output).toContain('Variables: 2'); expect(output).toContain('API_URL: https://api.example.com'); }); test('should mask sensitive variables', async () => { const result = await handler.handle({ collectionPath: '/valid/path', environmentName: 'dev' }); const output = (result.content[0] as any).text; expect(output).toContain('API_KEY: *** (masked)'); expect(output).not.toContain('test-key-123'); }); test('should handle invalid environment', async () => { mockBrunoCLI.validateEnvironment = vi.fn().mockResolvedValue(mockInvalidEnvironment); const result = await handler.handle({ collectionPath: '/valid/path', environmentName: 'staging' }); const output = (result.content[0] as any).text; expect(output).toContain('❌ Status: Invalid'); expect(output).toContain('Invalid variable syntax'); expect(output).toContain('Missing required variable'); }); test('should handle non-existent environment', async () => { mockBrunoCLI.validateEnvironment = vi.fn().mockResolvedValue(mockNonExistentEnvironment); const result = await handler.handle({ collectionPath: '/valid/path', environmentName: 'prod' }); const output = (result.content[0] as any).text; expect(output).toContain('❌ Status: Not Found'); expect(output).toContain('Environment file not found'); }); test('should display warnings when present', async () => { const envWithWarnings = { ...mockValidEnvironment, warnings: ['Variable API_KEY contains hardcoded secret'] }; mockBrunoCLI.validateEnvironment = vi.fn().mockResolvedValue(envWithWarnings); const result = await handler.handle({ collectionPath: '/valid/path', environmentName: 'dev' }); const output = (result.content[0] as any).text; expect(output).toContain('Warnings:'); expect(output).toContain('⚠️ Variable API_KEY contains hardcoded secret'); }); test('should mask variables containing "password"', async () => { const envWithPassword = { valid: true, exists: true, errors: [], warnings: [], variables: { DB_PASSWORD: 'secret123' } }; mockBrunoCLI.validateEnvironment = vi.fn().mockResolvedValue(envWithPassword); const result = await handler.handle({ collectionPath: '/valid/path', environmentName: 'test' }); expect((result.content[0] as any).text).toContain('DB_PASSWORD: *** (masked)'); }); test('should mask variables containing "secret"', async () => { const envWithSecret = { valid: true, exists: true, errors: [], warnings: [], variables: { APP_SECRET: 'secret123' } }; mockBrunoCLI.validateEnvironment = vi.fn().mockResolvedValue(envWithSecret); const result = await handler.handle({ collectionPath: '/valid/path', environmentName: 'test' }); expect((result.content[0] as any).text).toContain('APP_SECRET: *** (masked)'); }); test('should mask variables containing "token"', async () => { const envWithToken = { valid: true, exists: true, errors: [], warnings: [], variables: { AUTH_TOKEN: 'token123' } }; mockBrunoCLI.validateEnvironment = vi.fn().mockResolvedValue(envWithToken); const result = await handler.handle({ collectionPath: '/valid/path', environmentName: 'test' }); expect((result.content[0] as any).text).toContain('AUTH_TOKEN: *** (masked)'); }); test('should throw McpError when validation fails', async () => { vi.mocked(security.validateToolParameters).mockResolvedValue({ valid: false, errors: ['Invalid parameters'] }); await expect( handler.handle({ collectionPath: '/invalid/path', environmentName: 'dev' }) ).rejects.toThrow(McpError); }); test('should throw McpError on Bruno CLI failure', async () => { mockBrunoCLI.validateEnvironment = vi.fn().mockRejectedValue( new Error('Failed to read environment file') ); await expect( handler.handle({ collectionPath: '/valid/path', environmentName: 'dev' }) ).rejects.toThrow(McpError); }); test('should call maskSecretsInError on errors', async () => { const error = new Error('Error with SECRET=abc123'); mockBrunoCLI.validateEnvironment = vi.fn().mockRejectedValue(error); vi.mocked(security.maskSecretsInError).mockReturnValue('Error with SECRET=***'); await expect( handler.handle({ collectionPath: '/valid/path', environmentName: 'dev' }) ).rejects.toThrow(); expect(security.maskSecretsInError).toHaveBeenCalledWith(error); }); test('should throw error for missing required parameters', async () => { await expect(handler.handle({})).rejects.toThrow(); await expect(handler.handle({ collectionPath: '/path' })).rejects.toThrow(); await expect(handler.handle({ environmentName: 'dev' })).rejects.toThrow(); }); test('should throw error for invalid parameter types', async () => { await expect( handler.handle({ collectionPath: 123, environmentName: 'dev' }) ).rejects.toThrow(); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jcr82/bruno-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server