@aiconnect/agentjobs-mcp 1.1.0 → 1.3.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/.env.example +4 -0
- package/README.md +39 -6
- package/build/config.js +3 -1
- package/build/index.js +10 -3
- package/build/lib/agentJobsClient.js +11 -0
- package/build/test-tools.js +1 -1
- package/build/tools/cancel_job.js +5 -2
- package/build/tools/create_job.js +1 -1
- package/build/tools/get_context.js +61 -0
- package/build/tools/get_job.js +39 -9
- package/build/tools/get_job_activities.js +68 -0
- package/build/tools/get_jobs_stats.js +7 -17
- package/build/tools/list_jobs.js +44 -9
- package/build/utils/formatters.js +301 -31
- package/build/utils/schemas.js +23 -11
- package/build/utils/version.js +12 -0
- package/docs/agent-jobs-api.md +156 -0
- package/package.json +3 -2
- package/build/utils/formatters.test.js +0 -387
- package/build/utils/schemas.test.js +0 -40
|
@@ -1,387 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { formatJobDetails, formatJobStats, formatJobTypeDetails, formatJobTypeSummary } from './formatters.js';
|
|
3
|
-
describe('formatJobDetails', () => {
|
|
4
|
-
const fullJob = {
|
|
5
|
-
job_id: 'job_123',
|
|
6
|
-
job_type_id: 'type_abc',
|
|
7
|
-
org_id: 'org_xyz',
|
|
8
|
-
channel_code: 'ch_def',
|
|
9
|
-
chat_id: 'chat_456',
|
|
10
|
-
job_status: 'completed',
|
|
11
|
-
result: 'Success',
|
|
12
|
-
created_at: '2023-01-01T10:00:00.000Z',
|
|
13
|
-
updated_at: '2023-01-01T10:15:00.000Z',
|
|
14
|
-
scheduled_at: '2023-01-01T09:55:00.000Z',
|
|
15
|
-
last_task_created_at: '2023-01-01T10:10:00.000Z',
|
|
16
|
-
tags: 'tag1, tag2',
|
|
17
|
-
execution_log: ['Log entry 1', 'Log entry 2'],
|
|
18
|
-
tasks: [{ task_id: 'task_789', created_at: '2023-01-01T10:05:00.000Z' }],
|
|
19
|
-
flags: {
|
|
20
|
-
is_new_channel: true,
|
|
21
|
-
has_human_reply: false,
|
|
22
|
-
first_reply_at: '2023-01-01T10:02:00.000Z',
|
|
23
|
-
ignore_cooldown: true,
|
|
24
|
-
},
|
|
25
|
-
channel_data: {
|
|
26
|
-
platform: 'test_platform',
|
|
27
|
-
name: 'Test Channel',
|
|
28
|
-
},
|
|
29
|
-
job_config: {
|
|
30
|
-
max_task_retries: 3,
|
|
31
|
-
start_prompt: 'Initial prompt',
|
|
32
|
-
},
|
|
33
|
-
params: { key: 'value' },
|
|
34
|
-
};
|
|
35
|
-
it('should format a full job object correctly', () => {
|
|
36
|
-
const result = formatJobDetails(fullJob);
|
|
37
|
-
expect(result).toContain('Job ID: job_123');
|
|
38
|
-
expect(result).toContain('Status: completed');
|
|
39
|
-
expect(result).toContain('Result: Success');
|
|
40
|
-
expect(result).toContain('Tags: tag1, tag2');
|
|
41
|
-
expect(result).toContain('Total Tasks: 1');
|
|
42
|
-
expect(result).toContain('Retries Used: 0 / 3 (remaining: 3)');
|
|
43
|
-
expect(result).toContain('Duration: 20m 0s');
|
|
44
|
-
});
|
|
45
|
-
it('should handle minimal job object', () => {
|
|
46
|
-
const minimalJob = {
|
|
47
|
-
job_id: 'job_min',
|
|
48
|
-
job_type_id: 'type_min',
|
|
49
|
-
org_id: 'org_min',
|
|
50
|
-
channel_code: 'ch_min',
|
|
51
|
-
job_status: 'running',
|
|
52
|
-
created_at: new Date().toISOString(),
|
|
53
|
-
updated_at: new Date().toISOString(),
|
|
54
|
-
};
|
|
55
|
-
const result = formatJobDetails(minimalJob);
|
|
56
|
-
expect(result).toContain('Job ID: job_min');
|
|
57
|
-
expect(result).toContain('Status: running');
|
|
58
|
-
expect(result).toContain('Result: n/a');
|
|
59
|
-
expect(result).toContain('Total Tasks: 0');
|
|
60
|
-
});
|
|
61
|
-
it('should handle dates as numbers (timestamps)', () => {
|
|
62
|
-
const jobWithTimestamps = {
|
|
63
|
-
...fullJob,
|
|
64
|
-
created_at: new Date(fullJob.created_at).getTime(),
|
|
65
|
-
updated_at: new Date(fullJob.updated_at).getTime(),
|
|
66
|
-
};
|
|
67
|
-
const result = formatJobDetails(jobWithTimestamps);
|
|
68
|
-
expect(result).toContain(`Created At: ${new Date(jobWithTimestamps.created_at).toISOString()}`);
|
|
69
|
-
expect(result).toContain(`Updated At: ${new Date(jobWithTimestamps.updated_at).toISOString()}`);
|
|
70
|
-
});
|
|
71
|
-
it('should fall back to JSON.stringify for invalid job object', () => {
|
|
72
|
-
const invalidJob = { job_id: '123' }; // Missing required fields
|
|
73
|
-
const result = formatJobDetails(invalidJob);
|
|
74
|
-
expect(result).toContain('"job_id": "123"');
|
|
75
|
-
expect(result).toContain('Job Details (raw):');
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
describe('formatJobStats', () => {
|
|
79
|
-
it('should return correct percentages when total is greater than 0', () => {
|
|
80
|
-
const stats = {
|
|
81
|
-
status: {
|
|
82
|
-
completed: 10,
|
|
83
|
-
running: 5,
|
|
84
|
-
failed: 2,
|
|
85
|
-
canceled: 1,
|
|
86
|
-
waiting: 1,
|
|
87
|
-
scheduled: 1,
|
|
88
|
-
},
|
|
89
|
-
};
|
|
90
|
-
const filters = {};
|
|
91
|
-
const result = formatJobStats(stats, filters);
|
|
92
|
-
expect(result).toContain('Completed: 10 jobs (50.0%)');
|
|
93
|
-
expect(result).toContain('Running: 5 jobs (25.0%)');
|
|
94
|
-
expect(result).toContain('Failed: 2 jobs (10.0%)');
|
|
95
|
-
expect(result).toContain('Canceled: 1 jobs (5.0%)');
|
|
96
|
-
expect(result).toContain('Waiting: 1 jobs (5.0%)');
|
|
97
|
-
expect(result).toContain('Scheduled: 1 jobs (5.0%)');
|
|
98
|
-
expect(result).toContain('Total Jobs: 20');
|
|
99
|
-
});
|
|
100
|
-
it('should return 0.0% for all statuses when there are no jobs', () => {
|
|
101
|
-
const stats = {
|
|
102
|
-
status: {
|
|
103
|
-
completed: 0,
|
|
104
|
-
running: 0,
|
|
105
|
-
failed: 0,
|
|
106
|
-
canceled: 0,
|
|
107
|
-
waiting: 0,
|
|
108
|
-
scheduled: 0,
|
|
109
|
-
},
|
|
110
|
-
};
|
|
111
|
-
const filters = {};
|
|
112
|
-
const result = formatJobStats(stats, filters);
|
|
113
|
-
expect(result).toContain('Completed: 0 jobs (0.0%)');
|
|
114
|
-
expect(result).toContain('Running: 0 jobs (0.0%)');
|
|
115
|
-
expect(result).toContain('Failed: 0 jobs (0.0%)');
|
|
116
|
-
expect(result).toContain('Canceled: 0 jobs (0.0%)');
|
|
117
|
-
expect(result).toContain('Waiting: 0 jobs (0.0%)');
|
|
118
|
-
expect(result).toContain('Scheduled: 0 jobs (0.0%)');
|
|
119
|
-
expect(result).toContain('Total Jobs: 0');
|
|
120
|
-
});
|
|
121
|
-
it('should handle null filters gracefully', () => {
|
|
122
|
-
const stats = {
|
|
123
|
-
status: {
|
|
124
|
-
completed: 10,
|
|
125
|
-
running: 5,
|
|
126
|
-
failed: 2,
|
|
127
|
-
canceled: 1,
|
|
128
|
-
waiting: 1,
|
|
129
|
-
scheduled: 1,
|
|
130
|
-
},
|
|
131
|
-
};
|
|
132
|
-
const filters = null;
|
|
133
|
-
const result = formatJobStats(stats, filters);
|
|
134
|
-
expect(result).toContain('Period: All time');
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
describe('formatJobTypeDetails', () => {
|
|
138
|
-
const fullJobType = {
|
|
139
|
-
id: 'follow-up-v2',
|
|
140
|
-
name: 'Follow Up by DM',
|
|
141
|
-
org_id: 'acme-co',
|
|
142
|
-
version: 2,
|
|
143
|
-
visibility: 'private',
|
|
144
|
-
active: true,
|
|
145
|
-
description: 'Automated follow-up to contacts who haven\'t replied within 24h. Supports templated prompts and throttling.',
|
|
146
|
-
default_config: {
|
|
147
|
-
profile_id: 'default-bot',
|
|
148
|
-
max_follow_ups: 3,
|
|
149
|
-
max_task_retries: 2,
|
|
150
|
-
task_retry_interval: 30,
|
|
151
|
-
max_time_to_complete: 180,
|
|
152
|
-
failure_cooldown_minutes: 120,
|
|
153
|
-
start_prompt: 'Hello! Just circling back on our last conversation...'
|
|
154
|
-
},
|
|
155
|
-
params_schema: {
|
|
156
|
-
type: 'object',
|
|
157
|
-
required: ['recipient_id', 'initial_message'],
|
|
158
|
-
properties: {
|
|
159
|
-
recipient_id: { type: 'string', description: 'User ID of the recipient' },
|
|
160
|
-
initial_message: { type: 'string', description: 'Initial text to send to the recipient' },
|
|
161
|
-
locale: { type: 'string', default: 'en-US', description: 'Locale for message formatting' },
|
|
162
|
-
throttle_minutes: { type: 'number', description: 'Min minutes between attempts' },
|
|
163
|
-
metadata: { type: 'object', description: 'Free-form context data' }
|
|
164
|
-
}
|
|
165
|
-
},
|
|
166
|
-
tags: 'outreach, dm, v2',
|
|
167
|
-
created_at: '2025-08-02T14:03:28.120Z',
|
|
168
|
-
updated_at: '2025-08-12T09:22:01.553Z'
|
|
169
|
-
};
|
|
170
|
-
it('should format a full job type object correctly', () => {
|
|
171
|
-
const result = formatJobTypeDetails(fullJobType);
|
|
172
|
-
expect(result).toContain('ID: follow-up-v2');
|
|
173
|
-
expect(result).toContain('Name: Follow Up by DM');
|
|
174
|
-
expect(result).toContain('Version: 2');
|
|
175
|
-
expect(result).toContain('Visibility: private');
|
|
176
|
-
expect(result).toContain('Active: yes');
|
|
177
|
-
expect(result).toContain('Profile ID: default-bot');
|
|
178
|
-
expect(result).toContain('Max Follow-ups: 3');
|
|
179
|
-
expect(result).toContain('Max Task Retries: 2');
|
|
180
|
-
expect(result).toContain('Task Retry Interval: 30 min');
|
|
181
|
-
expect(result).toContain('retries up to 2 every 30 min');
|
|
182
|
-
expect(result).toContain('window 180 min');
|
|
183
|
-
expect(result).toContain('cooldown 120 min');
|
|
184
|
-
expect(result).toContain('Type: object | Required: 2 | Properties: 5');
|
|
185
|
-
expect(result).toContain('recipient_id: string (required)');
|
|
186
|
-
expect(result).toContain('locale: string — Locale for message formatting — Defaults to "en-US"');
|
|
187
|
-
expect(result).toContain('Tags: outreach, dm, v2');
|
|
188
|
-
});
|
|
189
|
-
it('should handle minimal job type object', () => {
|
|
190
|
-
const minimalJobType = {
|
|
191
|
-
id: 'minimal',
|
|
192
|
-
name: 'Minimal Type',
|
|
193
|
-
org_id: 'org-min',
|
|
194
|
-
created_at: new Date().toISOString(),
|
|
195
|
-
updated_at: new Date().toISOString()
|
|
196
|
-
};
|
|
197
|
-
const result = formatJobTypeDetails(minimalJobType);
|
|
198
|
-
expect(result).toContain('ID: minimal');
|
|
199
|
-
expect(result).toContain('Name: Minimal Type');
|
|
200
|
-
expect(result).toContain('Org ID: org-min');
|
|
201
|
-
expect(result).not.toContain('Version:');
|
|
202
|
-
expect(result).not.toContain('Description:');
|
|
203
|
-
});
|
|
204
|
-
it('should handle missing optional fields gracefully', () => {
|
|
205
|
-
const jobTypeNoConfig = {
|
|
206
|
-
id: 'no-config',
|
|
207
|
-
name: 'No Config Type',
|
|
208
|
-
org_id: 'org-test',
|
|
209
|
-
created_at: '2025-01-01T00:00:00Z',
|
|
210
|
-
updated_at: '2025-01-01T00:00:00Z'
|
|
211
|
-
};
|
|
212
|
-
const result = formatJobTypeDetails(jobTypeNoConfig, { showEmptySections: true });
|
|
213
|
-
expect(result).toContain('Default Config:');
|
|
214
|
-
expect(result).toContain('n/a');
|
|
215
|
-
expect(result).toContain('Params Schema:');
|
|
216
|
-
});
|
|
217
|
-
it('should truncate long strings with ellipsis', () => {
|
|
218
|
-
const longDescription = 'A'.repeat(500);
|
|
219
|
-
const longPrompt = 'B'.repeat(600);
|
|
220
|
-
const jobTypeWithLongText = {
|
|
221
|
-
id: 'long-text',
|
|
222
|
-
name: 'Long Text Type',
|
|
223
|
-
org_id: 'org-test',
|
|
224
|
-
description: longDescription,
|
|
225
|
-
default_config: {
|
|
226
|
-
start_prompt: longPrompt
|
|
227
|
-
},
|
|
228
|
-
created_at: '2025-01-01T00:00:00Z',
|
|
229
|
-
updated_at: '2025-01-01T00:00:00Z'
|
|
230
|
-
};
|
|
231
|
-
const result = formatJobTypeDetails(jobTypeWithLongText);
|
|
232
|
-
expect(result).toContain('A'.repeat(400) + '…');
|
|
233
|
-
expect(result).toContain('B'.repeat(500) + '…');
|
|
234
|
-
});
|
|
235
|
-
it('should normalize date fields (ms to ISO)', () => {
|
|
236
|
-
const jobTypeWithTimestamps = {
|
|
237
|
-
id: 'timestamps',
|
|
238
|
-
name: 'Timestamp Type',
|
|
239
|
-
org_id: 'org-test',
|
|
240
|
-
created_at: 1704067200000, // 2024-01-01T00:00:00.000Z
|
|
241
|
-
updated_at: 1704153600000 // 2024-01-02T00:00:00.000Z
|
|
242
|
-
};
|
|
243
|
-
const result = formatJobTypeDetails(jobTypeWithTimestamps);
|
|
244
|
-
expect(result).toContain('Created At: 2024-01-01T00:00:00.000Z');
|
|
245
|
-
expect(result).toContain('Updated At: 2024-01-02T00:00:00.000Z');
|
|
246
|
-
});
|
|
247
|
-
it('should handle tags as CSV string', () => {
|
|
248
|
-
const jobTypeWithCSVTags = {
|
|
249
|
-
id: 'csv-tags',
|
|
250
|
-
name: 'CSV Tags Type',
|
|
251
|
-
org_id: 'org-test',
|
|
252
|
-
tags: 'tag1, tag2 , tag3',
|
|
253
|
-
created_at: '2025-01-01T00:00:00Z',
|
|
254
|
-
updated_at: '2025-01-01T00:00:00Z'
|
|
255
|
-
};
|
|
256
|
-
const result = formatJobTypeDetails(jobTypeWithCSVTags);
|
|
257
|
-
expect(result).toContain('Tags: tag1, tag2, tag3');
|
|
258
|
-
});
|
|
259
|
-
it('should handle tags as array', () => {
|
|
260
|
-
const jobTypeWithArrayTags = {
|
|
261
|
-
id: 'array-tags',
|
|
262
|
-
name: 'Array Tags Type',
|
|
263
|
-
org_id: 'org-test',
|
|
264
|
-
tags: ['tag1', 'tag2', 'tag3'],
|
|
265
|
-
created_at: '2025-01-01T00:00:00Z',
|
|
266
|
-
updated_at: '2025-01-01T00:00:00Z'
|
|
267
|
-
};
|
|
268
|
-
const result = formatJobTypeDetails(jobTypeWithArrayTags);
|
|
269
|
-
expect(result).toContain('Tags: tag1, tag2, tag3');
|
|
270
|
-
});
|
|
271
|
-
it('should summarize large schemas with "+N more" indication', () => {
|
|
272
|
-
const properties = {};
|
|
273
|
-
for (let i = 1; i <= 30; i++) {
|
|
274
|
-
properties[`prop${i}`] = { type: 'string', description: `Property ${i}` };
|
|
275
|
-
}
|
|
276
|
-
const jobTypeWithLargeSchema = {
|
|
277
|
-
id: 'large-schema',
|
|
278
|
-
name: 'Large Schema Type',
|
|
279
|
-
org_id: 'org-test',
|
|
280
|
-
params_schema: {
|
|
281
|
-
type: 'object',
|
|
282
|
-
required: ['prop1', 'prop2'],
|
|
283
|
-
properties
|
|
284
|
-
},
|
|
285
|
-
created_at: '2025-01-01T00:00:00Z',
|
|
286
|
-
updated_at: '2025-01-01T00:00:00Z'
|
|
287
|
-
};
|
|
288
|
-
const result = formatJobTypeDetails(jobTypeWithLargeSchema);
|
|
289
|
-
expect(result).toContain('Properties: 30');
|
|
290
|
-
expect(result).toContain('+18 more…');
|
|
291
|
-
expect(result).toContain('prop1: string (required)');
|
|
292
|
-
expect(result).toContain('prop12: string');
|
|
293
|
-
});
|
|
294
|
-
it('should return raw JSON on parse failure', () => {
|
|
295
|
-
const invalidJobType = { notAValidField: 123 };
|
|
296
|
-
const result = formatJobTypeDetails(invalidJobType);
|
|
297
|
-
expect(result).toContain('Job Type Details (raw):');
|
|
298
|
-
expect(result).toContain('"notAValidField": 123');
|
|
299
|
-
});
|
|
300
|
-
it('should respect formatter options', () => {
|
|
301
|
-
const result = formatJobTypeDetails(fullJobType, {
|
|
302
|
-
includeSchema: false,
|
|
303
|
-
renderAsMarkdown: false
|
|
304
|
-
});
|
|
305
|
-
expect(result).not.toContain('## Job Type Details');
|
|
306
|
-
expect(result).toContain('Job Type Details\n===========');
|
|
307
|
-
expect(result).not.toContain('Params Schema:');
|
|
308
|
-
});
|
|
309
|
-
it('should handle custom truncation limits', () => {
|
|
310
|
-
const longJobType = {
|
|
311
|
-
id: 'custom-truncate',
|
|
312
|
-
name: 'Custom Truncate',
|
|
313
|
-
org_id: 'org-test',
|
|
314
|
-
description: 'X'.repeat(100),
|
|
315
|
-
default_config: {
|
|
316
|
-
start_prompt: 'Y'.repeat(100)
|
|
317
|
-
},
|
|
318
|
-
created_at: '2025-01-01T00:00:00Z',
|
|
319
|
-
updated_at: '2025-01-01T00:00:00Z'
|
|
320
|
-
};
|
|
321
|
-
const result = formatJobTypeDetails(longJobType, {
|
|
322
|
-
truncate: {
|
|
323
|
-
description: 50,
|
|
324
|
-
startPrompt: 30
|
|
325
|
-
}
|
|
326
|
-
});
|
|
327
|
-
expect(result).toContain('X'.repeat(50) + '…');
|
|
328
|
-
expect(result).toContain('Y'.repeat(30) + '…');
|
|
329
|
-
});
|
|
330
|
-
});
|
|
331
|
-
describe('formatJobTypeSummary', () => {
|
|
332
|
-
it('should format a complete job type summary', () => {
|
|
333
|
-
const jobType = {
|
|
334
|
-
id: 'summary-test',
|
|
335
|
-
name: 'Summary Test Type',
|
|
336
|
-
org_id: 'org-test',
|
|
337
|
-
active: true,
|
|
338
|
-
default_config: {
|
|
339
|
-
max_task_retries: 3,
|
|
340
|
-
task_retry_interval: 15,
|
|
341
|
-
max_time_to_complete: 60,
|
|
342
|
-
failure_cooldown_minutes: 30
|
|
343
|
-
},
|
|
344
|
-
params_schema: {
|
|
345
|
-
type: 'object',
|
|
346
|
-
required: ['field1', 'field2'],
|
|
347
|
-
properties: {
|
|
348
|
-
field1: { type: 'string' },
|
|
349
|
-
field2: { type: 'number' },
|
|
350
|
-
field3: { type: 'boolean' }
|
|
351
|
-
}
|
|
352
|
-
},
|
|
353
|
-
created_at: '2025-01-01T00:00:00Z',
|
|
354
|
-
updated_at: '2025-01-01T00:00:00Z'
|
|
355
|
-
};
|
|
356
|
-
const result = formatJobTypeSummary(jobType);
|
|
357
|
-
expect(result).toContain('ID: summary-test');
|
|
358
|
-
expect(result).toContain('Name: Summary Test Type');
|
|
359
|
-
expect(result).toContain('Active: yes');
|
|
360
|
-
expect(result).toContain('Retries: 3 every 15 min');
|
|
361
|
-
expect(result).toContain('Max Time: 60 min');
|
|
362
|
-
expect(result).toContain('Cooldown: 30 min');
|
|
363
|
-
expect(result).toContain('Params: required=2, props=3');
|
|
364
|
-
});
|
|
365
|
-
it('should handle minimal job type summary', () => {
|
|
366
|
-
const minimalJobType = {
|
|
367
|
-
id: 'minimal',
|
|
368
|
-
name: 'Minimal',
|
|
369
|
-
org_id: 'org-test',
|
|
370
|
-
created_at: '2025-01-01T00:00:00Z',
|
|
371
|
-
updated_at: '2025-01-01T00:00:00Z'
|
|
372
|
-
};
|
|
373
|
-
const result = formatJobTypeSummary(minimalJobType);
|
|
374
|
-
expect(result).toContain('ID: minimal');
|
|
375
|
-
expect(result).toContain('Name: Minimal');
|
|
376
|
-
expect(result).toContain('Active: n/a');
|
|
377
|
-
expect(result).toContain('Retries: n/a');
|
|
378
|
-
expect(result).toContain('Max Time: n/a');
|
|
379
|
-
expect(result).toContain('Cooldown: n/a');
|
|
380
|
-
expect(result).toContain('Params: n/a');
|
|
381
|
-
});
|
|
382
|
-
it('should return raw JSON for invalid job type', () => {
|
|
383
|
-
const invalidJobType = { invalid: true };
|
|
384
|
-
const result = formatJobTypeSummary(invalidJobType);
|
|
385
|
-
expect(result).toContain('"invalid": true');
|
|
386
|
-
});
|
|
387
|
-
});
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { flexibleDateTimeSchema } from './schemas.js';
|
|
3
|
-
describe('flexibleDateTimeSchema', () => {
|
|
4
|
-
// Test case 1: Valid ISO 8601 with UTC 'Z'
|
|
5
|
-
it('should validate a correct ISO 8601 string with Zulu time', () => {
|
|
6
|
-
const validDate = '2025-07-23T21:00:00Z';
|
|
7
|
-
const result = flexibleDateTimeSchema.safeParse(validDate);
|
|
8
|
-
expect(result.success).toBe(true);
|
|
9
|
-
});
|
|
10
|
-
// Test case 2: Valid ISO 8601 with a positive timezone offset
|
|
11
|
-
it('should validate a correct ISO 8601 string with a positive offset', () => {
|
|
12
|
-
const validDate = '2025-07-23T22:00:00+01:00';
|
|
13
|
-
const result = flexibleDateTimeSchema.safeParse(validDate);
|
|
14
|
-
expect(result.success).toBe(true);
|
|
15
|
-
});
|
|
16
|
-
// Test case 3: Valid ISO 8601 with a negative timezone offset
|
|
17
|
-
it('should validate a correct ISO 8601 string with a negative offset', () => {
|
|
18
|
-
const validDate = '2025-07-23T16:00:00-05:00';
|
|
19
|
-
const result = flexibleDateTimeSchema.safeParse(validDate);
|
|
20
|
-
expect(result.success).toBe(true);
|
|
21
|
-
});
|
|
22
|
-
// Test case 4: Invalid date format (not ISO 8601)
|
|
23
|
-
it('should not validate an incorrect date format', () => {
|
|
24
|
-
const invalidDate = '23/07/2025 21:00:00';
|
|
25
|
-
const result = flexibleDateTimeSchema.safeParse(invalidDate);
|
|
26
|
-
expect(result.success).toBe(false);
|
|
27
|
-
});
|
|
28
|
-
// Test case 5: Invalid date string (gibberish)
|
|
29
|
-
it('should not validate a gibberish string', () => {
|
|
30
|
-
const gibberish = 'not-a-date';
|
|
31
|
-
const result = flexibleDateTimeSchema.safeParse(gibberish);
|
|
32
|
-
expect(result.success).toBe(false);
|
|
33
|
-
});
|
|
34
|
-
// Test case 6: Empty string
|
|
35
|
-
it('should not validate an empty string', () => {
|
|
36
|
-
const emptyString = '';
|
|
37
|
-
const result = flexibleDateTimeSchema.safeParse(emptyString);
|
|
38
|
-
expect(result.success).toBe(false);
|
|
39
|
-
});
|
|
40
|
-
});
|