121 lines
5.6 KiB
TypeScript
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);
|
|
});
|
|
});
|