@jgardner04/ghost-mcp-server 1.6.0 → 1.8.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_improved.test.js +285 -0
- package/src/mcp_server_improved.js +124 -1
- package/src/schemas/__tests__/common.test.js +275 -0
- package/src/schemas/__tests__/postSchemas.test.js +194 -0
- package/src/schemas/__tests__/tagSchemas.test.js +262 -0
- package/src/schemas/common.js +227 -0
- package/src/schemas/index.js +39 -0
- package/src/schemas/memberSchemas.js +188 -0
- package/src/schemas/newsletterSchemas.js +168 -0
- package/src/schemas/pageSchemas.js +215 -0
- package/src/schemas/postSchemas.js +210 -0
- package/src/schemas/tagSchemas.js +136 -0
- package/src/schemas/tierSchemas.js +206 -0
- package/src/services/ghostServiceImproved.js +24 -3
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
emailSchema,
|
|
4
|
+
urlSchema,
|
|
5
|
+
isoDateSchema,
|
|
6
|
+
slugSchema,
|
|
7
|
+
ghostIdSchema,
|
|
8
|
+
nqlFilterSchema,
|
|
9
|
+
limitSchema,
|
|
10
|
+
pageSchema,
|
|
11
|
+
postStatusSchema,
|
|
12
|
+
visibilitySchema,
|
|
13
|
+
htmlContentSchema,
|
|
14
|
+
titleSchema,
|
|
15
|
+
excerptSchema,
|
|
16
|
+
metaTitleSchema,
|
|
17
|
+
metaDescriptionSchema,
|
|
18
|
+
featuredSchema,
|
|
19
|
+
featureImageSchema,
|
|
20
|
+
featureImageAltSchema,
|
|
21
|
+
tagNameSchema,
|
|
22
|
+
} from '../common.js';
|
|
23
|
+
|
|
24
|
+
describe('Common Schemas', () => {
|
|
25
|
+
describe('emailSchema', () => {
|
|
26
|
+
it('should accept valid email addresses', () => {
|
|
27
|
+
expect(() => emailSchema.parse('test@example.com')).not.toThrow();
|
|
28
|
+
expect(() => emailSchema.parse('user.name+tag@example.co.uk')).not.toThrow();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should reject invalid email addresses', () => {
|
|
32
|
+
expect(() => emailSchema.parse('not-an-email')).toThrow();
|
|
33
|
+
expect(() => emailSchema.parse('missing@domain')).toThrow();
|
|
34
|
+
expect(() => emailSchema.parse('@example.com')).toThrow();
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('urlSchema', () => {
|
|
39
|
+
it('should accept valid URLs', () => {
|
|
40
|
+
expect(() => urlSchema.parse('https://example.com')).not.toThrow();
|
|
41
|
+
expect(() => urlSchema.parse('http://localhost:3000/path')).not.toThrow();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should reject invalid URLs', () => {
|
|
45
|
+
expect(() => urlSchema.parse('not-a-url')).toThrow();
|
|
46
|
+
expect(() => urlSchema.parse('://invalid')).toThrow();
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('isoDateSchema', () => {
|
|
51
|
+
it('should accept valid ISO 8601 datetime strings', () => {
|
|
52
|
+
expect(() => isoDateSchema.parse('2024-01-15T10:30:00Z')).not.toThrow();
|
|
53
|
+
expect(() => isoDateSchema.parse('2024-01-15T10:30:00.000Z')).not.toThrow();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should reject invalid datetime strings', () => {
|
|
57
|
+
expect(() => isoDateSchema.parse('2024-01-15')).toThrow();
|
|
58
|
+
expect(() => isoDateSchema.parse('not-a-date')).toThrow();
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('slugSchema', () => {
|
|
63
|
+
it('should accept valid slugs', () => {
|
|
64
|
+
expect(() => slugSchema.parse('my-blog-post')).not.toThrow();
|
|
65
|
+
expect(() => slugSchema.parse('post-123')).not.toThrow();
|
|
66
|
+
expect(() => slugSchema.parse('simple')).not.toThrow();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should reject invalid slugs', () => {
|
|
70
|
+
expect(() => slugSchema.parse('My Post')).toThrow(); // spaces
|
|
71
|
+
expect(() => slugSchema.parse('post_123')).toThrow(); // underscores
|
|
72
|
+
expect(() => slugSchema.parse('Post-123')).toThrow(); // uppercase
|
|
73
|
+
expect(() => slugSchema.parse('post!')).toThrow(); // special chars
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('ghostIdSchema', () => {
|
|
78
|
+
it('should accept valid Ghost IDs', () => {
|
|
79
|
+
expect(() => ghostIdSchema.parse('507f1f77bcf86cd799439011')).not.toThrow();
|
|
80
|
+
expect(() => ghostIdSchema.parse('abcdef1234567890abcdef12')).not.toThrow();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should reject invalid Ghost IDs', () => {
|
|
84
|
+
expect(() => ghostIdSchema.parse('short')).toThrow(); // too short
|
|
85
|
+
expect(() => ghostIdSchema.parse('507f1f77bcf86cd799439011abc')).toThrow(); // too long
|
|
86
|
+
expect(() => ghostIdSchema.parse('507f1f77bcf86cd79943901G')).toThrow(); // invalid char
|
|
87
|
+
expect(() => ghostIdSchema.parse('507F1F77BCF86CD799439011')).toThrow(); // uppercase
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('nqlFilterSchema', () => {
|
|
92
|
+
it('should accept valid NQL filter strings', () => {
|
|
93
|
+
expect(() => nqlFilterSchema.parse('status:published')).not.toThrow();
|
|
94
|
+
expect(() => nqlFilterSchema.parse('tag:news+featured:true')).not.toThrow();
|
|
95
|
+
expect(() => nqlFilterSchema.parse("author:'John Doe'")).not.toThrow();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should reject NQL strings with disallowed characters', () => {
|
|
99
|
+
expect(() => nqlFilterSchema.parse('status;DROP TABLE')).toThrow();
|
|
100
|
+
expect(() => nqlFilterSchema.parse('test&invalid')).toThrow();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should allow undefined/optional', () => {
|
|
104
|
+
expect(() => nqlFilterSchema.parse(undefined)).not.toThrow();
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe('limitSchema', () => {
|
|
109
|
+
it('should accept valid limits', () => {
|
|
110
|
+
expect(limitSchema.parse(1)).toBe(1);
|
|
111
|
+
expect(limitSchema.parse(50)).toBe(50);
|
|
112
|
+
expect(limitSchema.parse(100)).toBe(100);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should reject invalid limits', () => {
|
|
116
|
+
expect(() => limitSchema.parse(0)).toThrow();
|
|
117
|
+
expect(() => limitSchema.parse(101)).toThrow();
|
|
118
|
+
expect(() => limitSchema.parse(-1)).toThrow();
|
|
119
|
+
expect(() => limitSchema.parse(1.5)).toThrow();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should use default value', () => {
|
|
123
|
+
expect(limitSchema.parse(undefined)).toBe(15);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('pageSchema', () => {
|
|
128
|
+
it('should accept valid page numbers', () => {
|
|
129
|
+
expect(pageSchema.parse(1)).toBe(1);
|
|
130
|
+
expect(pageSchema.parse(100)).toBe(100);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('should reject invalid page numbers', () => {
|
|
134
|
+
expect(() => pageSchema.parse(0)).toThrow();
|
|
135
|
+
expect(() => pageSchema.parse(-1)).toThrow();
|
|
136
|
+
expect(() => pageSchema.parse(1.5)).toThrow();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('should use default value', () => {
|
|
140
|
+
expect(pageSchema.parse(undefined)).toBe(1);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe('postStatusSchema', () => {
|
|
145
|
+
it('should accept valid statuses', () => {
|
|
146
|
+
expect(() => postStatusSchema.parse('draft')).not.toThrow();
|
|
147
|
+
expect(() => postStatusSchema.parse('published')).not.toThrow();
|
|
148
|
+
expect(() => postStatusSchema.parse('scheduled')).not.toThrow();
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('should reject invalid statuses', () => {
|
|
152
|
+
expect(() => postStatusSchema.parse('invalid')).toThrow();
|
|
153
|
+
expect(() => postStatusSchema.parse('DRAFT')).toThrow();
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe('visibilitySchema', () => {
|
|
158
|
+
it('should accept valid visibility values', () => {
|
|
159
|
+
expect(() => visibilitySchema.parse('public')).not.toThrow();
|
|
160
|
+
expect(() => visibilitySchema.parse('members')).not.toThrow();
|
|
161
|
+
expect(() => visibilitySchema.parse('paid')).not.toThrow();
|
|
162
|
+
expect(() => visibilitySchema.parse('tiers')).not.toThrow();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should reject invalid visibility values', () => {
|
|
166
|
+
expect(() => visibilitySchema.parse('private')).toThrow();
|
|
167
|
+
expect(() => visibilitySchema.parse('PUBLIC')).toThrow();
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe('htmlContentSchema', () => {
|
|
172
|
+
it('should accept non-empty HTML strings', () => {
|
|
173
|
+
expect(() => htmlContentSchema.parse('<p>Hello World</p>')).not.toThrow();
|
|
174
|
+
expect(() => htmlContentSchema.parse('Plain text')).not.toThrow();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should reject empty strings', () => {
|
|
178
|
+
expect(() => htmlContentSchema.parse('')).toThrow();
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
describe('titleSchema', () => {
|
|
183
|
+
it('should accept valid titles', () => {
|
|
184
|
+
expect(() => titleSchema.parse('My Blog Post')).not.toThrow();
|
|
185
|
+
expect(() => titleSchema.parse('A'.repeat(255))).not.toThrow();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('should reject invalid titles', () => {
|
|
189
|
+
expect(() => titleSchema.parse('')).toThrow();
|
|
190
|
+
expect(() => titleSchema.parse('A'.repeat(256))).toThrow();
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe('excerptSchema', () => {
|
|
195
|
+
it('should accept valid excerpts', () => {
|
|
196
|
+
expect(() => excerptSchema.parse('A short description')).not.toThrow();
|
|
197
|
+
expect(() => excerptSchema.parse('A'.repeat(500))).not.toThrow();
|
|
198
|
+
expect(() => excerptSchema.parse(undefined)).not.toThrow();
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('should reject too long excerpts', () => {
|
|
202
|
+
expect(() => excerptSchema.parse('A'.repeat(501))).toThrow();
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
describe('metaTitleSchema', () => {
|
|
207
|
+
it('should accept valid meta titles', () => {
|
|
208
|
+
expect(() => metaTitleSchema.parse('SEO Title')).not.toThrow();
|
|
209
|
+
expect(() => metaTitleSchema.parse('A'.repeat(300))).not.toThrow();
|
|
210
|
+
expect(() => metaTitleSchema.parse(undefined)).not.toThrow();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('should reject too long meta titles', () => {
|
|
214
|
+
expect(() => metaTitleSchema.parse('A'.repeat(301))).toThrow();
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
describe('metaDescriptionSchema', () => {
|
|
219
|
+
it('should accept valid meta descriptions', () => {
|
|
220
|
+
expect(() => metaDescriptionSchema.parse('SEO description')).not.toThrow();
|
|
221
|
+
expect(() => metaDescriptionSchema.parse('A'.repeat(500))).not.toThrow();
|
|
222
|
+
expect(() => metaDescriptionSchema.parse(undefined)).not.toThrow();
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('should reject too long meta descriptions', () => {
|
|
226
|
+
expect(() => metaDescriptionSchema.parse('A'.repeat(501))).toThrow();
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
describe('featuredSchema', () => {
|
|
231
|
+
it('should accept boolean values', () => {
|
|
232
|
+
expect(featuredSchema.parse(true)).toBe(true);
|
|
233
|
+
expect(featuredSchema.parse(false)).toBe(false);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('should use default value', () => {
|
|
237
|
+
expect(featuredSchema.parse(undefined)).toBe(false);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
describe('featureImageSchema', () => {
|
|
242
|
+
it('should accept valid image URLs', () => {
|
|
243
|
+
expect(() => featureImageSchema.parse('https://example.com/image.jpg')).not.toThrow();
|
|
244
|
+
expect(() => featureImageSchema.parse(undefined)).not.toThrow();
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('should reject invalid URLs', () => {
|
|
248
|
+
expect(() => featureImageSchema.parse('not-a-url')).toThrow();
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
describe('featureImageAltSchema', () => {
|
|
253
|
+
it('should accept valid alt text', () => {
|
|
254
|
+
expect(() => featureImageAltSchema.parse('Image description')).not.toThrow();
|
|
255
|
+
expect(() => featureImageAltSchema.parse('A'.repeat(125))).not.toThrow();
|
|
256
|
+
expect(() => featureImageAltSchema.parse(undefined)).not.toThrow();
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('should reject too long alt text', () => {
|
|
260
|
+
expect(() => featureImageAltSchema.parse('A'.repeat(126))).toThrow();
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
describe('tagNameSchema', () => {
|
|
265
|
+
it('should accept valid tag names', () => {
|
|
266
|
+
expect(() => tagNameSchema.parse('Technology')).not.toThrow();
|
|
267
|
+
expect(() => tagNameSchema.parse('A'.repeat(191))).not.toThrow();
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('should reject invalid tag names', () => {
|
|
271
|
+
expect(() => tagNameSchema.parse('')).toThrow();
|
|
272
|
+
expect(() => tagNameSchema.parse('A'.repeat(192))).toThrow();
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
});
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
createPostSchema,
|
|
4
|
+
updatePostSchema,
|
|
5
|
+
postQuerySchema,
|
|
6
|
+
postIdSchema,
|
|
7
|
+
postOutputSchema,
|
|
8
|
+
} from '../postSchemas.js';
|
|
9
|
+
|
|
10
|
+
describe('Post Schemas', () => {
|
|
11
|
+
describe('createPostSchema', () => {
|
|
12
|
+
it('should accept valid post creation data', () => {
|
|
13
|
+
const validPost = {
|
|
14
|
+
title: 'My Blog Post',
|
|
15
|
+
html: '<p>This is the content of the post.</p>',
|
|
16
|
+
status: 'draft',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
expect(() => createPostSchema.parse(validPost)).not.toThrow();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should accept minimal post creation data', () => {
|
|
23
|
+
const minimalPost = {
|
|
24
|
+
title: 'Title',
|
|
25
|
+
html: '<p>Content</p>',
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const result = createPostSchema.parse(minimalPost);
|
|
29
|
+
expect(result.title).toBe('Title');
|
|
30
|
+
expect(result.status).toBe('draft'); // default
|
|
31
|
+
expect(result.visibility).toBe('public'); // default
|
|
32
|
+
expect(result.featured).toBe(false); // default
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should accept post with all fields', () => {
|
|
36
|
+
const fullPost = {
|
|
37
|
+
title: 'Complete Post',
|
|
38
|
+
html: '<p>Full content</p>',
|
|
39
|
+
slug: 'complete-post',
|
|
40
|
+
status: 'published',
|
|
41
|
+
visibility: 'members',
|
|
42
|
+
featured: true,
|
|
43
|
+
feature_image: 'https://example.com/image.jpg',
|
|
44
|
+
feature_image_alt: 'Image description',
|
|
45
|
+
feature_image_caption: 'Photo caption',
|
|
46
|
+
excerpt: 'Brief summary',
|
|
47
|
+
custom_excerpt: 'Custom summary',
|
|
48
|
+
meta_title: 'SEO Title',
|
|
49
|
+
meta_description: 'SEO Description',
|
|
50
|
+
tags: ['tech', 'news'],
|
|
51
|
+
authors: ['author@example.com'],
|
|
52
|
+
published_at: '2024-01-15T10:30:00.000Z',
|
|
53
|
+
canonical_url: 'https://example.com/original',
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
expect(() => createPostSchema.parse(fullPost)).not.toThrow();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should reject post without title', () => {
|
|
60
|
+
const invalidPost = {
|
|
61
|
+
html: '<p>Content</p>',
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
expect(() => createPostSchema.parse(invalidPost)).toThrow();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should reject post without html', () => {
|
|
68
|
+
const invalidPost = {
|
|
69
|
+
title: 'Title',
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
expect(() => createPostSchema.parse(invalidPost)).toThrow();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should reject post with invalid status', () => {
|
|
76
|
+
const invalidPost = {
|
|
77
|
+
title: 'Title',
|
|
78
|
+
html: '<p>Content</p>',
|
|
79
|
+
status: 'invalid',
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
expect(() => createPostSchema.parse(invalidPost)).toThrow();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should reject post with too long title', () => {
|
|
86
|
+
const invalidPost = {
|
|
87
|
+
title: 'A'.repeat(256),
|
|
88
|
+
html: '<p>Content</p>',
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
expect(() => createPostSchema.parse(invalidPost)).toThrow();
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe('updatePostSchema', () => {
|
|
96
|
+
it('should accept partial post updates', () => {
|
|
97
|
+
const update = {
|
|
98
|
+
title: 'Updated Title',
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
expect(() => updatePostSchema.parse(update)).not.toThrow();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should accept empty update object', () => {
|
|
105
|
+
expect(() => updatePostSchema.parse({})).not.toThrow();
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe('postQuerySchema', () => {
|
|
110
|
+
it('should accept valid query parameters', () => {
|
|
111
|
+
const query = {
|
|
112
|
+
limit: 20,
|
|
113
|
+
page: 2,
|
|
114
|
+
filter: 'status:published+featured:true',
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
expect(() => postQuerySchema.parse(query)).not.toThrow();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should accept query with include parameter', () => {
|
|
121
|
+
const query = {
|
|
122
|
+
include: 'tags,authors',
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
expect(() => postQuerySchema.parse(query)).not.toThrow();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should accept empty query object', () => {
|
|
129
|
+
const result = postQuerySchema.parse({});
|
|
130
|
+
expect(result).toBeDefined();
|
|
131
|
+
// Note: optional fields with defaults don't apply when field is omitted
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe('postIdSchema', () => {
|
|
136
|
+
it('should accept valid Ghost ID', () => {
|
|
137
|
+
const validId = {
|
|
138
|
+
id: '507f1f77bcf86cd799439011',
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
expect(() => postIdSchema.parse(validId)).not.toThrow();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should reject invalid Ghost ID', () => {
|
|
145
|
+
const invalidId = {
|
|
146
|
+
id: 'invalid-id',
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
expect(() => postIdSchema.parse(invalidId)).toThrow();
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe('postOutputSchema', () => {
|
|
154
|
+
it('should accept valid post output from Ghost API', () => {
|
|
155
|
+
const apiPost = {
|
|
156
|
+
id: '507f1f77bcf86cd799439011',
|
|
157
|
+
uuid: '550e8400-e29b-41d4-a716-446655440000',
|
|
158
|
+
title: 'My Post',
|
|
159
|
+
slug: 'my-post',
|
|
160
|
+
html: '<p>Content</p>',
|
|
161
|
+
comment_id: null,
|
|
162
|
+
feature_image: 'https://example.com/image.jpg',
|
|
163
|
+
feature_image_alt: 'Alt text',
|
|
164
|
+
feature_image_caption: 'Caption',
|
|
165
|
+
featured: false,
|
|
166
|
+
status: 'published',
|
|
167
|
+
visibility: 'public',
|
|
168
|
+
created_at: '2024-01-15T10:30:00.000Z',
|
|
169
|
+
updated_at: '2024-01-15T10:30:00.000Z',
|
|
170
|
+
published_at: '2024-01-15T10:30:00.000Z',
|
|
171
|
+
custom_excerpt: 'Excerpt',
|
|
172
|
+
codeinjection_head: null,
|
|
173
|
+
codeinjection_foot: null,
|
|
174
|
+
custom_template: null,
|
|
175
|
+
canonical_url: null,
|
|
176
|
+
url: 'https://example.com/my-post',
|
|
177
|
+
excerpt: 'Auto excerpt',
|
|
178
|
+
reading_time: 5,
|
|
179
|
+
email_only: false,
|
|
180
|
+
og_image: null,
|
|
181
|
+
og_title: null,
|
|
182
|
+
og_description: null,
|
|
183
|
+
twitter_image: null,
|
|
184
|
+
twitter_title: null,
|
|
185
|
+
twitter_description: null,
|
|
186
|
+
meta_title: null,
|
|
187
|
+
meta_description: null,
|
|
188
|
+
email_subject: null,
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
expect(() => postOutputSchema.parse(apiPost)).not.toThrow();
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
});
|