/**
* Integration Tests for MCP Flow
*
* End-to-end tests for the complete MCP request flow.
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { SchemaValidator } from '../../src/mcp/validation/schema-validator.js';
import { SecurityScanner } from '../../src/mcp/validation/security-scanner.js';
import { PolicyEnforcer } from '../../src/mcp/validation/policy-enforcer.js';
import { DependencyChecker } from '../../src/mcp/validation/dependency-checker.js';
describe('MCP Request Flow Integration Tests', () => {
let schemaValidator;
let securityScanner;
let policyEnforcer;
let dependencyChecker;
beforeEach(() => {
schemaValidator = new SchemaValidator();
securityScanner = new SecurityScanner();
policyEnforcer = new PolicyEnforcer();
dependencyChecker = new DependencyChecker();
});
afterEach(() => {
// Clean up after each test
});
describe('Read-Only Operation Flow', () => {
it('should complete full read-only flow successfully', async () => {
const params = {
owner: 'testowner',
repo: 'testrepo',
path: 'README.md'
};
// Step 1: Schema validation
const schemaResult = await schemaValidator.validate('fetch_file', params);
expect(schemaResult.valid).toBe(true);
// Step 2: Security scanning (no content to scan)
const securityResult = await securityScanner.scan(params);
expect(securityResult.safe).toBe(true);
// Step 3: Policy enforcement
const policyResult = await policyEnforcer.check('fetch_file', params, 'user123');
expect(policyResult.allowed).toBe(true);
// Step 4: Dependency checking (not applicable)
const depResult = await dependencyChecker.check(params);
expect(depResult.vulnerabilities).toHaveLength(0);
});
it('should fail on schema validation error', async () => {
const params = {
// Missing required fields
};
const schemaResult = await schemaValidator.validate('fetch_file', params);
expect(schemaResult.valid).toBe(false);
expect(schemaResult.errors.length).toBeGreaterThan(0);
});
});
describe('Write Operation Flow', () => {
it('should complete full write flow successfully', async () => {
const params = {
owner: 'testowner',
repo: 'testrepo',
branch: 'feature/test',
files: [
{ path: 'test.js', content: 'console.log("Hello");' }
],
message: 'Test commit'
};
// Step 1: Schema validation
const schemaResult = await schemaValidator.validate('create_commit', params);
expect(schemaResult.valid).toBe(true);
// Step 2: Security scanning
const securityResult = await securityScanner.scan(params);
expect(securityResult.safe).toBe(true);
// Step 3: Policy enforcement
const policyResult = await policyEnforcer.check('create_commit', params, 'user123');
expect(policyResult.allowed).toBe(true);
});
it('should fail on security violation', async () => {
const params = {
owner: 'testowner',
repo: 'testrepo',
branch: 'feature/test',
files: [
{
path: 'config.js',
content: 'const API_KEY="ghp_1234567890abcdefghijklmnopqrstuv";'
}
],
message: 'Add config'
};
// Schema validation should pass
const schemaResult = await schemaValidator.validate('create_commit', params);
expect(schemaResult.valid).toBe(true);
// Security scanning should detect the token
const securityResult = await securityScanner.scan(params);
expect(securityResult.safe).toBe(false);
expect(securityResult.issues.length).toBeGreaterThan(0);
});
it('should fail on policy violation', async () => {
const params = {
owner: 'testowner',
repo: 'testrepo',
branch: 'main', // Blocked branch
files: [
{ path: 'test.js', content: 'console.log("test");' }
],
message: 'Test commit'
};
// Schema validation should pass
const schemaResult = await schemaValidator.validate('create_commit', params);
expect(schemaResult.valid).toBe(true);
// Security scanning should pass
const securityResult = await securityScanner.scan(params);
expect(securityResult.safe).toBe(true);
// Policy enforcement should block
const policyResult = await policyEnforcer.check('create_commit', params, 'user123');
expect(policyResult.allowed).toBe(false);
});
});
describe('Batch Operation Flow', () => {
it('should complete batch operation flow', async () => {
const params = {
owner: 'testowner',
repo: 'testrepo',
branch: 'feature/batch',
files: Array.from({ length: 150 }, (_, i) => ({
path: `files/file${i}.js`,
content: `console.log("File ${i}");`
})),
message: 'Batch commit',
batchSize: 50
};
// Schema validation
const schemaResult = await schemaValidator.validate('batch_create_commits', params);
expect(schemaResult.valid).toBe(true);
// Security scanning
const securityResult = await securityScanner.scan(params);
expect(securityResult.safe).toBe(true);
// Policy enforcement
const policyResult = await policyEnforcer.check('batch_create_commits', params, 'user123');
expect(policyResult.allowed).toBe(true);
});
});
describe('Security Scenarios', () => {
it('should detect multiple types of secrets', async () => {
const content = `
GITHUB_TOKEN=ghp_abcdefghijklmnopqrstuvwxyz01234
AWS_KEY=AKIAXXXXXXXXXXXXXXXXXXXXXXX
API_KEY=generic_api_key_1234567890abcdef
`;
const result = await securityScanner.scan({ content });
expect(result.safe).toBe(false);
expect(result.issues.length).toBeGreaterThanOrEqual(3);
const issueTypes = result.issues.map(i => i.type);
expect(issueTypes).toContain('secret');
});
it('should detect suspicious code patterns', async () => {
const content = `
eval(userInput);
document.write(unsafeContent);
element.innerHTML = userContent;
`;
const result = await securityScanner.scan({ content });
expect(result.safe).toBe(false);
expect(result.issues.length).toBeGreaterThanOrEqual(3);
});
it('should pass for clean code', async () => {
const content = `
function greet(name) {
return 'Hello, ' + name + '!';
}
export default greet;
`;
const result = await securityScanner.scan({ content });
expect(result.safe).toBe(true);
expect(result.issues).toHaveLength(0);
});
});
describe('Policy Enforcement Scenarios', () => {
it('should enforce branch protection', async () => {
const protectedBranches = ['main', 'master', 'production'];
for (const branch of protectedBranches) {
const params = {
owner: 'test',
repo: 'testrepo',
branch
};
const result = await policyEnforcer.check('create_commit', params, 'user123');
expect(result.allowed).toBe(false);
}
});
it('should allow non-protected branches', async () => {
const allowedBranches = ['feature/test', 'bugfix/123', 'dev'];
for (const branch of allowedBranches) {
const params = {
owner: 'test',
repo: 'testrepo',
branch
};
const result = await policyEnforcer.check('create_commit', params, 'user123');
expect(result.allowed).toBe(true);
}
});
it('should enforce file path restrictions', async () => {
const blockedPaths = ['.env', 'config/secrets.json'];
for (const path of blockedPaths) {
const params = {
owner: 'test',
repo: 'testrepo',
branch: 'feature/test',
files: [{ path, content: 'content' }]
};
const result = await policyEnforcer.check('create_commit', params, 'user123');
expect(result.allowed).toBe(false);
}
});
});
describe('Error Handling Scenarios', () => {
it('should handle invalid JSON in params', async () => {
const params = {
owner: 'test',
repo: 'testrepo',
files: 'not-an-array'
};
const result = await schemaValidator.validate('create_commit', params);
expect(result.valid).toBe(false);
});
it('should handle missing required fields', async () => {
const params = {
owner: 'test'
// Missing repo, files, message
};
const result = await schemaValidator.validate('create_commit', params);
expect(result.valid).toBe(false);
expect(result.errors.length).toBeGreaterThan(2);
});
it('should handle invalid data types', async () => {
const params = {
owner: 12345, // Should be string
repo: 'test',
files: [],
message: 'test'
};
const result = await schemaValidator.validate('create_commit', params);
expect(result.valid).toBe(false);
});
});
describe('Performance Scenarios', () => {
it('should handle large content efficiently', async () => {
const largeContent = 'x'.repeat(1000000); // 1MB
const startTime = Date.now();
const result = await securityScanner.scan({ content: largeContent });
const duration = Date.now() - startTime;
// Should complete in reasonable time
expect(duration).toBeLessThan(1000);
expect(result.safe).toBe(true);
});
it('should handle many files in batch', async () => {
const files = Array.from({ length: 200 }, (_, i) => ({
path: `file${i}.js`,
content: `console.log(${i});`
}));
const startTime = Date.now();
const result = await securityScanner.scan({ files });
const duration = Date.now() - startTime;
// Should complete in reasonable time
expect(duration).toBeLessThan(2000);
expect(result.safe).toBe(true);
});
});
});