test-comprehensive.jsโข9.2 kB
/**
* Comprehensive test suite for OneTech MCP Server
* Tests all functionality before NPM publish
*/
import { execFile } from "child_process";
import { promisify } from "util";
import { readFile, access, mkdir, rm } from "fs/promises";
import { join } from "path";
import { constants } from "fs";
const execFileAsync = promisify(execFile);
// Test configuration
const DEFAULT_MX_PATH = "D:\\Program Files\\Mendix\\11.3.0.80734\\modeler\\mx.exe";
const TEST_MPR = "d:\\kelly.seale\\CodeBase\\OneTech-main\\OneTech.mpr";
const TEST_MODULE = "RequestHub";
const TEST_OUTPUT = "D:\\kelly.seale\\CodeBase\\onetech-mcp-server\\test-validation";
// Test results
const results = {
passed: 0,
failed: 0,
tests: [],
};
function test(name, fn) {
return async () => {
try {
console.log(`\n๐งช ${name}...`);
await fn();
results.passed++;
results.tests.push({ name, status: "PASS" });
console.log(`โ
PASS: ${name}`);
} catch (error) {
results.failed++;
results.tests.push({ name, status: "FAIL", error: error.message });
console.log(`โ FAIL: ${name}`);
console.log(` Error: ${error.message}`);
}
};
}
// Test 1: mx.exe exists
const testMxExeExists = test("mx.exe exists and is accessible", async () => {
await access(DEFAULT_MX_PATH, constants.X_OK);
});
// Test 2: .mpr file exists
const testMprExists = test("OneTech.mpr file exists", async () => {
await access(TEST_MPR, constants.R_OK);
});
// Test 3: Create output directory
const testOutputDir = test("Can create output directory", async () => {
await rm(TEST_OUTPUT, { recursive: true, force: true });
await mkdir(TEST_OUTPUT, { recursive: true });
await access(TEST_OUTPUT, constants.W_OK);
});
// Test 4: Extract DomainModel
const testDomainModel = test("Extract DomainModel successfully", async () => {
const outputFile = join(TEST_OUTPUT, `${TEST_MODULE}-DomainModel.json`);
const args = ["dump-mpr", "--unit-type", "DomainModels$DomainModel", "--module-names", TEST_MODULE, "--output-file", outputFile, TEST_MPR];
await execFileAsync(DEFAULT_MX_PATH, args, { maxBuffer: 50 * 1024 * 1024, timeout: 60000 });
let content = await readFile(outputFile, "utf-8");
// Remove UTF-8 BOM if present
if (content.charCodeAt(0) === 0xfeff) {
content = content.substring(1);
}
const size = Buffer.byteLength(content, "utf-8");
if (size < 1000) throw new Error(`File too small: ${size} bytes`);
// Validate JSON
const json = JSON.parse(content);
if (!json || typeof json !== "object") throw new Error("Invalid JSON structure");
console.log(` Size: ${(size / 1024).toFixed(1)}KB`);
});
// Test 5: Extract Pages
const testPages = test("Extract Pages successfully", async () => {
const outputFile = join(TEST_OUTPUT, `${TEST_MODULE}-Pages.json`);
const args = ["dump-mpr", "--unit-type", "Pages$Page", "--module-names", TEST_MODULE, "--output-file", outputFile, TEST_MPR];
await execFileAsync(DEFAULT_MX_PATH, args, { maxBuffer: 50 * 1024 * 1024, timeout: 60000 });
let content = await readFile(outputFile, "utf-8");
// Remove UTF-8 BOM if present
if (content.charCodeAt(0) === 0xfeff) {
content = content.substring(1);
}
const size = Buffer.byteLength(content, "utf-8");
if (size < 1000) throw new Error(`File too small: ${size} bytes`);
const json = JSON.parse(content);
if (!json || typeof json !== "object") throw new Error("Invalid JSON structure");
console.log(` Size: ${(size / 1024).toFixed(1)}KB`);
});
// Test 6: Extract Microflows (may be empty)
const testMicroflows = test("Extract Microflows (may be empty)", async () => {
const outputFile = join(TEST_OUTPUT, `${TEST_MODULE}-Microflows.json`);
const args = ["dump-mpr", "--unit-type", "Microflows$Microflow", "--module-names", TEST_MODULE, "--output-file", outputFile, TEST_MPR];
await execFileAsync(DEFAULT_MX_PATH, args, { maxBuffer: 50 * 1024 * 1024, timeout: 60000 });
let content = await readFile(outputFile, "utf-8");
// Remove UTF-8 BOM if present
if (content.charCodeAt(0) === 0xfeff) {
content = content.substring(1);
}
const size = Buffer.byteLength(content, "utf-8");
const json = JSON.parse(content);
if (!json || typeof json !== "object") throw new Error("Invalid JSON structure");
const sizeKB = (size / 1024).toFixed(1);
const sizeMsg = size < 100 ? " (empty, expected)" : "";
console.log(` Size: ${sizeKB}KB${sizeMsg}`);
});
// Test 7: Extract Enumerations
const testEnumerations = test("Extract Enumerations successfully", async () => {
const outputFile = join(TEST_OUTPUT, `${TEST_MODULE}-Enumerations.json`);
const args = ["dump-mpr", "--unit-type", "Enumerations$Enumeration", "--module-names", TEST_MODULE, "--output-file", outputFile, TEST_MPR];
await execFileAsync(DEFAULT_MX_PATH, args, { maxBuffer: 50 * 1024 * 1024, timeout: 60000 });
let content = await readFile(outputFile, "utf-8");
// Remove UTF-8 BOM if present
if (content.charCodeAt(0) === 0xfeff) {
content = content.substring(1);
}
const size = Buffer.byteLength(content, "utf-8");
if (size < 100) throw new Error(`File too small: ${size} bytes`);
const json = JSON.parse(content);
if (!json || typeof json !== "object") throw new Error("Invalid JSON structure");
console.log(` Size: ${(size / 1024).toFixed(1)}KB`);
});
// Test 8: Error handling - invalid .mpr path
const testInvalidMpr = test("Handle invalid .mpr path gracefully", async () => {
const outputFile = join(TEST_OUTPUT, "test-invalid.json");
const args = ["dump-mpr", "--unit-type", "DomainModels$DomainModel", "--module-names", TEST_MODULE, "--output-file", outputFile, "D:\\NonExistent\\Invalid.mpr"];
try {
await execFileAsync(DEFAULT_MX_PATH, args, { maxBuffer: 50 * 1024 * 1024, timeout: 10000 });
throw new Error("Should have failed with invalid .mpr path");
} catch (error) {
// Expected to fail
if (error.message.includes("Should have failed")) throw error;
console.log(` Expected error: ${error.message.substring(0, 50)}...`);
}
});
// Test 9: Error handling - invalid module name
const testInvalidModule = test("Handle invalid module name gracefully", async () => {
const outputFile = join(TEST_OUTPUT, "test-invalid-module.json");
const args = ["dump-mpr", "--unit-type", "DomainModels$DomainModel", "--module-names", "NonExistentModule", "--output-file", outputFile, TEST_MPR];
// This may succeed but return empty results - both are acceptable
await execFileAsync(DEFAULT_MX_PATH, args, { maxBuffer: 50 * 1024 * 1024, timeout: 60000 });
let content = await readFile(outputFile, "utf-8");
// Remove UTF-8 BOM if present
if (content.charCodeAt(0) === 0xfeff) {
content = content.substring(1);
}
const json = JSON.parse(content);
console.log(` Result: ${JSON.stringify(json).length < 50 ? "Empty (expected)" : "Has data"}`);
});
// Test 10: Verify compiled TypeScript
const testCompiled = test("TypeScript compiled successfully", async () => {
await access("dist/index.js", constants.R_OK);
await access("dist/index.d.ts", constants.R_OK);
const code = await readFile("dist/index.js", "utf-8");
if (!code.includes("OneTech MCP Server")) throw new Error("Compiled code missing header");
if (!code.includes("onetech_extract_module")) throw new Error("Tool not found in compiled code");
console.log(` Size: ${(Buffer.byteLength(code, "utf-8") / 1024).toFixed(1)}KB`);
});
// Run all tests
async function runAllTests() {
console.log("\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ");
console.log("โ OneTech MCP Server - Test Suite โ");
console.log("โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ");
const startTime = Date.now();
await testMxExeExists();
await testMprExists();
await testOutputDir();
await testDomainModel();
await testPages();
await testMicroflows();
await testEnumerations();
await testInvalidMpr();
await testInvalidModule();
await testCompiled();
const duration = Date.now() - startTime;
console.log("\nโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ");
console.log("โ Test Results โ");
console.log("โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ");
console.log(`\nPassed: ${results.passed}`);
console.log(`Failed: ${results.failed}`);
console.log(`Total: ${results.passed + results.failed}`);
console.log(`Time: ${(duration / 1000).toFixed(1)}s`);
if (results.failed > 0) {
console.log("\nโ FAILED TESTS:");
results.tests
.filter((t) => t.status === "FAIL")
.forEach((t) => {
console.log(` - ${t.name}: ${t.error}`);
});
}
console.log("\n" + (results.failed === 0 ? "โ
ALL TESTS PASSED!" : "โ SOME TESTS FAILED"));
console.log(results.failed === 0 ? "\n๐ Ready to publish to NPM!\n" : "\nโ ๏ธ Fix errors before publishing\n");
return results.failed === 0;
}
runAllTests()
.then((success) => process.exit(success ? 0 : 1))
.catch((error) => {
console.error("Fatal error:", error);
process.exit(1);
});