directus

Форк
0
/
sanitize-query.test.ts 
395 строк · 10.3 Кб
1
import { useEnv } from '@directus/env';
2
import { parseFilter, parseJSON } from '@directus/utils';
3
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
4
import { sanitizeQuery } from './sanitize-query.js';
5

6
// This is required because logger uses global env which is imported before the tests run. Can be
7
// reduce to just mock the file when logger is also using useLogger everywhere @TODO
8
vi.mock('@directus/env', () => ({ useEnv: vi.fn().mockReturnValue({}) }));
9

10
vi.mock('@directus/utils', async () => {
11
	const actual = await vi.importActual<typeof import('@directus/utils')>('@directus/utils');
12

13
	return {
14
		...actual,
15
		parseJSON: vi.fn().mockImplementation(actual.parseJSON),
16
		parseFilter: vi.fn().mockImplementation((value) => value),
17
	};
18
});
19

20
beforeEach(() => {
21
	vi.mocked(useEnv).mockReturnValue({});
22
});
23

24
afterEach(() => {
25
	vi.clearAllMocks();
26
});
27

28
describe('limit', () => {
29
	test.each([-1, 0, 100])('should accept number %i', (limit) => {
30
		const sanitizedQuery = sanitizeQuery({ limit });
31

32
		expect(sanitizedQuery.limit).toBe(limit);
33
	});
34

35
	test('should accept string 1', () => {
36
		const limit = '1';
37

38
		const sanitizedQuery = sanitizeQuery({ limit });
39

40
		expect(sanitizedQuery.limit).toBe(1);
41
	});
42
});
43

44
describe('max limit', () => {
45
	test('should replace -1', () => {
46
		vi.mocked(useEnv).mockReturnValue({ QUERY_LIMIT_MAX: 100 });
47

48
		const sanitizedQuery = sanitizeQuery({ limit: -1 });
49

50
		expect(sanitizedQuery.limit).toBe(100);
51
	});
52

53
	test.each([1, 25, 150])('should accept number %i', (limit) => {
54
		vi.mocked(useEnv).mockReturnValue({ QUERY_LIMIT_MAX: 100 });
55

56
		const sanitizedQuery = sanitizeQuery({ limit });
57

58
		expect(sanitizedQuery.limit).toBe(limit);
59
	});
60

61
	test('should apply max if no limit passed in request', () => {
62
		vi.mocked(useEnv).mockReturnValue({ QUERY_LIMIT_DEFAULT: 100, QUERY_LIMIT_MAX: 1000 });
63

64
		const sanitizedQuery = sanitizeQuery({});
65

66
		expect(sanitizedQuery.limit).toBe(100);
67
	});
68

69
	test('should apply lower value if no limit passed in request', () => {
70
		vi.mocked(useEnv).mockReturnValue({ QUERY_LIMIT_MAX: 100, QUERY_LIMIT_DEFAULT: 25 });
71

72
		const sanitizedQuery = sanitizeQuery({});
73

74
		expect(sanitizedQuery.limit).toBe(25);
75
	});
76

77
	test('should apply limit from request if no max defined', () => {
78
		const sanitizedQuery = sanitizeQuery({ limit: 150 });
79

80
		expect(sanitizedQuery.limit).toBe(150);
81
	});
82

83
	test('should apply limit from request if max is unlimited', () => {
84
		vi.mocked(useEnv).mockReturnValue({ QUERY_LIMIT_MAX: -1 });
85

86
		const sanitizedQuery = sanitizeQuery({ limit: 150 });
87

88
		expect(sanitizedQuery.limit).toBe(150);
89
	});
90
});
91

92
describe('fields', () => {
93
	test('should accept valid value', () => {
94
		const fields = ['field_a', 'field_b'];
95

96
		const sanitizedQuery = sanitizeQuery({ fields });
97

98
		expect(sanitizedQuery.fields).toEqual(['field_a', 'field_b']);
99
	});
100

101
	test('should split as csv when it is a string', () => {
102
		const fields = 'field_a,field_b';
103

104
		const sanitizedQuery = sanitizeQuery({ fields });
105

106
		expect(sanitizedQuery.fields).toEqual(['field_a', 'field_b']);
107
	});
108

109
	test('should split as nested csv when it is an array', () => {
110
		const fields = ['field_a,field_b', 'field_c'];
111

112
		const sanitizedQuery = sanitizeQuery({ fields });
113

114
		expect(sanitizedQuery.fields).toEqual(['field_a', 'field_b', 'field_c']);
115
	});
116

117
	test('should trim', () => {
118
		const fields = ['   field_a   '];
119

120
		const sanitizedQuery = sanitizeQuery({ fields });
121

122
		expect(sanitizedQuery.fields).toEqual(['field_a']);
123
	});
124
});
125

