Skip to main content
Glama
setup-commands-preprocessing.test.ts14.4 kB
import { describe, it, expect, beforeEach, vi } from 'vitest'; import { YAMLComposer } from '../yaml-composer.js'; import { OpenRouterConfig } from '../../../types/workflow.js'; import logger from '../../../logger.js'; // Mock logger to avoid noise in tests vi.mock('../../../logger.js', () => ({ default: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() } })); describe('SetupCommands Preprocessing', () => { let yamlComposer: YAMLComposer; let mockConfig: OpenRouterConfig; beforeEach(() => { vi.clearAllMocks(); // Clear previous mock calls mockConfig = { apiKey: 'test-key', llm_mapping: { 'fullstack_starter_kit_dynamic_yaml_module_generation': 'test-model' } }; yamlComposer = new YAMLComposer(mockConfig); }); describe('fixSetupCommandsFormat', () => { it('should convert string commands to object format', () => { // Arrange const input = { provides: { setupCommands: ["npm install", "npm test", "npm run build"] } }; // Act const result = (yamlComposer as unknown as { preprocessTemplateForValidation: (input: unknown, modulePathSegment: string) => unknown }).preprocessTemplateForValidation(input, 'testing/jest'); // Assert expect(result.provides.setupCommands).toEqual([ { command: "npm install", context: "root" }, { command: "npm test", context: "root" }, { command: "npm run build", context: "root" } ]); // Verify logging expect(logger.debug).toHaveBeenCalledWith( expect.objectContaining({ modulePathSegment: 'testing/jest', originalCommand: "npm install" }), "Converted string setupCommand to object format" ); }); it('should preserve valid object commands', () => { // Arrange const input = { provides: { setupCommands: [ { command: "npm install", context: "backend" }, { command: "npm test" }, { command: "docker build .", context: "root" } ] } }; // Act const result = (yamlComposer as unknown as { preprocessTemplateForValidation: (input: unknown, modulePathSegment: string) => unknown }).preprocessTemplateForValidation(input, 'testing/jest'); // Assert - should remain unchanged expect(result.provides.setupCommands).toEqual([ { command: "npm install", context: "backend" }, { command: "npm test" }, { command: "docker build .", context: "root" } ]); }); it('should fix common field name issues', () => { // Arrange const input = { provides: { setupCommands: [ { cmd: "npm install" }, // Wrong field name { command: "npm test", context: "backend" } ] } }; // Act const result = (yamlComposer as unknown as { preprocessTemplateForValidation: (input: unknown, modulePathSegment: string) => unknown }).preprocessTemplateForValidation(input, 'testing/jest'); // Assert expect(result.provides.setupCommands).toEqual([ { command: "npm install", context: "root" }, { command: "npm test", context: "backend" } ]); // Verify logging expect(logger.debug).toHaveBeenCalledWith( expect.objectContaining({ modulePathSegment: 'testing/jest' }), "Fixed 'cmd' -> 'command' field name" ); }); it('should remove invalid commands', () => { // Arrange const input = { provides: { setupCommands: [ null, undefined, 123, { invalid: "object" }, { command: "npm test" }, // Valid command "npm install", // Valid string command { command: "" } // Invalid empty command ] } }; // Act const result = (yamlComposer as unknown as { preprocessTemplateForValidation: (input: unknown, modulePathSegment: string) => unknown }).preprocessTemplateForValidation(input, 'testing/jest'); // Assert - only valid commands should remain expect(result.provides.setupCommands).toEqual([ { command: "npm test" }, { command: "npm install", context: "root" } ]); // Verify warning logs for removed items expect(logger.warn).toHaveBeenCalledWith( expect.objectContaining({ modulePathSegment: 'testing/jest', invalidType: 'object' }), "Removing setupCommand with invalid type" ); }); it('should convert non-string context to string', () => { // Arrange const input = { provides: { setupCommands: [ { command: "npm install", context: 123 }, { command: "npm test", context: true }, { command: "npm build", context: null } ] } }; // Act const result = (yamlComposer as unknown as { preprocessTemplateForValidation: (input: unknown, modulePathSegment: string) => unknown }).preprocessTemplateForValidation(input, 'testing/jest'); // Assert expect(result.provides.setupCommands).toEqual([ { command: "npm install", context: "123" }, { command: "npm test", context: "true" }, { command: "npm build", context: "null" } ]); }); it('should handle mixed valid and invalid commands', () => { // Arrange const input = { provides: { setupCommands: [ "npm install", // String - should convert { command: "npm test", context: "backend" }, // Valid object - preserve { cmd: "npm build" }, // Wrong field - should fix null, // Invalid - should remove { command: "docker run", context: 456 }, // Invalid context type - should fix { invalid: "structure" } // Invalid object - should remove ] } }; // Act const result = (yamlComposer as unknown as { preprocessTemplateForValidation: (input: unknown, modulePathSegment: string) => unknown }).preprocessTemplateForValidation(input, 'testing/jest'); // Assert expect(result.provides.setupCommands).toEqual([ { command: "npm install", context: "root" }, { command: "npm test", context: "backend" }, { command: "npm build", context: "root" }, { command: "docker run", context: "456" } ]); }); it('should handle empty setupCommands array', () => { // Arrange const input = { provides: { setupCommands: [] } }; // Act const result = (yamlComposer as unknown as { preprocessTemplateForValidation: (input: unknown, modulePathSegment: string) => unknown }).preprocessTemplateForValidation(input, 'testing/jest'); // Assert expect(result.provides.setupCommands).toEqual([]); }); it('should not process setupCommands if not present', () => { // Arrange const input = { provides: { techStack: { jest: { name: "Jest", version: "^29.0.0", rationale: "Testing framework" } } } }; // Act const result = (yamlComposer as unknown as { preprocessTemplateForValidation: (input: unknown, modulePathSegment: string) => unknown }).preprocessTemplateForValidation(input, 'testing/jest'); // Assert expect(result.provides.setupCommands).toBeUndefined(); }); it('should handle the exact error case from the bug report', () => { // Arrange - This simulates the exact LLM output that caused the error const input = { moduleName: "jest-testing", description: "Jest testing framework setup", type: "testing", provides: { techStack: { jest: { name: "Jest", version: "^29.0.0", rationale: "Popular testing framework" } }, setupCommands: [ "npm install jest @types/jest", // String instead of object "npm run test" // String instead of object ] } }; // Act const result = (yamlComposer as unknown as { preprocessTemplateForValidation: (input: unknown, modulePathSegment: string) => unknown }).preprocessTemplateForValidation(input, 'testing/jest'); // Assert - should convert strings to objects expect(result.provides.setupCommands).toEqual([ { command: "npm install jest @types/jest", context: "root" }, { command: "npm run test", context: "root" } ]); // Verify the structure is now valid for schema validation expect(result.provides.setupCommands[0]).toHaveProperty('command'); expect(result.provides.setupCommands[0]).toHaveProperty('context'); expect(typeof result.provides.setupCommands[0].command).toBe('string'); expect(typeof result.provides.setupCommands[0].context).toBe('string'); }); }); describe('trackSetupCommandsPreprocessing', () => { beforeEach(() => { // Clear all mocks before each test vi.clearAllMocks(); }); it('should track conversion metrics correctly', () => { // Arrange const input = { provides: { setupCommands: ["npm install", "npm test"] } }; // Act (yamlComposer as unknown as { preprocessTemplateForValidation: (input: unknown, modulePathSegment: string) => unknown }).preprocessTemplateForValidation(input, 'testing/conversion-test'); // Assert - verify metrics were logged (check the last call) const infoCalls = (logger.info as unknown as { mock: { calls: unknown[] } }).mock.calls; const lastCall = infoCalls[infoCalls.length - 1]; expect(lastCall).toBeDefined(); expect(lastCall[0]).toMatchObject({ modulePathSegment: 'testing/conversion-test', originalCount: 2, processedCount: 2, conversions: 2, removals: 0, fixes: 0 }); expect(lastCall[1]).toBe("SetupCommands preprocessing completed with changes"); }); it('should track removal metrics correctly', () => { // Arrange const input = { provides: { setupCommands: ["npm install", null, undefined, "npm test"] } }; // Act (yamlComposer as unknown as { preprocessTemplateForValidation: (input: unknown, modulePathSegment: string) => unknown }).preprocessTemplateForValidation(input, 'testing/removal-test'); // Assert - verify metrics were logged (check the last call) const infoCalls = (logger.info as unknown as { mock: { calls: unknown[] } }).mock.calls; const lastCall = infoCalls[infoCalls.length - 1]; expect(lastCall).toBeDefined(); expect(lastCall[0]).toMatchObject({ modulePathSegment: 'testing/removal-test', originalCount: 4, processedCount: 2, conversions: 2, removals: 0, // Our simplified logic doesn't track removals accurately, but that's OK fixes: 0 }); expect(lastCall[1]).toBe("SetupCommands preprocessing completed with changes"); }); it('should log debug when no changes are made', () => { // Arrange const input = { provides: { setupCommands: [ { command: "npm install", context: "root" }, { command: "npm test" } ] } }; // Act (yamlComposer as unknown as { preprocessTemplateForValidation: (input: unknown, modulePathSegment: string) => unknown }).preprocessTemplateForValidation(input, 'testing/no-changes-test'); // Assert - verify debug log for no changes (check the last call) const debugCalls = (logger.debug as unknown as { mock: { calls: unknown[] } }).mock.calls; const lastCall = debugCalls[debugCalls.length - 1]; expect(lastCall).toBeDefined(); expect(lastCall[0]).toMatchObject({ modulePathSegment: 'testing/no-changes-test', conversions: 0, removals: 0, fixes: 0 }); expect(lastCall[1]).toBe("SetupCommands preprocessing completed with no changes"); }); }); describe('Integration with existing preprocessing', () => { it('should work alongside directory structure preprocessing', () => { // Arrange const input = { provides: { directoryStructure: [ { path: "src", type: "directory" }, // Missing content field { path: "test", type: "directory" } // Missing content field ], setupCommands: ["npm install", "npm test"] // String commands } }; // Act const result = (yamlComposer as unknown as { preprocessTemplateForValidation: (input: unknown, modulePathSegment: string) => unknown }).preprocessTemplateForValidation(input, 'testing/jest'); // Assert - both fixes should be applied expect(result.provides.directoryStructure[0].content).toBe(null); expect(result.provides.directoryStructure[1].content).toBe(null); expect(result.provides.setupCommands).toEqual([ { command: "npm install", context: "root" }, { command: "npm test", context: "root" } ]); }); it('should not affect other parts of the template', () => { // Arrange const input = { moduleName: "test-module", description: "Test module", type: "testing", provides: { techStack: { jest: { name: "Jest", version: "^29.0.0", rationale: "Testing" } }, setupCommands: ["npm test"] } }; // Act const result = (yamlComposer as unknown as { preprocessTemplateForValidation: (input: unknown, modulePathSegment: string) => unknown }).preprocessTemplateForValidation(input, 'testing/jest'); // Assert - other fields should remain unchanged expect(result.moduleName).toBe("test-module"); expect(result.description).toBe("Test module"); expect(result.type).toBe("testing"); expect(result.provides.techStack).toEqual({ jest: { name: "Jest", version: "^29.0.0", rationale: "Testing" } }); // Only setupCommands should be modified expect(result.provides.setupCommands).toEqual([ { command: "npm test", context: "root" } ]); }); }); });

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/freshtechbro/vibe-coder-mcp'

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