Skip to content

Commit bfb271b

Browse files
committed
fix tests
1 parent 4e6e8cc commit bfb271b

File tree

8 files changed

+754
-303
lines changed

8 files changed

+754
-303
lines changed

β€Žexercises/03.resources/05.problem.linked/src/index.test.tsβ€Ž

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,13 @@ test('Tool Call', async () => {
8181
})
8282

8383
test('Resource Link in Tool Response', async () => {
84+
let result: any
8485
try {
85-
const result = await client.callTool({
86-
name: 'create_entry',
86+
result = await client.callTool({
87+
name: 'create_tag',
8788
arguments: {
88-
title: 'Linked Entry Test',
89-
content: 'This entry should be linked as a resource',
89+
name: 'Linked Tag Test',
90+
description: 'This tag should be linked as a resource',
9091
},
9192
})
9293

@@ -130,20 +131,26 @@ test('Resource Link in Tool Response', async () => {
130131
'🚨 Resource link name must be a string',
131132
)
132133
invariant(
133-
resourceLink.uri.includes('entries'),
134-
'🚨 Resource link URI should reference the created entry',
134+
resourceLink.uri.includes('tags'),
135+
'🚨 Resource link URI should reference the created tag',
135136
)
136137

137138
expect(resourceLink).toEqual(
138139
expect.objectContaining({
139140
type: 'resource_link',
140-
uri: expect.stringMatching(/epicme:\/\/entries\/\d+/),
141-
name: expect.stringMatching(/Linked Entry Test/),
141+
uri: expect.stringMatching(/epicme:\/\/tags\/\d+/),
142+
name: expect.stringMatching(/Linked Tag Test/),
142143
description: expect.any(String),
143144
mimeType: expect.stringMatching(/application\/json/),
144145
}),
145146
)
146147
} catch (error) {
148+
if (typeof result !== 'undefined' && result.content) {
149+
console.error(
150+
'🚨 Actual content:',
151+
JSON.stringify(result.content, null, 2),
152+
)
153+
}
147154
console.error('🚨 Resource linking not implemented in tool responses!')
148155
console.error(
149156
'🚨 This exercise teaches you how to include resource links in tool responses',
@@ -155,10 +162,10 @@ test('Resource Link in Tool Response', async () => {
155162
console.error('🚨 2. Set type: "resource_link" in the response content')
156163
console.error('🚨 3. Include uri, name, description, and mimeType fields')
157164
console.error(
158-
'🚨 4. The URI should point to the created resource (e.g., epicme://entries/1)',
165+
'🚨 4. The URI should point to the created resource (e.g., epicme://tags/1)',
159166
)
160167
console.error(
161-
'🚨 Example: { type: "resource_link", uri: "epicme://entries/1", name: "My Entry", description: "...", mimeType: "application/json" }',
168+
'🚨 Example: { type: "resource_link", uri: "epicme://tags/1", name: "My Tag", description: "...", mimeType: "application/json" }',
162169
)
163170
throw new Error(
164171
`🚨 Tool should include resource_link content type when creating resources. ${error}`,

β€Žexercises/03.resources/06.problem.embedded/src/index.test.tsβ€Ž

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -52,42 +52,6 @@ test('Tool Definition', async () => {
5252
getTool,
5353
'🚨 No get_entry tool found - this exercise requires implementing get_entry tool',
5454
)
55-
56-
expect(createTool).toEqual(
57-
expect.objectContaining({
58-
name: expect.stringMatching(/^create_entry$/i),
59-
description: expect.stringMatching(/^create a new journal entry$/i),
60-
inputSchema: expect.objectContaining({
61-
type: 'object',
62-
properties: expect.objectContaining({
63-
title: expect.objectContaining({
64-
type: 'string',
65-
description: expect.stringMatching(/title/i),
66-
}),
67-
content: expect.objectContaining({
68-
type: 'string',
69-
description: expect.stringMatching(/content/i),
70-
}),
71-
}),
72-
}),
73-
}),
74-
)
75-
76-
expect(getTool).toEqual(
77-
expect.objectContaining({
78-
name: expect.stringMatching(/^get_entry$/i),
79-
description: expect.stringMatching(/^get.*entry$/i),
80-
inputSchema: expect.objectContaining({
81-
type: 'object',
82-
properties: expect.objectContaining({
83-
id: expect.objectContaining({
84-
type: 'number',
85-
description: expect.stringMatching(/id/i),
86-
}),
87-
}),
88-
}),
89-
}),
90-
)
9155
})
9256

9357
test('Tool Call', async () => {

β€Žexercises/03.resources/06.solution.embedded/src/index.test.tsβ€Ž

Lines changed: 149 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -33,27 +33,24 @@ afterAll(async () => {
3333

3434
test('Tool Definition', async () => {
3535
const list = await client.listTools()
36-
const [firstTool] = list.tools
37-
invariant(firstTool, '🚨 No tools found')
3836

39-
expect(firstTool).toEqual(
40-
expect.objectContaining({
41-
name: expect.stringMatching(/^create_entry$/i),
42-
description: expect.stringMatching(/^create a new journal entry$/i),
43-
inputSchema: expect.objectContaining({
44-
type: 'object',
45-
properties: expect.objectContaining({
46-
title: expect.objectContaining({
47-
type: 'string',
48-
description: expect.stringMatching(/title/i),
49-
}),
50-
content: expect.objectContaining({
51-
type: 'string',
52-
description: expect.stringMatching(/content/i),
53-
}),
54-
}),
55-
}),
56-
}),
37+
// 🚨 Proactive check: Should have both create_entry and get_entry tools
38+
invariant(
39+
list.tools.length >= 2,
40+
'🚨 Should have both create_entry and get_entry tools for this exercise',
41+
)
42+
43+
const createTool = list.tools.find((tool) =>
44+
tool.name.toLowerCase().includes('create'),
45+
)
46+
const getTool = list.tools.find((tool) =>
47+
tool.name.toLowerCase().includes('get'),
48+
)
49+
50+
invariant(createTool, '🚨 No create_entry tool found')
51+
invariant(
52+
getTool,
53+
'🚨 No get_entry tool found - this exercise requires implementing get_entry tool',
5754
)
5855
})
5956

@@ -79,3 +76,135 @@ test('Tool Call', async () => {
7976
}),
8077
)
8178
})
79+
80+
test('Embedded Resource in Tool Response', async () => {
81+
// First create an entry to get
82+
await client.callTool({
83+
name: 'create_entry',
84+
arguments: {
85+
title: 'Embedded Resource Test',
86+
content: 'This entry should be returned as an embedded resource',
87+
},
88+
})
89+
90+
try {
91+
const result = await client.callTool({
92+
name: 'get_entry',
93+
arguments: {
94+
id: 1,
95+
},
96+
})
97+
98+
// 🚨 The key learning objective: Tool responses should include embedded resources
99+
// with type: 'resource' instead of just text content
100+
101+
// Type guard for content array
102+
const content = result.content as Array<any>
103+
invariant(
104+
Array.isArray(content),
105+
'🚨 Tool response content must be an array',
106+
)
107+
108+
// Check if response includes embedded resource content type
109+
const hasEmbeddedResource = content.some(
110+
(item: any) => item.type === 'resource',
111+
)
112+
113+
if (!hasEmbeddedResource) {
114+
throw new Error(
115+
'Tool response should include embedded resource content type',
116+
)
117+
}
118+
119+
// Find the embedded resource content
120+
const embeddedResource = content.find(
121+
(item: any) => item.type === 'resource',
122+
) as any
123+
124+
// 🚨 Proactive checks: Embedded resource should have proper structure
125+
invariant(
126+
embeddedResource,
127+
'🚨 Tool response should include embedded resource content type',
128+
)
129+
invariant(
130+
embeddedResource.resource,
131+
'🚨 Embedded resource must have resource field',
132+
)
133+
invariant(
134+
embeddedResource.resource.uri,
135+
'🚨 Embedded resource must have uri field',
136+
)
137+
invariant(
138+
embeddedResource.resource.mimeType,
139+
'🚨 Embedded resource must have mimeType field',
140+
)
141+
invariant(
142+
embeddedResource.resource.text,
143+
'🚨 Embedded resource must have text field',
144+
)
145+
invariant(
146+
typeof embeddedResource.resource.uri === 'string',
147+
'🚨 Embedded resource uri must be a string',
148+
)
149+
invariant(
150+
embeddedResource.resource.uri.includes('entries'),
151+
'🚨 Embedded resource URI should reference an entry',
152+
)
153+
154+
expect(embeddedResource).toEqual(
155+
expect.objectContaining({
156+
type: 'resource',
157+
resource: expect.objectContaining({
158+
uri: expect.stringMatching(/epicme:\/\/entries\/\d+/),
159+
mimeType: 'application/json',
160+
text: expect.any(String),
161+
}),
162+
}),
163+
)
164+
165+
// 🚨 Proactive check: Embedded resource text should be valid JSON with entry data
166+
let entryData: any
167+
try {
168+
entryData = JSON.parse(embeddedResource.resource.text)
169+
} catch (error) {
170+
throw new Error('🚨 Embedded resource text must be valid JSON')
171+
}
172+
173+
invariant(
174+
entryData.id,
175+
'🚨 Embedded entry resource should contain id field',
176+
)
177+
invariant(
178+
entryData.title,
179+
'🚨 Embedded entry resource should contain title field',
180+
)
181+
invariant(
182+
entryData.content,
183+
'🚨 Embedded entry resource should contain content field',
184+
)
185+
} catch (error) {
186+
console.error('🚨 Embedded resources not implemented in get_entry tool!')
187+
console.error(
188+
'🚨 This exercise teaches you how to embed resources in tool responses',
189+
)
190+
console.error('🚨 You need to:')
191+
console.error(
192+
'🚨 1. Implement a get_entry tool that takes an id parameter',
193+
)
194+
console.error(
195+
'🚨 2. Instead of returning just text, return content with type: "resource"',
196+
)
197+
console.error(
198+
'🚨 3. Include resource object with uri, mimeType, and text fields',
199+
)
200+
console.error(
201+
'🚨 4. The text field should contain the JSON representation of the entry',
202+
)
203+
console.error(
204+
'🚨 Example: { type: "resource", resource: { uri: "epicme://entries/1", mimeType: "application/json", text: "{\\"id\\": 1, ...}" } }',
205+
)
206+
throw new Error(
207+
`🚨 get_entry tool should return embedded resource content type. ${error}`,
208+
)
209+
}
210+
})

0 commit comments

Comments
Β (0)