Files
makelore/tests/unit/data-service-plugin-adapter.test.ts

121 lines
5.6 KiB
TypeScript

// @vitest-environment node
import { describe, expect, it, vi } from 'vitest';
import { DATA_SERVICE_TOOL_DEFINITIONS } from '../../shared/coding-plugins';
import type { DataServiceOperations } from '../../electron/services/data-service-client';
import {
createDataServicePluginAdapter,
} from '../../electron/coding-plugins/adapters/data-service';
const context = {
conversationId: 'conversation-a',
runId: 'run-a',
resourceId: 'resource-a',
requestId: 'pi:run-a:resource-a',
localProjectId: 'local-project-a',
projectPath: 'C:\\projects\\demo',
durableProjectId: 'durable-project-a',
workerRole: 'parent' as const,
effectiveSkillIds: ['data-service'],
};
function success<T>(data: T) {
return {
success: true as const,
status: 200,
code: null,
error: null,
retryable: false as const,
data,
};
}
function operations(): DataServiceOperations {
return {
configure: vi.fn().mockResolvedValue(success({ configured: true })),
inspect: vi.fn().mockResolvedValue(success({ instance_id: 'instance-a' })),
listProjects: vi.fn().mockResolvedValue(success({ items: [], total: 0, instance_limit: 20 })),
getDocument: vi.fn().mockResolvedValue(success({ id: 'one', data: {}, revision: 1 })),
listDocuments: vi.fn().mockResolvedValue(success({ items: [], next_cursor: null, limit: 50 })),
putDocument: vi.fn().mockResolvedValue(success({ id: 'one', data: {}, revision: 1 })),
deleteDocument: vi.fn().mockResolvedValue(success(null)),
removeCollection: vi.fn().mockResolvedValue(success({ removed: true, usage: { document_count: 0, total_bytes: 0 } })),
reset: vi.fn().mockResolvedValue(success({ instance_id: 'instance-a' })),
removeProject: vi.fn().mockResolvedValue(success({ removed: true })),
};
}
describe('Data Service plugin adapter', () => {
it('validates the parsed tool supplied by the capability registry', async () => {
const dataService = operations();
const adapter = createDataServicePluginAdapter(dataService);
const inspect = DATA_SERVICE_TOOL_DEFINITIONS.find(({ name }) => name === 'data_service_inspect');
if (!inspect) throw new Error('inspect definition missing');
const result = await adapter.invoke(context, {
...inspect,
inputSchema: {
type: 'object',
additionalProperties: false,
required: ['registry_token'],
properties: { registry_token: { type: 'string', minLength: 1 } },
},
}, {});
expect(result).toMatchObject({
success: false, status: 422, code: 'plugin_input_invalid', data: null,
});
expect(dataService.inspect).not.toHaveBeenCalled();
});
it('maps all ten package operations through DataServiceOperations', async () => {
const dataService = operations();
const adapter = createDataServicePluginAdapter(dataService);
const inputs: Record<string, unknown> = {
data_service_configure: { collections: ['todos'] },
data_service_inspect: {},
data_service_list_projects: {},
data_service_get_document: { collection: 'todos', document_id: 'one' },
data_service_list_documents: { collection: 'todos', limit: 50, cursor: 'cursor-a' },
data_service_put_document: { collection: 'todos', document_id: 'one', data: { done: false }, if_revision: 1 },
data_service_delete_document: { collection: 'todos', document_id: 'one', if_revision: 1, confirmed: true },
data_service_remove_collection: { collection: 'todos', confirmed: true },
data_service_reset: { confirmed: true },
data_service_remove_project: { confirmed: true },
};
for (const tool of DATA_SERVICE_TOOL_DEFINITIONS) {
const result = await adapter.invoke(context, tool, inputs[tool.name]);
expect(result).toMatchObject({ success: true, payload_schema: 'data-service.v1' });
expect(result).not.toHaveProperty('plugin_id');
expect(result).not.toHaveProperty('request_id');
}
expect(dataService.configure).toHaveBeenCalledWith({ collections: ['todos'] }, context.projectPath);
expect(dataService.inspect).toHaveBeenCalledWith(context.projectPath);
expect(dataService.listProjects).toHaveBeenCalledWith();
expect(dataService.getDocument).toHaveBeenCalledWith({ collection: 'todos', document_id: 'one' }, context.projectPath);
expect(dataService.listDocuments).toHaveBeenCalledWith({ collection: 'todos', limit: 50, cursor: 'cursor-a' }, context.projectPath);
expect(dataService.putDocument).toHaveBeenCalledWith({
collection: 'todos', document_id: 'one', data: { done: false }, if_revision: 1,
}, context.projectPath);
expect(dataService.deleteDocument).toHaveBeenCalledWith({
collection: 'todos', document_id: 'one', if_revision: 1, confirmed: true,
}, context.projectPath);
});
it('returns bounded domain context and rejects forbidden or malformed input', async () => {
const dataService = operations();
const adapter = createDataServicePluginAdapter(dataService);
const put = DATA_SERVICE_TOOL_DEFINITIONS.find(({ name }) => name === 'data_service_put_document');
if (!put) throw new Error('put definition missing');
const invalid = await adapter.invoke(context, put, {
collection: 'todos', document_id: 'one', data: {}, projectPath: context.projectPath,
});
expect(invalid).toMatchObject({
success: false, status: 422, code: 'plugin_input_invalid', data: null,
});
const result = await adapter.invoke(context, put, { collection: 'todos', document_id: 'one', data: {} });
expect(result).toMatchObject({ success: true, payload_schema: 'data-service.v1' });
expect(dataService.putDocument).toHaveBeenCalledTimes(1);
});
});