126
describe('group', () => {
127
	test('should accept valid value', () => {
128
		const groupBy = ['group_a', 'group_b'];
129

130
		const sanitizedQuery = sanitizeQuery({ groupBy });
131

132
		expect(sanitizedQuery.group).toEqual(['group_a', 'group_b']);
133
	});
134

135
	test('should split as csv when it is a string', () => {
136
		const groupBy = 'group_a,group_b';
137

138
		const sanitizedQuery = sanitizeQuery({ groupBy });
139

140
		expect(sanitizedQuery.group).toEqual(['group_a', 'group_b']);
141
	});
142

143
	test('should split as nested csv when it is an array', () => {
144
		const groupBy = ['group_a,group_b', 'group_c'];
145

146
		const sanitizedQuery = sanitizeQuery({ groupBy });
147

148
		expect(sanitizedQuery.group).toEqual(['group_a', 'group_b', 'group_c']);
149
	});
150

151
	test('should trim', () => {
152
		const groupBy = ['   group_a   '];
153

154
		const sanitizedQuery = sanitizeQuery({ groupBy });
155

156
		expect(sanitizedQuery.group).toEqual(['group_a']);
157
	});
158
});
159

160
describe('aggregate', () => {
161
	test('should accept valid value', () => {
162
		const aggregate = { count: '*' };
163

164
		const sanitizedQuery = sanitizeQuery({ aggregate });
165

166
		expect(sanitizedQuery.aggregate).toEqual({ count: ['*'] });
167
	});
168

169
	test('should parse as json when it is a string', () => {
170
		const aggregate = '{ "count": "*" }';
171

172
		const sanitizedQuery = sanitizeQuery({ aggregate });
173

174
		expect(sanitizedQuery.aggregate).toEqual({ count: ['*'] });
175
	});
176
});
177

178
describe('sort', () => {
179
	test('should accept valid value', () => {
180
		const sort = ['field_a', 'field_b'];
181

182
		const sanitizedQuery = sanitizeQuery({ sort });
183

184
		expect(sanitizedQuery.sort).toEqual(['field_a', 'field_b']);
185
	});
186

187
	test('should split as csv when it is a string', () => {
188
		const sort = 'field_a,field_b';
189

190
		const sanitizedQuery = sanitizeQuery({ sort });
191

192
		expect(sanitizedQuery.sort).toEqual(['field_a', 'field_b']);
193
	});
194

195
	test('should trim csv array results', () => {
196
		const sort = 'field_a, field_b';
197

198
		const sanitizedQuery = sanitizeQuery({ sort });
199

200
		expect(sanitizedQuery.sort).toEqual(['field_a', 'field_b']);
201
	});
202
});
203

204
describe('filter', () => {
205
	test('should accept valid filter', () => {
206
		const filter = { field_a: { _eq: 'test' } };
207

208
		const sanitizedQuery = sanitizeQuery({ filter });
209

210
		expect(sanitizedQuery.filter).toEqual({ field_a: { _eq: 'test' } });
211
	});
212

213
	test('should throw error on invalid filter', () => {
214
		const filter = { field_a: null };
215

216
		vi.mocked(parseFilter).mockImplementationOnce(() => {
217
			throw new Error();
218
		});
219

220
		expect(() => sanitizeQuery({ filter })).toThrowError('Invalid query. Invalid filter object.');
221
	});
222

223
	test('should parse as json when it is a string', () => {
224
		const filter = '{ "field_a": { "_eq": "test" } }';
225

226
		const sanitizedQuery = sanitizeQuery({ filter });
227

228
		expect(sanitizedQuery.filter).toEqual({ field_a: { _eq: 'test' } });
229
	});
230

231
	test('should throw error on invalid json', () => {
232
		const filter = '{ "field_a": }';
233

234
		vi.mocked(parseJSON).mockImplementationOnce(() => {
235
			throw new Error();
236
		});
237

238
		expect(() => sanitizeQuery({ filter })).toThrowError('Invalid query. Invalid JSON for filter object.');
239
	});
240
});
241

242
describe('offset', () => {
243
	test('should accept number 1', () => {
244
		const offset = 1;
245

246
		const sanitizedQuery = sanitizeQuery({ offset });
247

248
		expect(sanitizedQuery.offset).toBe(1);
249
	});
250

251
	test('should accept string 1', () => {
252
		const offset = '1';
253

254
		const sanitizedQuery = sanitizeQuery({ offset });
255

256
		expect(sanitizedQuery.offset).toBe(1);
257
	});
258

259
	test('should accept zero #18370', () => {
260
		const offset = 0;
261

262
		const sanitizedQuery = sanitizeQuery({ offset });
263

264
		expect(sanitizedQuery.offset).toBe(0);
265
	});
266

267
	test('should accept string zero #18370', () => {
268
		const offset = '0';
269

270
		const sanitizedQuery = sanitizeQuery({ offset });
271

272
		expect(sanitizedQuery.offset).toBe(0);
273
	});
274
});
275

