Skip to main content
Glama

MCP Xcode

by Stefan-Nitu
TestErrorInjector.ts8.13 kB
import { readFileSync, writeFileSync, existsSync } from 'fs'; import { join } from 'path'; import { createModuleLogger } from '../../../logger'; import { gitResetTestArtifacts, gitResetFile } from './gitResetTestArtifacts'; const logger = createModuleLogger('TestErrorInjector'); /** * Helper class to inject specific error conditions into test projects * for testing error handling and display */ export class TestErrorInjector { private originalFiles: Map<string, string> = new Map(); /** * Inject a compile error into a Swift file */ injectCompileError(filePath: string, errorType: 'type-mismatch' | 'syntax' | 'missing-member' = 'type-mismatch') { this.backupFile(filePath); let content = readFileSync(filePath, 'utf8'); switch (errorType) { case 'type-mismatch': // Add a type mismatch error content = content.replace( 'let newItem = Item(timestamp: Date())', 'let x: String = 123 // Type mismatch error\n let newItem = Item(timestamp: Date())' ); break; case 'syntax': // Add a syntax error content = content.replace( 'import SwiftUI', 'import SwiftUI\nlet incomplete = // Syntax error' ); break; case 'missing-member': // Reference a non-existent property content = content.replace( 'modelContext.insert(newItem)', 'modelContext.insert(newItem)\n let _ = newItem.nonExistentProperty // Missing member error' ); break; } writeFileSync(filePath, content); logger.debug({ filePath, errorType }, 'Injected compile error'); } /** * Inject multiple compile errors into a file */ injectMultipleCompileErrors(filePath: string) { if (!existsSync(filePath)) { throw new Error(`File not found: ${filePath}`); } this.backupFile(filePath); let content = readFileSync(filePath, 'utf8'); // Add multiple different types of errors content = content.replace( 'let newItem = Item(timestamp: Date())', `let x: String = 123 // Type mismatch error 1 let y: Int = "hello" // Type mismatch error 2 let z = nonExistentFunction() // Undefined function error let newItem = Item(timestamp: Date())` ); writeFileSync(filePath, content); logger.debug({ filePath }, 'Injected multiple compile errors'); } /** * Remove code signing from a project to trigger signing errors */ injectCodeSigningError(projectPath: string) { const pbxprojPath = join(projectPath, 'project.pbxproj'); if (!existsSync(pbxprojPath)) { throw new Error(`Project file not found: ${pbxprojPath}`); } this.backupFile(pbxprojPath); let content = readFileSync(pbxprojPath, 'utf8'); // Change code signing settings to trigger errors content = content.replace( /CODE_SIGN_STYLE = Automatic;/g, 'CODE_SIGN_STYLE = Manual;' ); content = content.replace( /DEVELOPMENT_TEAM = [A-Z0-9]+;/g, 'DEVELOPMENT_TEAM = "";' ); writeFileSync(pbxprojPath, content); logger.debug({ projectPath }, 'Injected code signing error'); } /** * Inject a provisioning profile error by requiring a non-existent profile */ injectProvisioningError(projectPath: string) { const pbxprojPath = join(projectPath, 'project.pbxproj'); if (!existsSync(pbxprojPath)) { throw new Error(`Project file not found: ${pbxprojPath}`); } this.backupFile(pbxprojPath); let content = readFileSync(pbxprojPath, 'utf8'); // Add a non-existent provisioning profile requirement content = content.replace( /PRODUCT_BUNDLE_IDENTIFIER = /g, 'PROVISIONING_PROFILE_SPECIFIER = "NonExistent Profile";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = ' ); writeFileSync(pbxprojPath, content); logger.debug({ projectPath }, 'Injected provisioning profile error'); } /** * Inject a missing dependency error into a Swift package */ injectMissingDependency(packagePath: string) { const packageSwiftPath = join(packagePath, 'Package.swift'); if (!existsSync(packageSwiftPath)) { throw new Error(`Package.swift not found: ${packageSwiftPath}`); } this.backupFile(packageSwiftPath); let content = readFileSync(packageSwiftPath, 'utf8'); // In Swift Package Manager, the Package initializer arguments must be in order: // name, platforms?, products, dependencies?, targets // We need to insert dependencies after products but before targets // Find the end of products array and beginning of targets // Use [\s\S]*? for non-greedy multiline matching const regex = /(products:\s*\[[\s\S]*?\])(,\s*)(targets:)/; const match = content.match(regex); if (match) { // Insert dependencies between products and targets // Use a non-existent package to trigger dependency resolution error content = content.replace( regex, `$1,\n dependencies: [\n .package(url: "https://github.com/nonexistent-org/nonexistent-package.git", from: "1.0.0")\n ]$2$3` ); } // Now add the dependency to a target so it tries to use it // Find the first target that doesn't have dependencies yet const targetRegex = /\.target\(\s*name:\s*"([^"]+)"\s*\)/; const targetMatch = content.match(targetRegex); if (targetMatch) { const targetName = targetMatch[1]; // Replace the target to add dependencies content = content.replace( targetMatch[0], `.target(\n name: "${targetName}",\n dependencies: [\n .product(name: "NonExistentPackage", package: "nonexistent-package")\n ])` ); // Also add import to a source file to trigger the error at compile time const sourcePath = join(packagePath, 'Sources', targetName); if (existsSync(sourcePath)) { const sourceFiles = require('fs').readdirSync(sourcePath, { recursive: true }) .filter((f: string) => f.endsWith('.swift')); if (sourceFiles.length > 0) { const sourceFile = join(sourcePath, sourceFiles[0]); this.backupFile(sourceFile); let sourceContent = readFileSync(sourceFile, 'utf8'); sourceContent = 'import NonExistentPackage\n' + sourceContent; writeFileSync(sourceFile, sourceContent); } } } writeFileSync(packageSwiftPath, content); logger.debug({ packagePath }, 'Injected missing dependency error'); } /** * Inject a platform compatibility error */ injectPlatformError(projectPath: string) { const pbxprojPath = join(projectPath, 'project.pbxproj'); if (!existsSync(pbxprojPath)) { throw new Error(`Project file not found: ${pbxprojPath}`); } this.backupFile(pbxprojPath); let content = readFileSync(pbxprojPath, 'utf8'); // Set incompatible deployment targets content = content.replace( /IPHONEOS_DEPLOYMENT_TARGET = \d+\.\d+;/g, 'IPHONEOS_DEPLOYMENT_TARGET = 99.0;' // Impossible iOS version ); writeFileSync(pbxprojPath, content); logger.debug({ projectPath }, 'Injected platform compatibility error'); } /** * Backup a file before modifying it */ private backupFile(filePath: string) { if (!this.originalFiles.has(filePath)) { const content = readFileSync(filePath, 'utf8'); this.originalFiles.set(filePath, content); logger.debug({ filePath }, 'Backed up original file'); } } /** * Restore all modified files to their original state */ restoreAll() { // Use git to reset all test_artifacts gitResetTestArtifacts(); // Clear our tracking this.originalFiles.clear(); } /** * Restore a specific file */ restoreFile(filePath: string) { // Use git to reset the specific file gitResetFile(filePath); // Remove from our tracking this.originalFiles.delete(filePath); } }

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/Stefan-Nitu/mcp-xcode'

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