Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
254 changes: 254 additions & 0 deletions apps/mcp-server/src/__tests__/dataset-tools.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
/**
* Dataset Tools Tests
*/

import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
import { Logger } from "@lighthouse-tooling/shared";
import { MockLighthouseService } from "../services/MockLighthouseService.js";
import {
LighthouseCreateDatasetTool,
LighthouseListDatasetsTool,
LighthouseGetDatasetTool,
LighthouseUpdateDatasetTool,
} from "../tools/index.js";

describe("Dataset Tools", () => {
let mockService: MockLighthouseService;
let logger: Logger;

beforeEach(() => {
mockService = new MockLighthouseService();
logger = Logger.getInstance({ level: "error", component: "test" });
});

afterEach(() => {
mockService.clear();
});

describe("LighthouseCreateDatasetTool", () => {
let tool: LighthouseCreateDatasetTool;

beforeEach(() => {
tool = new LighthouseCreateDatasetTool(mockService, logger);
});

it("should have correct tool definition", () => {
const definition = LighthouseCreateDatasetTool.getDefinition();
expect(definition.name).toBe("lighthouse_create_dataset");
expect(definition.description).toContain("Create a new dataset");
expect(definition.requiresAuth).toBe(true);
});

it("should validate required parameters", async () => {
const result = await tool.execute({});
expect(result.success).toBe(false);
expect(result.error).toContain("name is required");
});

it("should validate filePaths parameter", async () => {
const result = await tool.execute({
name: "Test Dataset",
filePaths: [],
});
expect(result.success).toBe(false);
expect(result.error).toContain("filePaths is required and must be a non-empty array");
});

it("should create dataset successfully with valid parameters", async () => {
// Create test files first
const testFiles = ["/tmp/test1.txt", "/tmp/test2.txt"];

// Mock FileUtils to avoid actual file operations
const { FileUtils } = await import("@lighthouse-tooling/shared");
vi.spyOn(FileUtils, "fileExists").mockResolvedValue(true);
vi.spyOn(FileUtils, "getFileInfo").mockResolvedValue({
path: "/tmp/test.txt",
name: "test.txt",
extension: ".txt",
size: 1024,
lastModified: new Date(),
hash: "mock-hash",
});

const result = await tool.execute({
name: "Test Dataset",
description: "A test dataset",
filePaths: testFiles,
encrypt: false,
tags: ["test", "dataset"],
});

expect(result.success).toBe(true);
expect(result.data).toBeDefined();
expect(result.data.dataset.name).toBe("Test Dataset");
expect(result.data.dataset.fileCount).toBe(2);
});

it("should handle file validation errors", async () => {
// Mock FileUtils to simulate file not found
const { FileUtils } = await import("@lighthouse-tooling/shared");
vi.spyOn(FileUtils, "fileExists").mockResolvedValue(false);

const result = await tool.execute({
name: "Test Dataset",
filePaths: ["/nonexistent/file.txt"],
});

expect(result.success).toBe(false);
expect(result.error).toContain("File not found");
});
});

describe("LighthouseListDatasetsTool", () => {
let tool: LighthouseListDatasetsTool;

beforeEach(() => {
tool = new LighthouseListDatasetsTool(mockService, logger);
});

it("should have correct tool definition", () => {
const definition = LighthouseListDatasetsTool.getDefinition();
expect(definition.name).toBe("lighthouse_list_datasets");
expect(definition.description).toContain("List all datasets");
expect(definition.requiresAuth).toBe(true);
});

it("should list datasets with default parameters", async () => {
const result = await tool.execute({});
expect(result.success).toBe(true);
expect(result.data).toBeDefined();
expect(result.data.datasets).toBeInstanceOf(Array);
expect(result.data.pagination).toBeDefined();
});

it("should validate limit parameter", async () => {
const result = await tool.execute({
limit: 150, // exceeds max of 100
});
expect(result.success).toBe(false);
expect(result.error).toContain("limit must be between 1 and 100");
});

it("should validate offset parameter", async () => {
const result = await tool.execute({
offset: -1,
});
expect(result.success).toBe(false);
expect(result.error).toContain("offset must be 0 or greater");
});
});

describe("LighthouseGetDatasetTool", () => {
let tool: LighthouseGetDatasetTool;

beforeEach(() => {
tool = new LighthouseGetDatasetTool(mockService, logger);
});

it("should have correct tool definition", () => {
const definition = LighthouseGetDatasetTool.getDefinition();
expect(definition.name).toBe("lighthouse_get_dataset");
expect(definition.description).toContain("Retrieve detailed information");
expect(definition.requiresAuth).toBe(true);
});

it("should validate datasetId parameter", async () => {
const result = await tool.execute({});
expect(result.success).toBe(false);
expect(result.error).toContain("datasetId is required");
});

it("should retrieve dataset successfully", async () => {
// First create a dataset
await mockService.createDataset({
name: "Test Dataset",
description: "Test description",
filePaths: [],
encrypt: false,
});

// Get the created dataset ID (it will be the first one)
const datasets = await mockService.listDatasets();
const datasetId = datasets.datasets[0].id;

const result = await tool.execute({
datasetId,
});
expect(result.success).toBe(true);
expect(result.data).toBeDefined();
expect(result.data.dataset.id).toBe(datasetId);
});

it("should handle non-existent dataset", async () => {
const result = await tool.execute({
datasetId: "nonexistent",
});
expect(result.success).toBe(false);
expect(result.error).toContain("Dataset not found");
});
});

describe("LighthouseUpdateDatasetTool", () => {
let tool: LighthouseUpdateDatasetTool;

beforeEach(() => {
tool = new LighthouseUpdateDatasetTool(mockService, logger);
});

it("should have correct tool definition", () => {
const definition = LighthouseUpdateDatasetTool.getDefinition();
expect(definition.name).toBe("lighthouse_update_dataset");
expect(definition.description).toContain("Update an existing dataset");
expect(definition.requiresAuth).toBe(true);
});

it("should validate datasetId parameter", async () => {
const result = await tool.execute({});
expect(result.success).toBe(false);
expect(result.error).toContain("datasetId is required");
});

it("should require at least one update operation", async () => {
const result = await tool.execute({
datasetId: "dataset_1",
});
expect(result.success).toBe(false);
expect(result.error).toContain("At least one update operation must be specified");
});

it("should update dataset with new description", async () => {
// First create a dataset
await mockService.createDataset({
name: "Test Dataset",
description: "Original description",
filePaths: [],
encrypt: false,
});

// Get the created dataset ID
const datasets = await mockService.listDatasets();
const datasetId = datasets.datasets[0].id;

const result = await tool.execute({
datasetId,
description: "Updated description",
});
expect(result.success).toBe(true);
expect(result.data).toBeDefined();
expect(result.data.dataset.description).toBe("Updated description");
});

it("should validate addFiles parameter", async () => {
// Mock FileUtils to simulate file not found
const { FileUtils } = await import("@lighthouse-tooling/shared");
vi.spyOn(FileUtils, "fileExists").mockResolvedValue(false);

const result = await tool.execute({
datasetId: "dataset_1",
addFiles: ["/nonexistent/file.txt"],
});
expect(result.success).toBe(false);
expect(result.error).toContain("File not found");
});
});
});
55 changes: 31 additions & 24 deletions apps/mcp-server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ import { MockDatasetService } from "./services/MockDatasetService.js";
import {
LighthouseUploadFileTool,
LighthouseFetchFileTool,
LighthouseCreateDatasetTool,
LighthouseListDatasetsTool,
LighthouseGetDatasetTool,
LighthouseUpdateDatasetTool,
LighthouseGenerateKeyTool,
LighthouseSetupAccessControlTool,
} from "./tools/index.js";
Expand Down Expand Up @@ -125,56 +129,59 @@ export class LighthouseMCPServer {
// Create tool instances with service dependencies
const uploadFileTool = new LighthouseUploadFileTool(this.lighthouseService, this.logger);
const fetchFileTool = new LighthouseFetchFileTool(this.lighthouseService, this.logger);
const createDatasetTool = new LighthouseCreateDatasetTool(this.lighthouseService, this.logger);
const listDatasetsTool = new LighthouseListDatasetsTool(this.lighthouseService, this.logger);
const getDatasetTool = new LighthouseGetDatasetTool(this.lighthouseService, this.logger);
const updateDatasetTool = new LighthouseUpdateDatasetTool(this.lighthouseService, this.logger);
const generateKeyTool = new LighthouseGenerateKeyTool(this.lighthouseService, this.logger);
const setupAccessControlTool = new LighthouseSetupAccessControlTool(
this.lighthouseService,
this.logger,
);

// Register lighthouse_upload_file tool
// Register file operation tools
this.registry.register(
LighthouseUploadFileTool.getDefinition(),
async (args) => await uploadFileTool.execute(args),
);

// Register lighthouse_fetch_file tool
this.registry.register(
LighthouseFetchFileTool.getDefinition(),
async (args) => await fetchFileTool.execute(args),
);

// Register lighthouse_generate_key tool
// Register dataset management tools
this.registry.register(
LighthouseCreateDatasetTool.getDefinition(),
async (args) => await createDatasetTool.execute(args),
);

this.registry.register(
LighthouseListDatasetsTool.getDefinition(),
async (args) => await listDatasetsTool.execute(args),
);

this.registry.register(
LighthouseGetDatasetTool.getDefinition(),
async (args) => await getDatasetTool.execute(args),
);

this.registry.register(
LighthouseUpdateDatasetTool.getDefinition(),
async (args) => await updateDatasetTool.execute(args),
);

// Register encryption tools
this.registry.register(
LighthouseGenerateKeyTool.getDefinition(),
async (args) => await generateKeyTool.execute(args),
);

// Register lighthouse_setup_access_control tool
this.registry.register(
LighthouseSetupAccessControlTool.getDefinition(),
async (args) => await setupAccessControlTool.execute(args),
);

// Register lighthouse_create_dataset tool (keeping existing implementation)
const datasetTool = LIGHTHOUSE_MCP_TOOLS.find((t) => t.name === "lighthouse_create_dataset");
if (datasetTool) {
this.registry.register(datasetTool, async (args) => {
const result = await this.datasetService.createDataset({
name: args.name as string,
description: args.description as string | undefined,
files: args.files as string[],
metadata: args.metadata as Record<string, unknown> | undefined,
encrypt: args.encrypt as boolean | undefined,
});

return {
success: true,
data: result,
executionTime: 0,
};
});
}

const registeredTools = this.registry.listTools();
const registrationTime = Date.now() - startTime;

Expand Down
Loading
Loading