@mapbox/mcp-server 0.0.2 → 0.1.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/dist/cjs/package.json +3 -0
- package/dist/esm/package.json +3 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/MapboxApiBasedTool.d.ts +75 -0
- package/dist/tools/MapboxApiBasedTool.d.ts.map +1 -0
- package/dist/tools/MapboxApiBasedTool.js +78 -0
- package/dist/tools/MapboxApiBasedTool.js.map +1 -0
- package/dist/tools/category-search-tool/CategorySearchTool.d.ts +39 -0
- package/dist/tools/category-search-tool/CategorySearchTool.d.ts.map +1 -0
- package/dist/tools/category-search-tool/CategorySearchTool.js +638 -0
- package/dist/tools/category-search-tool/CategorySearchTool.js.map +1 -0
- package/dist/tools/category-search-tool/CategorySearchTool.test.d.ts +2 -0
- package/dist/tools/category-search-tool/CategorySearchTool.test.d.ts.map +1 -0
- package/dist/tools/category-search-tool/CategorySearchTool.test.js +289 -0
- package/dist/tools/category-search-tool/CategorySearchTool.test.js.map +1 -0
- package/dist/tools/directions-tool/DirectionsTool.d.ts +53 -0
- package/dist/tools/directions-tool/DirectionsTool.d.ts.map +1 -0
- package/dist/tools/directions-tool/DirectionsTool.js +405 -0
- package/dist/tools/directions-tool/DirectionsTool.js.map +1 -0
- package/dist/tools/directions-tool/DirectionsTool.test.d.ts +2 -0
- package/dist/tools/directions-tool/DirectionsTool.test.d.ts.map +1 -0
- package/dist/tools/directions-tool/DirectionsTool.test.js +867 -0
- package/dist/tools/directions-tool/DirectionsTool.test.js.map +1 -0
- package/dist/tools/forward-geocode-tool/ForwardGeocodeTool.d.ts +51 -0
- package/dist/tools/forward-geocode-tool/ForwardGeocodeTool.d.ts.map +1 -0
- package/dist/tools/forward-geocode-tool/ForwardGeocodeTool.js +182 -0
- package/dist/tools/forward-geocode-tool/ForwardGeocodeTool.js.map +1 -0
- package/dist/tools/forward-geocode-tool/ForwardGeocodeTool.test.d.ts +2 -0
- package/dist/tools/forward-geocode-tool/ForwardGeocodeTool.test.d.ts.map +1 -0
- package/dist/tools/forward-geocode-tool/ForwardGeocodeTool.test.js +335 -0
- package/dist/tools/forward-geocode-tool/ForwardGeocodeTool.test.js.map +1 -0
- package/dist/tools/isochrone-tool/IsochroneTool.d.ts +44 -0
- package/dist/tools/isochrone-tool/IsochroneTool.d.ts.map +1 -0
- package/dist/tools/isochrone-tool/IsochroneTool.js +108 -0
- package/dist/tools/isochrone-tool/IsochroneTool.js.map +1 -0
- package/dist/tools/isochrone-tool/IsochroneTool.test.d.ts +2 -0
- package/dist/tools/isochrone-tool/IsochroneTool.test.d.ts.map +1 -0
- package/dist/tools/isochrone-tool/IsochroneTool.test.js +110 -0
- package/dist/tools/isochrone-tool/IsochroneTool.test.js.map +1 -0
- package/dist/tools/matrix-tool/MatrixTool.d.ts +35 -0
- package/dist/tools/matrix-tool/MatrixTool.d.ts.map +1 -0
- package/dist/tools/matrix-tool/MatrixTool.js +195 -0
- package/dist/tools/matrix-tool/MatrixTool.js.map +1 -0
- package/dist/tools/matrix-tool/MatrixTool.test.d.ts +2 -0
- package/dist/tools/matrix-tool/MatrixTool.test.d.ts.map +1 -0
- package/dist/tools/matrix-tool/MatrixTool.test.js +803 -0
- package/dist/tools/matrix-tool/MatrixTool.test.js.map +1 -0
- package/dist/tools/poi-search-tool/PoiSearchTool.d.ts +54 -0
- package/dist/tools/poi-search-tool/PoiSearchTool.d.ts.map +1 -0
- package/dist/tools/poi-search-tool/PoiSearchTool.js +193 -0
- package/dist/tools/poi-search-tool/PoiSearchTool.js.map +1 -0
- package/dist/tools/poi-search-tool/PoiSearchTool.test.d.ts +2 -0
- package/dist/tools/poi-search-tool/PoiSearchTool.test.d.ts.map +1 -0
- package/dist/tools/poi-search-tool/PoiSearchTool.test.js +338 -0
- package/dist/tools/poi-search-tool/PoiSearchTool.test.js.map +1 -0
- package/dist/tools/reverse-geocode-tool/ReverseGeocodeTool.d.ts +42 -0
- package/dist/tools/reverse-geocode-tool/ReverseGeocodeTool.d.ts.map +1 -0
- package/dist/tools/reverse-geocode-tool/ReverseGeocodeTool.js +131 -0
- package/dist/tools/reverse-geocode-tool/ReverseGeocodeTool.js.map +1 -0
- package/dist/tools/reverse-geocode-tool/ReverseGeocodeTool.test.d.ts +2 -0
- package/dist/tools/reverse-geocode-tool/ReverseGeocodeTool.test.d.ts.map +1 -0
- package/dist/tools/reverse-geocode-tool/ReverseGeocodeTool.test.js +340 -0
- package/dist/tools/reverse-geocode-tool/ReverseGeocodeTool.test.js.map +1 -0
- package/dist/tools/static-map-image-tool/StaticMapImageTool.d.ts +148 -0
- package/dist/tools/static-map-image-tool/StaticMapImageTool.d.ts.map +1 -0
- package/dist/tools/static-map-image-tool/StaticMapImageTool.js +406 -0
- package/dist/tools/static-map-image-tool/StaticMapImageTool.js.map +1 -0
- package/dist/tools/static-map-image-tool/StaticMapImageTool.test.d.ts +2 -0
- package/dist/tools/static-map-image-tool/StaticMapImageTool.test.d.ts.map +1 -0
- package/dist/tools/static-map-image-tool/StaticMapImageTool.test.js +440 -0
- package/dist/tools/static-map-image-tool/StaticMapImageTool.test.js.map +1 -0
- package/dist/utils/requestUtils.d.ts +6 -0
- package/dist/utils/requestUtils.d.ts.map +1 -0
- package/dist/utils/requestUtils.js +29 -0
- package/dist/utils/requestUtils.js.map +1 -0
- package/dist/utils/requestUtils.test-helpers.d.ts +3 -0
- package/dist/utils/requestUtils.test-helpers.d.ts.map +1 -0
- package/dist/utils/requestUtils.test-helpers.js +28 -0
- package/dist/utils/requestUtils.test-helpers.js.map +1 -0
- package/dist/utils/versionUtils.d.ts +9 -0
- package/dist/utils/versionUtils.d.ts.map +1 -0
- package/dist/utils/versionUtils.js +25 -0
- package/dist/utils/versionUtils.js.map +1 -0
- package/dist/version.json +6 -0
- package/package.json +2 -2
|
@@ -0,0 +1,867 @@
|
|
|
1
|
+
process.env.MAPBOX_ACCESS_TOKEN = 'test-token';
|
|
2
|
+
import { cleanup } from '../../utils/requestUtils.js';
|
|
3
|
+
import { setupFetch, assertHeadersSent } from '../../utils/requestUtils.test-helpers.js';
|
|
4
|
+
import { DirectionsTool } from './DirectionsTool.js';
|
|
5
|
+
describe('DirectionsTool', () => {
|
|
6
|
+
afterEach(() => {
|
|
7
|
+
jest.restoreAllMocks();
|
|
8
|
+
cleanup();
|
|
9
|
+
});
|
|
10
|
+
it('sends custom header', async () => {
|
|
11
|
+
const mockFetch = setupFetch();
|
|
12
|
+
await new DirectionsTool().run({
|
|
13
|
+
coordinates: [
|
|
14
|
+
[-74.102094, 40.692815],
|
|
15
|
+
[-74.1022094, 40.792815]
|
|
16
|
+
]
|
|
17
|
+
});
|
|
18
|
+
assertHeadersSent(mockFetch);
|
|
19
|
+
});
|
|
20
|
+
it('constructs correct URL with required parameters', async () => {
|
|
21
|
+
const mockFetch = setupFetch();
|
|
22
|
+
await new DirectionsTool().run({
|
|
23
|
+
coordinates: [
|
|
24
|
+
[-73.989, 40.733],
|
|
25
|
+
[-73.979, 40.743]
|
|
26
|
+
]
|
|
27
|
+
});
|
|
28
|
+
const calledUrl = mockFetch.mock.calls[0][0];
|
|
29
|
+
expect(calledUrl).toContain('directions/v5/mapbox/driving-traffic');
|
|
30
|
+
expect(calledUrl).toContain('-73.989%2C40.733%3B-73.979%2C40.743');
|
|
31
|
+
expect(calledUrl).toContain('access_token=');
|
|
32
|
+
assertHeadersSent(mockFetch);
|
|
33
|
+
});
|
|
34
|
+
it('includes all optional parameters in URL', async () => {
|
|
35
|
+
const mockFetch = setupFetch();
|
|
36
|
+
await new DirectionsTool().run({
|
|
37
|
+
coordinates: [
|
|
38
|
+
[-122.42, 37.78],
|
|
39
|
+
[-122.4, 37.79],
|
|
40
|
+
[-122.39, 37.77]
|
|
41
|
+
],
|
|
42
|
+
routing_profile: 'walking',
|
|
43
|
+
geometries: 'geojson',
|
|
44
|
+
alternatives: true,
|
|
45
|
+
annotations: ['distance', 'duration', 'speed'],
|
|
46
|
+
exclude: 'ferry'
|
|
47
|
+
});
|
|
48
|
+
const calledUrl = mockFetch.mock.calls[0][0];
|
|
49
|
+
expect(calledUrl).toContain('directions/v5/mapbox/walking');
|
|
50
|
+
expect(calledUrl).toContain('-122.42%2C37.78%3B-122.4%2C37.79%3B-122.39%2C37.77');
|
|
51
|
+
expect(calledUrl).toContain('geometries=geojson');
|
|
52
|
+
expect(calledUrl).toContain('alternatives=true');
|
|
53
|
+
expect(calledUrl).toContain('annotations=distance%2Cduration%2Cspeed');
|
|
54
|
+
expect(calledUrl).toContain('overview=full');
|
|
55
|
+
expect(calledUrl).toContain('exclude=ferry');
|
|
56
|
+
assertHeadersSent(mockFetch);
|
|
57
|
+
});
|
|
58
|
+
it('uses default parameters when not specified', async () => {
|
|
59
|
+
const mockFetch = setupFetch();
|
|
60
|
+
await new DirectionsTool().run({
|
|
61
|
+
coordinates: [
|
|
62
|
+
[-118.24, 34.05],
|
|
63
|
+
[-118.3, 34.02]
|
|
64
|
+
]
|
|
65
|
+
});
|
|
66
|
+
const calledUrl = mockFetch.mock.calls[0][0];
|
|
67
|
+
expect(calledUrl).toContain('directions/v5/mapbox/driving-traffic');
|
|
68
|
+
expect(calledUrl).toContain('geometries=polyline');
|
|
69
|
+
expect(calledUrl).toContain('alternatives=false');
|
|
70
|
+
expect(calledUrl).not.toContain('annotations=');
|
|
71
|
+
expect(calledUrl).not.toContain('exclude=');
|
|
72
|
+
assertHeadersSent(mockFetch);
|
|
73
|
+
});
|
|
74
|
+
it('handles exclude parameter with point format', async () => {
|
|
75
|
+
const mockFetch = setupFetch();
|
|
76
|
+
await new DirectionsTool().run({
|
|
77
|
+
coordinates: [
|
|
78
|
+
[-74.0, 40.7],
|
|
79
|
+
[-73.9, 40.8]
|
|
80
|
+
],
|
|
81
|
+
exclude: 'toll,point(-73.95 40.75)'
|
|
82
|
+
});
|
|
83
|
+
const calledUrl = mockFetch.mock.calls[0][0];
|
|
84
|
+
const comma = '%2C';
|
|
85
|
+
const space = '%20';
|
|
86
|
+
const openPar = '%28';
|
|
87
|
+
const closePar = '%29';
|
|
88
|
+
expect(calledUrl).toContain(`exclude=toll${comma}point${openPar}-73.95${space}40.75${closePar}`);
|
|
89
|
+
assertHeadersSent(mockFetch);
|
|
90
|
+
});
|
|
91
|
+
it('handles fetch errors gracefully', async () => {
|
|
92
|
+
const mockFetch = setupFetch({
|
|
93
|
+
ok: false,
|
|
94
|
+
status: 404,
|
|
95
|
+
statusText: 'Not Found'
|
|
96
|
+
});
|
|
97
|
+
const result = await new DirectionsTool().run({
|
|
98
|
+
coordinates: [
|
|
99
|
+
[-73.989, 40.733],
|
|
100
|
+
[-73.979, 40.743]
|
|
101
|
+
]
|
|
102
|
+
});
|
|
103
|
+
expect(result.is_error).toBe(true);
|
|
104
|
+
expect(result.content[0]).toMatchObject({
|
|
105
|
+
type: 'text',
|
|
106
|
+
text: 'Internal error has occurred.'
|
|
107
|
+
});
|
|
108
|
+
assertHeadersSent(mockFetch);
|
|
109
|
+
});
|
|
110
|
+
it('validates coordinates constraints - minimum required', async () => {
|
|
111
|
+
const tool = new DirectionsTool();
|
|
112
|
+
// Test with only one coordinate (invalid)
|
|
113
|
+
await expect(tool.run({
|
|
114
|
+
coordinates: [[-73.989, 40.733]]
|
|
115
|
+
})).resolves.toMatchObject({
|
|
116
|
+
is_error: true
|
|
117
|
+
});
|
|
118
|
+
// Test with zero coordinates (invalid)
|
|
119
|
+
await expect(tool.run({
|
|
120
|
+
coordinates: []
|
|
121
|
+
})).resolves.toMatchObject({
|
|
122
|
+
is_error: true
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
it('validates coordinates constraints - maximum allowed', async () => {
|
|
126
|
+
const tool = new DirectionsTool();
|
|
127
|
+
// Create an array of 26 coordinates (one more than allowed)
|
|
128
|
+
const tooManyCoords = Array(26).fill([-73.989, 40.733]);
|
|
129
|
+
await expect(tool.run({
|
|
130
|
+
coordinates: tooManyCoords
|
|
131
|
+
})).resolves.toMatchObject({
|
|
132
|
+
is_error: true
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
it('successfully processes exactly 2 coordinates (minimum allowed)', async () => {
|
|
136
|
+
const mockFetch = setupFetch();
|
|
137
|
+
await new DirectionsTool().run({
|
|
138
|
+
coordinates: [
|
|
139
|
+
[-73.989, 40.733],
|
|
140
|
+
[-73.979, 40.743]
|
|
141
|
+
]
|
|
142
|
+
});
|
|
143
|
+
const calledUrl = mockFetch.mock.calls[0][0];
|
|
144
|
+
expect(calledUrl).toContain('-73.989%2C40.733%3B-73.979%2C40.743');
|
|
145
|
+
assertHeadersSent(mockFetch);
|
|
146
|
+
});
|
|
147
|
+
it('successfully processes exactly 25 coordinates (maximum allowed)', async () => {
|
|
148
|
+
const mockFetch = setupFetch();
|
|
149
|
+
// Create an array of exactly 25 coordinates (maximum allowed)
|
|
150
|
+
const maxCoords = Array(25)
|
|
151
|
+
.fill(0)
|
|
152
|
+
.map((_, i) => [-74 + i * 0.01, 40 + i * 0.01]);
|
|
153
|
+
await new DirectionsTool().run({
|
|
154
|
+
coordinates: maxCoords
|
|
155
|
+
});
|
|
156
|
+
const calledUrl = mockFetch.mock.calls[0][0];
|
|
157
|
+
// Check that all coordinates are properly encoded
|
|
158
|
+
for (let i = 0; i < maxCoords.length; i++) {
|
|
159
|
+
const [lng, lat] = maxCoords[i];
|
|
160
|
+
const semicolon = i < 24 ? '%3B' : '';
|
|
161
|
+
const expectedCoord = `${lng}%2C${lat}` + semicolon;
|
|
162
|
+
expect(calledUrl).toContain(expectedCoord);
|
|
163
|
+
}
|
|
164
|
+
assertHeadersSent(mockFetch);
|
|
165
|
+
});
|
|
166
|
+
describe('walking parameters validations', () => {
|
|
167
|
+
it('accepts walking_speed with walking profile', async () => {
|
|
168
|
+
const mockFetch = setupFetch();
|
|
169
|
+
await new DirectionsTool().run({
|
|
170
|
+
coordinates: [
|
|
171
|
+
[-73.989, 40.733],
|
|
172
|
+
[-73.979, 40.743]
|
|
173
|
+
],
|
|
174
|
+
routing_profile: 'walking',
|
|
175
|
+
walking_speed: 2.5
|
|
176
|
+
});
|
|
177
|
+
const calledUrl = mockFetch.mock.calls[0][0];
|
|
178
|
+
expect(calledUrl).toContain('walking_speed=2.5');
|
|
179
|
+
assertHeadersSent(mockFetch);
|
|
180
|
+
});
|
|
181
|
+
it('accepts walkway_bias with walking profile', async () => {
|
|
182
|
+
const mockFetch = setupFetch();
|
|
183
|
+
await new DirectionsTool().run({
|
|
184
|
+
coordinates: [
|
|
185
|
+
[-73.989, 40.733],
|
|
186
|
+
[-73.979, 40.743]
|
|
187
|
+
],
|
|
188
|
+
routing_profile: 'walking',
|
|
189
|
+
walkway_bias: 0.8
|
|
190
|
+
});
|
|
191
|
+
const calledUrl = mockFetch.mock.calls[0][0];
|
|
192
|
+
expect(calledUrl).toContain('walkway_bias=0.8');
|
|
193
|
+
assertHeadersSent(mockFetch);
|
|
194
|
+
});
|
|
195
|
+
it('rejects walking_speed with non-walking profiles', async () => {
|
|
196
|
+
const tool = new DirectionsTool();
|
|
197
|
+
// Test with driving profile
|
|
198
|
+
await expect(tool.run({
|
|
199
|
+
coordinates: [
|
|
200
|
+
[-73.989, 40.733],
|
|
201
|
+
[-73.979, 40.743]
|
|
202
|
+
],
|
|
203
|
+
routing_profile: 'driving',
|
|
204
|
+
walking_speed: 2.0
|
|
205
|
+
})).resolves.toMatchObject({
|
|
206
|
+
is_error: true
|
|
207
|
+
});
|
|
208
|
+
// Test with cycling profile
|
|
209
|
+
await expect(tool.run({
|
|
210
|
+
coordinates: [
|
|
211
|
+
[-73.989, 40.733],
|
|
212
|
+
[-73.979, 40.743]
|
|
213
|
+
],
|
|
214
|
+
routing_profile: 'cycling',
|
|
215
|
+
walking_speed: 2.0
|
|
216
|
+
})).resolves.toMatchObject({
|
|
217
|
+
is_error: true
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
it('rejects walkway_bias with non-walking profiles', async () => {
|
|
221
|
+
const tool = new DirectionsTool();
|
|
222
|
+
// Test with driving-traffic profile
|
|
223
|
+
await expect(tool.run({
|
|
224
|
+
coordinates: [
|
|
225
|
+
[-73.989, 40.733],
|
|
226
|
+
[-73.979, 40.743]
|
|
227
|
+
],
|
|
228
|
+
routing_profile: 'driving-traffic',
|
|
229
|
+
walkway_bias: 0.5
|
|
230
|
+
})).resolves.toMatchObject({
|
|
231
|
+
is_error: true
|
|
232
|
+
});
|
|
233
|
+
// Test with cycling profile
|
|
234
|
+
await expect(tool.run({
|
|
235
|
+
coordinates: [
|
|
236
|
+
[-73.989, 40.733],
|
|
237
|
+
[-73.979, 40.743]
|
|
238
|
+
],
|
|
239
|
+
routing_profile: 'cycling',
|
|
240
|
+
walkway_bias: -0.8
|
|
241
|
+
})).resolves.toMatchObject({
|
|
242
|
+
is_error: true
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
it('validates walking_speed value ranges', async () => {
|
|
246
|
+
const tool = new DirectionsTool();
|
|
247
|
+
// Test with value below minimum (0.14 m/s)
|
|
248
|
+
await expect(tool.run({
|
|
249
|
+
coordinates: [
|
|
250
|
+
[-73.989, 40.733],
|
|
251
|
+
[-73.979, 40.743]
|
|
252
|
+
],
|
|
253
|
+
routing_profile: 'walking',
|
|
254
|
+
walking_speed: 0.1
|
|
255
|
+
})).resolves.toMatchObject({
|
|
256
|
+
is_error: true
|
|
257
|
+
});
|
|
258
|
+
// Test with value above maximum (6.94 m/s)
|
|
259
|
+
await expect(tool.run({
|
|
260
|
+
coordinates: [
|
|
261
|
+
[-73.989, 40.733],
|
|
262
|
+
[-73.979, 40.743]
|
|
263
|
+
],
|
|
264
|
+
routing_profile: 'walking',
|
|
265
|
+
walking_speed: 7.5
|
|
266
|
+
})).resolves.toMatchObject({
|
|
267
|
+
is_error: true
|
|
268
|
+
});
|
|
269
|
+
// Test with valid value
|
|
270
|
+
const mockFetch = setupFetch();
|
|
271
|
+
await tool.run({
|
|
272
|
+
coordinates: [
|
|
273
|
+
[-73.989, 40.733],
|
|
274
|
+
[-73.979, 40.743]
|
|
275
|
+
],
|
|
276
|
+
routing_profile: 'walking',
|
|
277
|
+
walking_speed: 3.0
|
|
278
|
+
});
|
|
279
|
+
const calledUrl = mockFetch.mock.calls[0][0];
|
|
280
|
+
expect(calledUrl).toContain('walking_speed=3');
|
|
281
|
+
});
|
|
282
|
+
it('validates walkway_bias value ranges', async () => {
|
|
283
|
+
const tool = new DirectionsTool();
|
|
284
|
+
// Test with value below minimum (-1)
|
|
285
|
+
await expect(tool.run({
|
|
286
|
+
coordinates: [
|
|
287
|
+
[-73.989, 40.733],
|
|
288
|
+
[-73.979, 40.743]
|
|
289
|
+
],
|
|
290
|
+
routing_profile: 'walking',
|
|
291
|
+
walkway_bias: -1.5
|
|
292
|
+
})).resolves.toMatchObject({
|
|
293
|
+
is_error: true
|
|
294
|
+
});
|
|
295
|
+
// Test with value above maximum (1)
|
|
296
|
+
await expect(tool.run({
|
|
297
|
+
coordinates: [
|
|
298
|
+
[-73.989, 40.733],
|
|
299
|
+
[-73.979, 40.743]
|
|
300
|
+
],
|
|
301
|
+
routing_profile: 'walking',
|
|
302
|
+
walkway_bias: 1.2
|
|
303
|
+
})).resolves.toMatchObject({
|
|
304
|
+
is_error: true
|
|
305
|
+
});
|
|
306
|
+
// Test with valid values
|
|
307
|
+
const mockFetch = setupFetch();
|
|
308
|
+
await tool.run({
|
|
309
|
+
coordinates: [
|
|
310
|
+
[-73.989, 40.733],
|
|
311
|
+
[-73.979, 40.743]
|
|
312
|
+
],
|
|
313
|
+
routing_profile: 'walking',
|
|
314
|
+
walkway_bias: -0.5
|
|
315
|
+
});
|
|
316
|
+
const calledUrl = mockFetch.mock.calls[0][0];
|
|
317
|
+
expect(calledUrl).toContain('walkway_bias=-0.5');
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
describe('exclude parameter and routing profile validations', () => {
|
|
321
|
+
it('accepts driving-specific exclusions with driving profiles', async () => {
|
|
322
|
+
const mockFetch = setupFetch();
|
|
323
|
+
const tool = new DirectionsTool();
|
|
324
|
+
// Test with driving profile
|
|
325
|
+
await expect(tool.run({
|
|
326
|
+
coordinates: [
|
|
327
|
+
[-73.989, 40.733],
|
|
328
|
+
[-73.979, 40.743]
|
|
329
|
+
],
|
|
330
|
+
routing_profile: 'driving',
|
|
331
|
+
exclude: 'toll,motorway,unpaved'
|
|
332
|
+
})).resolves.not.toMatchObject({
|
|
333
|
+
is_error: true
|
|
334
|
+
});
|
|
335
|
+
// Test with driving-traffic profile
|
|
336
|
+
await expect(tool.run({
|
|
337
|
+
coordinates: [
|
|
338
|
+
[-73.989, 40.733],
|
|
339
|
+
[-73.979, 40.743]
|
|
340
|
+
],
|
|
341
|
+
routing_profile: 'driving-traffic',
|
|
342
|
+
exclude: 'tunnel,country_border,state_border'
|
|
343
|
+
})).resolves.not.toMatchObject({
|
|
344
|
+
is_error: true
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
it('rejects driving-specific exclusions with non-driving profiles', async () => {
|
|
348
|
+
const tool = new DirectionsTool();
|
|
349
|
+
// Test with walking profile
|
|
350
|
+
await expect(tool.run({
|
|
351
|
+
coordinates: [
|
|
352
|
+
[-73.989, 40.733],
|
|
353
|
+
[-73.979, 40.743]
|
|
354
|
+
],
|
|
355
|
+
routing_profile: 'walking',
|
|
356
|
+
exclude: 'toll'
|
|
357
|
+
})).resolves.toMatchObject({
|
|
358
|
+
is_error: true
|
|
359
|
+
});
|
|
360
|
+
// Test with cycling profile
|
|
361
|
+
await expect(tool.run({
|
|
362
|
+
coordinates: [
|
|
363
|
+
[-73.989, 40.733],
|
|
364
|
+
[-73.979, 40.743]
|
|
365
|
+
],
|
|
366
|
+
routing_profile: 'cycling',
|
|
367
|
+
exclude: 'motorway'
|
|
368
|
+
})).resolves.toMatchObject({
|
|
369
|
+
is_error: true
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
it('accepts common exclusions with all routing profiles', async () => {
|
|
373
|
+
const mockFetch = setupFetch();
|
|
374
|
+
const tool = new DirectionsTool();
|
|
375
|
+
// Test with driving profile
|
|
376
|
+
await expect(tool.run({
|
|
377
|
+
coordinates: [
|
|
378
|
+
[-73.989, 40.733],
|
|
379
|
+
[-73.979, 40.743]
|
|
380
|
+
],
|
|
381
|
+
routing_profile: 'driving',
|
|
382
|
+
exclude: 'ferry'
|
|
383
|
+
})).resolves.not.toMatchObject({
|
|
384
|
+
is_error: true
|
|
385
|
+
});
|
|
386
|
+
// Test with walking profile
|
|
387
|
+
await expect(tool.run({
|
|
388
|
+
coordinates: [
|
|
389
|
+
[-73.989, 40.733],
|
|
390
|
+
[-73.979, 40.743]
|
|
391
|
+
],
|
|
392
|
+
routing_profile: 'walking',
|
|
393
|
+
exclude: 'ferry'
|
|
394
|
+
})).resolves.not.toMatchObject({
|
|
395
|
+
is_error: true
|
|
396
|
+
});
|
|
397
|
+
// Test with cycling profile
|
|
398
|
+
await expect(tool.run({
|
|
399
|
+
coordinates: [
|
|
400
|
+
[-73.989, 40.733],
|
|
401
|
+
[-73.979, 40.743]
|
|
402
|
+
],
|
|
403
|
+
routing_profile: 'cycling',
|
|
404
|
+
exclude: 'cash_only_tolls'
|
|
405
|
+
})).resolves.not.toMatchObject({
|
|
406
|
+
is_error: true
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
it('accepts point exclusions with driving profiles and rejects with non-driving profiles', async () => {
|
|
410
|
+
const mockFetch = setupFetch();
|
|
411
|
+
const tool = new DirectionsTool();
|
|
412
|
+
// Test with driving profile - should work
|
|
413
|
+
await expect(tool.run({
|
|
414
|
+
coordinates: [
|
|
415
|
+
[-73.989, 40.733],
|
|
416
|
+
[-73.979, 40.743]
|
|
417
|
+
],
|
|
418
|
+
routing_profile: 'driving',
|
|
419
|
+
exclude: 'point(-73.95 40.75)'
|
|
420
|
+
})).resolves.not.toMatchObject({
|
|
421
|
+
is_error: true
|
|
422
|
+
});
|
|
423
|
+
// Test with walking profile - should fail
|
|
424
|
+
await expect(tool.run({
|
|
425
|
+
coordinates: [
|
|
426
|
+
[-73.989, 40.733],
|
|
427
|
+
[-73.979, 40.743]
|
|
428
|
+
],
|
|
429
|
+
routing_profile: 'walking',
|
|
430
|
+
exclude: 'point(-73.95 40.75)'
|
|
431
|
+
})).resolves.toMatchObject({
|
|
432
|
+
is_error: true
|
|
433
|
+
});
|
|
434
|
+
// Test with cycling profile - should fail
|
|
435
|
+
await expect(tool.run({
|
|
436
|
+
coordinates: [
|
|
437
|
+
[-73.989, 40.733],
|
|
438
|
+
[-73.979, 40.743]
|
|
439
|
+
],
|
|
440
|
+
routing_profile: 'cycling',
|
|
441
|
+
exclude: 'point(-73.95 40.75)'
|
|
442
|
+
})).resolves.toMatchObject({
|
|
443
|
+
is_error: true
|
|
444
|
+
});
|
|
445
|
+
});
|
|
446
|
+
it('handles multiple exclusions in a single request correctly', async () => {
|
|
447
|
+
const mockFetch = setupFetch();
|
|
448
|
+
const tool = new DirectionsTool();
|
|
449
|
+
// All valid exclusions for driving profile
|
|
450
|
+
await expect(tool.run({
|
|
451
|
+
coordinates: [
|
|
452
|
+
[-73.989, 40.733],
|
|
453
|
+
[-73.979, 40.743]
|
|
454
|
+
],
|
|
455
|
+
routing_profile: 'driving',
|
|
456
|
+
exclude: 'toll,motorway,ferry,cash_only_tolls,point(-73.95 40.75)'
|
|
457
|
+
})).resolves.not.toMatchObject({
|
|
458
|
+
is_error: true
|
|
459
|
+
});
|
|
460
|
+
// Mixed valid and invalid exclusions (ferry is valid for walking, toll is not)
|
|
461
|
+
await expect(tool.run({
|
|
462
|
+
coordinates: [
|
|
463
|
+
[-73.989, 40.733],
|
|
464
|
+
[-73.979, 40.743]
|
|
465
|
+
],
|
|
466
|
+
routing_profile: 'walking',
|
|
467
|
+
exclude: 'ferry,toll'
|
|
468
|
+
})).resolves.toMatchObject({
|
|
469
|
+
is_error: true
|
|
470
|
+
});
|
|
471
|
+
// All valid exclusions for cycling profile
|
|
472
|
+
await expect(tool.run({
|
|
473
|
+
coordinates: [
|
|
474
|
+
[-73.989, 40.733],
|
|
475
|
+
[-73.979, 40.743]
|
|
476
|
+
],
|
|
477
|
+
routing_profile: 'cycling',
|
|
478
|
+
exclude: 'ferry,cash_only_tolls'
|
|
479
|
+
})).resolves.not.toMatchObject({
|
|
480
|
+
is_error: true
|
|
481
|
+
});
|
|
482
|
+
});
|
|
483
|
+
});
|
|
484
|
+
describe('depart_at parameter validations', () => {
|
|
485
|
+
it('accepts depart_at with driving profiles', async () => {
|
|
486
|
+
const mockFetch = setupFetch();
|
|
487
|
+
const tool = new DirectionsTool();
|
|
488
|
+
const validDateTime = '2025-06-05T10:30:00Z';
|
|
489
|
+
// Test with driving profile
|
|
490
|
+
await expect(tool.run({
|
|
491
|
+
coordinates: [
|
|
492
|
+
[-73.989, 40.733],
|
|
493
|
+
[-73.979, 40.743]
|
|
494
|
+
],
|
|
495
|
+
routing_profile: 'driving',
|
|
496
|
+
depart_at: validDateTime
|
|
497
|
+
})).resolves.not.toMatchObject({
|
|
498
|
+
is_error: true
|
|
499
|
+
});
|
|
500
|
+
const calledUrlDriving = mockFetch.mock.calls[0][0];
|
|
501
|
+
expect(calledUrlDriving).toContain(`depart_at=${encodeURIComponent(validDateTime)}`);
|
|
502
|
+
// Test with driving-traffic profile
|
|
503
|
+
await expect(tool.run({
|
|
504
|
+
coordinates: [
|
|
505
|
+
[-73.989, 40.733],
|
|
506
|
+
[-73.979, 40.743]
|
|
507
|
+
],
|
|
508
|
+
routing_profile: 'driving-traffic',
|
|
509
|
+
depart_at: validDateTime
|
|
510
|
+
})).resolves.not.toMatchObject({
|
|
511
|
+
is_error: true
|
|
512
|
+
});
|
|
513
|
+
const calledUrlTraffic = mockFetch.mock.calls[1][0];
|
|
514
|
+
expect(calledUrlTraffic).toContain(`depart_at=${encodeURIComponent(validDateTime)}`);
|
|
515
|
+
});
|
|
516
|
+
describe('vehicle dimension parameters validations', () => {
|
|
517
|
+
it('accepts vehicle dimensions with driving profiles', async () => {
|
|
518
|
+
const mockFetch = setupFetch();
|
|
519
|
+
const tool = new DirectionsTool();
|
|
520
|
+
// Test with driving profile
|
|
521
|
+
await expect(tool.run({
|
|
522
|
+
coordinates: [
|
|
523
|
+
[-73.989, 40.733],
|
|
524
|
+
[-73.979, 40.743]
|
|
525
|
+
],
|
|
526
|
+
routing_profile: 'driving',
|
|
527
|
+
max_height: 4.5,
|
|
528
|
+
max_width: 2.5,
|
|
529
|
+
max_weight: 7.8
|
|
530
|
+
})).resolves.not.toMatchObject({
|
|
531
|
+
is_error: true
|
|
532
|
+
});
|
|
533
|
+
const calledUrlDriving = mockFetch.mock.calls[0][0];
|
|
534
|
+
expect(calledUrlDriving).toContain('max_height=4.5');
|
|
535
|
+
expect(calledUrlDriving).toContain('max_width=2.5');
|
|
536
|
+
expect(calledUrlDriving).toContain('max_weight=7.8');
|
|
537
|
+
// Test with driving-traffic profile
|
|
538
|
+
await expect(tool.run({
|
|
539
|
+
coordinates: [
|
|
540
|
+
[-73.989, 40.733],
|
|
541
|
+
[-73.979, 40.743]
|
|
542
|
+
],
|
|
543
|
+
routing_profile: 'driving-traffic',
|
|
544
|
+
max_height: 3.2
|
|
545
|
+
})).resolves.not.toMatchObject({
|
|
546
|
+
is_error: true
|
|
547
|
+
});
|
|
548
|
+
const calledUrlTraffic = mockFetch.mock.calls[1][0];
|
|
549
|
+
expect(calledUrlTraffic).toContain('max_height=3.2');
|
|
550
|
+
});
|
|
551
|
+
it('rejects vehicle dimensions with non-driving profiles', async () => {
|
|
552
|
+
const tool = new DirectionsTool();
|
|
553
|
+
// Test with walking profile
|
|
554
|
+
await expect(tool.run({
|
|
555
|
+
coordinates: [
|
|
556
|
+
[-73.989, 40.733],
|
|
557
|
+
[-73.979, 40.743]
|
|
558
|
+
],
|
|
559
|
+
routing_profile: 'walking',
|
|
560
|
+
max_height: 4.5
|
|
561
|
+
})).resolves.toMatchObject({
|
|
562
|
+
is_error: true
|
|
563
|
+
});
|
|
564
|
+
// Test with cycling profile
|
|
565
|
+
await expect(tool.run({
|
|
566
|
+
coordinates: [
|
|
567
|
+
[-73.989, 40.733],
|
|
568
|
+
[-73.979, 40.743]
|
|
569
|
+
],
|
|
570
|
+
routing_profile: 'cycling',
|
|
571
|
+
max_width: 2.0
|
|
572
|
+
})).resolves.toMatchObject({
|
|
573
|
+
is_error: true
|
|
574
|
+
});
|
|
575
|
+
});
|
|
576
|
+
it('validates dimension value ranges', async () => {
|
|
577
|
+
const tool = new DirectionsTool();
|
|
578
|
+
// Test invalid height (too high)
|
|
579
|
+
await expect(tool.run({
|
|
580
|
+
coordinates: [
|
|
581
|
+
[-73.989, 40.733],
|
|
582
|
+
[-73.979, 40.743]
|
|
583
|
+
],
|
|
584
|
+
routing_profile: 'driving',
|
|
585
|
+
max_height: 15.0
|
|
586
|
+
})).resolves.toMatchObject({
|
|
587
|
+
is_error: true
|
|
588
|
+
});
|
|
589
|
+
// Test invalid width (negative)
|
|
590
|
+
await expect(tool.run({
|
|
591
|
+
coordinates: [
|
|
592
|
+
[-73.989, 40.733],
|
|
593
|
+
[-73.979, 40.743]
|
|
594
|
+
],
|
|
595
|
+
routing_profile: 'driving',
|
|
596
|
+
max_width: -1.0
|
|
597
|
+
})).resolves.toMatchObject({
|
|
598
|
+
is_error: true
|
|
599
|
+
});
|
|
600
|
+
// Test invalid weight (too heavy)
|
|
601
|
+
await expect(tool.run({
|
|
602
|
+
coordinates: [
|
|
603
|
+
[-73.989, 40.733],
|
|
604
|
+
[-73.979, 40.743]
|
|
605
|
+
],
|
|
606
|
+
routing_profile: 'driving',
|
|
607
|
+
max_weight: 150.0
|
|
608
|
+
})).resolves.toMatchObject({
|
|
609
|
+
is_error: true
|
|
610
|
+
});
|
|
611
|
+
});
|
|
612
|
+
});
|
|
613
|
+
it('rejects depart_at with non-driving profiles', async () => {
|
|
614
|
+
const tool = new DirectionsTool();
|
|
615
|
+
const validDateTime = '2025-06-05T10:30:00Z';
|
|
616
|
+
// Test with walking profile
|
|
617
|
+
await expect(tool.run({
|
|
618
|
+
coordinates: [
|
|
619
|
+
[-73.989, 40.733],
|
|
620
|
+
[-73.979, 40.743]
|
|
621
|
+
],
|
|
622
|
+
routing_profile: 'walking',
|
|
623
|
+
depart_at: validDateTime
|
|
624
|
+
})).resolves.toMatchObject({
|
|
625
|
+
is_error: true
|
|
626
|
+
});
|
|
627
|
+
// Test with cycling profile
|
|
628
|
+
await expect(tool.run({
|
|
629
|
+
coordinates: [
|
|
630
|
+
[-73.989, 40.733],
|
|
631
|
+
[-73.979, 40.743]
|
|
632
|
+
],
|
|
633
|
+
routing_profile: 'cycling',
|
|
634
|
+
depart_at: validDateTime
|
|
635
|
+
})).resolves.toMatchObject({
|
|
636
|
+
is_error: true
|
|
637
|
+
});
|
|
638
|
+
});
|
|
639
|
+
it('accepts valid date-time formats', async () => {
|
|
640
|
+
const mockFetch = setupFetch();
|
|
641
|
+
const tool = new DirectionsTool();
|
|
642
|
+
const baseCoordinates = [
|
|
643
|
+
[-73.989, 40.733],
|
|
644
|
+
[-73.979, 40.743]
|
|
645
|
+
];
|
|
646
|
+
// Format 1: YYYY-MM-DDThh:mm:ssZ
|
|
647
|
+
await expect(tool.run({
|
|
648
|
+
coordinates: baseCoordinates,
|
|
649
|
+
depart_at: '2025-06-05T10:30:00Z'
|
|
650
|
+
})).resolves.not.toMatchObject({
|
|
651
|
+
is_error: true
|
|
652
|
+
});
|
|
653
|
+
// Format 2: YYYY-MM-DDThh:mmss±hh:mm
|
|
654
|
+
await expect(tool.run({
|
|
655
|
+
coordinates: baseCoordinates,
|
|
656
|
+
depart_at: '2025-06-05T10:30:00+02:00'
|
|
657
|
+
})).resolves.not.toMatchObject({
|
|
658
|
+
is_error: true
|
|
659
|
+
});
|
|
660
|
+
// Format 3: YYYY-MM-DDThh:mm
|
|
661
|
+
await expect(tool.run({
|
|
662
|
+
coordinates: baseCoordinates,
|
|
663
|
+
depart_at: '2025-06-05T10:30'
|
|
664
|
+
})).resolves.not.toMatchObject({
|
|
665
|
+
is_error: true
|
|
666
|
+
});
|
|
667
|
+
});
|
|
668
|
+
it('rejects invalid date-time formats', async () => {
|
|
669
|
+
const tool = new DirectionsTool();
|
|
670
|
+
const baseCoordinates = [
|
|
671
|
+
[-73.989, 40.733],
|
|
672
|
+
[-73.979, 40.743]
|
|
673
|
+
];
|
|
674
|
+
// Invalid format examples
|
|
675
|
+
const invalidFormats = [
|
|
676
|
+
'2025/06/05 10:30:00', // Wrong delimiter
|
|
677
|
+
'2025-06-05 10:30:00', // Missing T
|
|
678
|
+
'2025-06-05T10:30:00+0200', // Missing colon in timezone
|
|
679
|
+
'25-6-5T10:30:00Z', // Incorrect date format
|
|
680
|
+
'2025-06-05T10:30:00ZZ', // Double timezone
|
|
681
|
+
'2025-06-05', // Missing time
|
|
682
|
+
'10:30:00' // Missing date
|
|
683
|
+
];
|
|
684
|
+
// Test each format separately
|
|
685
|
+
for (let i = 0; i < invalidFormats.length; i++) {
|
|
686
|
+
const format = invalidFormats[i];
|
|
687
|
+
// Test each invalid format individually for better error reporting
|
|
688
|
+
const result = await tool.run({
|
|
689
|
+
coordinates: baseCoordinates,
|
|
690
|
+
depart_at: format
|
|
691
|
+
});
|
|
692
|
+
expect(result.is_error).toBe(true);
|
|
693
|
+
}
|
|
694
|
+
});
|
|
695
|
+
it('rejects dates with invalid components', async () => {
|
|
696
|
+
const tool = new DirectionsTool();
|
|
697
|
+
const baseCoordinates = [
|
|
698
|
+
[-73.989, 40.733],
|
|
699
|
+
[-73.979, 40.743]
|
|
700
|
+
];
|
|
701
|
+
// Invalid time components
|
|
702
|
+
const invalidDates = [
|
|
703
|
+
'2025-13-05T10:30:00Z', // Invalid month (13)
|
|
704
|
+
'2025-06-32T10:30:00Z', // Invalid day (32)
|
|
705
|
+
'2025-06-05T24:30:00Z', // Invalid hour (24)
|
|
706
|
+
'2025-06-05T10:60:00Z', // Invalid minute (60)
|
|
707
|
+
'2025-06-05T10:30:60Z' // Invalid second (60)
|
|
708
|
+
];
|
|
709
|
+
for (const date of invalidDates) {
|
|
710
|
+
await expect(tool.run({
|
|
711
|
+
coordinates: baseCoordinates,
|
|
712
|
+
depart_at: date
|
|
713
|
+
})).resolves.toMatchObject({
|
|
714
|
+
is_error: true
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
});
|
|
718
|
+
});
|
|
719
|
+
describe('arrive_by parameter validations', () => {
|
|
720
|
+
it('accepts arrive_by with driving profile only', async () => {
|
|
721
|
+
const validDateTime = '2025-06-05T10:30:00Z';
|
|
722
|
+
const mockFetch = setupFetch();
|
|
723
|
+
// Test with driving profile - should work
|
|
724
|
+
await new DirectionsTool().run({
|
|
725
|
+
coordinates: [
|
|
726
|
+
[-74.1, 40.7],
|
|
727
|
+
[-74.2, 40.8]
|
|
728
|
+
],
|
|
729
|
+
routing_profile: 'driving',
|
|
730
|
+
arrive_by: validDateTime
|
|
731
|
+
});
|
|
732
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
733
|
+
const calledUrl = mockFetch.mock.calls[0][0];
|
|
734
|
+
expect(calledUrl).toContain(`arrive_by=${encodeURIComponent(validDateTime)}`);
|
|
735
|
+
});
|
|
736
|
+
it('rejects arrive_by with non-driving profiles', async () => {
|
|
737
|
+
const validDateTime = '2025-06-05T10:30:00Z';
|
|
738
|
+
// Test with driving-traffic profile
|
|
739
|
+
const result1 = await new DirectionsTool().run({
|
|
740
|
+
coordinates: [
|
|
741
|
+
[-74.1, 40.7],
|
|
742
|
+
[-74.2, 40.8]
|
|
743
|
+
],
|
|
744
|
+
routing_profile: 'driving-traffic',
|
|
745
|
+
arrive_by: validDateTime
|
|
746
|
+
});
|
|
747
|
+
expect(result1.is_error).toBe(true);
|
|
748
|
+
// Test with walking profile
|
|
749
|
+
const result2 = await new DirectionsTool().run({
|
|
750
|
+
coordinates: [
|
|
751
|
+
[-74.1, 40.7],
|
|
752
|
+
[-74.2, 40.8]
|
|
753
|
+
],
|
|
754
|
+
routing_profile: 'walking',
|
|
755
|
+
arrive_by: validDateTime
|
|
756
|
+
});
|
|
757
|
+
expect(result2.is_error).toBe(true);
|
|
758
|
+
// Test with cycling profile
|
|
759
|
+
const result3 = await new DirectionsTool().run({
|
|
760
|
+
coordinates: [
|
|
761
|
+
[-74.1, 40.7],
|
|
762
|
+
[-74.2, 40.8]
|
|
763
|
+
],
|
|
764
|
+
routing_profile: 'cycling',
|
|
765
|
+
arrive_by: validDateTime
|
|
766
|
+
});
|
|
767
|
+
expect(result3.is_error).toBe(true);
|
|
768
|
+
});
|
|
769
|
+
it('rejects when both arrive_by and depart_at are provided', async () => {
|
|
770
|
+
const result = await new DirectionsTool().run({
|
|
771
|
+
coordinates: [
|
|
772
|
+
[-74.1, 40.7],
|
|
773
|
+
[-74.2, 40.8]
|
|
774
|
+
],
|
|
775
|
+
routing_profile: 'driving',
|
|
776
|
+
depart_at: '2025-06-05T09:30:00Z',
|
|
777
|
+
arrive_by: '2025-06-05T10:30:00Z'
|
|
778
|
+
});
|
|
779
|
+
expect(result.is_error).toBe(true);
|
|
780
|
+
});
|
|
781
|
+
it('accepts valid ISO 8601 formats for arrive_by', async () => {
|
|
782
|
+
const mockFetch = setupFetch();
|
|
783
|
+
// Test with Z format
|
|
784
|
+
await new DirectionsTool().run({
|
|
785
|
+
coordinates: [
|
|
786
|
+
[-74.1, 40.7],
|
|
787
|
+
[-74.2, 40.8]
|
|
788
|
+
],
|
|
789
|
+
routing_profile: 'driving',
|
|
790
|
+
arrive_by: '2025-06-05T10:30:00Z'
|
|
791
|
+
});
|
|
792
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
793
|
+
mockFetch.mockClear();
|
|
794
|
+
// Test with timezone offset format
|
|
795
|
+
await new DirectionsTool().run({
|
|
796
|
+
coordinates: [
|
|
797
|
+
[-74.1, 40.7],
|
|
798
|
+
[-74.2, 40.8]
|
|
799
|
+
],
|
|
800
|
+
routing_profile: 'driving',
|
|
801
|
+
arrive_by: '2025-06-05T10:30:00+02:00'
|
|
802
|
+
});
|
|
803
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
804
|
+
mockFetch.mockClear();
|
|
805
|
+
// Test with simple time format (no seconds, no timezone)
|
|
806
|
+
await new DirectionsTool().run({
|
|
807
|
+
coordinates: [
|
|
808
|
+
[-74.1, 40.7],
|
|
809
|
+
[-74.2, 40.8]
|
|
810
|
+
],
|
|
811
|
+
routing_profile: 'driving',
|
|
812
|
+
arrive_by: '2025-06-05T10:30'
|
|
813
|
+
});
|
|
814
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
815
|
+
});
|
|
816
|
+
it('rejects invalid formats for arrive_by', async () => {
|
|
817
|
+
const invalidFormats = [
|
|
818
|
+
// Invalid date formats
|
|
819
|
+
'2025/06/05T10:30:00Z',
|
|
820
|
+
'5-6-2025T10:30:00Z',
|
|
821
|
+
'2025-6-5T10:30:00Z',
|
|
822
|
+
// Invalid time formats
|
|
823
|
+
'2025-06-05T1:30:00Z',
|
|
824
|
+
'2025-06-05T10-30-00Z',
|
|
825
|
+
// Missing T separator
|
|
826
|
+
'2025-06-05 10:30:00Z',
|
|
827
|
+
// Completely wrong formats
|
|
828
|
+
'10:30 June 5, 2025',
|
|
829
|
+
'June 5, 2025 10:30 AM',
|
|
830
|
+
'Tomorrow at 10:30'
|
|
831
|
+
];
|
|
832
|
+
for (const format of invalidFormats) {
|
|
833
|
+
const result = await new DirectionsTool().run({
|
|
834
|
+
coordinates: [
|
|
835
|
+
[-74.1, 40.7],
|
|
836
|
+
[-74.2, 40.8]
|
|
837
|
+
],
|
|
838
|
+
routing_profile: 'driving',
|
|
839
|
+
arrive_by: format
|
|
840
|
+
});
|
|
841
|
+
expect(result.is_error).toBe(true);
|
|
842
|
+
}
|
|
843
|
+
});
|
|
844
|
+
it('validates date and time component ranges for arrive_by', async () => {
|
|
845
|
+
const invalidDates = [
|
|
846
|
+
'2025-13-05T10:30:00Z', // Invalid month (13)
|
|
847
|
+
'2025-06-32T10:30:00Z', // Invalid day (32)
|
|
848
|
+
'2025-02-30T10:30:00Z', // Invalid day in February
|
|
849
|
+
'2025-06-05T24:30:00Z', // Invalid hour (24)
|
|
850
|
+
'2025-06-05T10:60:00Z', // Invalid minute (60)
|
|
851
|
+
'2025-06-05T10:30:60Z' // Invalid second (60)
|
|
852
|
+
];
|
|
853
|
+
for (const date of invalidDates) {
|
|
854
|
+
const result = await new DirectionsTool().run({
|
|
855
|
+
coordinates: [
|
|
856
|
+
[-74.1, 40.7],
|
|
857
|
+
[-74.2, 40.8]
|
|
858
|
+
],
|
|
859
|
+
routing_profile: 'driving',
|
|
860
|
+
arrive_by: date
|
|
861
|
+
});
|
|
862
|
+
expect(result.is_error).toBe(true);
|
|
863
|
+
}
|
|
864
|
+
});
|
|
865
|
+
});
|
|
866
|
+
});
|
|
867
|
+
//# sourceMappingURL=DirectionsTool.test.js.map
|