@r5v/mongoose-paginate 1.0.13 → 1.0.15

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.
Files changed (104) hide show
  1. package/README.md +210 -17
  2. package/dist/{types/aggregationPagingQuery.d.ts → aggregationPagingQuery.d.ts} +6 -7
  3. package/dist/aggregationPagingQuery.d.ts.map +1 -0
  4. package/dist/aggregationPagingQuery.js +19 -4
  5. package/dist/index.d.ts +9 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +18 -1
  8. package/dist/{types/pagingQuery.d.ts → pagingQuery.d.ts} +6 -7
  9. package/dist/pagingQuery.d.ts.map +1 -0
  10. package/dist/pagingQuery.js +8 -3
  11. package/dist/tests/aggregationPagingQuery.spec.d.ts +2 -0
  12. package/dist/tests/aggregationPagingQuery.spec.d.ts.map +1 -0
  13. package/dist/tests/aggregationPagingQuery.spec.js +419 -0
  14. package/dist/tests/buildPopulateFromString.spec.d.ts +2 -0
  15. package/dist/tests/buildPopulateFromString.spec.d.ts.map +1 -0
  16. package/dist/tests/buildPopulateFromString.spec.js +223 -0
  17. package/dist/tests/dotNotation.spec.d.ts.map +1 -0
  18. package/dist/tests/findProtectedPaths.spec.d.ts.map +1 -0
  19. package/dist/tests/findProtectedPaths.spec.js +128 -6
  20. package/dist/tests/getPathsWithRef.spec.d.ts.map +1 -0
  21. package/dist/tests/getPathsWithRef.spec.js +92 -18
  22. package/dist/tests/getPropertyFromDotNotation.spec.d.ts.map +1 -0
  23. package/dist/tests/insertPopulate.spec.d.ts.map +1 -0
  24. package/dist/tests/isJsonString.spec.d.ts +2 -0
  25. package/dist/tests/isJsonString.spec.d.ts.map +1 -0
  26. package/dist/tests/isJsonString.spec.js +116 -0
  27. package/dist/tests/isValidDateString.spec.d.ts +2 -0
  28. package/dist/tests/isValidDateString.spec.d.ts.map +1 -0
  29. package/dist/tests/isValidDateString.spec.js +116 -0
  30. package/dist/tests/pagingQuery.spec.d.ts.map +1 -0
  31. package/dist/tests/pagingQuery.spec.js +238 -13
  32. package/dist/tests/parseParams.spec.d.ts +2 -0
  33. package/dist/tests/parseParams.spec.d.ts.map +1 -0
  34. package/dist/tests/parseParams.spec.js +212 -0
  35. package/dist/tests/parseSortString.spec.d.ts.map +1 -0
  36. package/dist/tests/schemaTraversal.spec.d.ts +2 -0
  37. package/dist/tests/schemaTraversal.spec.d.ts.map +1 -0
  38. package/dist/tests/schemaTraversal.spec.js +139 -0
  39. package/dist/types/index.d.ts +72 -3
  40. package/dist/types/index.d.ts.map +1 -1
  41. package/dist/utils/buildPopulateFromString.d.ts +8 -0
  42. package/dist/utils/buildPopulateFromString.d.ts.map +1 -0
  43. package/dist/utils/buildPopulateFromString.js +54 -49
  44. package/dist/utils/dotNotation.d.ts +11 -0
  45. package/dist/utils/dotNotation.d.ts.map +1 -0
  46. package/dist/utils/dotNotation.js +2 -2
  47. package/dist/utils/findKeyWithValue.d.ts.map +1 -0
  48. package/dist/utils/findProtectedPaths.d.ts.map +1 -0
  49. package/dist/utils/findProtectedPaths.js +14 -56
  50. package/dist/utils/getPathsWithRef.d.ts +5 -0
  51. package/dist/utils/getPathsWithRef.d.ts.map +1 -0
  52. package/dist/utils/getPathsWithRef.js +59 -96
  53. package/dist/utils/index.d.ts +9 -0
  54. package/dist/utils/index.d.ts.map +1 -0
  55. package/dist/utils/index.js +33 -0
  56. package/dist/utils/isJsonString.d.ts.map +1 -0
  57. package/dist/utils/isValidDateString.d.ts.map +1 -0
  58. package/dist/utils/parseParams.d.ts.map +1 -0
  59. package/dist/utils/parseParams.js +14 -12
  60. package/dist/utils/parsePopulateQuery.d.ts.map +1 -0
  61. package/dist/utils/parsePopulateQuery.js +1 -1
  62. package/dist/{types/utils → utils}/parseSortString.d.ts +1 -1
  63. package/dist/utils/parseSortString.d.ts.map +1 -0
  64. package/dist/utils/schemaTraversal.d.ts +9 -0
  65. package/dist/utils/schemaTraversal.d.ts.map +1 -0
  66. package/dist/utils/schemaTraversal.js +37 -0
  67. package/package.json +23 -2
  68. package/dist/types/aggregationPagingQuery.d.ts.map +0 -1
  69. package/dist/types/pagingQuery.d.ts.map +0 -1
  70. package/dist/types/tests/dotNotation.spec.d.ts.map +0 -1
  71. package/dist/types/tests/findProtectedPaths.spec.d.ts.map +0 -1
  72. package/dist/types/tests/getPathsWithRef.spec.d.ts.map +0 -1
  73. package/dist/types/tests/getPropertyFromDotNotation.spec.d.ts.map +0 -1
  74. package/dist/types/tests/insertPopulate.spec.d.ts.map +0 -1
  75. package/dist/types/tests/pagingQuery.spec.d.ts.map +0 -1
  76. package/dist/types/tests/parseSortString.spec.d.ts.map +0 -1
  77. package/dist/types/types/index.d.ts +0 -61
  78. package/dist/types/types/index.d.ts.map +0 -1
  79. package/dist/types/utils/buildPopulateFromString.d.ts +0 -8
  80. package/dist/types/utils/buildPopulateFromString.d.ts.map +0 -1
  81. package/dist/types/utils/dotNotation.d.ts +0 -11
  82. package/dist/types/utils/dotNotation.d.ts.map +0 -1
  83. package/dist/types/utils/findKeyWithValue.d.ts.map +0 -1
  84. package/dist/types/utils/findProtectedPaths.d.ts.map +0 -1
  85. package/dist/types/utils/getPathsWithRef.d.ts +0 -5
  86. package/dist/types/utils/getPathsWithRef.d.ts.map +0 -1
  87. package/dist/types/utils/isJsonString.d.ts.map +0 -1
  88. package/dist/types/utils/isValidDateString.d.ts.map +0 -1
  89. package/dist/types/utils/parseParams.d.ts.map +0 -1
  90. package/dist/types/utils/parsePopulateQuery.d.ts.map +0 -1
  91. package/dist/types/utils/parseSortString.d.ts.map +0 -1
  92. /package/dist/{types/tests → tests}/dotNotation.spec.d.ts +0 -0
  93. /package/dist/{types/tests → tests}/findProtectedPaths.spec.d.ts +0 -0
  94. /package/dist/{types/tests → tests}/getPathsWithRef.spec.d.ts +0 -0
  95. /package/dist/{types/tests → tests}/getPropertyFromDotNotation.spec.d.ts +0 -0
  96. /package/dist/{types/tests → tests}/insertPopulate.spec.d.ts +0 -0
  97. /package/dist/{types/tests → tests}/pagingQuery.spec.d.ts +0 -0
  98. /package/dist/{types/tests → tests}/parseSortString.spec.d.ts +0 -0
  99. /package/dist/{types/utils → utils}/findKeyWithValue.d.ts +0 -0
  100. /package/dist/{types/utils → utils}/findProtectedPaths.d.ts +0 -0
  101. /package/dist/{types/utils → utils}/isJsonString.d.ts +0 -0
  102. /package/dist/{types/utils → utils}/isValidDateString.d.ts +0 -0
  103. /package/dist/{types/utils → utils}/parseParams.d.ts +0 -0
  104. /package/dist/{types/utils → utils}/parsePopulateQuery.d.ts +0 -0
