@jgardner04/ghost-mcp-server 1.10.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/__tests__/mcp_server.test.js +10 -4
- package/src/__tests__/mcp_server_improved.test.js +192 -149
- package/src/__tests__/mcp_server_pages.test.js +72 -68
- package/src/errors/__tests__/index.test.js +70 -0
- package/src/errors/index.js +10 -0
- package/src/mcp_server.js +9 -19
- package/src/mcp_server_improved.js +815 -424
- package/src/schemas/__tests__/common.test.js +84 -0
- package/src/schemas/common.js +50 -3
- package/src/services/__tests__/ghostServiceImproved.members.test.js +12 -61
- package/src/services/__tests__/ghostServiceImproved.tiers.test.js +392 -0
- package/src/services/__tests__/postService.test.js +7 -99
- package/src/services/__tests__/tierService.test.js +372 -0
- package/src/services/ghostServiceImproved.js +140 -21
- package/src/services/postService.js +4 -30
- package/src/services/tierService.js +304 -0
- package/src/utils/__tests__/tempFileManager.test.js +316 -0
- package/src/utils/__tests__/validation.test.js +163 -0
- package/src/utils/tempFileManager.js +113 -0
- package/src/utils/validation.js +28 -0
|
@@ -91,15 +91,21 @@ vi.mock('axios', () => ({
|
|
|
91
91
|
}));
|
|
92
92
|
|
|
93
93
|
// Mock fs
|
|
94
|
-
const mockUnlink = vi.fn((path, cb) => cb(null));
|
|
95
94
|
const mockCreateWriteStream = vi.fn();
|
|
96
95
|
vi.mock('fs', () => ({
|
|
97
96
|
default: {
|
|
98
|
-
unlink: (...args) => mockUnlink(...args),
|
|
99
97
|
createWriteStream: (...args) => mockCreateWriteStream(...args),
|
|
100
98
|
},
|
|
101
99
|
}));
|
|
102
100
|
|
|
101
|
+
// Mock tempFileManager
|
|
102
|
+
const mockTrackTempFile = vi.fn();
|
|
103
|
+
const mockCleanupTempFiles = vi.fn().mockResolvedValue(undefined);
|
|
104
|
+
vi.mock('../utils/tempFileManager.js', () => ({
|
|
105
|
+
trackTempFile: (...args) => mockTrackTempFile(...args),
|
|
106
|
+
cleanupTempFiles: (...args) => mockCleanupTempFiles(...args),
|
|
107
|
+
}));
|
|
108
|
+
|
|
103
109
|
// Mock os
|
|
104
110
|
vi.mock('os', () => ({
|
|
105
111
|
default: { tmpdir: vi.fn().mockReturnValue('/tmp') },
|
|
@@ -133,12 +139,13 @@ describe('mcp_server_improved - ghost_get_posts tool', () => {
|
|
|
133
139
|
expect(tool).toBeDefined();
|
|
134
140
|
expect(tool.description).toContain('posts');
|
|
135
141
|
expect(tool.schema).toBeDefined();
|
|
136
|
-
|
|
137
|
-
expect(tool.schema.
|
|
138
|
-
expect(tool.schema.
|
|
139
|
-
expect(tool.schema.
|
|
140
|
-
expect(tool.schema.
|
|
141
|
-
expect(tool.schema.
|
|
142
|
+
// Zod schemas store field definitions in schema.shape
|
|
143
|
+
expect(tool.schema.shape.limit).toBeDefined();
|
|
144
|
+
expect(tool.schema.shape.page).toBeDefined();
|
|
145
|
+
expect(tool.schema.shape.status).toBeDefined();
|
|
146
|
+
expect(tool.schema.shape.include).toBeDefined();
|
|
147
|
+
expect(tool.schema.shape.filter).toBeDefined();
|
|
148
|
+
expect(tool.schema.shape.order).toBeDefined();
|
|
142
149
|
});
|
|
143
150
|
|
|
144
151
|
it('should retrieve posts with default options', async () => {
|
|
@@ -168,22 +175,24 @@ describe('mcp_server_improved - ghost_get_posts tool', () => {
|
|
|
168
175
|
|
|
169
176
|
it('should validate limit is between 1 and 100', () => {
|
|
170
177
|
const tool = mockTools.get('ghost_get_posts');
|
|
171
|
-
|
|
178
|
+
// Zod schemas store field definitions in schema.shape
|
|
179
|
+
const shape = tool.schema.shape;
|
|
172
180
|
|
|
173
181
|
// Test that limit schema exists and has proper validation
|
|
174
|
-
expect(
|
|
175
|
-
expect(() =>
|
|
176
|
-
expect(() =>
|
|
177
|
-
expect(
|
|
182
|
+
expect(shape.limit).toBeDefined();
|
|
183
|
+
expect(() => shape.limit.parse(0)).toThrow();
|
|
184
|
+
expect(() => shape.limit.parse(101)).toThrow();
|
|
185
|
+
expect(shape.limit.parse(50)).toBe(50);
|
|
178
186
|
});
|
|
179
187
|
|
|
180
188
|
it('should validate page is at least 1', () => {
|
|
181
189
|
const tool = mockTools.get('ghost_get_posts');
|
|
182
|
-
|
|
190
|
+
// Zod schemas store field definitions in schema.shape
|
|
191
|
+
const shape = tool.schema.shape;
|
|
183
192
|
|
|
184
|
-
expect(
|
|
185
|
-
expect(() =>
|
|
186
|
-
expect(
|
|
193
|
+
expect(shape.page).toBeDefined();
|
|
194
|
+
expect(() => shape.page.parse(0)).toThrow();
|
|
195
|
+
expect(shape.page.parse(1)).toBe(1);
|
|
187
196
|
});
|
|
188
197
|
|
|
189
198
|
it('should pass status filter', async () => {
|
|
@@ -198,14 +207,15 @@ describe('mcp_server_improved - ghost_get_posts tool', () => {
|
|
|
198
207
|
|
|
199
208
|
it('should validate status enum values', () => {
|
|
200
209
|
const tool = mockTools.get('ghost_get_posts');
|
|
201
|
-
|
|
210
|
+
// Zod schemas store field definitions in schema.shape
|
|
211
|
+
const shape = tool.schema.shape;
|
|
202
212
|
|
|
203
|
-
expect(
|
|
204
|
-
expect(() =>
|
|
205
|
-
expect(
|
|
206
|
-
expect(
|
|
207
|
-
expect(
|
|
208
|
-
expect(
|
|
213
|
+
expect(shape.status).toBeDefined();
|
|
214
|
+
expect(() => shape.status.parse('invalid')).toThrow();
|
|
215
|
+
expect(shape.status.parse('published')).toBe('published');
|
|
216
|
+
expect(shape.status.parse('draft')).toBe('draft');
|
|
217
|
+
expect(shape.status.parse('scheduled')).toBe('scheduled');
|
|
218
|
+
expect(shape.status.parse('all')).toBe('all');
|
|
209
219
|
});
|
|
210
220
|
|
|
211
221
|
it('should pass include parameter', async () => {
|
|
@@ -322,14 +332,15 @@ describe('mcp_server_improved - ghost_get_post tool', () => {
|
|
|
322
332
|
expect(tool).toBeDefined();
|
|
323
333
|
expect(tool.description).toContain('post');
|
|
324
334
|
expect(tool.schema).toBeDefined();
|
|
325
|
-
|
|
326
|
-
expect(tool.schema.
|
|
327
|
-
expect(tool.schema.
|
|
335
|
+
// Zod schemas store field definitions in schema.shape
|
|
336
|
+
expect(tool.schema.shape.id).toBeDefined();
|
|
337
|
+
expect(tool.schema.shape.slug).toBeDefined();
|
|
338
|
+
expect(tool.schema.shape.include).toBeDefined();
|
|
328
339
|
});
|
|
329
340
|
|
|
330
341
|
it('should retrieve post by ID', async () => {
|
|
331
342
|
const mockPost = {
|
|
332
|
-
id: '
|
|
343
|
+
id: '507f1f77bcf86cd799439011',
|
|
333
344
|
title: 'Test Post',
|
|
334
345
|
slug: 'test-post',
|
|
335
346
|
html: '<p>Content</p>',
|
|
@@ -338,16 +349,16 @@ describe('mcp_server_improved - ghost_get_post tool', () => {
|
|
|
338
349
|
mockGetPost.mockResolvedValue(mockPost);
|
|
339
350
|
|
|
340
351
|
const tool = mockTools.get('ghost_get_post');
|
|
341
|
-
const result = await tool.handler({ id: '
|
|
352
|
+
const result = await tool.handler({ id: '507f1f77bcf86cd799439011' });
|
|
342
353
|
|
|
343
|
-
expect(mockGetPost).toHaveBeenCalledWith('
|
|
344
|
-
expect(result.content[0].text).toContain('"id": "
|
|
354
|
+
expect(mockGetPost).toHaveBeenCalledWith('507f1f77bcf86cd799439011', {});
|
|
355
|
+
expect(result.content[0].text).toContain('"id": "507f1f77bcf86cd799439011"');
|
|
345
356
|
expect(result.content[0].text).toContain('"title": "Test Post"');
|
|
346
357
|
});
|
|
347
358
|
|
|
348
359
|
it('should retrieve post by slug', async () => {
|
|
349
360
|
const mockPost = {
|
|
350
|
-
id: '
|
|
361
|
+
id: '507f1f77bcf86cd799439011',
|
|
351
362
|
title: 'Test Post',
|
|
352
363
|
slug: 'test-post',
|
|
353
364
|
html: '<p>Content</p>',
|
|
@@ -364,7 +375,7 @@ describe('mcp_server_improved - ghost_get_post tool', () => {
|
|
|
364
375
|
|
|
365
376
|
it('should pass include parameter with ID', async () => {
|
|
366
377
|
const mockPost = {
|
|
367
|
-
id: '
|
|
378
|
+
id: '507f1f77bcf86cd799439011',
|
|
368
379
|
title: 'Post with relations',
|
|
369
380
|
tags: [{ name: 'tech' }],
|
|
370
381
|
authors: [{ name: 'John' }],
|
|
@@ -372,14 +383,16 @@ describe('mcp_server_improved - ghost_get_post tool', () => {
|
|
|
372
383
|
mockGetPost.mockResolvedValue(mockPost);
|
|
373
384
|
|
|
374
385
|
const tool = mockTools.get('ghost_get_post');
|
|
375
|
-
await tool.handler({ id: '
|
|
386
|
+
await tool.handler({ id: '507f1f77bcf86cd799439011', include: 'tags,authors' });
|
|
376
387
|
|
|
377
|
-
expect(mockGetPost).toHaveBeenCalledWith('
|
|
388
|
+
expect(mockGetPost).toHaveBeenCalledWith('507f1f77bcf86cd799439011', {
|
|
389
|
+
include: 'tags,authors',
|
|
390
|
+
});
|
|
378
391
|
});
|
|
379
392
|
|
|
380
393
|
it('should pass include parameter with slug', async () => {
|
|
381
394
|
const mockPost = {
|
|
382
|
-
id: '
|
|
395
|
+
id: '507f1f77bcf86cd799439011',
|
|
383
396
|
title: 'Post with relations',
|
|
384
397
|
slug: 'test-post',
|
|
385
398
|
tags: [{ name: 'tech' }],
|
|
@@ -393,20 +406,20 @@ describe('mcp_server_improved - ghost_get_post tool', () => {
|
|
|
393
406
|
});
|
|
394
407
|
|
|
395
408
|
it('should prefer ID over slug when both provided', async () => {
|
|
396
|
-
const mockPost = { id: '
|
|
409
|
+
const mockPost = { id: '507f1f77bcf86cd799439011', title: 'Test Post', slug: 'test-post' };
|
|
397
410
|
mockGetPost.mockResolvedValue(mockPost);
|
|
398
411
|
|
|
399
412
|
const tool = mockTools.get('ghost_get_post');
|
|
400
|
-
await tool.handler({ id: '
|
|
413
|
+
await tool.handler({ id: '507f1f77bcf86cd799439011', slug: 'wrong-slug' });
|
|
401
414
|
|
|
402
|
-
expect(mockGetPost).toHaveBeenCalledWith('
|
|
415
|
+
expect(mockGetPost).toHaveBeenCalledWith('507f1f77bcf86cd799439011', {});
|
|
403
416
|
});
|
|
404
417
|
|
|
405
418
|
it('should handle not found errors', async () => {
|
|
406
419
|
mockGetPost.mockRejectedValue(new Error('Post not found'));
|
|
407
420
|
|
|
408
421
|
const tool = mockTools.get('ghost_get_post');
|
|
409
|
-
const result = await tool.handler({ id: '
|
|
422
|
+
const result = await tool.handler({ id: '507f1f77bcf86cd799439099' });
|
|
410
423
|
|
|
411
424
|
expect(result.isError).toBe(true);
|
|
412
425
|
expect(result.content[0].text).toContain('Post not found');
|
|
@@ -424,7 +437,7 @@ describe('mcp_server_improved - ghost_get_post tool', () => {
|
|
|
424
437
|
|
|
425
438
|
it('should return formatted JSON response', async () => {
|
|
426
439
|
const mockPost = {
|
|
427
|
-
id: '
|
|
440
|
+
id: '507f1f77bcf86cd799439011',
|
|
428
441
|
uuid: 'uuid-123',
|
|
429
442
|
title: 'Test Post',
|
|
430
443
|
slug: 'test-post',
|
|
@@ -436,11 +449,11 @@ describe('mcp_server_improved - ghost_get_post tool', () => {
|
|
|
436
449
|
mockGetPost.mockResolvedValue(mockPost);
|
|
437
450
|
|
|
438
451
|
const tool = mockTools.get('ghost_get_post');
|
|
439
|
-
const result = await tool.handler({ id: '
|
|
452
|
+
const result = await tool.handler({ id: '507f1f77bcf86cd799439011' });
|
|
440
453
|
|
|
441
454
|
expect(result.content).toBeDefined();
|
|
442
455
|
expect(result.content[0].type).toBe('text');
|
|
443
|
-
expect(result.content[0].text).toContain('"id": "
|
|
456
|
+
expect(result.content[0].text).toContain('"id": "507f1f77bcf86cd799439011"');
|
|
444
457
|
expect(result.content[0].text).toContain('"title": "Test Post"');
|
|
445
458
|
expect(result.content[0].text).toContain('"status": "published"');
|
|
446
459
|
});
|
|
@@ -472,23 +485,24 @@ describe('mcp_server_improved - ghost_update_post tool', () => {
|
|
|
472
485
|
expect(tool).toBeDefined();
|
|
473
486
|
expect(tool.description).toContain('Updates an existing post');
|
|
474
487
|
expect(tool.schema).toBeDefined();
|
|
475
|
-
|
|
476
|
-
expect(tool.schema.
|
|
477
|
-
expect(tool.schema.
|
|
478
|
-
expect(tool.schema.
|
|
479
|
-
expect(tool.schema.
|
|
480
|
-
expect(tool.schema.
|
|
481
|
-
expect(tool.schema.
|
|
482
|
-
expect(tool.schema.
|
|
483
|
-
expect(tool.schema.
|
|
484
|
-
expect(tool.schema.
|
|
485
|
-
expect(tool.schema.
|
|
486
|
-
expect(tool.schema.
|
|
488
|
+
// Zod schemas store field definitions in schema.shape
|
|
489
|
+
expect(tool.schema.shape.id).toBeDefined();
|
|
490
|
+
expect(tool.schema.shape.title).toBeDefined();
|
|
491
|
+
expect(tool.schema.shape.html).toBeDefined();
|
|
492
|
+
expect(tool.schema.shape.status).toBeDefined();
|
|
493
|
+
expect(tool.schema.shape.tags).toBeDefined();
|
|
494
|
+
expect(tool.schema.shape.feature_image).toBeDefined();
|
|
495
|
+
expect(tool.schema.shape.feature_image_alt).toBeDefined();
|
|
496
|
+
expect(tool.schema.shape.feature_image_caption).toBeDefined();
|
|
497
|
+
expect(tool.schema.shape.meta_title).toBeDefined();
|
|
498
|
+
expect(tool.schema.shape.meta_description).toBeDefined();
|
|
499
|
+
expect(tool.schema.shape.published_at).toBeDefined();
|
|
500
|
+
expect(tool.schema.shape.custom_excerpt).toBeDefined();
|
|
487
501
|
});
|
|
488
502
|
|
|
489
503
|
it('should update post title', async () => {
|
|
490
504
|
const mockUpdatedPost = {
|
|
491
|
-
id: '
|
|
505
|
+
id: '507f1f77bcf86cd799439011',
|
|
492
506
|
title: 'Updated Title',
|
|
493
507
|
slug: 'test-post',
|
|
494
508
|
html: '<p>Content</p>',
|
|
@@ -498,15 +512,17 @@ describe('mcp_server_improved - ghost_update_post tool', () => {
|
|
|
498
512
|
mockUpdatePost.mockResolvedValue(mockUpdatedPost);
|
|
499
513
|
|
|
500
514
|
const tool = mockTools.get('ghost_update_post');
|
|
501
|
-
const result = await tool.handler({ id: '
|
|
515
|
+
const result = await tool.handler({ id: '507f1f77bcf86cd799439011', title: 'Updated Title' });
|
|
502
516
|
|
|
503
|
-
expect(mockUpdatePost).toHaveBeenCalledWith('
|
|
517
|
+
expect(mockUpdatePost).toHaveBeenCalledWith('507f1f77bcf86cd799439011', {
|
|
518
|
+
title: 'Updated Title',
|
|
519
|
+
});
|
|
504
520
|
expect(result.content[0].text).toContain('"title": "Updated Title"');
|
|
505
521
|
});
|
|
506
522
|
|
|
507
523
|
it('should update post content', async () => {
|
|
508
524
|
const mockUpdatedPost = {
|
|
509
|
-
id: '
|
|
525
|
+
id: '507f1f77bcf86cd799439011',
|
|
510
526
|
title: 'Test Post',
|
|
511
527
|
html: '<p>Updated content</p>',
|
|
512
528
|
status: 'published',
|
|
@@ -515,15 +531,20 @@ describe('mcp_server_improved - ghost_update_post tool', () => {
|
|
|
515
531
|
mockUpdatePost.mockResolvedValue(mockUpdatedPost);
|
|
516
532
|
|
|
517
533
|
const tool = mockTools.get('ghost_update_post');
|
|
518
|
-
const result = await tool.handler({
|
|
534
|
+
const result = await tool.handler({
|
|
535
|
+
id: '507f1f77bcf86cd799439011',
|
|
536
|
+
html: '<p>Updated content</p>',
|
|
537
|
+
});
|
|
519
538
|
|
|
520
|
-
expect(mockUpdatePost).toHaveBeenCalledWith('
|
|
539
|
+
expect(mockUpdatePost).toHaveBeenCalledWith('507f1f77bcf86cd799439011', {
|
|
540
|
+
html: '<p>Updated content</p>',
|
|
541
|
+
});
|
|
521
542
|
expect(result.content[0].text).toContain('Updated content');
|
|
522
543
|
});
|
|
523
544
|
|
|
524
545
|
it('should update post status', async () => {
|
|
525
546
|
const mockUpdatedPost = {
|
|
526
|
-
id: '
|
|
547
|
+
id: '507f1f77bcf86cd799439011',
|
|
527
548
|
title: 'Test Post',
|
|
528
549
|
html: '<p>Content</p>',
|
|
529
550
|
status: 'published',
|
|
@@ -532,15 +553,17 @@ describe('mcp_server_improved - ghost_update_post tool', () => {
|
|
|
532
553
|
mockUpdatePost.mockResolvedValue(mockUpdatedPost);
|
|
533
554
|
|
|
534
555
|
const tool = mockTools.get('ghost_update_post');
|
|
535
|
-
const result = await tool.handler({ id: '
|
|
556
|
+
const result = await tool.handler({ id: '507f1f77bcf86cd799439011', status: 'published' });
|
|
536
557
|
|
|
537
|
-
expect(mockUpdatePost).toHaveBeenCalledWith('
|
|
558
|
+
expect(mockUpdatePost).toHaveBeenCalledWith('507f1f77bcf86cd799439011', {
|
|
559
|
+
status: 'published',
|
|
560
|
+
});
|
|
538
561
|
expect(result.content[0].text).toContain('"status": "published"');
|
|
539
562
|
});
|
|
540
563
|
|
|
541
564
|
it('should update post tags', async () => {
|
|
542
565
|
const mockUpdatedPost = {
|
|
543
|
-
id: '
|
|
566
|
+
id: '507f1f77bcf86cd799439011',
|
|
544
567
|
title: 'Test Post',
|
|
545
568
|
html: '<p>Content</p>',
|
|
546
569
|
tags: [{ name: 'tech' }, { name: 'javascript' }],
|
|
@@ -549,16 +572,21 @@ describe('mcp_server_improved - ghost_update_post tool', () => {
|
|
|
549
572
|
mockUpdatePost.mockResolvedValue(mockUpdatedPost);
|
|
550
573
|
|
|
551
574
|
const tool = mockTools.get('ghost_update_post');
|
|
552
|
-
const result = await tool.handler({
|
|
575
|
+
const result = await tool.handler({
|
|
576
|
+
id: '507f1f77bcf86cd799439011',
|
|
577
|
+
tags: ['tech', 'javascript'],
|
|
578
|
+
});
|
|
553
579
|
|
|
554
|
-
expect(mockUpdatePost).toHaveBeenCalledWith('
|
|
580
|
+
expect(mockUpdatePost).toHaveBeenCalledWith('507f1f77bcf86cd799439011', {
|
|
581
|
+
tags: ['tech', 'javascript'],
|
|
582
|
+
});
|
|
555
583
|
expect(result.content[0].text).toContain('tech');
|
|
556
584
|
expect(result.content[0].text).toContain('javascript');
|
|
557
585
|
});
|
|
558
586
|
|
|
559
587
|
it('should update post featured image', async () => {
|
|
560
588
|
const mockUpdatedPost = {
|
|
561
|
-
id: '
|
|
589
|
+
id: '507f1f77bcf86cd799439011',
|
|
562
590
|
title: 'Test Post',
|
|
563
591
|
feature_image: 'https://example.com/new-image.jpg',
|
|
564
592
|
feature_image_alt: 'New image',
|
|
@@ -568,12 +596,12 @@ describe('mcp_server_improved - ghost_update_post tool', () => {
|
|
|
568
596
|
|
|
569
597
|
const tool = mockTools.get('ghost_update_post');
|
|
570
598
|
const result = await tool.handler({
|
|
571
|
-
id: '
|
|
599
|
+
id: '507f1f77bcf86cd799439011',
|
|
572
600
|
feature_image: 'https://example.com/new-image.jpg',
|
|
573
601
|
feature_image_alt: 'New image',
|
|
574
602
|
});
|
|
575
603
|
|
|
576
|
-
expect(mockUpdatePost).toHaveBeenCalledWith('
|
|
604
|
+
expect(mockUpdatePost).toHaveBeenCalledWith('507f1f77bcf86cd799439011', {
|
|
577
605
|
feature_image: 'https://example.com/new-image.jpg',
|
|
578
606
|
feature_image_alt: 'New image',
|
|
579
607
|
});
|
|
@@ -582,7 +610,7 @@ describe('mcp_server_improved - ghost_update_post tool', () => {
|
|
|
582
610
|
|
|
583
611
|
it('should update SEO meta fields', async () => {
|
|
584
612
|
const mockUpdatedPost = {
|
|
585
|
-
id: '
|
|
613
|
+
id: '507f1f77bcf86cd799439011',
|
|
586
614
|
title: 'Test Post',
|
|
587
615
|
meta_title: 'SEO Title',
|
|
588
616
|
meta_description: 'SEO Description',
|
|
@@ -592,12 +620,12 @@ describe('mcp_server_improved - ghost_update_post tool', () => {
|
|
|
592
620
|
|
|
593
621
|
const tool = mockTools.get('ghost_update_post');
|
|
594
622
|
const result = await tool.handler({
|
|
595
|
-
id: '
|
|
623
|
+
id: '507f1f77bcf86cd799439011',
|
|
596
624
|
meta_title: 'SEO Title',
|
|
597
625
|
meta_description: 'SEO Description',
|
|
598
626
|
});
|
|
599
627
|
|
|
600
|
-
expect(mockUpdatePost).toHaveBeenCalledWith('
|
|
628
|
+
expect(mockUpdatePost).toHaveBeenCalledWith('507f1f77bcf86cd799439011', {
|
|
601
629
|
meta_title: 'SEO Title',
|
|
602
630
|
meta_description: 'SEO Description',
|
|
603
631
|
});
|
|
@@ -607,7 +635,7 @@ describe('mcp_server_improved - ghost_update_post tool', () => {
|
|
|
607
635
|
|
|
608
636
|
it('should update multiple fields at once', async () => {
|
|
609
637
|
const mockUpdatedPost = {
|
|
610
|
-
id: '
|
|
638
|
+
id: '507f1f77bcf86cd799439011',
|
|
611
639
|
title: 'Updated Title',
|
|
612
640
|
html: '<p>Updated content</p>',
|
|
613
641
|
status: 'published',
|
|
@@ -618,14 +646,14 @@ describe('mcp_server_improved - ghost_update_post tool', () => {
|
|
|
618
646
|
|
|
619
647
|
const tool = mockTools.get('ghost_update_post');
|
|
620
648
|
const result = await tool.handler({
|
|
621
|
-
id: '
|
|
649
|
+
id: '507f1f77bcf86cd799439011',
|
|
622
650
|
title: 'Updated Title',
|
|
623
651
|
html: '<p>Updated content</p>',
|
|
624
652
|
status: 'published',
|
|
625
653
|
tags: ['tech'],
|
|
626
654
|
});
|
|
627
655
|
|
|
628
|
-
expect(mockUpdatePost).toHaveBeenCalledWith('
|
|
656
|
+
expect(mockUpdatePost).toHaveBeenCalledWith('507f1f77bcf86cd799439011', {
|
|
629
657
|
title: 'Updated Title',
|
|
630
658
|
html: '<p>Updated content</p>',
|
|
631
659
|
status: 'published',
|
|
@@ -638,7 +666,7 @@ describe('mcp_server_improved - ghost_update_post tool', () => {
|
|
|
638
666
|
mockUpdatePost.mockRejectedValue(new Error('Post not found'));
|
|
639
667
|
|
|
640
668
|
const tool = mockTools.get('ghost_update_post');
|
|
641
|
-
const result = await tool.handler({ id: '
|
|
669
|
+
const result = await tool.handler({ id: '507f1f77bcf86cd799439099', title: 'New Title' });
|
|
642
670
|
|
|
643
671
|
expect(result.isError).toBe(true);
|
|
644
672
|
expect(result.content[0].text).toContain('Post not found');
|
|
@@ -648,7 +676,7 @@ describe('mcp_server_improved - ghost_update_post tool', () => {
|
|
|
648
676
|
mockUpdatePost.mockRejectedValue(new Error('Validation failed: Title is required'));
|
|
649
677
|
|
|
650
678
|
const tool = mockTools.get('ghost_update_post');
|
|
651
|
-
const result = await tool.handler({ id: '
|
|
679
|
+
const result = await tool.handler({ id: '507f1f77bcf86cd799439011', title: '' });
|
|
652
680
|
|
|
653
681
|
expect(result.isError).toBe(true);
|
|
654
682
|
expect(result.content[0].text).toContain('Validation failed');
|
|
@@ -658,7 +686,7 @@ describe('mcp_server_improved - ghost_update_post tool', () => {
|
|
|
658
686
|
mockUpdatePost.mockRejectedValue(new Error('Ghost API error: Server timeout'));
|
|
659
687
|
|
|
660
688
|
const tool = mockTools.get('ghost_update_post');
|
|
661
|
-
const result = await tool.handler({ id: '
|
|
689
|
+
const result = await tool.handler({ id: '507f1f77bcf86cd799439011', title: 'Updated' });
|
|
662
690
|
|
|
663
691
|
expect(result.isError).toBe(true);
|
|
664
692
|
expect(result.content[0].text).toContain('Ghost API error');
|
|
@@ -666,7 +694,7 @@ describe('mcp_server_improved - ghost_update_post tool', () => {
|
|
|
666
694
|
|
|
667
695
|
it('should return formatted JSON response', async () => {
|
|
668
696
|
const mockUpdatedPost = {
|
|
669
|
-
id: '
|
|
697
|
+
id: '507f1f77bcf86cd799439011',
|
|
670
698
|
uuid: 'uuid-123',
|
|
671
699
|
title: 'Updated Post',
|
|
672
700
|
slug: 'updated-post',
|
|
@@ -678,11 +706,11 @@ describe('mcp_server_improved - ghost_update_post tool', () => {
|
|
|
678
706
|
mockUpdatePost.mockResolvedValue(mockUpdatedPost);
|
|
679
707
|
|
|
680
708
|
const tool = mockTools.get('ghost_update_post');
|
|
681
|
-
const result = await tool.handler({ id: '
|
|
709
|
+
const result = await tool.handler({ id: '507f1f77bcf86cd799439011', title: 'Updated Post' });
|
|
682
710
|
|
|
683
711
|
expect(result.content).toBeDefined();
|
|
684
712
|
expect(result.content[0].type).toBe('text');
|
|
685
|
-
expect(result.content[0].text).toContain('"id": "
|
|
713
|
+
expect(result.content[0].text).toContain('"id": "507f1f77bcf86cd799439011"');
|
|
686
714
|
expect(result.content[0].text).toContain('"title": "Updated Post"');
|
|
687
715
|
expect(result.content[0].text).toContain('"status": "published"');
|
|
688
716
|
});
|
|
@@ -707,17 +735,20 @@ describe('mcp_server_improved - ghost_delete_post tool', () => {
|
|
|
707
735
|
expect(tool.description).toContain('Deletes a post');
|
|
708
736
|
expect(tool.description).toContain('permanent');
|
|
709
737
|
expect(tool.schema).toBeDefined();
|
|
710
|
-
|
|
738
|
+
// Zod schemas store field definitions in schema.shape
|
|
739
|
+
expect(tool.schema.shape.id).toBeDefined();
|
|
711
740
|
});
|
|
712
741
|
|
|
713
742
|
it('should delete post by ID', async () => {
|
|
714
743
|
mockDeletePost.mockResolvedValue({ deleted: true });
|
|
715
744
|
|
|
716
745
|
const tool = mockTools.get('ghost_delete_post');
|
|
717
|
-
const result = await tool.handler({ id: '
|
|
746
|
+
const result = await tool.handler({ id: '507f1f77bcf86cd799439011' });
|
|
718
747
|
|
|
719
|
-
expect(mockDeletePost).toHaveBeenCalledWith('
|
|
720
|
-
expect(result.content[0].text).toContain(
|
|
748
|
+
expect(mockDeletePost).toHaveBeenCalledWith('507f1f77bcf86cd799439011');
|
|
749
|
+
expect(result.content[0].text).toContain(
|
|
750
|
+
'Post 507f1f77bcf86cd799439011 has been successfully deleted'
|
|
751
|
+
);
|
|
721
752
|
expect(result.isError).toBeUndefined();
|
|
722
753
|
});
|
|
723
754
|
|
|
@@ -725,7 +756,7 @@ describe('mcp_server_improved - ghost_delete_post tool', () => {
|
|
|
725
756
|
mockDeletePost.mockRejectedValue(new Error('Post not found'));
|
|
726
757
|
|
|
727
758
|
const tool = mockTools.get('ghost_delete_post');
|
|
728
|
-
const result = await tool.handler({ id: '
|
|
759
|
+
const result = await tool.handler({ id: '507f1f77bcf86cd799439099' });
|
|
729
760
|
|
|
730
761
|
expect(result.isError).toBe(true);
|
|
731
762
|
expect(result.content[0].text).toContain('Post not found');
|
|
@@ -735,7 +766,7 @@ describe('mcp_server_improved - ghost_delete_post tool', () => {
|
|
|
735
766
|
mockDeletePost.mockRejectedValue(new Error('Ghost API error: Permission denied'));
|
|
736
767
|
|
|
737
768
|
const tool = mockTools.get('ghost_delete_post');
|
|
738
|
-
const result = await tool.handler({ id: '
|
|
769
|
+
const result = await tool.handler({ id: '507f1f77bcf86cd799439011' });
|
|
739
770
|
|
|
740
771
|
expect(result.isError).toBe(true);
|
|
741
772
|
expect(result.content[0].text).toContain('Ghost API error');
|
|
@@ -745,18 +776,20 @@ describe('mcp_server_improved - ghost_delete_post tool', () => {
|
|
|
745
776
|
mockDeletePost.mockResolvedValue({ deleted: true });
|
|
746
777
|
|
|
747
778
|
const tool = mockTools.get('ghost_delete_post');
|
|
748
|
-
const result = await tool.handler({ id: '
|
|
779
|
+
const result = await tool.handler({ id: '507f1f77bcf86cd799439011' });
|
|
749
780
|
|
|
750
781
|
expect(result.content).toBeDefined();
|
|
751
782
|
expect(result.content[0].type).toBe('text');
|
|
752
|
-
expect(result.content[0].text).toBe(
|
|
783
|
+
expect(result.content[0].text).toBe(
|
|
784
|
+
'Post 507f1f77bcf86cd799439011 has been successfully deleted.'
|
|
785
|
+
);
|
|
753
786
|
});
|
|
754
787
|
|
|
755
788
|
it('should handle network errors', async () => {
|
|
756
789
|
mockDeletePost.mockRejectedValue(new Error('Network error: Connection refused'));
|
|
757
790
|
|
|
758
791
|
const tool = mockTools.get('ghost_delete_post');
|
|
759
|
-
const result = await tool.handler({ id: '
|
|
792
|
+
const result = await tool.handler({ id: '507f1f77bcf86cd799439012' });
|
|
760
793
|
|
|
761
794
|
expect(result.isError).toBe(true);
|
|
762
795
|
expect(result.content[0].text).toContain('Network error');
|
|
@@ -781,9 +814,10 @@ describe('mcp_server_improved - ghost_search_posts tool', () => {
|
|
|
781
814
|
expect(tool).toBeDefined();
|
|
782
815
|
expect(tool.description).toContain('Search');
|
|
783
816
|
expect(tool.schema).toBeDefined();
|
|
784
|
-
|
|
785
|
-
expect(tool.schema.
|
|
786
|
-
expect(tool.schema.
|
|
817
|
+
// Zod schemas store field definitions in schema.shape
|
|
818
|
+
expect(tool.schema.shape.query).toBeDefined();
|
|
819
|
+
expect(tool.schema.shape.status).toBeDefined();
|
|
820
|
+
expect(tool.schema.shape.limit).toBeDefined();
|
|
787
821
|
});
|
|
788
822
|
|
|
789
823
|
it('should search posts with query only', async () => {
|
|
@@ -825,24 +859,26 @@ describe('mcp_server_improved - ghost_search_posts tool', () => {
|
|
|
825
859
|
|
|
826
860
|
it('should validate limit is between 1 and 50', () => {
|
|
827
861
|
const tool = mockTools.get('ghost_search_posts');
|
|
828
|
-
|
|
862
|
+
// Zod schemas store field definitions in schema.shape
|
|
863
|
+
const shape = tool.schema.shape;
|
|
829
864
|
|
|
830
|
-
expect(
|
|
831
|
-
expect(() =>
|
|
832
|
-
expect(() =>
|
|
833
|
-
expect(
|
|
865
|
+
expect(shape.limit).toBeDefined();
|
|
866
|
+
expect(() => shape.limit.parse(0)).toThrow();
|
|
867
|
+
expect(() => shape.limit.parse(51)).toThrow();
|
|
868
|
+
expect(shape.limit.parse(25)).toBe(25);
|
|
834
869
|
});
|
|
835
870
|
|
|
836
871
|
it('should validate status enum values', () => {
|
|
837
872
|
const tool = mockTools.get('ghost_search_posts');
|
|
838
|
-
|
|
873
|
+
// Zod schemas store field definitions in schema.shape
|
|
874
|
+
const shape = tool.schema.shape;
|
|
839
875
|
|
|
840
|
-
expect(
|
|
841
|
-
expect(() =>
|
|
842
|
-
expect(
|
|
843
|
-
expect(
|
|
844
|
-
expect(
|
|
845
|
-
expect(
|
|
876
|
+
expect(shape.status).toBeDefined();
|
|
877
|
+
expect(() => shape.status.parse('invalid')).toThrow();
|
|
878
|
+
expect(shape.status.parse('published')).toBe('published');
|
|
879
|
+
expect(shape.status.parse('draft')).toBe('draft');
|
|
880
|
+
expect(shape.status.parse('scheduled')).toBe('scheduled');
|
|
881
|
+
expect(shape.status.parse('all')).toBe('all');
|
|
846
882
|
});
|
|
847
883
|
|
|
848
884
|
it('should pass all parameters combined', async () => {
|
|
@@ -863,13 +899,12 @@ describe('mcp_server_improved - ghost_search_posts tool', () => {
|
|
|
863
899
|
});
|
|
864
900
|
|
|
865
901
|
it('should handle errors from searchPosts', async () => {
|
|
866
|
-
|
|
867
|
-
|
|
902
|
+
// Empty query is now caught by Zod validation
|
|
868
903
|
const tool = mockTools.get('ghost_search_posts');
|
|
869
904
|
const result = await tool.handler({ query: '' });
|
|
870
905
|
|
|
871
906
|
expect(result.isError).toBe(true);
|
|
872
|
-
expect(result.content[0].text).toContain('
|
|
907
|
+
expect(result.content[0].text).toContain('VALIDATION_ERROR');
|
|
873
908
|
});
|
|
874
909
|
|
|
875
910
|
it('should handle Ghost API errors', async () => {
|
|
@@ -930,14 +965,15 @@ describe('ghost_get_tag', () => {
|
|
|
930
965
|
|
|
931
966
|
it('should have correct schema with id and slug as optional', () => {
|
|
932
967
|
const tool = mockTools.get('ghost_get_tag');
|
|
933
|
-
|
|
934
|
-
expect(tool.schema.
|
|
935
|
-
expect(tool.schema.
|
|
968
|
+
// Zod schemas store field definitions in schema.shape
|
|
969
|
+
expect(tool.schema.shape.id).toBeDefined();
|
|
970
|
+
expect(tool.schema.shape.slug).toBeDefined();
|
|
971
|
+
expect(tool.schema.shape.include).toBeDefined();
|
|
936
972
|
});
|
|
937
973
|
|
|
938
974
|
it('should retrieve tag by ID', async () => {
|
|
939
975
|
const mockTag = {
|
|
940
|
-
id: '
|
|
976
|
+
id: '507f1f77bcf86cd799439011',
|
|
941
977
|
name: 'Test Tag',
|
|
942
978
|
slug: 'test-tag',
|
|
943
979
|
description: 'A test tag',
|
|
@@ -945,18 +981,18 @@ describe('ghost_get_tag', () => {
|
|
|
945
981
|
mockGetTag.mockResolvedValue(mockTag);
|
|
946
982
|
|
|
947
983
|
const tool = mockTools.get('ghost_get_tag');
|
|
948
|
-
const result = await tool.handler({ id: '
|
|
984
|
+
const result = await tool.handler({ id: '507f1f77bcf86cd799439011' });
|
|
949
985
|
|
|
950
|
-
expect(mockGetTag).toHaveBeenCalledWith('
|
|
986
|
+
expect(mockGetTag).toHaveBeenCalledWith('507f1f77bcf86cd799439011', {});
|
|
951
987
|
expect(result.content).toBeDefined();
|
|
952
988
|
expect(result.content[0].type).toBe('text');
|
|
953
|
-
expect(result.content[0].text).toContain('"id": "
|
|
989
|
+
expect(result.content[0].text).toContain('"id": "507f1f77bcf86cd799439011"');
|
|
954
990
|
expect(result.content[0].text).toContain('"name": "Test Tag"');
|
|
955
991
|
});
|
|
956
992
|
|
|
957
993
|
it('should retrieve tag by slug', async () => {
|
|
958
994
|
const mockTag = {
|
|
959
|
-
id: '
|
|
995
|
+
id: '507f1f77bcf86cd799439011',
|
|
960
996
|
name: 'Test Tag',
|
|
961
997
|
slug: 'test-tag',
|
|
962
998
|
description: 'A test tag',
|
|
@@ -972,7 +1008,7 @@ describe('ghost_get_tag', () => {
|
|
|
972
1008
|
|
|
973
1009
|
it('should support include parameter for post count', async () => {
|
|
974
1010
|
const mockTag = {
|
|
975
|
-
id: '
|
|
1011
|
+
id: '507f1f77bcf86cd799439011',
|
|
976
1012
|
name: 'Test Tag',
|
|
977
1013
|
slug: 'test-tag',
|
|
978
1014
|
count: { posts: 5 },
|
|
@@ -980,9 +1016,9 @@ describe('ghost_get_tag', () => {
|
|
|
980
1016
|
mockGetTag.mockResolvedValue(mockTag);
|
|
981
1017
|
|
|
982
1018
|
const tool = mockTools.get('ghost_get_tag');
|
|
983
|
-
const result = await tool.handler({ id: '
|
|
1019
|
+
const result = await tool.handler({ id: '507f1f77bcf86cd799439011', include: 'count.posts' });
|
|
984
1020
|
|
|
985
|
-
expect(mockGetTag).toHaveBeenCalledWith('
|
|
1021
|
+
expect(mockGetTag).toHaveBeenCalledWith('507f1f77bcf86cd799439011', { include: 'count.posts' });
|
|
986
1022
|
expect(result.content[0].text).toContain('"count"');
|
|
987
1023
|
});
|
|
988
1024
|
|
|
@@ -999,7 +1035,7 @@ describe('ghost_get_tag', () => {
|
|
|
999
1035
|
mockGetTag.mockRejectedValue(new Error('Tag not found'));
|
|
1000
1036
|
|
|
1001
1037
|
const tool = mockTools.get('ghost_get_tag');
|
|
1002
|
-
const result = await tool.handler({ id: '
|
|
1038
|
+
const result = await tool.handler({ id: '507f1f77bcf86cd799439099' });
|
|
1003
1039
|
|
|
1004
1040
|
expect(result.isError).toBe(true);
|
|
1005
1041
|
expect(result.content[0].text).toContain('Tag not found');
|
|
@@ -1022,48 +1058,54 @@ describe('ghost_update_tag', () => {
|
|
|
1022
1058
|
|
|
1023
1059
|
it('should have correct schema with all update fields', () => {
|
|
1024
1060
|
const tool = mockTools.get('ghost_update_tag');
|
|
1025
|
-
|
|
1026
|
-
expect(tool.schema.
|
|
1027
|
-
expect(tool.schema.
|
|
1028
|
-
expect(tool.schema.
|
|
1029
|
-
expect(tool.schema.
|
|
1030
|
-
expect(tool.schema.
|
|
1031
|
-
expect(tool.schema.
|
|
1061
|
+
// Zod schemas store field definitions in schema.shape
|
|
1062
|
+
expect(tool.schema.shape.id).toBeDefined();
|
|
1063
|
+
expect(tool.schema.shape.name).toBeDefined();
|
|
1064
|
+
expect(tool.schema.shape.slug).toBeDefined();
|
|
1065
|
+
expect(tool.schema.shape.description).toBeDefined();
|
|
1066
|
+
expect(tool.schema.shape.feature_image).toBeDefined();
|
|
1067
|
+
expect(tool.schema.shape.meta_title).toBeDefined();
|
|
1068
|
+
expect(tool.schema.shape.meta_description).toBeDefined();
|
|
1032
1069
|
});
|
|
1033
1070
|
|
|
1034
1071
|
it('should update tag name', async () => {
|
|
1035
1072
|
const mockUpdatedTag = {
|
|
1036
|
-
id: '
|
|
1073
|
+
id: '507f1f77bcf86cd799439011',
|
|
1037
1074
|
name: 'Updated Tag',
|
|
1038
1075
|
slug: 'updated-tag',
|
|
1039
1076
|
};
|
|
1040
1077
|
mockUpdateTag.mockResolvedValue(mockUpdatedTag);
|
|
1041
1078
|
|
|
1042
1079
|
const tool = mockTools.get('ghost_update_tag');
|
|
1043
|
-
const result = await tool.handler({ id: '
|
|
1080
|
+
const result = await tool.handler({ id: '507f1f77bcf86cd799439011', name: 'Updated Tag' });
|
|
1044
1081
|
|
|
1045
|
-
expect(mockUpdateTag).toHaveBeenCalledWith('
|
|
1082
|
+
expect(mockUpdateTag).toHaveBeenCalledWith('507f1f77bcf86cd799439011', { name: 'Updated Tag' });
|
|
1046
1083
|
expect(result.content[0].text).toContain('"name": "Updated Tag"');
|
|
1047
1084
|
});
|
|
1048
1085
|
|
|
1049
1086
|
it('should update tag description', async () => {
|
|
1050
1087
|
const mockUpdatedTag = {
|
|
1051
|
-
id: '
|
|
1088
|
+
id: '507f1f77bcf86cd799439011',
|
|
1052
1089
|
name: 'Test Tag',
|
|
1053
1090
|
description: 'New description',
|
|
1054
1091
|
};
|
|
1055
1092
|
mockUpdateTag.mockResolvedValue(mockUpdatedTag);
|
|
1056
1093
|
|
|
1057
1094
|
const tool = mockTools.get('ghost_update_tag');
|
|
1058
|
-
const result = await tool.handler({
|
|
1095
|
+
const result = await tool.handler({
|
|
1096
|
+
id: '507f1f77bcf86cd799439011',
|
|
1097
|
+
description: 'New description',
|
|
1098
|
+
});
|
|
1059
1099
|
|
|
1060
|
-
expect(mockUpdateTag).toHaveBeenCalledWith('
|
|
1100
|
+
expect(mockUpdateTag).toHaveBeenCalledWith('507f1f77bcf86cd799439011', {
|
|
1101
|
+
description: 'New description',
|
|
1102
|
+
});
|
|
1061
1103
|
expect(result.content[0].text).toContain('"description": "New description"');
|
|
1062
1104
|
});
|
|
1063
1105
|
|
|
1064
1106
|
it('should update multiple fields at once', async () => {
|
|
1065
1107
|
const mockUpdatedTag = {
|
|
1066
|
-
id: '
|
|
1108
|
+
id: '507f1f77bcf86cd799439011',
|
|
1067
1109
|
name: 'Updated Tag',
|
|
1068
1110
|
slug: 'updated-tag',
|
|
1069
1111
|
description: 'Updated description',
|
|
@@ -1073,13 +1115,13 @@ describe('ghost_update_tag', () => {
|
|
|
1073
1115
|
|
|
1074
1116
|
const tool = mockTools.get('ghost_update_tag');
|
|
1075
1117
|
await tool.handler({
|
|
1076
|
-
id: '
|
|
1118
|
+
id: '507f1f77bcf86cd799439011',
|
|
1077
1119
|
name: 'Updated Tag',
|
|
1078
1120
|
description: 'Updated description',
|
|
1079
1121
|
meta_title: 'Updated Meta',
|
|
1080
1122
|
});
|
|
1081
1123
|
|
|
1082
|
-
expect(mockUpdateTag).toHaveBeenCalledWith('
|
|
1124
|
+
expect(mockUpdateTag).toHaveBeenCalledWith('507f1f77bcf86cd799439011', {
|
|
1083
1125
|
name: 'Updated Tag',
|
|
1084
1126
|
description: 'Updated description',
|
|
1085
1127
|
meta_title: 'Updated Meta',
|
|
@@ -1088,7 +1130,7 @@ describe('ghost_update_tag', () => {
|
|
|
1088
1130
|
|
|
1089
1131
|
it('should update tag feature image', async () => {
|
|
1090
1132
|
const mockUpdatedTag = {
|
|
1091
|
-
id: '
|
|
1133
|
+
id: '507f1f77bcf86cd799439011',
|
|
1092
1134
|
name: 'Test Tag',
|
|
1093
1135
|
feature_image: 'https://example.com/image.jpg',
|
|
1094
1136
|
};
|
|
@@ -1096,11 +1138,11 @@ describe('ghost_update_tag', () => {
|
|
|
1096
1138
|
|
|
1097
1139
|
const tool = mockTools.get('ghost_update_tag');
|
|
1098
1140
|
await tool.handler({
|
|
1099
|
-
id: '
|
|
1141
|
+
id: '507f1f77bcf86cd799439011',
|
|
1100
1142
|
feature_image: 'https://example.com/image.jpg',
|
|
1101
1143
|
});
|
|
1102
1144
|
|
|
1103
|
-
expect(mockUpdateTag).toHaveBeenCalledWith('
|
|
1145
|
+
expect(mockUpdateTag).toHaveBeenCalledWith('507f1f77bcf86cd799439011', {
|
|
1104
1146
|
feature_image: 'https://example.com/image.jpg',
|
|
1105
1147
|
});
|
|
1106
1148
|
});
|
|
@@ -1110,14 +1152,14 @@ describe('ghost_update_tag', () => {
|
|
|
1110
1152
|
const result = await tool.handler({ name: 'Test' });
|
|
1111
1153
|
|
|
1112
1154
|
expect(result.isError).toBe(true);
|
|
1113
|
-
expect(result.content[0].text).toContain('
|
|
1155
|
+
expect(result.content[0].text).toContain('VALIDATION_ERROR');
|
|
1114
1156
|
});
|
|
1115
1157
|
|
|
1116
1158
|
it('should handle validation error', async () => {
|
|
1117
1159
|
mockUpdateTag.mockRejectedValue(new Error('Validation failed'));
|
|
1118
1160
|
|
|
1119
1161
|
const tool = mockTools.get('ghost_update_tag');
|
|
1120
|
-
const result = await tool.handler({ id: '
|
|
1162
|
+
const result = await tool.handler({ id: '507f1f77bcf86cd799439011', name: '' });
|
|
1121
1163
|
|
|
1122
1164
|
expect(result.isError).toBe(true);
|
|
1123
1165
|
expect(result.content[0].text).toContain('Validation failed');
|
|
@@ -1127,7 +1169,7 @@ describe('ghost_update_tag', () => {
|
|
|
1127
1169
|
mockUpdateTag.mockRejectedValue(new Error('Tag not found'));
|
|
1128
1170
|
|
|
1129
1171
|
const tool = mockTools.get('ghost_update_tag');
|
|
1130
|
-
const result = await tool.handler({ id: '
|
|
1172
|
+
const result = await tool.handler({ id: '507f1f77bcf86cd799439099', name: 'Test' });
|
|
1131
1173
|
|
|
1132
1174
|
expect(result.isError).toBe(true);
|
|
1133
1175
|
expect(result.content[0].text).toContain('Tag not found');
|
|
@@ -1150,16 +1192,17 @@ describe('ghost_delete_tag', () => {
|
|
|
1150
1192
|
|
|
1151
1193
|
it('should have correct schema with id field', () => {
|
|
1152
1194
|
const tool = mockTools.get('ghost_delete_tag');
|
|
1153
|
-
|
|
1195
|
+
// Zod schemas store field definitions in schema.shape
|
|
1196
|
+
expect(tool.schema.shape.id).toBeDefined();
|
|
1154
1197
|
});
|
|
1155
1198
|
|
|
1156
1199
|
it('should delete tag successfully', async () => {
|
|
1157
1200
|
mockDeleteTag.mockResolvedValue({ success: true });
|
|
1158
1201
|
|
|
1159
1202
|
const tool = mockTools.get('ghost_delete_tag');
|
|
1160
|
-
const result = await tool.handler({ id: '
|
|
1203
|
+
const result = await tool.handler({ id: '507f1f77bcf86cd799439011' });
|
|
1161
1204
|
|
|
1162
|
-
expect(mockDeleteTag).toHaveBeenCalledWith('
|
|
1205
|
+
expect(mockDeleteTag).toHaveBeenCalledWith('507f1f77bcf86cd799439011');
|
|
1163
1206
|
expect(result.content[0].text).toContain('successfully deleted');
|
|
1164
1207
|
expect(result.isError).toBeUndefined();
|
|
1165
1208
|
});
|
|
@@ -1169,14 +1212,14 @@ describe('ghost_delete_tag', () => {
|
|
|
1169
1212
|
const result = await tool.handler({});
|
|
1170
1213
|
|
|
1171
1214
|
expect(result.isError).toBe(true);
|
|
1172
|
-
expect(result.content[0].text).toContain('
|
|
1215
|
+
expect(result.content[0].text).toContain('VALIDATION_ERROR');
|
|
1173
1216
|
});
|
|
1174
1217
|
|
|
1175
1218
|
it('should handle not found error', async () => {
|
|
1176
1219
|
mockDeleteTag.mockRejectedValue(new Error('Tag not found'));
|
|
1177
1220
|
|
|
1178
1221
|
const tool = mockTools.get('ghost_delete_tag');
|
|
1179
|
-
const result = await tool.handler({ id: '
|
|
1222
|
+
const result = await tool.handler({ id: '507f1f77bcf86cd799439099' });
|
|
1180
1223
|
|
|
1181
1224
|
expect(result.isError).toBe(true);
|
|
1182
1225
|
expect(result.content[0].text).toContain('Tag not found');
|
|
@@ -1186,7 +1229,7 @@ describe('ghost_delete_tag', () => {
|
|
|
1186
1229
|
mockDeleteTag.mockRejectedValue(new Error('Failed to delete tag'));
|
|
1187
1230
|
|
|
1188
1231
|
const tool = mockTools.get('ghost_delete_tag');
|
|
1189
|
-
const result = await tool.handler({ id: '
|
|
1232
|
+
const result = await tool.handler({ id: '507f1f77bcf86cd799439011' });
|
|
1190
1233
|
|
|
1191
1234
|
expect(result.isError).toBe(true);
|
|
1192
1235
|
expect(result.content[0].text).toContain('Failed to delete tag');
|