276
describe('page', () => {
277
	test('should accept number 1', () => {
278
		const page = 1;
279

280
		const sanitizedQuery = sanitizeQuery({ page });
281

282
		expect(sanitizedQuery.page).toBe(1);
283
	});
284

285
	test('should accept string 1', () => {
286
		const page = '1';
287

288
		const sanitizedQuery = sanitizeQuery({ page });
289

290
		expect(sanitizedQuery.page).toBe(1);
291
	});
292

293
	test('should ignore zero', () => {
294
		const page = 0;
295

296
		const sanitizedQuery = sanitizeQuery({ page });
297

298
		expect(sanitizedQuery.page).toBeUndefined();
299
	});
300
});
301

302
describe('meta', () => {
303
	test.each([
304
		{ input: '*', expected: ['total_count', 'filter_count'] },
305
		{ input: 'total_count', expected: ['total_count'] },
306
		{ input: 'total_count,filter_count', expected: ['total_count', 'filter_count'] },
307
		{ input: ['total_count', 'filter_count'], expected: ['total_count', 'filter_count'] },
308
	])('should accept $input', ({ input, expected }) => {
309
		const sanitizedQuery = sanitizeQuery({ meta: input }) as any;
310

311
		expect(sanitizedQuery.meta).toEqual(expected);
312
	});
313
});
314

315
describe('search', () => {
316
	test('should accept valid value', () => {
317
		const search = 'test';
318

319
		const sanitizedQuery = sanitizeQuery({ search });
320

321
		expect(sanitizedQuery.search).toBe('test');
322
	});
323

324
	test('should ignore non-string', () => {
325
		const search = ['test'];
326

327
		const sanitizedQuery = sanitizeQuery({ search });
328

329
		expect(sanitizedQuery.search).toBeUndefined();
330
	});
331
});
332

333
describe('export', () => {
334
	test('should accept valid value', () => {
335
		const format = 'json';
336

337
		const sanitizedQuery = sanitizeQuery({ export: format });
338

339
		expect(sanitizedQuery.export).toBe('json');
340
	});
341
});
342

343
describe('deep', () => {
344
	test('should accept valid value', () => {
345
		const deep = { deep: { relational_field: { _sort: ['name'] } } };
346

347
		const sanitizedQuery = sanitizeQuery({ deep });
348

349
		expect(sanitizedQuery.deep).toEqual({ deep: { relational_field: { _sort: ['name'] } } });
350
	});
351

352
	test('should parse as json when it is a string', () => {
353
		const deep = { deep: { relational_field: { _sort: ['name'] } } };
354

355
		const sanitizedQuery = sanitizeQuery({ deep });
356

357
		expect(sanitizedQuery.deep).toEqual({ deep: { relational_field: { _sort: ['name'] } } });
358
	});
359

360
	test('should ignore non-underscore-prefixed queries', () => {
361
		const deep = { deep: { relational_field_a: { _sort: ['name'] }, relational_field_b: { sort: ['name'] } } };
362

363
		const sanitizedQuery = sanitizeQuery({ deep });
364

365
		expect(sanitizedQuery.deep).toEqual({ deep: { relational_field_a: { _sort: ['name'] } } });
366
	});
367

368
	test('should work in combination with query limit', () => {
369
		vi.mocked(useEnv).mockReturnValue({ QUERY_LIMIT_DEFAULT: 100, QUERY_LIMIT_MAX: 1000 });
370

371
		const deep = { deep: { relational_field_a: { _sort: ['name'] } } };
372

373
		const sanitizedQuery = sanitizeQuery({ deep });
374

375
		expect(sanitizedQuery.deep).toEqual({ deep: { relational_field_a: { _limit: 100, _sort: ['name'] } } });
376
	});
377
});
378

379
describe('alias', () => {
380
	test('should accept valid value', () => {
381
		const alias = { field_a: 'testField' };
382

383
		const sanitizedQuery = sanitizeQuery({ alias });
384

385
		expect(sanitizedQuery.alias).toEqual({ field_a: 'testField' });
386
	});
387

388
	test('should parse as json when it is a string', () => {
389
		const alias = '{ "field_a": "testField" }';
390

391
		const sanitizedQuery = sanitizeQuery({ alias });
392

393
		expect(sanitizedQuery.alias).toEqual({ field_a: 'testField' });
394
	});
395
});
396

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.