combined-memory-mcp
Version:
MCP server for Combined Memory API - AI-powered chat with unlimited context, memory management, voice agents, and 500+ tool integrations
185 lines (184 loc) • 9.87 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const openapiProcessor_1 = require("../../src/openapiProcessor");
const path_1 = __importDefault(require("path"));
const promises_1 = __importDefault(require("fs/promises"));
const test_config_1 = require("../fixtures/test-config");
const globals_1 = require("@jest/globals");
// Define constants
const configPath = '../../src/config';
describe('OpenAPI Processor Integration Tests', () => {
const originalEnv = Object.assign({}, process.env);
const originalArgv = [...process.argv];
const fixturesPath = path_1.default.resolve(process.cwd(), 'test/fixtures');
beforeEach(() => {
// Reset environment variables before each test
process.env = Object.assign({}, originalEnv);
process.argv = [...originalArgv];
// Set up test OpenAPI file path
process.env.OPENAPI_SPEC_PATH = path_1.default.resolve(process.cwd(), test_config_1.testConfig.openApiFile);
// Clear jest module mocks between tests
globals_1.jest.resetModules();
globals_1.jest.dontMock('../../src/config');
});
afterAll(() => {
// Restore original environment after all tests
process.env = originalEnv;
process.argv = originalArgv;
});
it('should load and process OpenAPI specification from file', () => __awaiter(void 0, void 0, void 0, function* () {
// Explicitly mock config with no overlays for this test
globals_1.jest.resetModules();
globals_1.jest.doMock('../../src/config', () => ({
config: {
specPath: path_1.default.resolve(process.cwd(), test_config_1.testConfig.openApiFile),
overlayPaths: [],
mcpPort: 8080,
targetApiBaseUrl: undefined,
apiKey: undefined,
securitySchemeName: undefined,
securityCredentials: {},
customHeaders: {},
disableXMcp: false,
filter: {
whitelist: null,
blacklist: [],
},
}
}));
// Import the module with our mocks applied
const { getProcessedOpenApi: getProcessedOpenApiClean } = require('../../src/openapiProcessor');
const openApiSpec = yield getProcessedOpenApiClean();
expect(openApiSpec).toBeDefined();
expect(openApiSpec.openapi).toBe('3.0.0');
expect(openApiSpec.info.title).toBe('Petstore API');
expect(openApiSpec.paths).toBeDefined();
expect(openApiSpec.paths["/pets"]).toBeDefined();
expect(openApiSpec.paths["/pets/{petId}"]).toBeDefined();
expect(openApiSpec.components.schemas.Pet).toBeDefined();
}));
it('should apply overlay configuration when provided', () => __awaiter(void 0, void 0, void 0, function* () {
const overlayFilePath = path_1.default.resolve(process.cwd(), 'test/fixtures/petstore-overlay.json');
// Clear any cached modules
globals_1.jest.resetModules();
// Read the overlay file contents directly to confirm it has what we expect
const overlayContent = yield promises_1.default.readFile(overlayFilePath, 'utf8');
const overlay = JSON.parse(overlayContent);
// Verify this is a proper OpenAPI Overlay Spec 1.0.0 document
expect(overlay.overlay).toBe('1.0.0');
expect(overlay.info.title).toBe('Modified Petstore API Overlay');
expect(overlay.actions).toBeInstanceOf(Array);
// The info.title in the overlay document itself is 'Modified Petstore API Overlay'
// but the action will update the OpenAPI document title to 'Modified Petstore API'
const titleUpdateAction = overlay.actions.find(action => { var _a; return action.target === '$.info' && ((_a = action.update) === null || _a === void 0 ? void 0 : _a.title) === 'Modified Petstore API'; });
expect(titleUpdateAction).toBeDefined();
// Mock the config module to include our overlay
globals_1.jest.doMock('../../src/config', () => ({
config: {
specPath: path_1.default.resolve(process.cwd(), test_config_1.testConfig.openApiFile),
overlayPaths: [overlayFilePath],
mcpPort: 8080,
targetApiBaseUrl: undefined,
apiKey: undefined,
securitySchemeName: undefined,
securityCredentials: {},
filter: {
whitelist: null,
blacklist: [],
},
}
}));
// Import the processor module with our mock config
const { getProcessedOpenApi: getProcessedOpenApiWithOverlay } = require('../../src/openapiProcessor');
const openApiSpec = yield getProcessedOpenApiWithOverlay();
// The overlay should be applied to the spec
expect(openApiSpec).toBeDefined();
// Verify that the overlay changes were applied based on the actual actions
// Instead of hardcoded values, we'll check against the actions in the overlay
const infoAction = overlay.actions.find(a => a.target === '$.info');
expect(infoAction).toBeDefined();
expect(openApiSpec.info.title).toBe(infoAction.update.title);
expect(openApiSpec.info.version).toBe(infoAction.update.version);
// Validate path-level overlay changes
const pathAction = overlay.actions.find(a => a.target === "$.paths['/pets'].get");
expect(pathAction).toBeDefined();
expect(openApiSpec.paths["/pets"].get.summary).toBe(pathAction.update.summary);
expect(openApiSpec.paths["/pets"].get.description).toBe(pathAction.update.description);
// Validate parameter overlay changes
const petIdParam = openApiSpec.paths["/pets/{petId}"].get.parameters.find((p) => p.name === 'petId' && p.in === 'path');
const paramAction = overlay.actions.find(a => a.target.includes('petId'));
expect(petIdParam).toBeDefined();
expect(paramAction).toBeDefined();
expect(petIdParam.description).toBe(paramAction.update.description);
expect(petIdParam.schema.type).toBe(paramAction.update.schema.type);
expect(petIdParam.schema.format).toBe(paramAction.update.schema.format);
}));
it('should throw error for invalid OpenAPI file path', () => __awaiter(void 0, void 0, void 0, function* () {
// Mock the config with an invalid file path
globals_1.jest.resetModules();
globals_1.jest.doMock('../../src/config', () => ({
config: {
specPath: '/path/to/nonexistent/openapi.json',
overlayPaths: [],
mcpPort: 8080,
targetApiBaseUrl: undefined,
apiKey: undefined,
securitySchemeName: undefined,
securityCredentials: {},
filter: {
whitelist: null,
blacklist: [],
},
}
}));
// Import the processor module with our mocks applied
const { getProcessedOpenApi: getProcessedOpenApiWithInvalidPath } = require('../../src/openapiProcessor');
// Expect the function to throw an error
yield expect(getProcessedOpenApiWithInvalidPath()).rejects.toThrow();
// Reset mocks
globals_1.jest.resetModules();
globals_1.jest.dontMock('../../src/config');
}));
it('should create a valid OpenAPI spec object', () => __awaiter(void 0, void 0, void 0, function* () {
// Test that the processed OpenAPI spec has all required properties
const openApiSpec = yield (0, openapiProcessor_1.getProcessedOpenApi)();
// Check basic structure requirements
expect(openApiSpec).toHaveProperty('openapi');
expect(openApiSpec).toHaveProperty('info');
expect(openApiSpec).toHaveProperty('paths');
// Validate info section
expect(openApiSpec.info).toHaveProperty('title');
expect(openApiSpec.info).toHaveProperty('version');
// Validate paths have operations
// Find at least one valid operation with standard HTTP methods
let hasValidOperation = false;
Object.values(openApiSpec.paths).forEach((pathItem) => {
['get', 'post', 'put', 'delete', 'patch'].forEach(method => {
if (pathItem[method])
hasValidOperation = true;
});
});
expect(hasValidOperation).toBe(true);
// Check that all operations have required fields
Object.entries(openApiSpec.paths).forEach(([path, pathItem]) => {
Object.entries(pathItem).forEach(([method, operation]) => {
if (['get', 'post', 'put', 'delete', 'patch'].includes(method)) {
expect(operation).toHaveProperty('operationId');
expect(operation).toHaveProperty('responses');
}
});
});
}));
});