Files
2026-07-13 12:30:05 +08:00

185 lines
5.2 KiB
TypeScript

/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import assert from 'node:assert';
import crypto from 'node:crypto';
import {existsSync, rmSync} from 'node:fs';
import {dirname} from 'node:path';
import {describe, it, afterEach, beforeEach} from 'node:test';
import {
handleResponse,
startDaemon,
stopDaemon,
} from '../../src/daemon/client.js';
import {isDaemonRunning} from '../../src/daemon/utils.js';
describe('daemon client', () => {
describe('start/stop', () => {
let sessionId: string;
beforeEach(async () => {
sessionId = crypto.randomUUID();
await stopDaemon(sessionId);
});
afterEach(async () => {
await stopDaemon(sessionId);
});
it('should start and stop daemon', async () => {
assert.ok(
!isDaemonRunning(sessionId),
'Daemon should not be running initially',
);
await startDaemon([], sessionId);
assert.ok(
isDaemonRunning(sessionId),
'Daemon should be running after start',
);
await stopDaemon(sessionId);
assert.ok(
!isDaemonRunning(sessionId),
'Daemon should not be running after stop',
);
});
it('should handle starting daemon when already running', async () => {
await startDaemon([], sessionId);
assert.ok(isDaemonRunning(sessionId), 'Daemon should be running');
// Starting again should be a no-op
await startDaemon([], sessionId);
assert.ok(isDaemonRunning(sessionId), 'Daemon should still be running');
});
it('should handle stopping daemon when not running', async () => {
assert.ok(
!isDaemonRunning(sessionId),
'Daemon should not be running initially',
);
// Stopping when not running should be a no-op
await stopDaemon(sessionId);
assert.ok(
!isDaemonRunning(sessionId),
'Daemon should still not be running',
);
});
});
describe('parsing', () => {
it('handles MCP response with text format', async () => {
const textResponse = {content: [{type: 'text' as const, text: 'test'}]};
assert.strictEqual(await handleResponse(textResponse, 'md'), 'test');
});
it('handles JSON response', async () => {
const jsonResponse = {
content: [],
structuredContent: {
test: 'data',
number: 123,
},
};
assert.strictEqual(
await handleResponse(jsonResponse, 'json'),
JSON.stringify(jsonResponse.structuredContent),
);
});
it('handles error response when isError is true', async () => {
const errorResponse = {
isError: true,
content: [{type: 'text' as const, text: 'Something went wrong'}],
};
assert.strictEqual(
await handleResponse(errorResponse, 'md'),
JSON.stringify(errorResponse.content),
);
});
it('handles text response when json format is requested but no structured content', async () => {
const textResponse = {
content: [{type: 'text' as const, text: 'Fall through text'}],
};
assert.deepStrictEqual(
await handleResponse(textResponse, 'json'),
JSON.stringify(['Fall through text']),
);
});
it('supports images', async () => {
const unsupportedContentResponse = {
content: [
{
type: 'image' as const,
data: 'base64data',
mimeType: 'image/png',
},
],
structuredContent: {},
};
const response = await handleResponse(unsupportedContentResponse, 'md');
assert.ok(response.includes('.png'));
});
it('includes saved image file paths in structured JSON responses', async () => {
const imageContentResponse = {
content: [
{
type: 'text' as const,
text: 'Took a screenshot.',
},
{
type: 'image' as const,
data: Buffer.from('image data').toString('base64'),
mimeType: 'image/png',
},
],
structuredContent: {
message: 'Took a screenshot.',
},
};
let filePath: string | undefined;
try {
const response = await handleResponse(imageContentResponse, 'json');
const parsed = JSON.parse(response) as {
message: string;
images: Array<{filePath: string; mimeType: string}>;
};
assert.strictEqual(parsed.message, 'Took a screenshot.');
assert.strictEqual(parsed.images.length, 1);
assert.strictEqual(parsed.images[0].mimeType, 'image/png');
filePath = parsed.images[0].filePath;
assert.ok(filePath.endsWith('.png'));
assert.ok(existsSync(filePath));
} finally {
if (filePath) {
rmSync(dirname(filePath), {recursive: true, force: true});
}
}
});
it('uses the webp extension for WebP images', async () => {
const webpContentResponse = {
content: [
{
type: 'image' as const,
data: 'base64data',
mimeType: 'image/webp',
},
],
structuredContent: {},
};
const response = await handleResponse(webpContentResponse, 'md');
assert.ok(response.includes('.webp'));
});
});
});