Skip to content

Commit e6109c6

Browse files
committed
Checkpoint before follow-up message
1 parent 49f6c4b commit e6109c6

File tree

5 files changed

+124
-0
lines changed

5 files changed

+124
-0
lines changed

exercises/02.start/01.problem/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"@types/node": "^24.1.0",
2626
"cross-env": "^10.0.0",
2727
"eslint": "^9.32.0",
28+
"execa": "^9.5.1",
2829
"prettier": "^3.6.2",
2930
"typescript": "^5.9.2",
3031
"vitest": "^3.2.4",
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { execa } from 'execa'
2+
import type { TestProject } from 'vitest/node'
3+
4+
export default function setup(project: TestProject) {
5+
let appServerProcess: ReturnType<typeof execa> | null = null
6+
let mcpServerProcess: ReturnType<typeof execa> | null = null
7+
8+
const startServers = async () => {
9+
// Start the app server from the root directory
10+
console.log('Starting app server...')
11+
appServerProcess = execa(
12+
'npm',
13+
[
14+
'run',
15+
'dev',
16+
'--prefix',
17+
'./epicshop/epic-me',
18+
'--',
19+
'--clearScreen=false',
20+
'--logLevel=error',
21+
'--strictPort',
22+
],
23+
{
24+
cwd: process.cwd().replace(/exercises\/.*$/, ''),
25+
stdio: 'pipe',
26+
env: {
27+
...process.env,
28+
PORT: '7788',
29+
},
30+
},
31+
)
32+
33+
// Start the MCP server from the exercise directory
34+
console.log('Starting MCP server...')
35+
mcpServerProcess = execa('npm', ['run', 'dev:server'], {
36+
cwd: process.cwd(),
37+
stdio: 'pipe',
38+
env: {
39+
...process.env,
40+
PORT: '8787',
41+
},
42+
})
43+
44+
// Wait a bit for servers to start
45+
await new Promise((resolve) => setTimeout(resolve, 3000))
46+
console.log('Servers started successfully')
47+
}
48+
49+
const cleanup = async () => {
50+
console.log('Cleaning up servers...')
51+
52+
if (mcpServerProcess) {
53+
mcpServerProcess.kill('SIGTERM')
54+
try {
55+
await mcpServerProcess
56+
} catch {
57+
// Process was killed, which is expected
58+
}
59+
}
60+
61+
if (appServerProcess) {
62+
appServerProcess.kill('SIGTERM')
63+
try {
64+
await appServerProcess
65+
} catch {
66+
// Process was killed, which is expected
67+
}
68+
}
69+
70+
console.log('Servers cleaned up')
71+
}
72+
73+
// Start servers immediately
74+
startServers().catch((error) => {
75+
console.error('Failed to start servers:', error)
76+
process.exit(1)
77+
})
78+
79+
// Return cleanup function
80+
return cleanup
81+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
2+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
3+
import { test, expect } from 'vitest'
4+
5+
async function setupClient() {
6+
const client = new Client(
7+
{
8+
name: 'EpicMeTester',
9+
version: '1.0.0',
10+
},
11+
{ capabilities: {} },
12+
)
13+
14+
const transport = new StreamableHTTPClientTransport({
15+
url: 'http://localhost:8787/mcp',
16+
})
17+
18+
await client.connect(transport)
19+
20+
return {
21+
client,
22+
async [Symbol.asyncDispose]() {
23+
await client.transport?.close()
24+
},
25+
}
26+
}
27+
28+
test('ping works', async () => {
29+
await using setup = await setupClient()
30+
const { client } = setup
31+
32+
const result = await client.ping()
33+
expect(result).toEqual({})
34+
})
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { defineConfig } from 'vitest/config'
2+
3+
export default defineConfig({
4+
test: {
5+
globalSetup: './src/globalSetup.ts',
6+
},
7+
})

package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)