@skyramp/mcp
Version:
Skyramp MCP (Model Context Protocol) Server - AI-powered test generation and execution
195 lines (194 loc) • 8.38 kB
JavaScript
// @ts-ignore
import { registerInitTestbotTool } from "./initTestbotTool.js";
import { simpleGit } from "simple-git";
import * as fs from "fs/promises";
import * as fsSync from "fs";
import * as path from "path";
import * as os from "os";
// Mock logger and AnalyticsService to avoid side effects
jest.mock("../utils/logger.js", () => ({
logger: { info: jest.fn(), error: jest.fn() },
}));
jest.mock("../services/AnalyticsService.js", () => ({
AnalyticsService: { pushMCPToolEvent: jest.fn() },
}));
/**
* Captures the tool handler callback from registerInitTestbotTool
* by providing a fake McpServer with a mock registerTool method.
*/
function captureToolHandler() {
let handler;
const fakeServer = {
registerTool: (_name, _opts, fn) => {
handler = fn;
},
};
registerInitTestbotTool(fakeServer);
return handler;
}
/**
* Creates a temp directory, initializes a git repo, and optionally
* adds a GitHub remote.
*/
async function createTempRepo(options) {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "init-testbot-test-"));
const git = simpleGit(tmpDir);
await git.init();
if (options?.addGithubRemote !== false) {
await git.addRemote("origin", "https://github.com/test-org/test-repo.git");
}
return tmpDir;
}
describe("registerInitTestbotTool", () => {
let handler;
let tmpDirs = [];
beforeAll(() => {
handler = captureToolHandler();
});
afterEach(async () => {
for (const dir of tmpDirs) {
await fs.rm(dir, { recursive: true, force: true });
}
tmpDirs = [];
});
it("should register the tool on the server", () => {
const registerToolMock = jest.fn();
const fakeServer = { registerTool: registerToolMock };
registerInitTestbotTool(fakeServer);
expect(registerToolMock).toHaveBeenCalledWith("skyramp_init_testbot", expect.objectContaining({
description: expect.any(String),
inputSchema: expect.any(Object),
}), expect.any(Function));
});
it("should return error for non-git directory", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "init-testbot-test-"));
tmpDirs.push(tmpDir);
const result = await handler({
repository_path: tmpDir,
agent_type: "cursor",
});
expect(result.isError).toBe(true);
expect(result.content[0].text).toContain("is not a Git repository");
});
it("should return error for git repo without GitHub remote", async () => {
const tmpDir = await createTempRepo({ addGithubRemote: false });
tmpDirs.push(tmpDir);
const result = await handler({
repository_path: tmpDir,
agent_type: "cursor",
});
expect(result.isError).toBe(true);
expect(result.content[0].text).toContain("No GitHub remote found");
});
it("should create workflow file on success", async () => {
const tmpDir = await createTempRepo();
tmpDirs.push(tmpDir);
const result = await handler({
repository_path: tmpDir,
agent_type: "cursor",
});
expect(result.isError).toBeUndefined();
expect(result.content[0].text).toContain("workflow created successfully");
const workflowPath = path.join(tmpDir, ".github", "workflows", "skyramp-test-bot.yml");
expect(fsSync.existsSync(workflowPath)).toBe(true);
});
it("should return already-exists message when workflow file exists", async () => {
const tmpDir = await createTempRepo();
tmpDirs.push(tmpDir);
// Create the workflow file first
const workflowDir = path.join(tmpDir, ".github", "workflows");
await fs.mkdir(workflowDir, { recursive: true });
await fs.writeFile(path.join(workflowDir, "skyramp-test-bot.yml"), "existing");
const result = await handler({
repository_path: tmpDir,
agent_type: "cursor",
});
expect(result.isError).toBeUndefined();
expect(result.content[0].text).toContain("already exists");
// Verify original file was not overwritten
const content = await fs.readFile(path.join(workflowDir, "skyramp-test-bot.yml"), "utf-8");
expect(content).toBe("existing");
});
it("should generate workflow with cursor API key by default", async () => {
const tmpDir = await createTempRepo();
tmpDirs.push(tmpDir);
await handler({
repository_path: tmpDir,
agent_type: "cursor",
});
const workflowContent = await fs.readFile(path.join(tmpDir, ".github", "workflows", "skyramp-test-bot.yml"), "utf-8");
expect(workflowContent).toContain("cursor_api_key");
expect(workflowContent).not.toContain("copilot_api_key");
});
it("should generate workflow with copilot API key when agent_type is copilot", async () => {
const tmpDir = await createTempRepo();
tmpDirs.push(tmpDir);
await handler({
repository_path: tmpDir,
agent_type: "copilot",
});
const workflowContent = await fs.readFile(path.join(tmpDir, ".github", "workflows", "skyramp-test-bot.yml"), "utf-8");
expect(workflowContent).toContain("copilot_api_key");
expect(workflowContent).not.toContain("cursor_api_key");
});
it("should generate workflow without branches key when target_branches is not provided", async () => {
const tmpDir = await createTempRepo();
tmpDirs.push(tmpDir);
await handler({
repository_path: tmpDir,
agent_type: "cursor",
});
const workflowContent = await fs.readFile(path.join(tmpDir, ".github", "workflows", "skyramp-test-bot.yml"), "utf-8");
expect(workflowContent).toContain("pull_request:");
expect(workflowContent).not.toContain("branches:");
});
it("should generate workflow with branches key when target_branches is provided", async () => {
const tmpDir = await createTempRepo();
tmpDirs.push(tmpDir);
await handler({
repository_path: tmpDir,
agent_type: "cursor",
target_branches: ["main", "develop"],
});
const workflowContent = await fs.readFile(path.join(tmpDir, ".github", "workflows", "skyramp-test-bot.yml"), "utf-8");
expect(workflowContent).toContain("branches:");
expect(workflowContent).toContain("- 'main'");
expect(workflowContent).toContain("- 'develop'");
});
it("should include setup instructions with correct secret names for cursor", async () => {
const tmpDir = await createTempRepo();
tmpDirs.push(tmpDir);
const result = await handler({
repository_path: tmpDir,
agent_type: "cursor",
});
expect(result.content[0].text).toContain("CURSOR_API_KEY");
expect(result.content[0].text).toContain("SKYRAMP_LICENSE");
expect(result.content[0].text).toContain("all pull requests");
});
it("should include setup instructions with correct secret names for copilot", async () => {
const tmpDir = await createTempRepo();
tmpDirs.push(tmpDir);
const result = await handler({
repository_path: tmpDir,
agent_type: "copilot",
target_branches: ["main"],
});
expect(result.content[0].text).toContain("COPILOT_API_KEY");
expect(result.content[0].text).toContain("pull requests targeting: main");
});
it("should generate valid YAML workflow structure", async () => {
const tmpDir = await createTempRepo();
tmpDirs.push(tmpDir);
await handler({
repository_path: tmpDir,
agent_type: "cursor",
});
const workflowContent = await fs.readFile(path.join(tmpDir, ".github", "workflows", "skyramp-test-bot.yml"), "utf-8");
expect(workflowContent).toContain("name: Skyramp Test Automation");
expect(workflowContent).toContain("runs-on: ubuntu-latest");
expect(workflowContent).toContain("uses: actions/checkout@v4");
expect(workflowContent).toContain("uses: skyramp/test-bot@v1");
expect(workflowContent).toContain("skyramp_license_file:");
});
});