-
Notifications
You must be signed in to change notification settings - Fork 0
Add MCP test coverage for restart, reload, flush, reloadLogs, and dump tools #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…ush_logs, pm2_reload_logs, and pm2_dump tools Co-authored-by: elasticdotventures <35611074+elasticdotventures@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds test coverage for five PM2 MCP server tools that were previously untested: pm2_restart_process, pm2_reload_process, pm2_flush_logs, pm2_reload_logs, and pm2_dump.
- Adds comprehensive test coverage for process restart and reload operations
- Adds test coverage for log management operations (flush and reload)
- Adds test coverage for PM2's dump/persist functionality
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| await startEcho(name); | ||
| await new Promise(resolve => setTimeout(resolve, 200)); | ||
|
|
||
| const dumpRes = await callToolWithTimeout('pm2_dump'); | ||
| should(dumpRes.isError).not.equal(true); | ||
| should(dumpRes.structuredContent.action).eql('dump'); | ||
|
|
||
| await callToolWithTimeout('pm2_delete_process', { process: name }); |
Copilot
AI
Dec 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cleanup logic in this test could fail to execute if any of the assertions above fail. Consider wrapping the test logic in a try-finally block to ensure the process is always deleted, preventing test pollution:
it('pm2_dump persists the current process list', async () => {
const name = 'mcp-dump-test';
try {
await startEcho(name);
await new Promise(resolve => setTimeout(resolve, 200));
const dumpRes = await callToolWithTimeout('pm2_dump');
should(dumpRes.isError).not.equal(true);
should(dumpRes.structuredContent.action).eql('dump');
} finally {
await callToolWithTimeout('pm2_delete_process', { process: name });
}
});| await startEcho(name); | |
| await new Promise(resolve => setTimeout(resolve, 200)); | |
| const dumpRes = await callToolWithTimeout('pm2_dump'); | |
| should(dumpRes.isError).not.equal(true); | |
| should(dumpRes.structuredContent.action).eql('dump'); | |
| await callToolWithTimeout('pm2_delete_process', { process: name }); | |
| try { | |
| await startEcho(name); | |
| await new Promise(resolve => setTimeout(resolve, 200)); | |
| const dumpRes = await callToolWithTimeout('pm2_dump'); | |
| should(dumpRes.isError).not.equal(true); | |
| should(dumpRes.structuredContent.action).eql('dump'); | |
| } finally { | |
| await callToolWithTimeout('pm2_delete_process', { process: name }); | |
| } |
| await startEcho(name); | ||
| await new Promise(resolve => setTimeout(resolve, 200)); | ||
|
|
||
| const restartRes = await callToolWithTimeout('pm2_restart_process', { process: name }); | ||
| should(restartRes.isError).not.equal(true); | ||
| should(restartRes.structuredContent.action).eql('restart'); | ||
| should(restartRes.structuredContent.process).eql(name); | ||
| should(restartRes.structuredContent.processes).be.an.Array(); | ||
|
|
||
| await callToolWithTimeout('pm2_delete_process', { process: name }); |
Copilot
AI
Dec 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cleanup logic in this test could fail to execute if any of the assertions above fail. Consider wrapping the test logic in a try-finally block to ensure the process is always deleted, preventing test pollution:
it('pm2_restart_process restarts a running process', async () => {
const name = 'mcp-restart-test';
try {
await startEcho(name);
await new Promise(resolve => setTimeout(resolve, 200));
const restartRes = await callToolWithTimeout('pm2_restart_process', { process: name });
should(restartRes.isError).not.equal(true);
should(restartRes.structuredContent.action).eql('restart');
should(restartRes.structuredContent.process).eql(name);
should(restartRes.structuredContent.processes).be.an.Array();
} finally {
await callToolWithTimeout('pm2_delete_process', { process: name });
}
});| await startEcho(name); | |
| await new Promise(resolve => setTimeout(resolve, 200)); | |
| const restartRes = await callToolWithTimeout('pm2_restart_process', { process: name }); | |
| should(restartRes.isError).not.equal(true); | |
| should(restartRes.structuredContent.action).eql('restart'); | |
| should(restartRes.structuredContent.process).eql(name); | |
| should(restartRes.structuredContent.processes).be.an.Array(); | |
| await callToolWithTimeout('pm2_delete_process', { process: name }); | |
| try { | |
| await startEcho(name); | |
| await new Promise(resolve => setTimeout(resolve, 200)); | |
| const restartRes = await callToolWithTimeout('pm2_restart_process', { process: name }); | |
| should(restartRes.isError).not.equal(true); | |
| should(restartRes.structuredContent.action).eql('restart'); | |
| should(restartRes.structuredContent.process).eql(name); | |
| should(restartRes.structuredContent.processes).be.an.Array(); | |
| } finally { | |
| await callToolWithTimeout('pm2_delete_process', { process: name }); | |
| } |
| await startEcho(name); | ||
| await new Promise(resolve => setTimeout(resolve, 200)); | ||
|
|
||
| const reloadRes = await callToolWithTimeout('pm2_reload_process', { process: name }); | ||
| should(reloadRes.isError).not.equal(true); | ||
| should(reloadRes.structuredContent.action).eql('reload'); | ||
| should(reloadRes.structuredContent.process).eql(name); | ||
| should(reloadRes.structuredContent.processes).be.an.Array(); | ||
|
|
||
| await callToolWithTimeout('pm2_delete_process', { process: name }); |
Copilot
AI
Dec 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cleanup logic in this test could fail to execute if any of the assertions above fail. Consider wrapping the test logic in a try-finally block to ensure the process is always deleted, preventing test pollution:
it('pm2_reload_process reloads a running process', async () => {
const name = 'mcp-reload-test';
try {
await startEcho(name);
await new Promise(resolve => setTimeout(resolve, 200));
const reloadRes = await callToolWithTimeout('pm2_reload_process', { process: name });
should(reloadRes.isError).not.equal(true);
should(reloadRes.structuredContent.action).eql('reload');
should(reloadRes.structuredContent.process).eql(name);
should(reloadRes.structuredContent.processes).be.an.Array();
} finally {
await callToolWithTimeout('pm2_delete_process', { process: name });
}
});| await startEcho(name); | |
| await new Promise(resolve => setTimeout(resolve, 200)); | |
| const reloadRes = await callToolWithTimeout('pm2_reload_process', { process: name }); | |
| should(reloadRes.isError).not.equal(true); | |
| should(reloadRes.structuredContent.action).eql('reload'); | |
| should(reloadRes.structuredContent.process).eql(name); | |
| should(reloadRes.structuredContent.processes).be.an.Array(); | |
| await callToolWithTimeout('pm2_delete_process', { process: name }); | |
| try { | |
| await startEcho(name); | |
| await new Promise(resolve => setTimeout(resolve, 200)); | |
| const reloadRes = await callToolWithTimeout('pm2_reload_process', { process: name }); | |
| should(reloadRes.isError).not.equal(true); | |
| should(reloadRes.structuredContent.action).eql('reload'); | |
| should(reloadRes.structuredContent.process).eql(name); | |
| should(reloadRes.structuredContent.processes).be.an.Array(); | |
| } finally { | |
| await callToolWithTimeout('pm2_delete_process', { process: name }); | |
| } |
| await startEcho(name); | ||
| await new Promise(resolve => setTimeout(resolve, 200)); | ||
|
|
||
| const flushRes = await callToolWithTimeout('pm2_flush_logs', { process: name }); | ||
| should(flushRes.isError).not.equal(true); | ||
| should(flushRes.structuredContent.action).eql('flush'); | ||
| should(flushRes.structuredContent.process).eql(name); | ||
|
|
||
| await callToolWithTimeout('pm2_delete_process', { process: name }); |
Copilot
AI
Dec 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cleanup logic in this test could fail to execute if any of the assertions above fail. Consider wrapping the test logic in a try-finally block to ensure the process is always deleted, preventing test pollution:
it('pm2_flush_logs flushes log files for a process', async () => {
const name = 'mcp-flush-test';
try {
await startEcho(name);
await new Promise(resolve => setTimeout(resolve, 200));
const flushRes = await callToolWithTimeout('pm2_flush_logs', { process: name });
should(flushRes.isError).not.equal(true);
should(flushRes.structuredContent.action).eql('flush');
should(flushRes.structuredContent.process).eql(name);
} finally {
await callToolWithTimeout('pm2_delete_process', { process: name });
}
});| await startEcho(name); | |
| await new Promise(resolve => setTimeout(resolve, 200)); | |
| const flushRes = await callToolWithTimeout('pm2_flush_logs', { process: name }); | |
| should(flushRes.isError).not.equal(true); | |
| should(flushRes.structuredContent.action).eql('flush'); | |
| should(flushRes.structuredContent.process).eql(name); | |
| await callToolWithTimeout('pm2_delete_process', { process: name }); | |
| try { | |
| await startEcho(name); | |
| await new Promise(resolve => setTimeout(resolve, 200)); | |
| const flushRes = await callToolWithTimeout('pm2_flush_logs', { process: name }); | |
| should(flushRes.isError).not.equal(true); | |
| should(flushRes.structuredContent.action).eql('flush'); | |
| should(flushRes.structuredContent.process).eql(name); | |
| } finally { | |
| await callToolWithTimeout('pm2_delete_process', { process: name }); | |
| } |
| await startEcho(name); | ||
| await new Promise(resolve => setTimeout(resolve, 200)); | ||
|
|
||
| const reloadLogsRes = await callToolWithTimeout('pm2_reload_logs'); | ||
| should(reloadLogsRes.isError).not.equal(true); | ||
| should(reloadLogsRes.structuredContent.action).eql('reloadLogs'); | ||
|
|
||
| await callToolWithTimeout('pm2_delete_process', { process: name }); |
Copilot
AI
Dec 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cleanup logic in this test could fail to execute if any of the assertions above fail. Consider wrapping the test logic in a try-finally block to ensure the process is always deleted, preventing test pollution:
it('pm2_reload_logs rotates and reopens log files', async () => {
const name = 'mcp-reloadlogs-test';
try {
await startEcho(name);
await new Promise(resolve => setTimeout(resolve, 200));
const reloadLogsRes = await callToolWithTimeout('pm2_reload_logs');
should(reloadLogsRes.isError).not.equal(true);
should(reloadLogsRes.structuredContent.action).eql('reloadLogs');
} finally {
await callToolWithTimeout('pm2_delete_process', { process: name });
}
});| await startEcho(name); | |
| await new Promise(resolve => setTimeout(resolve, 200)); | |
| const reloadLogsRes = await callToolWithTimeout('pm2_reload_logs'); | |
| should(reloadLogsRes.isError).not.equal(true); | |
| should(reloadLogsRes.structuredContent.action).eql('reloadLogs'); | |
| await callToolWithTimeout('pm2_delete_process', { process: name }); | |
| try { | |
| await startEcho(name); | |
| await new Promise(resolve => setTimeout(resolve, 200)); | |
| const reloadLogsRes = await callToolWithTimeout('pm2_reload_logs'); | |
| should(reloadLogsRes.isError).not.equal(true); | |
| should(reloadLogsRes.structuredContent.action).eql('reloadLogs'); | |
| } finally { | |
| await callToolWithTimeout('pm2_delete_process', { process: name }); | |
| } |
Addresses code review feedback requesting test coverage for MCP server tools that were untested.
Added tests for:
pm2_restart_process- verifies restart action and structured responsepm2_reload_process- verifies zero-downtime reload behaviorpm2_flush_logs- verifies log flush for a processpm2_reload_logs- verifies log rotation/reopenpm2_dump- verifies process list persistenceEach test follows existing pattern: start echo process → call tool → assert structured response → cleanup.
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.