@@ -1,21 +1,246 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
2
44
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const mockingoose = require('mockingoose');
45
+ const pagingQuery_1 = require("../pagingQuery");
4
46
  const node_mocks_http_1 = require("node-mocks-http");
5
- const req = (0, node_mocks_http_1.createRequest)({ query: {
6
- $filter: '{"a":123}',
7
- $sort: 'name 1', '$populate': 'books'
8
- } });
47
+ const mockingoose = require('mockingoose');
48
+ const mongoose_1 = __importStar(require("mongoose"));
49
+ // Create a test schema and model
50
+ const TestSchema = new mongoose_1.Schema({
51
+ name: { type: String, required: true },
52
+ age: { type: Number },
53
+ email: { type: String },
54
+ active: { type: Boolean, default: true },
55
+ createdAt: { type: Date, default: Date.now }
56
+ });
57
+ const TestModel = mongoose_1.default.model('TestModel', TestSchema);
9
58
  describe('PagingQuery', () => {
10
59
  beforeEach(() => {
11
- // Reset mockingoose before each test to ensure isolation
12
- // mockingoose.resetAll();
60
+ mockingoose.resetAll();
61
+ });
62
+ describe('constructor', () => {
63
+ test('should throw error if request is invalid', () => {
64
+ expect(() => {
65
+ new pagingQuery_1.PagingQuery(null, TestModel);
66
+ }).toThrow('Invalid request object: must have a query property');
67
+ });
68
+ test('should throw error if request has no query property', () => {
69
+ expect(() => {
70
+ new pagingQuery_1.PagingQuery({}, TestModel);
71
+ }).toThrow('Invalid request object: must have a query property');
72
+ });
73
+ test('should throw error if model is invalid', () => {
74
+ const req = (0, node_mocks_http_1.createRequest)({ query: {} });
75
+ expect(() => {
76
+ new pagingQuery_1.PagingQuery(req, null);
77
+ }).toThrow('Invalid model: must be a Mongoose model');
78
+ });
79
+ test('should throw error if model does not have find function', () => {
80
+ const req = (0, node_mocks_http_1.createRequest)({ query: {} });
81
+ expect(() => {
82
+ new pagingQuery_1.PagingQuery(req, {});
83
+ }).toThrow('Invalid model: must be a Mongoose model');
84
+ });
85
+ test('should create instance with default params', () => {
86
+ const req = (0, node_mocks_http_1.createRequest)({ query: {} });
87
+ const pq = new pagingQuery_1.PagingQuery(req, TestModel);
88
+ expect(pq.params.$limit).toBe(25);
89
+ expect(pq.params.$skip).toBe(0);
90
+ expect(pq.params.$paging).toBe(true);
91
+ expect(pq.params.$filter).toEqual({});
92
+ expect(pq.params.$sort).toEqual([]);
93
+ expect(pq.params.$populate).toEqual([]);
94
+ expect(pq.params.$select).toBe('');
95
+ expect(pq.params.$lean).toBe(false);
96
+ });
97
+ test('should parse filter from query string', () => {
98
+ const req = (0, node_mocks_http_1.createRequest)({
99
+ query: { $filter: '{"name":"John"}' }
100
+ });
101
+ const pq = new pagingQuery_1.PagingQuery(req, TestModel);
102
+ expect(pq.params.$filter).toEqual({ name: 'John' });
103
+ });
104
+ test('should parse limit and skip', () => {
105
+ const req = (0, node_mocks_http_1.createRequest)({
106
+ query: { $limit: '50', $skip: '10' }
107
+ });
108
+ const pq = new pagingQuery_1.PagingQuery(req, TestModel);
109
+ expect(pq.params.$limit).toBe(50);
110
+ expect(pq.params.$skip).toBe(10);
111
+ });
112
+ test('should parse sort string', () => {
113
+ const req = (0, node_mocks_http_1.createRequest)({
114
+ query: { $sort: 'name 1, age -1' }
115
+ });
116
+ const pq = new pagingQuery_1.PagingQuery(req, TestModel);
117
+ expect(pq.params.$sort).toEqual([
118
+ ['name', 1],
119
+ ['age', -1]
120
+ ]);
121
+ });
122
+ test('should parse populate array', () => {
123
+ const req = (0, node_mocks_http_1.createRequest)({
124
+ query: { $populate: 'author, books' }
125
+ });
126
+ const pq = new pagingQuery_1.PagingQuery(req, TestModel);
127
+ expect(pq.params.$populate).toEqual(['author', 'books']);
128
+ });
129
+ test('should parse paging false', () => {
130
+ const req = (0, node_mocks_http_1.createRequest)({
131
+ query: { $paging: 'false' }
132
+ });
133
+ const pq = new pagingQuery_1.PagingQuery(req, TestModel);
134
+ expect(pq.params.$paging).toBe(false);
135
+ });
136
+ test('should parse paging no', () => {
137
+ const req = (0, node_mocks_http_1.createRequest)({
138
+ query: { $paging: 'no' }
139
+ });
140
+ const pq = new pagingQuery_1.PagingQuery(req, TestModel);
141
+ expect(pq.params.$paging).toBe(false);
142
+ });
143
+ test('should parse paging 0', () => {
144
+ const req = (0, node_mocks_http_1.createRequest)({
145
+ query: { $paging: '0' }
146
+ });
147
+ const pq = new pagingQuery_1.PagingQuery(req, TestModel);
148
+ expect(pq.params.$paging).toBe(false);
149
+ });
150
+ test('should parse select string', () => {
151
+ const req = (0, node_mocks_http_1.createRequest)({
152
+ query: { $select: 'name,age,email' }
153
+ });
154
+ const pq = new pagingQuery_1.PagingQuery(req, TestModel);
155
+ expect(pq.params.$select).toBe('name,age,email');
156
+ });
157
+ test('should parse lean option', () => {
158
+ const req = (0, node_mocks_http_1.createRequest)({
159
+ query: { $lean: 'true' }
160
+ });
161
+ const pq = new pagingQuery_1.PagingQuery(req, TestModel);
162
+ expect(pq.params.$lean).toBe(true);
163
+ });
164
+ });
165
+ describe('options', () => {
166
+ test('should disable paging when disablePaging option is set', () => {
167
+ const req = (0, node_mocks_http_1.createRequest)({ query: {} });
168
+ const pq = new pagingQuery_1.PagingQuery(req, TestModel, { disablePaging: true });
169
+ expect(pq.params.$paging).toBe(false);
170
+ });
171
+ test('should disable paging when single option is set', () => {
172
+ const req = (0, node_mocks_http_1.createRequest)({ query: {} });
173
+ const pq = new pagingQuery_1.PagingQuery(req, TestModel, { single: true });
174
+ expect(pq.params.$paging).toBe(false);
175
+ });
176
+ test('should apply static filter', () => {
177
+ const req = (0, node_mocks_http_1.createRequest)({
178
+ query: { $filter: '{"name":"John"}' }
179
+ });
180
+ const pq = new pagingQuery_1.PagingQuery(req, TestModel, {
181
+ staticFilter: { active: true }
182
+ });
183
+ expect(pq.params.$filter).toEqual({ name: 'John' });
184
+ expect(pq.options.staticFilter).toEqual({ active: true });
185
+ });
186
+ test('should disable filter when disableFilter option is set', () => {
187
+ const req = (0, node_mocks_http_1.createRequest)({
188
+ query: { $filter: '{"name":"John"}' }
189
+ });
190
+ const pq = new pagingQuery_1.PagingQuery(req, TestModel, {
191
+ disableFilter: true,
192
+ staticFilter: { active: true }
193
+ });
194
+ expect(pq.options.disableFilter).toBe(true);
195
+ });
196
+ });
197
+ describe('exec', () => {
198
+ test('should execute query without paging and return results', () => __awaiter(void 0, void 0, void 0, function* () {
199
+ const mockData = [{ name: 'John', age: 30 }, { name: 'Jane', age: 25 }];
200
+ mockingoose(TestModel).toReturn(mockData, 'find');
201
+ const req = (0, node_mocks_http_1.createRequest)({
202
+ query: { $paging: 'false' }
203
+ });
204
+ const pq = new pagingQuery_1.PagingQuery(req, TestModel);
205
+ const result = yield pq.exec();
206
+ // mockingoose adds additional fields like _id, createdAt, active
207
+ expect(result).toHaveLength(2);
208
+ expect(result[0]).toMatchObject({ name: 'John', age: 30 });
209
+ expect(result[1]).toMatchObject({ name: 'Jane', age: 25 });
210
+ }));
211
+ test('should execute query with paging and return paginated results', () => __awaiter(void 0, void 0, void 0, function* () {
212
+ const mockData = [{ name: 'John', age: 30 }, { name: 'Jane', age: 25 }];
213
+ mockingoose(TestModel).toReturn(mockData, 'find');
214
+ mockingoose(TestModel).toReturn(100, 'countDocuments');
215
+ const req = (0, node_mocks_http_1.createRequest)({ query: {} });
216
+ const pq = new pagingQuery_1.PagingQuery(req, TestModel);
217
+ const result = yield pq.exec();
218
+ expect(result).toHaveProperty('items');
219
+ expect(result).toHaveProperty('totalRows');
220
+ expect(result).toHaveProperty('rows');
221
+ expect(result).toHaveProperty('limit');
222
+ expect(result).toHaveProperty('skip');
223
+ expect(result.limit).toBe(25);
224
+ expect(result.skip).toBe(0);
225
+ }));
226
+ test('should execute single query (findOne)', () => __awaiter(void 0, void 0, void 0, function* () {
227
+ const mockData = { name: 'John', age: 30 };
228
+ mockingoose(TestModel).toReturn(mockData, 'findOne');
229
+ const req = (0, node_mocks_http_1.createRequest)({ query: {} });
230
+ const pq = new pagingQuery_1.PagingQuery(req, TestModel, { single: true });
231
+ const result = yield pq.exec();
232
+ // mockingoose adds additional fields like _id, createdAt, active
233
+ expect(result).toMatchObject({ name: 'John', age: 30 });
234
+ }));
13
235
  });
14
- test('should have standard params on new', () => {
15
- /*
16
- mockingoose(Author)
17
- const obj = new PagingQuery(req, Author, {sanitizeFilter: true})
18
-
19
- assert.equal(obj.params.$limit,25)*/
236
+ describe('error handling', () => {
237
+ test('should throw error for invalid JSON in filter', () => {
238
+ const req = (0, node_mocks_http_1.createRequest)({
239
+ query: { $filter: 'invalid json' }
240
+ });
241
+ expect(() => {
242
+ new pagingQuery_1.PagingQuery(req, TestModel);
243
+ }).toThrow('Invalid JSON in $filter parameter');
244
+ });
20
245
  });
21
246
  });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=parseParams.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseParams.spec.d.ts","sourceRoot":"","sources":["../../src/tests/parseParams.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,212 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const parseParams_1 = require("../utils/parseParams");
4
+ describe('parseParams', () => {
5
+ const defaultPagingParams = {
6
+ $filter: {},
7
+ $limit: 25,
8
+ $skip: 0,
9
+ $sort: [],
10
+ $paging: true,
11
+ $populate: [],
12
+ $select: '',
13
+ $lean: false
14
+ };
15
+ const defaultAggregateParams = {
16
+ $filter: {},
17
+ $limit: 25,
18
+ $skip: 0,
19
+ $sort: {},
20
+ $paging: true,
21
+ $populate: [],
22
+ $select: '',
23
+ $count: [],
24
+ $postFilter: {},
25
+ $preSort: {}
26
+ };
27
+ describe('basic parsing', () => {
28
+ test('should return empty object for null params', () => {
29
+ const result = (0, parseParams_1.parseParams)(defaultPagingParams, null);
30
+ expect(result).toEqual({});
31
+ });
32
+ test('should return default params when no matching keys', () => {
33
+ const result = (0, parseParams_1.parseParams)(defaultPagingParams, { unknownKey: 'value' });
34
+ expect(result.$limit).toBe(25);
35
+ expect(result.$skip).toBe(0);
36
+ });
37
+ });
38
+ describe('$filter parsing', () => {
39
+ test('should parse JSON filter string', () => {
40
+ const result = (0, parseParams_1.parseParams)(defaultPagingParams, { $filter: '{"name":"John"}' });
41
+ expect(result.$filter).toEqual({ name: 'John' });
42
+ });
43
+ test('should handle complex filter object', () => {
44
+ const filter = '{"$and":[{"age":{"$gt":18}},{"active":true}]}';
45
+ const result = (0, parseParams_1.parseParams)(defaultPagingParams, { $filter: filter });
46
+ expect(result.$filter).toEqual({
47
+ $and: [{ age: { $gt: 18 } }, { active: true }]
48
+ });
49
+ });
50
+ test('should throw error for invalid JSON filter', () => {
51
+ expect(() => {
52
+ (0, parseParams_1.parseParams)(defaultPagingParams, { $filter: 'invalid json' });
53
+ }).toThrow('Invalid JSON in $filter parameter');
54
+ });
55
+ test('should pass through object filter', () => {
56
+ const result = (0, parseParams_1.parseParams)(defaultPagingParams, { $filter: { name: 'John' } });
57
+ expect(result.$filter).toEqual({ name: 'John' });
58
+ });
59
+ });
60
+ describe('$postFilter parsing', () => {
61
+ test('should parse JSON postFilter string', () => {
62
+ const result = (0, parseParams_1.parseParams)(defaultAggregateParams, { $postFilter: '{"status":"active"}' }, true);
63
+ expect(result.$postFilter).toEqual({ status: 'active' });
64
+ });
65
+ test('should throw error for invalid JSON postFilter', () => {
66
+ expect(() => {
67
+ (0, parseParams_1.parseParams)(defaultAggregateParams, { $postFilter: 'invalid' }, true);
68
+ }).toThrow('Invalid JSON in $postFilter parameter');
69
+ });
70
+ });
71
+ describe('$limit and $skip parsing', () => {
72
+ test('should parse limit as integer', () => {
73
+ const result = (0, parseParams_1.parseParams)(defaultPagingParams, { $limit: '50' });
74
+ expect(result.$limit).toBe(50);
75
+ });
76
+ test('should parse skip as integer', () => {
77
+ const result = (0, parseParams_1.parseParams)(defaultPagingParams, { $skip: '100' });
78
+ expect(result.$skip).toBe(100);
79
+ });
80
+ test('should handle string numbers', () => {
81
+ const result = (0, parseParams_1.parseParams)(defaultPagingParams, { $limit: '10', $skip: '20' });
82
+ expect(result.$limit).toBe(10);
83
+ expect(result.$skip).toBe(20);
84
+ });
85
+ test('should handle NaN values', () => {
86
+ const result = (0, parseParams_1.parseParams)(defaultPagingParams, { $limit: 'abc' });
87
+ expect(Number.isNaN(result.$limit)).toBe(true);
88
+ });
89
+ });
90
+ describe('$paging parsing', () => {
91
+ test('should set paging false for "false"', () => {
92
+ const result = (0, parseParams_1.parseParams)(defaultPagingParams, { $paging: 'false' });
93
+ expect(result.$paging).toBe(false);
94
+ });
95
+ test('should set paging false for "no"', () => {
96
+ const result = (0, parseParams_1.parseParams)(defaultPagingParams, { $paging: 'no' });
97
+ expect(result.$paging).toBe(false);
98
+ });
99
+ test('should set paging false for "0"', () => {
100
+ const result = (0, parseParams_1.parseParams)(defaultPagingParams, { $paging: '0' });
101
+ expect(result.$paging).toBe(false);
102
+ });
103
+ test('should set paging true for "true"', () => {
104
+ const result = (0, parseParams_1.parseParams)(defaultPagingParams, { $paging: 'true' });
105
+ expect(result.$paging).toBe(true);
106
+ });
107
+ test('should set paging true for "yes"', () => {
108
+ const result = (0, parseParams_1.parseParams)(defaultPagingParams, { $paging: 'yes' });
109
+ expect(result.$paging).toBe(true);
110
+ });
111
+ test('should be case insensitive', () => {
112
+ const result = (0, parseParams_1.parseParams)(defaultPagingParams, { $paging: 'FALSE' });
113
+ expect(result.$paging).toBe(false);
114
+ });
115
+ });
116
+ describe('$sort parsing', () => {
117
+ test('should parse sort string for paging query', () => {
118
+ const result = (0, parseParams_1.parseParams)(defaultPagingParams, { $sort: 'name 1' });
119
+ expect(result.$sort).toEqual([['name', 1]]);
120
+ });
121
+ test('should parse multiple sort fields', () => {
122
+ const result = (0, parseParams_1.parseParams)(defaultPagingParams, { $sort: 'name 1, age -1' });
123
+ expect(result.$sort).toEqual([['name', 1], ['age', -1]]);
124
+ });
125
+ test('should parse sort string for aggregate query', () => {
126
+ const result = (0, parseParams_1.parseParams)(defaultAggregateParams, { $sort: 'name 1' }, true);
127
+ expect(result.$sort).toEqual({ name: 1 });
128
+ });
129
+ test('should parse preSort for aggregate query', () => {
130
+ const result = (0, parseParams_1.parseParams)(defaultAggregateParams, { $preSort: 'createdAt -1' }, true);
131
+ expect(result.$preSort).toEqual({ createdAt: -1 });
132
+ });
133
+ });
134
+ describe('$count and $populate parsing', () => {
135
+ test('should parse count as array', () => {
136
+ const result = (0, parseParams_1.parseParams)(defaultAggregateParams, { $count: 'items,orders' }, true);
137
+ expect(result.$count).toEqual(['items', 'orders']);
138
+ });
139
+ test('should parse populate as array', () => {
140
+ const result = (0, parseParams_1.parseParams)(defaultPagingParams, { $populate: 'author,books' });
141
+ expect(result.$populate).toEqual(['author', 'books']);
142
+ });
143
+ test('should trim whitespace from array items', () => {
144
+ const result = (0, parseParams_1.parseParams)(defaultPagingParams, { $populate: 'author , books , category' });
145
+ expect(result.$populate).toEqual(['author', 'books', 'category']);
146
+ });
147
+ });
148
+ describe('$select parsing', () => {
149
+ test('should parse select string', () => {
150
+ const result = (0, parseParams_1.parseParams)(defaultPagingParams, { $select: 'name,age,email' });
151
+ expect(result.$select).toBe('name,age,email');
152
+ });
153
+ test('should preserve select string as-is', () => {
154
+ const result = (0, parseParams_1.parseParams)(defaultPagingParams, { $select: '-password -secret' });
155
+ expect(result.$select).toBe('-password -secret');
156
+ });
157
+ });
158
+ describe('$lean parsing', () => {
159
+ test('should set lean to true when present', () => {
160
+ const result = (0, parseParams_1.parseParams)(defaultPagingParams, { $lean: 'true' });
161
+ expect(result.$lean).toBe(true);
162
+ });
163
+ test('should set lean to true for any value', () => {
164
+ const result = (0, parseParams_1.parseParams)(defaultPagingParams, { $lean: 'anything' });
165
+ expect(result.$lean).toBe(true);
166
+ });
167
+ });
168
+ describe('combined parsing', () => {
169
+ test('should parse all params together', () => {
170
+ const params = {
171
+ $filter: '{"active":true}',
172
+ $limit: '10',
173
+ $skip: '5',
174
+ $sort: 'name 1',
175
+ $paging: 'true',
176
+ $populate: 'author',
177
+ $select: 'name,email',
178
+ $lean: 'true'
179
+ };
180
+ const result = (0, parseParams_1.parseParams)(defaultPagingParams, params);
181
+ expect(result.$filter).toEqual({ active: true });
182
+ expect(result.$limit).toBe(10);
183
+ expect(result.$skip).toBe(5);
184
+ expect(result.$sort).toEqual([['name', 1]]);
185
+ expect(result.$paging).toBe(true);
186
+ expect(result.$populate).toEqual(['author']);
187
+ expect(result.$select).toBe('name,email');
188
+ expect(result.$lean).toBe(true);
189
+ });
190
+ test('should parse aggregate params together', () => {
191
+ const params = {
192
+ $filter: '{"status":"active"}',
193
+ $limit: '20',
194
+ $skip: '10',
195
+ $sort: 'createdAt -1',
196
+ $preSort: 'priority 1',
197
+ $postFilter: '{"verified":true}',
198
+ $count: 'items',
199
+ $select: 'name,status'
200
+ };
201
+ const result = (0, parseParams_1.parseParams)(defaultAggregateParams, params, true);
202
+ expect(result.$filter).toEqual({ status: 'active' });
203
+ expect(result.$limit).toBe(20);
204
+ expect(result.$skip).toBe(10);
205
+ expect(result.$sort).toEqual({ createdAt: -1 });
206
+ expect(result.$preSort).toEqual({ priority: 1 });
207
+ expect(result.$postFilter).toEqual({ verified: true });
208
+ expect(result.$count).toEqual(['items']);
209
+ expect(result.$select).toBe('name,status');
210
+ });
211
+ });
212
+ });
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseSortString.spec.d.ts","sourceRoot":"","sources":["../../src/tests/parseSortString.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=schemaTraversal.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemaTraversal.spec.d.ts","sourceRoot":"","sources":["../../src/tests/schemaTraversal.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const schemaTraversal_1 = require("../utils/schemaTraversal");
4
+ describe('schemaTraversal', () => {
5
+ describe('buildFullPath', () => {
6
+ test('should return child path when parent is empty', () => {
7
+ expect((0, schemaTraversal_1.buildFullPath)('', 'child')).toBe('child');
8
+ });
9
+ test('should return parent path when child is empty', () => {
10
+ expect((0, schemaTraversal_1.buildFullPath)('parent', '')).toBe('parent');
11
+ });
12
+ test('should combine parent and child with dot notation', () => {
13
+ expect((0, schemaTraversal_1.buildFullPath)('parent', 'child')).toBe('parent.child');
14
+ });
15
+ test('should handle deeply nested paths', () => {
16
+ expect((0, schemaTraversal_1.buildFullPath)('parent.nested', 'child')).toBe('parent.nested.child');
17
+ });
18
+ test('should handle empty strings for both', () => {
19
+ expect((0, schemaTraversal_1.buildFullPath)('', '')).toBe('');
20
+ });
21
+ });
22
+ describe('traverseSchemaObject', () => {
23
+ test('should call callback for current object', () => {
24
+ const obj = { path: 'test', options: { type: 'String' } };
25
+ const callback = jest.fn();
26
+ (0, schemaTraversal_1.traverseSchemaObject)(obj, callback);
27
+ expect(callback).toHaveBeenCalledWith(obj, '');
28
+ });
29
+ test('should handle null object', () => {
30
+ const callback = jest.fn();
31
+ (0, schemaTraversal_1.traverseSchemaObject)(null, callback);
32
+ expect(callback).not.toHaveBeenCalled();
33
+ });
34
+ test('should handle undefined object', () => {
35
+ const callback = jest.fn();
36
+ (0, schemaTraversal_1.traverseSchemaObject)(undefined, callback);
37
+ expect(callback).not.toHaveBeenCalled();
38
+ });
39
+ test('should handle non-object values', () => {
40
+ const callback = jest.fn();
41
+ (0, schemaTraversal_1.traverseSchemaObject)('string', callback);
42
+ (0, schemaTraversal_1.traverseSchemaObject)(123, callback);
43
+ (0, schemaTraversal_1.traverseSchemaObject)(true, callback);
44
+ expect(callback).not.toHaveBeenCalled();
45
+ });
46
+ test('should traverse nested paths in options.type.paths', () => {
47
+ const obj = {
48
+ path: 'parent',
49
+ options: {
50
+ type: {
51
+ paths: {
52
+ child1: { path: 'child1', selected: false },
53
+ child2: { path: 'child2', selected: true }
54
+ }
55
+ }
56
+ }
57
+ };
58
+ const visitedPaths = [];
59
+ const callback = jest.fn((obj, path) => {
60
+ if (obj.path)
61
+ visitedPaths.push(obj.path);
62
+ });
63
+ (0, schemaTraversal_1.traverseSchemaObject)(obj, callback);
64
+ expect(callback).toHaveBeenCalledTimes(3); // parent + 2 children
65
+ expect(visitedPaths).toContain('parent');
66
+ expect(visitedPaths).toContain('child1');
67
+ expect(visitedPaths).toContain('child2');
68
+ });
69
+ test('should traverse nested type object without paths', () => {
70
+ const obj = {
71
+ path: 'parent',
72
+ options: {
73
+ type: {
74
+ ref: 'SomeModel',
75
+ nested: { path: 'nested' }
76
+ }
77
+ }
78
+ };
79
+ const callback = jest.fn();
80
+ (0, schemaTraversal_1.traverseSchemaObject)(obj, callback);
81
+ // Should call for parent and nested type
82
+ expect(callback).toHaveBeenCalled();
83
+ });
84
+ test('should use provided currentPath', () => {
85
+ const obj = { path: 'test' };
86
+ const callback = jest.fn();
87
+ (0, schemaTraversal_1.traverseSchemaObject)(obj, callback, 'existingPath');
88
+ expect(callback).toHaveBeenCalledWith(obj, 'existingPath');
89
+ });
90
+ test('should use obj.path as parent path for nested traversal', () => {
91
+ const obj = {
92
+ path: 'parent',
93
+ options: {
94
+ type: {
95
+ paths: {
96
+ child: { path: 'child', selected: false }
97
+ }
98
+ }
99
+ }
100
+ };
101
+ const paths = [];
102
+ const callback = jest.fn((obj, path) => {
103
+ paths.push(path);
104
+ });
105
+ (0, schemaTraversal_1.traverseSchemaObject)(obj, callback);
106
+ // First call with empty path, second call with 'parent' as path
107
+ expect(paths).toContain('');
108
+ expect(paths).toContain('parent');
109
+ });
110
+ test('should handle object with no options', () => {
111
+ const obj = { path: 'simple' };
112
+ const callback = jest.fn();
113
+ (0, schemaTraversal_1.traverseSchemaObject)(obj, callback);
114
+ expect(callback).toHaveBeenCalledWith(obj, '');
115
+ expect(callback).toHaveBeenCalledTimes(1);
116
+ });
117
+ test('should handle object with options but no type', () => {
118
+ const obj = { path: 'test', options: { required: true } };
119
+ const callback = jest.fn();
120
+ (0, schemaTraversal_1.traverseSchemaObject)(obj, callback);
121
+ expect(callback).toHaveBeenCalledWith(obj, '');
122
+ expect(callback).toHaveBeenCalledTimes(1);
123
+ });
124
+ test('should handle empty paths object', () => {
125
+ const obj = {
126
+ path: 'parent',
127
+ options: {
128
+ type: {
129
+ paths: {}
130
+ }
131
+ }
132
+ };
133
+ const callback = jest.fn();
134
+ (0, schemaTraversal_1.traverseSchemaObject)(obj, callback);
135
+ // Should only call for parent since paths is empty
136
+ expect(callback).toHaveBeenCalledTimes(1);
137
+ });
138
+ });
139
+ });