Skip to content

Commit 18c12bd

Browse files
committed
test: add integration and package tests
1 parent 33c421c commit 18c12bd

File tree

2 files changed

+158
-0
lines changed

2 files changed

+158
-0
lines changed

src/integration.test.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { type ChildProcess, spawn } from 'node:child_process';
2+
import { dirname, join } from 'node:path';
3+
import { fileURLToPath } from 'node:url';
4+
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
5+
6+
const __dirname = dirname(fileURLToPath(import.meta.url));
7+
8+
describe('Build artifact integration tests', () => {
9+
let serverProcess: ChildProcess;
10+
let serverOutput = '';
11+
let serverError = '';
12+
13+
beforeAll(async () => {
14+
// Build the project first
15+
await new Promise((resolve, reject) => {
16+
const buildProcess = spawn('yarn', ['build'], {
17+
cwd: join(__dirname, '..'),
18+
shell: true,
19+
});
20+
21+
buildProcess.on('close', (code) => {
22+
if (code === 0) {
23+
resolve(undefined);
24+
} else {
25+
reject(new Error(`Build failed with code ${code}`));
26+
}
27+
});
28+
});
29+
30+
// Start the server in MCP mode (no TTY, no extra args)
31+
serverProcess = spawn('node', [join(__dirname, '../dist/index.js')], {
32+
stdio: ['pipe', 'pipe', 'pipe'],
33+
env: { ...process.env, NODE_ENV: 'test' },
34+
});
35+
36+
// Capture output
37+
serverProcess.stdout?.on('data', (data) => {
38+
serverOutput += data.toString();
39+
});
40+
41+
serverProcess.stderr?.on('data', (data) => {
42+
serverError += data.toString();
43+
});
44+
45+
// Wait for server to start
46+
await new Promise((resolve) => setTimeout(resolve, 1000));
47+
});
48+
49+
afterAll(() => {
50+
if (serverProcess) {
51+
serverProcess.kill();
52+
}
53+
});
54+
55+
it('should start without errors', () => {
56+
expect(serverError).toContain('MCP Wayback Machine server running on stdio');
57+
expect(serverProcess.killed).toBe(false);
58+
});
59+
60+
it('should respond to list_tools request', async () => {
61+
const request = {
62+
jsonrpc: '2.0',
63+
method: 'tools/list',
64+
params: {},
65+
id: 1,
66+
};
67+
68+
// Send request
69+
serverProcess.stdin?.write(`${JSON.stringify(request)}\n`);
70+
71+
// Wait for response
72+
await new Promise((resolve) => setTimeout(resolve, 500));
73+
74+
// The response should be in stdout (not stderr)
75+
expect(serverOutput.length).toBeGreaterThan(0);
76+
});
77+
78+
it('should handle malformed requests gracefully', async () => {
79+
const malformedRequest = 'not json';
80+
81+
serverProcess.stdin?.write(`${malformedRequest}\n`);
82+
83+
// Wait for potential error handling
84+
await new Promise((resolve) => setTimeout(resolve, 500));
85+
86+
// Server should still be running
87+
expect(serverProcess.killed).toBe(false);
88+
});
89+
});
90+
91+
describe('Executable permissions', () => {
92+
it('should have shebang in built file', async () => {
93+
const fs = await import('node:fs/promises');
94+
const builtFile = join(__dirname, '../dist/index.js');
95+
96+
const content = await fs.readFile(builtFile, 'utf-8');
97+
expect(content).toMatch(/^#!/);
98+
expect(content).toContain('#!/usr/bin/env node');
99+
});
100+
});

src/package.test.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { readFileSync } from 'node:fs';
2+
import { dirname, join } from 'node:path';
3+
import { fileURLToPath } from 'node:url';
4+
import { describe, expect, it } from 'vitest';
5+
6+
const __dirname = dirname(fileURLToPath(import.meta.url));
7+
8+
describe('Package configuration', () => {
9+
const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));
10+
11+
it('should have correct bin configuration', () => {
12+
expect(packageJson.bin).toBeDefined();
13+
// Handle both string and object forms of bin
14+
if (typeof packageJson.bin === 'string') {
15+
// If bin is a string, it should be the path directly
16+
expect(packageJson.bin).toBe('dist/index.js');
17+
} else {
18+
// If bin is an object, check the property
19+
expect(typeof packageJson.bin).toBe('object');
20+
expect(packageJson.bin).toHaveProperty('mcp-wayback-machine');
21+
expect(packageJson.bin['mcp-wayback-machine']).toBe('dist/index.js');
22+
}
23+
});
24+
25+
it('should have correct main entry point', () => {
26+
expect(packageJson.main).toBe('dist/index.js');
27+
});
28+
29+
it('should be configured as ES module', () => {
30+
expect(packageJson.type).toBe('module');
31+
});
32+
33+
it('should have all required dependencies', () => {
34+
expect(packageJson.dependencies).toHaveProperty('@modelcontextprotocol/sdk');
35+
expect(packageJson.dependencies).toHaveProperty('zod');
36+
});
37+
38+
it('should have correct repository information', () => {
39+
expect(packageJson.repository.url).toBe(
40+
'git+https://github.com/Mearman/mcp-wayback-machine.git',
41+
);
42+
expect(packageJson.author).toBe('Joseph Mearman');
43+
});
44+
45+
it('should include necessary files in npm package', () => {
46+
expect(packageJson.files).toContain('dist/**/*.js');
47+
expect(packageJson.files).toContain('dist/**/*.d.ts');
48+
expect(packageJson.files).toContain('LICENSE');
49+
expect(packageJson.files).toContain('README.md');
50+
});
51+
52+
it('should have required scripts', () => {
53+
expect(packageJson.scripts).toHaveProperty('build');
54+
expect(packageJson.scripts).toHaveProperty('test');
55+
expect(packageJson.scripts).toHaveProperty('start');
56+
expect(packageJson.scripts).toHaveProperty('prepublishOnly');
57+
});
58+
});

0 commit comments

Comments
 (0)